## File: README.md

*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)).
Trusted by NASA for [Mars missions since 2020](https://github.com/readme/featured/nasa-ingenuity-helicopter)!

Its main goal is to help you to write **concise** and **correct** software without slowing down your code.


## Sponsors

*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek).
Especially those generously supporting us at the *The Organization* tier and higher:











## Example

*attrs* gives you a class decorator and a way to declaratively define the attributes on that class:



```pycon
>>> from attrs import asdict, define, make_class, Factory

>>> @define
... class SomeClass:
...     a_number: int = 42
...     list_of_numbers: list[int] = Factory(list)
...
...     def hard_math(self, another_number):
...         return self.a_number + sum(self.list_of_numbers) * another_number


>>> sc = SomeClass(1, [1, 2, 3])
>>> sc
SomeClass(a_number=1, list_of_numbers=[1, 2, 3])

>>> sc.hard_math(3)
19
>>> sc == SomeClass(1, [1, 2, 3])
True
>>> sc != SomeClass(2, [3, 2, 1])
True

>>> asdict(sc)
{'a_number': 1, 'list_of_numbers': [1, 2, 3]}

>>> SomeClass()
SomeClass(a_number=42, list_of_numbers=[])

>>> C = make_class("C", ["a", "b"])
>>> C("foo", "bar")
C(a='foo', b='bar')
```

After *declaring* your attributes, *attrs* gives you:

- a concise and explicit overview of the class's attributes,
- a nice human-readable `__repr__`,
- equality-checking methods,
- an initializer,
- and much more,

*without* writing dull boilerplate code again and again and *without* runtime performance penalties.

---

This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0.
The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**.

Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation!


### Hate Type Annotations!?

No problem!
Types are entirely **optional** with *attrs*.
Simply assign `attrs.field()` to the attributes instead of annotating them with types:

```python
from attrs import define, field


@define
class SomeClass:
    a_number = field(default=42)
    list_of_numbers = field(factory=list)
```


## Data Classes

On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*).
In practice it does a lot more and is more flexible.
For instance, it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), has a replacement for `__init_subclass__`, and allows for stepping through the generated methods using a debugger.

For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice.


## Project Information

- [**Changelog**](https://www.attrs.org/en/stable/changelog.html)
- [**Documentation**](https://www.attrs.org/)
- [**PyPI**](https://pypi.org/project/attrs/)
- [**Source Code**](https://github.com/python-attrs/attrs)
- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md)
- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs)
- **Get Help**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs)


### *attrs* for Enterprise

Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek).

The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications.
Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.

---

## File: docs/api.rst

API Reference
=============

.. module:: attrs

*attrs* works by decorating a class using `attrs.define` or `attr.s` and then defining attributes on the class using `attrs.field`, `attr.ib`, or type annotations.

What follows is the dry API explanation for people who understand how *attrs* works.
If you'd like a hands-on tutorial, have a look at `examples`.

If you're confused by the many names, please check out `names` for clarification, but the `TL;DR <https://en.wikipedia.org/wiki/TL;DR>`_ is that as of version 21.3.0, *attrs* consists of **two** top-level package names:

- The classic ``attr`` that powers the venerable `attr.s` and `attr.ib`.
- The newer ``attrs`` that only contains most modern APIs and relies on `attrs.define` and `attrs.field` to define your classes.
  Additionally, some of the APIs that also exist in ``attr`` have nicer defaults (for example, `attrs.asdict`).

The ``attrs`` namespace is built *on top of* ``attr`` -- which will *never* go away -- and is just as stable, since it doesn't constitute a rewrite.
To keep repetition low and this document at a reasonable size, the ``attr`` namespace is `documented on a separate page <api-attr>`.


Core
----

.. autofunction:: attrs.define

.. function:: mutable(same_as_define)

   Same as `attrs.define`.

   .. versionadded:: 20.1.0

.. function:: frozen(same_as_define)

   Behaves the same as `attrs.define` but sets *frozen=True* and *on_setattr=None*.

   .. versionadded:: 20.1.0

.. autofunction:: field

.. autoclass:: Attribute
   :members: evolve

   For example:

   .. doctest::

      >>> import attrs
      >>> from attrs import define, field

      >>> @define
      ... class C:
      ...     x = field()
      >>> attrs.fields(C).x
      Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='x')


.. autofunction:: make_class

   This is handy if you want to programmatically create classes.

   For example:

   .. doctest::

      >>> C1 = attrs.make_class("C1", ["x", "y"])
      >>> C1(1, 2)
      C1(x=1, y=2)
      >>> C2 = attrs.make_class("C2", {
      ...     "x": field(default=42),
      ...     "y": field(factory=list)
      ... })
      >>> C2()
      C2(x=42, y=[])


.. autoclass:: Factory

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(default=attrs.Factory(list))
      ...     y = field(default=attrs.Factory(
      ...         lambda self: set(self.x),
      ...         takes_self=True)
      ...     )
      >>> C()
      C(x=[], y=set())
      >>> C([1, 2, 3])
      C(x=[1, 2, 3], y={1, 2, 3})


.. autodata:: attrs.NOTHING
   :no-value:


Exceptions
----------

.. module:: attrs.exceptions

All exceptions are available from both ``attr.exceptions`` and ``attrs.exceptions`` and are the same thing.
That means that it doesn't matter from from which namespace they've been raised and/or caught:

.. doctest::

   >>> import attrs, attr
   >>> try:
   ...     raise attrs.exceptions.FrozenError()
   ... except attr.exceptions.FrozenError:
   ...     print("this works!")
   this works!

.. autoexception:: PythonTooOldError
.. autoexception:: FrozenError
.. autoexception:: FrozenInstanceError
.. autoexception:: FrozenAttributeError
.. autoexception:: AttrsAttributeNotFoundError
.. autoexception:: NotAnAttrsClassError
.. autoexception:: DefaultAlreadySetError
.. autoexception:: NotCallableError
.. autoexception:: UnannotatedAttributeError

   For example::

       @attr.s(auto_attribs=True)
       class C:
           x: int
           y = attr.ib()  # <- ERROR!


.. _helpers:

Helpers
-------

*attrs* comes with a bunch of helper methods that make working with it easier:

.. currentmodule:: attrs

.. autofunction:: attrs.cmp_using

.. autofunction:: attrs.fields

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field()
      ...     y = field()
      >>> attrs.fields(C)
      (Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='x'), Attribute(name='y', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='y'))
      >>> attrs.fields(C(1, 2)) is attrs.fields(C)
      True
      >>> attrs.fields(C)[1]
      Attribute(name='y', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='y')
      >>> attrs.fields(C).y is attrs.fields(C)[1]
      True

.. autofunction:: attrs.fields_dict

   For example:

   .. doctest::

      >>> @attr.s
      ... class C:
      ...     x = attr.ib()
      ...     y = attr.ib()
      >>> attrs.fields_dict(C)
      {'x': Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='x'), 'y': Attribute(name='y', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='y')}
      >>> attr.fields_dict(C)['y']
      Attribute(name='y', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='y')
      >>> attrs.fields_dict(C)['y'] is attrs.fields(C).y
      True

.. autofunction:: attrs.has

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     pass
      >>> attrs.has(C)
      True
      >>> attrs.has(object)
      False

.. autofunction:: attrs.inspect

   For example:

   .. doctest::

      >>> @define
      ... class CInspect:
      ...     pass
      >>> attrs.inspect(CInspect)  # doctest: +ELLIPSIS
      ClassProps(is_exception=False, is_slotted=True, has_weakref_slot=True, is_frozen=False, kw_only=<KeywordOnly.NO: 'no'>, collected_fields_by_mro=True, added_init=True, added_repr=True, added_eq=True, added_ordering=False, hashability=<Hashability.UNHASHABLE: 'unhashable'>, added_match_args=True, added_str=False, added_pickling=True, on_setattr_hook=<function pipe.<locals>.wrapped_pipe at ...>, field_transformer=None)

.. autoclass:: attrs.ClassProps
.. autoclass:: attrs.ClassProps.Hashability
   :members: HASHABLE, HASHABLE_CACHED, UNHASHABLE, LEAVE_ALONE
.. autoclass:: attrs.ClassProps.KeywordOnly
   :members: NO, YES, FORCE

.. autofunction:: attrs.resolve_types

    For example:

    .. doctest::

        >>> import typing
        >>> @define
        ... class A:
        ...     a: typing.List['A']
        ...     b: 'B'
        ...
        >>> @define
        ... class B:
        ...     a: A
        ...
        >>> attrs.fields(A).a.type
        typing.List[ForwardRef('A')]
        >>> attrs.fields(A).b.type
        'B'
        >>> attrs.resolve_types(A, globals(), locals())
        <class 'A'>
        >>> attrs.fields(A).a.type
        typing.List[A]
        >>> attrs.fields(A).b.type
        <class 'B'>

.. autofunction:: attrs.asdict

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x: int
      ...     y: int
      >>> attrs.asdict(C(1, C(2, 3)))
      {'x': 1, 'y': {'x': 2, 'y': 3}}

.. autofunction:: attrs.astuple

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field()
      ...     y = field()
      >>> attrs.astuple(C(1,2))
      (1, 2)

.. module:: attrs.filters

*attrs* includes helpers for filtering the attributes in `attrs.asdict` and `attrs.astuple`:

.. autofunction:: include

.. autofunction:: exclude

See :func:`attrs.asdict` for examples.

All objects from ``attrs.filters`` are also available from ``attr.filters`` (it's the same module in a different namespace).

----

.. currentmodule:: attrs

.. autofunction:: attrs.evolve

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x: int
      ...     y: int
      >>> i1 = C(1, 2)
      >>> i1
      C(x=1, y=2)
      >>> i2 = attrs.evolve(i1, y=3)
      >>> i2
      C(x=1, y=3)
      >>> i1 == i2
      False

   ``evolve`` creates a new instance using ``__init__``.
   This fact has several implications:

   * private attributes should be specified without the leading underscore, just like in ``__init__``.
   * attributes with ``init=False`` can't be set with ``evolve``.
   * the usual ``__init__`` validators will validate the new values.

.. autofunction:: attrs.validate

   For example:

   .. doctest::

      >>> @define(on_setattr=attrs.setters.NO_OP)
      ... class C:
      ...     x = field(validator=attrs.validators.instance_of(int))
      >>> i = C(1)
      >>> i.x = "1"
      >>> attrs.validate(i)
      Traceback (most recent call last):
         ...
      TypeError: ("'x' must be <class 'int'> (got '1' that is a <class 'str'>).", ...)


.. _api-validators:

Validators
----------

.. module:: attrs.validators

*attrs* comes with some common validators in the ``attrs.validators`` module.
All objects from ``attrs.validators`` are also available from ``attr.validators`` (it's the same module in a different namespace).

.. autofunction:: attrs.validators.lt

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(validator=attrs.validators.lt(42))
      >>> C(41)
      C(x=41)
      >>> C(42)
      Traceback (most recent call last):
         ...
      ValueError: ("'x' must be < 42: 42")

.. autofunction:: attrs.validators.le

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(validator=attrs.validators.le(42))
      >>> C(42)
      C(x=42)
      >>> C(43)
      Traceback (most recent call last):
         ...
      ValueError: ("'x' must be <= 42: 43")

.. autofunction:: attrs.validators.ge

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = attrs.field(validator=attrs.validators.ge(42))
      >>> C(42)
      C(x=42)
      >>> C(41)
      Traceback (most recent call last):
         ...
      ValueError: ("'x' must be => 42: 41")

.. autofunction:: attrs.validators.gt

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(validator=attrs.validators.gt(42))
      >>> C(43)
      C(x=43)
      >>> C(42)
      Traceback (most recent call last):
         ...
      ValueError: ("'x' must be > 42: 42")

.. autofunction:: attrs.validators.ne

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(validator=attrs.validators.ne(42))
      >>> C(43)
      C(x=43)
      >>> C(42)
      Traceback (most recent call last):
         ...
      ValueError: ("'x' must be != 42: 42")

.. autofunction:: attrs.validators.max_len

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(validator=attrs.validators.max_len(4))
      >>> C("spam")
      C(x='spam')
      >>> C("bacon")
      Traceback (most recent call last):
         ...
      ValueError: ("Length of 'x' must be <= 4: 5")

.. autofunction:: attrs.validators.min_len

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(validator=attrs.validators.min_len(1))
      >>> C("bacon")
      C(x='bacon')
      >>> C("")
      Traceback (most recent call last):
         ...
      ValueError: ("Length of 'x' must be => 1: 0")

.. autofunction:: attrs.validators.instance_of

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(validator=attrs.validators.instance_of(int))
      >>> C(42)
      C(x=42)
      >>> C("42")
      Traceback (most recent call last):
         ...
      TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=>, type=None, kw_only=False), <type 'int'>, '42')
      >>> C(None)
      Traceback (most recent call last):
         ...
      TypeError: ("'x' must be <type 'int'> (got None that is a <type 'NoneType'>).", Attribute(name='x', default=NOTHING, validator=>, repr=True, cmp=True, hash=None, init=True, type=None, kw_only=False), <type 'int'>, None)

.. autofunction:: attrs.validators.in_

   For example:

   .. doctest::

      >>> import enum
      >>> class State(enum.Enum):
      ...     ON = "on"
      ...     OFF = "off"
      >>> @define
      ... class C:
      ...     state = field(validator=attrs.validators.in_(State))
      ...     val = field(validator=attrs.validators.in_([1, 2, 3]))
      >>> C(State.ON, 1)
      C(state=<State.ON: 'on'>, val=1)
      >>> C("On", 1)
      Traceback (most recent call last):
         ...
      ValueError: 'state' must be in <enum 'State'> (got 'On'), Attribute(name='state', default=NOTHING, validator=>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None), <enum 'State'>, 'on')
      >>> C(State.ON, 4)
      Traceback (most recent call last):
      ...
      ValueError: 'val' must be in [1, 2, 3] (got 4), Attribute(name='val', default=NOTHING, validator=, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None), [1, 2, 3], 4)

.. autofunction:: attrs.validators.and_

   For convenience, it's also possible to pass a list to `attrs.field`'s validator argument.

   Thus the following two statements are equivalent::

      x = field(validator=attrs.validators.and_(v1, v2, v3))
      x = field(validator=[v1, v2, v3])

.. autofunction:: attrs.validators.or_

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     val: int | list[int] = field(
      ...         validator=attrs.validators.or_(
      ...             attrs.validators.instance_of(int),
      ...             attrs.validators.deep_iterable(attrs.validators.instance_of(int)),
      ...         )
      ...     )
      >>> C(42)
      C(val=42)
      >>> C([1, 2, 3])
      C(val=[1, 2, 3])
      >>> C(val='42')
      Traceback (most recent call last):
         ...
      ValueError: None of (>, <deep_iterable validator for iterables of >>) satisfied for value '42'

.. autofunction:: attrs.validators.not_

   For example:

   .. doctest::

      >>> reserved_names = {"id", "time", "source"}
      >>> @define
      ... class Measurement:
      ...     tags = field(
      ...         validator=attrs.validators.deep_mapping(
      ...             key_validator=attrs.validators.not_(
      ...                 attrs.validators.in_(reserved_names),
      ...                 msg="reserved tag key",
      ...             ),
      ...             value_validator=attrs.validators.instance_of((str, int)),
      ...         )
      ...     )
      >>> Measurement(tags={"source": "universe"})
      Traceback (most recent call last):
         ...
      ValueError: ("reserved tag key", Attribute(name='tags', default=NOTHING, validator=<not_ validator wrapping , capturing (<class 'ValueError'>, <class 'TypeError'>)>, type=None, kw_only=False), , {'source_': 'universe'}, (<class 'ValueError'>, <class 'TypeError'>))
      >>> Measurement(tags={"source_": "universe"})
      Measurement(tags={'source_': 'universe'})


.. autofunction:: attrs.validators.optional

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(
      ...         validator=attrs.validators.optional(
      ...             attrs.validators.instance_of(int)
      ...         ))
      >>> C(42)
      C(x=42)
      >>> C("42")
      Traceback (most recent call last):
         ...
      TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=>, type=None, kw_only=False), <type 'int'>, '42')
      >>> C(None)
      C(x=None)


.. autofunction:: attrs.validators.is_callable

    For example:

    .. doctest::

        >>> @define
        ... class C:
        ...     x = field(validator=attrs.validators.is_callable())
        >>> C(isinstance)
        C(x=)
        >>> C("not a callable")
        Traceback (most recent call last):
            ...
        attr.exceptions.NotCallableError: 'x' must be callable (got 'not a callable' that is a <class 'str'>).


.. autofunction:: attrs.validators.matches_re

    For example:

    .. doctest::

        >>> @define
        ... class User:
        ...     email = field(validator=attrs.validators.matches_re(
        ...         r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"))
        >>> User(email="user@example.com")
        User(email='user@example.com')
        >>> User(email="user@example.com@test.com")
        Traceback (most recent call last):
            ...
        ValueError: ("'email' must match regex '(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\\\.[a-zA-Z0-9-.]+$)' ('user@example.com@test.com' doesn't)", Attribute(name='email', default=NOTHING, validator=<matches_re validator for pattern re.compile('(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$)')>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), re.compile('(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$)'), 'user@example.com@test.com')


.. autofunction:: attrs.validators.deep_iterable

    For example:

    .. doctest::

        >>> @define
        ... class C:
        ...     x = field(validator=attrs.validators.deep_iterable(
        ...             member_validator=attrs.validators.instance_of(int),
        ...             iterable_validator=attrs.validators.instance_of(list)
        ...     ))
        >>> C(x=[1, 2, 3])
        C(x=[1, 2, 3])
        >>> C(x=set([1, 2, 3]))
        Traceback (most recent call last):
            ...
        TypeError: ("'x' must be <class 'list'> (got {1, 2, 3} that is a <class 'set'>).", Attribute(name='x', default=NOTHING, validator=<deep_iterable validator for > iterables of >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), <class 'list'>, {1, 2, 3})
        >>> C(x=[1, 2, "3"])
        Traceback (most recent call last):
            ...
        TypeError: ("'x' must be <class 'int'> (got '3' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=<deep_iterable validator for > iterables of >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), <class 'int'>, '3')


.. autofunction:: attrs.validators.deep_mapping

    For example:

    .. doctest::

        >>> @define
        ... class C:
        ...     x = field(validator=attrs.validators.deep_mapping(
        ...             key_validator=attrs.validators.instance_of(str),
        ...             value_validator=attrs.validators.instance_of(int),
        ...             mapping_validator=attrs.validators.instance_of(dict)
        ...     ))
        >>> C(x={"a": 1, "b": 2})
        C(x={'a': 1, 'b': 2})
        >>> C(x=None)
        Traceback (most recent call last):
            ...
        TypeError: ("'x' must be <class 'dict'> (got None that is a <class 'NoneType'>).", Attribute(name='x', default=NOTHING, validator=<deep_mapping validator for objects mapping > to >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), <class 'dict'>, None)
        >>> C(x={"a": 1.0, "b": 2})
        Traceback (most recent call last):
            ...
        TypeError: ("'x' must be <class 'int'> (got 1.0 that is a <class 'float'>).", Attribute(name='x', default=NOTHING, validator=<deep_mapping validator for objects mapping > to >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), <class 'int'>, 1.0)
        >>> C(x={"a": 1, 7: 2})
        Traceback (most recent call last):
            ...
        TypeError: ("'x' must be <class 'str'> (got 7 that is a <class 'int'>).", Attribute(name='x', default=NOTHING, validator=<deep_mapping validator for objects mapping > to >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), <class 'str'>, 7)

Validators can be both globally and locally disabled:

.. autofunction:: attrs.validators.set_disabled

.. autofunction:: attrs.validators.get_disabled

.. autofunction:: attrs.validators.disabled


Converters
----------

.. autoclass:: attrs.Converter

   For example:

   .. doctest::

      >>> def complicated(value, self_, field):
      ...     return int(value) * self_.factor + field.metadata["offset"]
      >>> @define
      ... class C:
      ...     factor = 5  # not an *attrs* field
      ...     x = field(
      ...         metadata={"offset": 200},
      ...         converter=attrs.Converter(
      ...             complicated,
      ...             takes_self=True, takes_field=True
      ...     ))
      >>> C("42")
      C(x=410)


.. module:: attrs.converters

All objects from ``attrs.converters`` are also available from ``attr.converters`` (it's the same module in a different namespace).

.. autofunction:: attrs.converters.pipe

   For convenience, it's also possible to pass a list to `attrs.field` / `attr.ib`'s converter arguments.

   Thus the following two statements are equivalent::

      x = attrs.field(converter=attrs.converter.pipe(c1, c2, c3))
      x = attrs.field(converter=[c1, c2, c3])

.. autofunction:: attrs.converters.optional

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(converter=attrs.converters.optional(int))
      >>> C(None)
      C(x=None)
      >>> C(42)
      C(x=42)


.. autofunction:: attrs.converters.default_if_none

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(
      ...         converter=attrs.converters.default_if_none("")
      ...     )
      >>> C(None)
      C(x='')


.. autofunction:: attrs.converters.to_bool(val)

   For example:

   .. doctest::

      >>> @define
      ... class C:
      ...     x = field(
      ...         converter=attrs.converters.to_bool
      ...     )
      >>> C("yes")
      C(x=True)
      >>> C(0)
      C(x=False)
      >>> C("norway")
      Traceback (most recent call last):
         File "<stdin>", line 1, in <module>
      ValueError: Cannot convert value to bool: norway




.. _api_setters:

Setters
-------

.. module:: attrs.setters

These are helpers that you can use together with `attrs.define`'s and `attrs.field`'s ``on_setattr`` arguments.
All setters in ``attrs.setters`` are also available from ``attr.setters`` (it's the same module in a different namespace).

.. autofunction:: frozen
.. autofunction:: validate
.. autofunction:: convert
.. autofunction:: pipe

.. data:: NO_OP

   Sentinel for disabling class-wide *on_setattr* hooks for certain attributes.

   Does not work in `attrs.setters.pipe` or within lists.

   .. versionadded:: 20.1.0

   For example, only ``x`` is frozen here:

   .. doctest::

     >>> @define(on_setattr=attr.setters.frozen)
     ... class C:
     ...     x = field()
     ...     y = field(on_setattr=attr.setters.NO_OP)
     >>> c = C(1, 2)
     >>> c.y = 3
     >>> c.y
     3
     >>> c.x = 4
     Traceback (most recent call last):
         ...
     attrs.exceptions.FrozenAttributeError: ()

   .. tip::
      Use `attrs.define`'s *frozen* argument (or `attrs.frozen`) to freeze whole classes; it is more efficient.

---

## File: docs/api-attr.rst

API Reference for the ``attr`` Namespace
========================================

.. note::

   These are the traditional APIs whose creation predates type annotations.
   They are **not** deprecated, but we suggest using the :mod:`attrs` namespace for new code, because they look nicer and have better defaults.

   See also :doc:`names`.

.. module:: attr


Core
----

.. autofunction:: attr.s(these=None, repr_ns=None, repr=None, cmp=None, hash=None, init=None, slots=False, frozen=False, weakref_slot=True, str=False, auto_attribs=False, kw_only=False, cache_hash=False, auto_exc=False, eq=None, order=None, auto_detect=False, collect_by_mro=False, getstate_setstate=None, on_setattr=None, field_transformer=None, match_args=True, unsafe_hash=None)

   For example:

   .. doctest::

      >>> import attr
      >>> @attr.s
      ... class C:
      ...     _private = attr.ib()
      >>> C(private=42)
      C(_private=42)
      >>> class D:
      ...     def __init__(self, x):
      ...         self.x = x
      >>> D(1)
      <D object at ...>
      >>> D = attr.s(these={"x": attr.ib()}, init=False)(D)
      >>> D(1)
      D(x=1)
      >>> @attr.s(auto_exc=True)
      ... class Error(Exception):
      ...     x = attr.ib()
      ...     y = attr.ib(default=42, init=False)
      >>> Error("foo")
      Error(x='foo', y=42)
      >>> raise Error("foo")
      Traceback (most recent call last):
         ...
      Error: ('foo', 42)
      >>> raise ValueError("foo", 42)   # for comparison
      Traceback (most recent call last):
         ...
      ValueError: ('foo', 42)


.. autofunction:: attr.ib

   .. note::

      *attrs* also comes with a serious-business alias ``attr.attrib``.

   The object returned by `attr.ib` also allows for setting the default and the validator using decorators:

   .. doctest::

      >>> @attr.s
      ... class C:
      ...     x = attr.ib()
      ...     y = attr.ib()
      ...     @x.validator
      ...     def _any_name_except_a_name_of_an_attribute(self, attribute, value):
      ...         if value < 0:
      ...             raise ValueError("x must be positive")
      ...     @y.default
      ...     def _any_name_except_a_name_of_an_attribute(self):
      ...         return self.x + 1
      >>> C(1)
      C(x=1, y=2)
      >>> C(-1)
      Traceback (most recent call last):
          ...
      ValueError: x must be positive

.. function:: attrs

   Serious business alias for `attr.s`.

.. function:: define

   Same as `attrs.define`.

.. function:: mutable

   Same as `attrs.mutable`.

.. function:: frozen

   Same as `attrs.frozen`.

.. function:: field

   Same as `attrs.field`.

.. class:: Attribute

   Same as `attrs.Attribute`.

.. function:: make_class

   Same as `attrs.make_class`.

.. autoclass:: Factory
   :noindex:

   Same as `attrs.Factory`.


.. data:: NOTHING

   Same as `attrs.NOTHING`.


Exceptions
----------

.. module:: attr.exceptions

All exceptions are available from both ``attr.exceptions`` and `attrs.exceptions` (it's the same module in a different namespace).

Please refer to `attrs.exceptions` for details.


Helpers
-------

.. currentmodule:: attr

.. function:: cmp_using

   Same as `attrs.cmp_using`.

.. function:: fields

   Same as `attrs.fields`.

.. function:: fields_dict

   Same as `attrs.fields_dict`.

.. function:: has

   Same as `attrs.has`.

.. function:: resolve_types

   Same as `attrs.resolve_types`.

.. autofunction:: asdict
.. autofunction:: astuple

.. module:: attr.filters

.. function:: include

   Same as `attrs.filters.include`.

.. function:: exclude

   Same as `attrs.filters.exclude`.

See :func:`attrs.asdict` for examples.

All objects from `attrs.filters` are also available in ``attr.filters``.

----

.. currentmodule:: attr

.. function:: evolve

   Same as `attrs.evolve`.

.. function:: validate

   Same as `attrs.validate`.


Validators
----------

.. module:: attr.validators

All objects from `attrs.validators` are also available in ``attr.validators``.
Please refer to the former for details.


Converters
----------

.. module:: attr.converters

All objects from `attrs.converters` are also available from ``attr.converters``.
Please refer to the former for details.


Setters
-------

.. module:: attr.setters

All objects from `attrs.setters` are also available in ``attr.setters``.
Please refer to the former for details.


Deprecated APIs
---------------

.. currentmodule:: attr

To help you write backward compatible code that doesn't throw warnings on modern releases, the ``attr`` module has an ``__version_info__`` attribute as of version 19.2.0.
It behaves similarly to `sys.version_info` and is an instance of `attr.VersionInfo`:

.. autoclass:: VersionInfo

   With its help you can write code like this:

   >>> if getattr(attr, "__version_info__", (0,)) >= (19, 2):
   ...     cmp_off = {"eq": False}
   ... else:
   ...     cmp_off = {"cmp": False}
   >>> cmp_off == {"eq":  False}
   True
   >>> @attr.s(**cmp_off)
   ... class C:
   ...     pass


----

.. autofunction:: assoc

Before *attrs* got `attrs.validators.set_disabled` and  `attrs.validators.set_disabled`, it had the following APIs to globally enable and disable validators.
They won't be removed, but are discouraged to use:

.. autofunction:: set_run_validators
.. autofunction:: get_run_validators

----

The serious-business aliases used to be called ``attr.attributes`` and ``attr.attr``.
There are no plans to remove them but they shouldn't be used in new code.

---

## File: docs/comparison.md

# Comparison

By default, two instances of *attrs* classes are equal if they have the same type and all their fields are equal.
For that, *attrs* writes `__eq__` and `__ne__` methods for you.

Additionally, if you pass `order=True`, *attrs* will also create a complete set of ordering methods: `__le__`, `__lt__`, `__ge__`, and `__gt__`.

For equality, *attrs* will generate a statement comparing the types of both instances,
and then comparing each attribute in turn using `==`.

For order, *attrs* will:

- Check if the types of the instances you're comparing are equal,
- if so, create a tuple of all field values for each instance,
- and finally perform the desired comparison operation on those tuples.

(custom-comparison)=

## Customization

As with other features, you can exclude fields from being involved in comparison operations:

```{doctest}
>>> from attrs import define, field
>>> @define
... class C:
...     x: int
...     y: int = field(eq=False)

>>> C(1, 2) == C(1, 3)
True
```

Additionally you can also pass a *callable* instead of a bool to both *eq* and *order*.
It is then used as a key function like you may know from {func}`sorted`:

```{doctest}
>>> @define
... class S:
...     x: str = field(eq=str.lower)

>>> S("foo") == S("FOO")
True

>>> @define(order=True)
... class C:
...     x: str = field(order=int)

>>> C("10") > C("2")
True
```

This is especially useful when you have fields with objects that have atypical comparison properties.
Common examples of such objects are [NumPy arrays](https://github.com/python-attrs/attrs/issues/435).

To save you unnecessary boilerplate, *attrs* comes with the {func}`attrs.cmp_using` helper to create such functions.
For NumPy arrays it would look like this:

```python
import numpy


@define
class C:
    an_array = field(eq=attrs.cmp_using(eq=numpy.array_equal))
```

:::{warning}
Please note that *eq* and *order* are set *independently*, because *order* is `False` by default in {func}`~attrs.define` (but not in {func}`attr.s`).
You can set both at once by using the *cmp* argument that we've undeprecated just for this use-case.
:::

---

## File: docs/examples.md

# *attrs* by Example

## Basics

The simplest possible usage is:

```{doctest}
>>> from attrs import define, field
>>> @define
... class Empty:
...     pass
>>> Empty()
Empty()
>>> Empty() == Empty()
True
>>> Empty() is Empty()
False
```

So in other words: *attrs* is useful even without actual {term}`fields <field>`!

But you'll usually want some data on your classes, so let's add some:

```{doctest}
>>> @define
... class Coordinates:
...     x: int
...     y: int
```

By default, all features are added, so you immediately have a fully functional data class with a nice `repr` string and comparison methods.

```{doctest}
>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
```

As shown, the generated `__init__` method allows for both positional and keyword arguments.

---

Unlike Data Classes, *attrs* doesn't force you to use type annotations.
So, the previous example could also have been written as:

```{doctest}
>>> @define
... class Coordinates:
...     x = field()
...     y = field()
>>> Coordinates(1, 2)
Coordinates(x=1, y=2)
```

:::{caution}
If a class body contains a field that is defined using {func}`attrs.field` (or {func}`attr.ib`), but **lacks a type annotation**, *attrs* switches to a no-typing mode and ignores fields that have type annotations but are not defined using {func}`attrs.field` (or {func}`attr.ib`).
:::

---

For private attributes, *attrs* will strip the leading underscores for keyword arguments:

```{doctest}
>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
```

If you want to initialize your private attributes yourself, you can do that too:

```{doctest}
>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
```

If you prefer to expose your privates, you can use keyword argument aliases:

```{doctest}
>>> @define
... class C:
...     _x: int = field(alias="_x")
>>> C(_x=1)
C(_x=1)
```

An additional way of defining attributes is supported too.
This is useful in times when you want to enhance classes that are not yours (nice `__repr__` for Django models anyone?):

```{doctest}
>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
```

[Subclassing is bad for you](https://www.youtube.com/watch?v=3MNVP9-hglc) (except when doing [strict specialization](https://hynek.me/articles/python-subclassing-redux/)), but *attrs* will still do what you'd hope for:

```{doctest}
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
```

{term}`Slotted classes <slotted classes>`, which are the default for the new APIs, don't play well with multiple inheritance so we don't use them in the example.

The order of the attributes is defined by the [MRO](https://www.python.org/download/releases/2.3/mro/).


### Keyword-only Attributes

You can also add [keyword-only](https://docs.python.org/3/glossary.html#keyword-only-parameter) attributes:

```{doctest}
>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
```

`kw_only` may also be specified at decorator level, and will apply to all attributes:

```{doctest}
>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)
```

If you create an attribute with `init=False`, the `kw_only` argument is ignored.

Keyword-only attributes allow subclasses to add attributes without default values, even if the base class defines attributes with default values:

```{doctest}
>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
...
TypeError: B() missing 1 required keyword-only argument: 'b'
```

If you don't set `kw_only=True`, then there is no valid attribute ordering, and you'll get an error:

```{doctest}
>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int
Traceback (most recent call last):
...
ValueError: No mandatory attributes allowed after an attribute with a default value or factory.  Attribute in question: Attribute(name='b', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, converter=None, metadata=mappingproxy({}), type=int, kw_only=False)
```

(asdict)=

## Converting to Collections Types

When you have a class with data, it often is very convenient to transform that class into a {class}`dict` (for example if you want to serialize it to JSON):

```{doctest}
>>> from attrs import asdict
>>> asdict(Coordinates(x=1, y=2))
{'x': 1, 'y': 2}
```

Some fields cannot or should not be transformed.
For that, {func}`attrs.asdict` offers a callback that decides whether an attribute should be included:

```{doctest}
>>> @define
... class User:
...     email: str
...     password: str

>>> @define
... class UserList:
...     users: list[User]

>>> asdict(UserList([User("jane@doe.invalid", "s33kred"),
...                  User("joe@doe.invalid", "p4ssw0rd")]),
...        filter=lambda attr, value: attr.name != "password")
{'users': [{'email': 'jane@doe.invalid'}, {'email': 'joe@doe.invalid'}]}
```

For the common case where you want to [`include`](attrs.filters.include) or [`exclude`](attrs.filters.exclude) certain types, string name or attributes, *attrs* ships with a few helpers:

```{doctest}
>>> from attrs import asdict, filters, fields

>>> @define
... class User:
...     login: str
...     password: str
...     email: str
...     id: int

>>> asdict(
...     User("jane", "s33kred", "jane@example.com", 42),
...     filter=filters.exclude(fields(User).password, "email", int))
{'login': 'jane'}

>>> @define
... class C:
...     x: str
...     y: str
...     z: int

>>> asdict(C("foo", "2", 3),
...        filter=filters.include(int, fields(C).x))
{'x': 'foo', 'z': 3}

>>> asdict(C("foo", "2", 3),
...        filter=filters.include(fields(C).x, "z"))
{'x': 'foo', 'z': 3}
```

:::{note}
Though using string names directly is convenient, mistyping attribute names will silently do the wrong thing and neither Python nor your type checker can help you.
{func}`attrs.fields()` will raise an `AttributeError` when the field doesn't exist while literal string names won't.
Using {func}`attrs.fields()` to get attributes is worth being recommended in most cases.

```{doctest}
>>> asdict(
...     User("jane", "s33kred", "jane@example.com", 42),
...     filter=filters.exclude("passwd")
... )
{'login': 'jane', 'password': 's33kred', 'email': 'jane@example.com', 'id': 42}

>>> asdict(
...     User("jane", "s33kred", "jane@example.com", 42),
...     filter=fields(User).passwd
... )
Traceback (most recent call last):
...
AttributeError: 'UserAttributes' object has no attribute 'passwd'. Did you mean: 'password'?
```
:::

Other times, all you want is a tuple and *attrs* won't let you down:

```{doctest}
>>> import sqlite3
>>> from attrs import astuple

>>> @define
... class Foo:
...    a: int
...    b: int

>>> foo = Foo(2, 3)
>>> with sqlite3.connect(":memory:") as conn:
...    c = conn.cursor()
...    c.execute("CREATE TABLE foo (x INTEGER PRIMARY KEY ASC, y)") #doctest: +ELLIPSIS
...    c.execute("INSERT INTO foo VALUES (?, ?)", astuple(foo)) #doctest: +ELLIPSIS
...    foo2 = Foo(*c.execute("SELECT x, y FROM foo").fetchone())
<sqlite3.Cursor object at ...>
<sqlite3.Cursor object at ...>
>>> foo == foo2
True
```

For more advanced transformations and conversions, we recommend you look at a companion library (such as [*cattrs*](https://catt.rs/)).


## Defaults

Sometimes you want to have default values for your initializer.
And sometimes you even want mutable objects as default values (ever accidentally used `def f(arg=[])`?).
*attrs* has you covered in both cases:

```{doctest}
>>> import collections

>>> @define
... class Connection:
...     socket: int
...     @classmethod
...     def connect(cls, db_string):
...        # ... connect somehow to db_string ...
...        return cls(socket=42)

>>> @define
... class ConnectionPool:
...     db_string: str
...     pool: collections.deque = Factory(collections.deque)
...     debug: bool = False
...     def get_connection(self):
...         try:
...             return self.pool.pop()
...         except IndexError:
...             if self.debug:
...                 print("New connection!")
...             return Connection.connect(self.db_string)
...     def free_connection(self, conn):
...         if self.debug:
...             print("Connection returned!")
...         self.pool.appendleft(conn)
...
>>> cp = ConnectionPool("postgres://localhost")
>>> cp
ConnectionPool(db_string='postgres://localhost', pool=deque([]), debug=False)
>>> conn = cp.get_connection()
>>> conn
Connection(socket=42)
>>> cp.free_connection(conn)
>>> cp
ConnectionPool(db_string='postgres://localhost', pool=deque([Connection(socket=42)]), debug=False)
```

More information on why class methods for constructing objects are awesome can be found in this insightful [blog post](https://web.archive.org/web/20210130220433/http://as.ynchrono.us/2014/12/asynchronous-object-initialization.html).

Default factories can also be set using the `factory` argument to {func}`~attrs.field`, and using a decorator.
The method receives the partially initialized instance which enables you to base a default value on other attributes:

```{doctest}
>>> @define
... class C:
...     x: int = 1
...     y: int = field()
...     @y.default
...     def _any_name_except_a_name_of_an_attribute(self):
...         return self.x + 1
...     z: list = field(factory=list)
>>> C()
C(x=1, y=2, z=[])
```

Please keep in mind that the decorator approach *only* works if the attribute in question has a {func}`~attrs.field` assigned to it.
As a result, annotating an attribute with a type is *not* enough if you use `@default`.

(examples-validators)=

## Validators

Although your initializers should do as little as possible (ideally: just initialize your instance according to the arguments!), it can come in handy to do some kind of validation on the arguments.

*attrs* offers two ways to define validators for each attribute and it's up to you to choose which one suits your style and project better.

You can use a decorator:

```{doctest}
>>> @define
... class C:
...     x: int = field()
...     @x.validator
...     def check(self, attribute, value):
...         if value > 42:
...             raise ValueError("x must be smaller or equal to 42")
>>> C(42)
C(x=42)
>>> C(43)
Traceback (most recent call last):
   ...
ValueError: x must be smaller or equal to 42
```

...or a callable...

```{doctest}
>>> from attrs import validators

>>> def x_smaller_than_y(instance, attribute, value):
...     if value >= instance.y:
...         raise ValueError("'x' has to be smaller than 'y'!")
>>> @define
... class C:
...     x: int = field(validator=[validators.instance_of(int),
...                               x_smaller_than_y])
...     y: int
>>> C(x=3, y=4)
C(x=3, y=4)
>>> C(x=4, y=3)
Traceback (most recent call last):
   ...
ValueError: 'x' has to be smaller than 'y'!
```

...or both at once:

```{doctest}
>>> @define
... class C:
...     x: int = field(validator=validators.instance_of(int))
...     @x.validator
...     def fits_byte(self, attribute, value):
...         if not 0 <= value < 256:
...             raise ValueError("value out of bounds")
>>> C(128)
C(x=128)
>>> C("128")
Traceback (most recent call last):
   ...
TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, metadata=mappingproxy({}), type=int, converter=None, kw_only=False), <class 'int'>, '128')
>>> C(256)
Traceback (most recent call last):
   ...
ValueError: value out of bounds
```

Please note that the decorator approach only works if -- and only if! -- the attribute in question has a {func}`~attrs.field` assigned.
Therefore if you use `@validator`, it is *not* enough to annotate said attribute with a type.

*attrs* ships with a bunch of validators, make sure to [check them out](api-validators) before writing your own:

```{doctest}
>>> @define
... class C:
...     x: int = field(validator=validators.instance_of(int))
>>> C(42)
C(x=42)
>>> C("42")
Traceback (most recent call last):
   ...
TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, factory=NOTHING, validator=>, type=None, kw_only=False), <type 'int'>, '42')
```

If using the old-school {func}`attr.s` decorator, validators only run on initialization by default.
If using the newer {func}`attrs.define` and friends, validators run on initialization *and* on attribute setting.
This behavior can be changed using the *on_setattr* argument.

Check out {ref}`validators` for more details.


## Conversion

Attributes can have a `converter` function specified, which will be called with the attribute's passed-in value to get a new value to use.
This can be useful for doing type-conversions on values that you don't want to force your callers to do.

```{doctest}
>>> @define
... class C:
...     x: int = field(converter=int)
>>> o = C("1")
>>> o.x
1
>>> o.x = "2"
>>> o.x
2
```

If using the old-school {func}`attr.s` decorator, converters only run on initialization by default.
If using the newer {func}`attrs.define` and friends, converters run on initialization *and* on attribute setting.
This behavior can be changed using the *on_setattr* argument.

Check out {ref}`converters` for more details.


## Running Custom Code Before/After Setting Attributes

If your `__setattr__` hook needs to run custom code before **and** after setting attributes, you can pass a generator to the `on_setattr` argument:

```{doctest}
>>> from typing import Generator
>>> def gen_hook(instance: Any, attribute: attr.Attribute[int], value: Any) -> Generator[int]:
...     print(f"value coming in: {value!r}")
...     new_value = int(value) * 2 + 286
...     yield new_value
...     print(f"set {attribute.name} to {new_value!r}")
>>> @define
... class C:
...     x: int = field(on_setattr=gen_hook)
>>> c = C(42)  # doesn't run on __init__
>>> c.x = "67"
value coming in: '67'
set x to 420
>>> c.x
420
```


(metadata)=

## Metadata

All *attrs* attributes may include arbitrary metadata in the form of a read-only dictionary.

```{doctest}
>>> from attrs import fields

>>> @define
... class C:
...    x = field(metadata={'my_metadata': 1})
>>> fields(C).x.metadata
mappingproxy({'my_metadata': 1})
>>> fields(C).x.metadata['my_metadata']
1
```

Metadata is not used by *attrs*, and is meant to enable rich functionality in third-party libraries.
The metadata dictionary follows the normal dictionary rules:
Keys need to be hashable, and both keys and values are recommended to be immutable.

If you're the author of a third-party library with *attrs* integration, please see [*Extending Metadata*](extending-metadata).


## Types

*attrs* also allows you to associate a type with an attribute using either the *type* argument to using {pep}`526`-annotations or {func}`attrs.field`/{func}`attr.ib`:

```{doctest}
>>> @define
... class C:
...     x: int
>>> fields(C).x.type
<class 'int'>

>>> import attr
>>> @attr.s
... class C:
...     x = attr.ib(type=int)
>>> fields(C).x.type
<class 'int'>
```

If you don't mind annotating *all* attributes, you can even drop the `attrs.field` and assign default values instead:

```{doctest}
>>> import typing

>>> @define
... class AutoC:
...     cls_var: typing.ClassVar[int] = 5  # this one is ignored
...     l: list[int] = Factory(list)
...     x: int = 1
...     foo: str = "every attrib needs a type if auto_attribs=True"
...     bar: typing.Any = None
>>> fields(AutoC).l.type
list[int]
>>> fields(AutoC).x.type
<class 'int'>
>>> fields(AutoC).foo.type
<class 'str'>
>>> fields(AutoC).bar.type
typing.Any
>>> AutoC()
AutoC(l=[], x=1, foo='every attrib needs a type if auto_attribs=True', bar=None)
>>> AutoC.cls_var
5
```

The generated `__init__` method will have an attribute called `__annotations__` that contains this type information.

If your annotations contain strings (for example, forward references),
you can resolve these after all references have been defined by using {func}`attrs.resolve_types`.
This will replace the *type* attribute in the respective fields.

```{doctest}
>>> from attrs import resolve_types

>>> @define
... class A:
...     a: 'list[A]'
...     b: 'B'
...
>>> @define
... class B:
...     a: A
...
>>> fields(A).a.type
'list[A]'
>>> fields(A).b.type
'B'
>>> resolve_types(A, globals(), locals())
<class 'A'>
>>> fields(A).a.type
list[A]
>>> fields(A).b.type
<class 'B'>
```

:::{note}
If you find yourself using string type annotations to handle forward references, wrap the entire type annotation in quotes instead of only the type you need a forward reference to (so `'list[A]'` instead of `list['A']`).
This is a limitation of the Python typing system.
:::

:::{warning}
*attrs* itself doesn't have any features that work on top of type metadata.
However it's useful for writing your own validators or serialization frameworks.
:::


## Slots

{term}`Slotted classes <slotted classes>` have several advantages on CPython.
Defining `__slots__` by hand is tedious, in *attrs* it's just a matter of using {func}`attrs.define` or passing `slots=True` to {func}`attr.s`:

```{doctest}
>>> @define
... class Coordinates:
...     x: int
...     y: int

>>> import attr

>>> @attr.s(slots=True)
... class Coordinates:
...     x: int
...     y: int
```

{func}`~attrs.define` sets `slots=True` by default.


## Immutability

Sometimes you have instances that shouldn't be changed after instantiation.
Immutability is especially popular in functional programming and is generally a very good thing.
If you'd like to enforce it, *attrs* will try to help:

```{doctest}
>>> from attrs import frozen

>>> @frozen
... class C:
...     x: int
>>> i = C(1)
>>> i.x = 2
Traceback (most recent call last):
   ...
attrs.exceptions.FrozenInstanceError: can't set attribute
>>> i.x
1
```

Please note that true immutability is impossible in Python but it will [get](how-frozen) you 99% there.
By themselves, immutable classes are useful for long-lived objects that should never change; like configurations for example.

In order to use them in regular program flow, you'll need a way to easily create new instances with changed attributes.
In Clojure that function is called [*assoc*](https://clojuredocs.org/clojure.core/assoc) and *attrs* shamelessly imitates it: {func}`attrs.evolve`:

```{doctest}
>>> from attrs import evolve, frozen

>>> @frozen
... class C:
...     x: int
...     y: int
>>> i1 = C(1, 2)
>>> i1
C(x=1, y=2)
>>> i2 = evolve(i1, y=3)
>>> i2
C(x=1, y=3)
>>> i1 == i2
False
```

On Python 3.13 and later, you can also use {func}`copy.replace` from the standard library:

```{doctest}
>>> import copy
>>> @frozen
... class C:
...     x: int
...     y: int
>>> i = C(1, 2)
>>> copy.replace(i, y=3)
C(x=1, y=3)
```


## Other Goodies

When building systems that have something resembling a plugin interface, you may want to have a registry of all classes that implement a certain interface:

```{doctest}
>>> REGISTRY = []
>>> class Base:  # does NOT have to be an attrs class!
...     @classmethod
...     def __attrs_init_subclass__(cls):
...         REGISTRY.append(cls)
>>> @define
... class Impl(Base):
...     pass
>>> REGISTRY
[<class 'Impl'>]
```

Sometimes you may want to create a class programmatically.
*attrs* gives you {func}`attrs.make_class` for that:

```{doctest}
>>> from attrs import make_class
>>> @define
... class C1:
...     x = field(type=int)
...     y = field()
>>> C2 = make_class("C2", {"x": field(type=int), "y": field()})
>>> fields(C1) == fields(C2)
True
>>> fields(C2).x.type
<class 'int'>
```

You can still have power over the attributes if you pass a dictionary of name: {func}`~attrs.field` mappings and can pass the same arguments as you can to `@attrs.define`:

```{doctest}
>>> C = make_class("C", {"x": field(default=42),
...                      "y": field(default=Factory(list))},
...                repr=False)
>>> i = C()
>>> i  # no repr added!
<__main__.C object at ...>
>>> i.x
42
>>> i.y
[]
```

If you need to dynamically make a class with {func}`~attrs.make_class` and it needs to be a subclass of something else than {class}`object`, use the `bases` argument:

```{doctest}
>>> class D:
...    def __eq__(self, other):
...        return True  # arbitrary example
>>> C = make_class("C", {}, bases=(D,), cmp=False)
>>> isinstance(C(), D)
True
```

Sometimes, you want to have your class's `__init__` method do more than just
the initialization, validation, etc. that gets done for you automatically when
using `@define`.
To do this, just define a `__attrs_post_init__` method in your class.
It will get called at the end of the generated `__init__` method.

```{doctest}
>>> @define
... class C:
...     x: int
...     y: int
...     z: int = field(init=False)
...
...     def __attrs_post_init__(self):
...         self.z = self.x + self.y
>>> obj = C(x=1, y=2)
>>> obj
C(x=1, y=2, z=3)
```

You can exclude single attributes from certain methods:

```{doctest}
>>> @define
... class C:
...     user: str
...     password: str = field(repr=False)
>>> C("me", "s3kr3t")
C(user='me')
```

Alternatively, to influence how the generated `__repr__()` method formats a specific attribute, specify a custom callable to be used instead of the `repr()` built-in function:

```{doctest}
>>> @define
... class C:
...     user: str
...     password: str = field(repr=lambda value: '***')
>>> C("me", "s3kr3t")
C(user='me', password=***)
```

---

## File: docs/extending.md

# Extending

Each *attrs*-decorated class has a `__attrs_attrs__` class attribute.
It's a tuple of {class}`attrs.Attribute` carrying metadata about each attribute.

So it is fairly simple to build your own decorators on top of *attrs*:

```{doctest}
>>> from attrs import define
>>> def print_attrs(cls):
...     print(cls.__attrs_attrs__)
...     return cls
>>> @print_attrs
... @define
... class C:
...     a: int
(Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=<class 'int'>, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a'),)
```

:::{warning}
The {func}`attrs.define` / {func}`attr.s` decorator **must** be applied first because it puts `__attrs_attrs__` in place!
That means that is has to come *after* your decorator because:

```python
@a
@b
def f():
    pass
```

is just [syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) for:

```python
def original_f():
    pass


f = a(b(original_f))
```
:::


## Wrapping the Decorator

A more elegant way can be to wrap *attrs* altogether and build a class [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) on top of it.

An example for that is the package [*environ-config*](https://github.com/hynek/environ-config) that uses *attrs* under the hood to define environment-based configurations declaratively without exposing *attrs* APIs at all.

Another common use case is to overwrite *attrs*'s defaults.


### Mypy

Unfortunately, decorator wrapping currently [confuses](https://github.com/python/mypy/issues/5406) Mypy's *attrs* plugin.
At the moment, the best workaround is to hold your nose, write a fake Mypy plugin, and mutate a bunch of global variables:

```python
from mypy.plugin import Plugin
from mypy.plugins.attrs import (
    attr_attrib_makers,
    attr_class_makers,
    attr_dataclass_makers,
)

# These work just like `attr.dataclass`.
attr_dataclass_makers.add("my_module.method_looks_like_attr_dataclass")

# This works just like `attr.s`.
attr_class_makers.add("my_module.method_looks_like_attr_s")

# These are our `attr.ib` makers.
attr_attrib_makers.add("my_module.method_looks_like_attrib")


class MyPlugin(Plugin):
    # Our plugin does nothing but it has to exist so this file gets loaded.
    pass


def plugin(version):
    return MyPlugin
```

Then tell Mypy about your plugin using your project's `mypy.ini`:

```ini
[mypy]
plugins=
```

:::{warning}
Please note that it is currently *impossible* to let Mypy know that you've changed defaults like *eq* or *order*.
You can only use this trick to tell Mypy that a class is actually an *attrs* class.
:::


### Pyright

Generic decorator wrapping is supported in [Pyright](https://github.com/microsoft/pyright) via `typing.dataclass_transform` / {pep}`681`.

For a custom wrapping of the form:

```
@typing.dataclass_transform(field_specifiers=(attr.attrib, attrs.field))
def custom_define(f):
    return attrs.define(f)
```

## Types

*attrs* offers two ways of attaching type information to attributes:

- {pep}`526` annotations,
- and the *type* argument to {func}`attr.ib` / {func}`attrs.field`.

This information is available to you:

```{doctest}
>>> from attrs import define, field, fields
>>> @define
... class C:
...     x: int = field()
...     y = field(type=str)
>>> fields(C).x.type
<class 'int'>
>>> fields(C).y.type
<class 'str'>
```

Currently, *attrs* doesn't do anything with this information but it's very useful if you'd like to write your own validators or serializers!

Originally, we didn't add the *type* argument to the new {func}`attrs.field` API, because type annotations are the preferred way.
But we reintroduced it later, so `field` can be used with the {func}`attrs.make_class` function.
We strongly discourage the use of the *type* parameter outside of {func}`attrs.make_class`.

(extending-metadata)=

## Metadata

If you're the author of a third-party library with *attrs* integration, you may want to take advantage of attribute metadata.

Here are some tips for effective use of metadata:

- Try making your metadata keys and values immutable.
  This keeps the entire {class}`~attrs.Attribute` instances immutable too.

- To avoid metadata key collisions, consider exposing your metadata keys from your modules.:

  ```python
  from mylib import MY_METADATA_KEY


  @define
  class C:
      x = field(metadata={MY_METADATA_KEY: 1})
  ```

  Metadata should be composable, so consider supporting this approach even if you decide implementing your metadata in one of the following ways.

- Expose `field` wrappers for your specific metadata.
  This is a more graceful approach if your users don't require metadata from other libraries.

  ```{doctest}
  >>> from attrs import fields, NOTHING
  >>> MY_TYPE_METADATA = '__my_type_metadata'
  >>>
  >>> def typed(
  ...     cls, default=NOTHING, validator=None, repr=True,
  ...     eq=True, order=None, hash=None, init=True, metadata=None,
  ...     converter=None
  ... ):
  ...     metadata = metadata or {}
  ...     metadata[MY_TYPE_METADATA] = cls
  ...     return field(
  ...         default=default, validator=validator, repr=repr,
  ...         eq=eq, order=order, hash=hash, init=init,
  ...         metadata=metadata, converter=converter
  ...     )
  >>>
  >>> @define
  ... class C:
  ...     x: int = typed(int, default=1, init=False)
  >>> fields(C).x.metadata[MY_TYPE_METADATA]
  <class 'int'>
  ```

(transform-fields)=

## Automatic Field Transformation and Modification

*attrs* allows you to automatically modify or transform the class' fields while the class is being created.
You do this by passing a *field_transformer* hook to {func}`~attrs.define` (and friends).
Its main purpose is to automatically add converters to attributes based on their type to aid the development of API clients and other typed data loaders.

This hook must have the following signature:

```{eval-rst}
.. function:: your_hook(cls: type, fields: list[attrs.Attribute]) -> list[attrs.Attribute]
   :noindex:
```

- *cls* is your class right *before* it is being converted into an attrs class.
  This means it does not yet have the `__attrs_attrs__` attribute.
- *fields* is a list of all `attrs.Attribute` instances that will later be set to `__attrs_attrs__`.
  You can modify these attributes any way you want:
  You can add converters, change types, and even remove attributes completely or create new ones!

For example, let's assume that you really don't like floats:

```{doctest}
>>> def drop_floats(cls, fields):
...     return [f for f in fields if f.type not in {float, 'float'}]
...
>>> @frozen(field_transformer=drop_floats)
... class Data:
...     a: int
...     b: float
...     c: str
...
>>> Data(42, "spam")
Data(a=42, c='spam')
```

A more realistic example would be to automatically convert data that you, for example, load from JSON:

```{doctest}
>>> from datetime import datetime
>>>
>>> def auto_convert(cls, fields):
...     results = []
...     for field in fields:
...         if field.converter is not None:
...             results.append(field)
...             continue
...         if field.type in {datetime, 'datetime'}:
...             converter = (lambda d: datetime.fromisoformat(d) if isinstance(d, str) else d)
...         else:
...             converter = None
...         results.append(field.evolve(converter=converter))
...     return results
...
>>> @frozen(field_transformer=auto_convert)
... class Data:
...     a: int
...     b: str
...     c: datetime
...
>>> from_json = {"a": 3, "b": "spam", "c": "2020-05-04T13:37:00"}
>>> Data(**from_json)  # ****
Data(a=3, b='spam', c=datetime.datetime(2020, 5, 4, 13, 37))
```

Or, perhaps you would prefer to generate dataclass-compatible `__init__` signatures via a default field *alias*.
Note, *field_transformer* receives {class}`attrs.Attribute` instances with default aliases already resolved, so the leading-underscore stripping has already been applied.
You can use the `attrs.Attribute.alias_is_default` flag to detect whether an alias was explicitly provided by the user or auto-generated.

```{doctest}
>>> def dataclass_names(cls, fields):
...     return [
...         field.evolve(alias=field.name)
...         if field.alias_is_default
...         else field
...         for field in fields
...     ]
...
>>> @frozen(field_transformer=dataclass_names)
... class Data:
...     public: int
...     _private: str
...     explicit: str = field(alias="aliased_name")
...
>>> Data(public=42, _private="spam", aliased_name="yes")
Data(public=42, _private='spam', explicit='yes')
```

## Customize Value Serialization in `asdict()`

*attrs* allows you to serialize instances of *attrs* classes to dicts using the {func}`attrs.asdict` function.
However, the result can not always be serialized since most data types will remain as they are:

```{doctest}
>>> import json
>>> import datetime
>>> from attrs import asdict
>>>
>>> @frozen
... class Data:
...    dt: datetime.datetime
...
>>> data = asdict(Data(datetime.datetime(2020, 5, 4, 13, 37)))
>>> data
{'dt': datetime.datetime(2020, 5, 4, 13, 37)}
>>> json.dumps(data)
Traceback (most recent call last):
   ...
TypeError: Object of type datetime is not JSON serializable
```

To help you with this, {func}`~attrs.asdict` allows you to pass a *value_serializer* hook.
It has the signature

```{eval-rst}
.. function:: your_hook(inst: type, field: attrs.Attribute, value: typing.Any) -> typing.Any
   :noindex:
```

```{doctest}
>>> from attr import asdict
>>> def serialize(inst, field, value):
...     if isinstance(value, datetime.datetime):
...         return value.isoformat()
...     return value
...
>>> data = asdict(
...     Data(datetime.datetime(2020, 5, 4, 13, 37)),
...     value_serializer=serialize,
... )
>>> data
{'dt': '2020-05-04T13:37:00'}
>>> json.dumps(data)
'{"dt": "2020-05-04T13:37:00"}'
```

---

## File: docs/glossary.md

# Glossary

:::{glossary}
dunder methods
  "Dunder" is a contraction of "double underscore".

  It's methods like `__init__` or `__eq__` that are sometimes also called *magic methods* or it's said that they implement an *object protocol*.

  In spoken form, you'd call `__init__` just "dunder init".

  Its first documented use is a [mailing list posting](https://mail.python.org/pipermail/python-list/2002-September/155836.html) by Mark Jackson from 2002.


dict classes
  A regular class whose attributes are stored in the {attr}`object.__dict__` attribute of every single instance.
  This is quite wasteful especially for objects with very few data attributes and the space consumption can become significant when creating large numbers of instances.

  This is the type of class you get by default both with and without *attrs* (except with the next APIs {func}`attrs.define()`, [`attrs.mutable()`](attrs.mutable), and [`attrs.frozen()`](attrs.frozen)).


slotted classes
  A class whose instances have no {attr}`object.__dict__` attribute and [define](https://docs.python.org/3/reference/datamodel.html#slots) their attributes in a `object.__slots__` attribute instead.
  In *attrs*, they are created by passing `slots=True` to `@attr.s` (and are on by default in {func}`attrs.define()`, [`attrs.mutable()`](attrs.mutable), and [`attrs.frozen()`](attrs.frozen)).

  Their main advantage is that they use less memory on CPython[^pypy] and are slightly faster.

  However, they also come with several possibly surprising gotchas:

  - Slotted classes don't allow for any other attribute to be set except for those defined in one of the class' hierarchies `__slots__`:

    ```{doctest}
    >>> from attr import define
    >>> @define
    ... class Coordinates:
    ...     x: int
    ...     y: int
    ...
    >>> c = Coordinates(x=1, y=2)
    >>> c.z = 3
    Traceback (most recent call last):
        ...
    AttributeError: 'Coordinates' object has no attribute 'z'
    ```

  - Slotted classes can inherit from other classes just like non-slotted classes, but some of the benefits of slotted classes are lost if you do that.
    If you must inherit from other classes, try to inherit only from other slotted classes.

  - However, [it's not possible](https://docs.python.org/3/reference/datamodel.html#slots) to inherit from more than one class that has attributes in `__slots__` (you will get an `TypeError: multiple bases have instance lay-out conflict`).

  - It's not possible to monkeypatch methods on slotted classes.
    This can feel limiting in test code, however the need to monkeypatch your own classes is usually a design smell.

    If you really need to monkeypatch an instance in your tests, but don't want to give up on the advantages of slotted classes in production code, you can always subclass a slotted class as a dict class with no further changes and all the limitations go away:

    ```{doctest}
    >>> import unittest.mock
    >>> @define
    ... class Slotted:
    ...     x: int
    ...
    ...     def method(self):
    ...         return self.x
    >>> s = Slotted(42)
    >>> s.method()
    42
    >>> with unittest.mock.patch.object(s, "method", return_value=23):
    ...     pass
    Traceback (most recent call last):
      ...
    AttributeError: 'Slotted' object attribute 'method' is read-only
    >>> @define(slots=False)
    ... class Dicted(Slotted):
    ...     pass
    >>> d = Dicted(42)
    >>> d.method()
    42
    >>> with unittest.mock.patch.object(d, "method", return_value=23):
    ...     assert 23 == d.method()
    ```

  - Slotted classes must implement {meth}`__getstate__ <object.__getstate__>` and {meth}`__setstate__ <object.__setstate__>` to be serializable with {mod}`pickle` protocol 0 and 1.
    Therefore, *attrs* creates these methods automatically for slotted classes.

    :::{note}
    When decorating with `@attr.s(slots=True)` and the class already implements the {meth}`__getstate__ <object.__getstate__>` and {meth}`__setstate__ <object.__setstate__>` methods, they will be *overwritten* by *attrs* autogenerated implementation by default.

    This can be avoided by setting `@attr.s(getstate_setstate=False)` or by setting `@attr.s(auto_detect=True)`.

    {func}`~attrs.define` sets `auto_detect=True` by default.
    :::

    Also, [think twice](https://www.youtube.com/watch?v=7KnfGDajDQw) before using {mod}`pickle`.

  - Slotted classes are weak-referenceable by default.
    This can be disabled in CPython by passing `weakref_slot=False` to `@attr.s` [^pypyweakref].

  - Since it's currently impossible to make a class slotted after it's been created, *attrs* has to replace your class with a new one.
    While it tries to do that as graciously as possible, certain metaclass features like {meth}`object.__init_subclass__` do not work with slotted classes.

  - The {attr}`type.__subclasses__` attribute needs a garbage collection run (which can be manually triggered using {func}`gc.collect`), for the original class to be removed.
    See issue [#407](https://github.com/python-attrs/attrs/issues/407) for more details.

  - Pickling of slotted classes will fail if you define a class with missing attributes.

    This situation can occur if you define an `attrs.field(init=False)` and don't set the attribute by hand before pickling.

  - You can't call a bare `super()` in a decorated method like `contextlib.contextmanager`.
    Use `super(YourClass, self)` until we get a way to work around this from Python.
    See also [#1038](https://github.com/python-attrs/attrs/issues/1038) for more details.


field
  As the project name suggests, *attrs* is all about attributes.
  We especially tried to emphasize that we only care about attributes and not about the classes themselves -- because we believe the class belongs to the user.

  This explains why the traditional API uses an {func}`attr.ib` (or ``attrib``) function to define attributes and we still use the term throughout the documentation.

  However, with the emergence of {mod}`dataclasses`, [Pydantic](https://docs.pydantic.dev/latest/concepts/fields/), and other libraries, the term "field" has become a common term for a predefined attribute on a class in the Python ecosystem.

  So with our new APIs, we've embraced it too by calling the function to create them {func}`attrs.field`, and use the term "field" throughout the documentation interchangeably.

  See also {doc}`names`.

attribute
  See {term}`field`.
:::

[^pypy]: On PyPy, there is no memory advantage in using slotted classes.

[^pypyweakref]: On PyPy, slotted classes are naturally weak-referenceable so `weakref_slot=False` has no effect.

---

## File: docs/hashing.md

# Hashing

## Hash Method Generation

:::{warning}
The overarching theme is to never set the `@attrs.define(unsafe_hash=X)` parameter yourself.
Leave it at `None` which means that *attrs* will do the right thing for you, depending on the other parameters:

- If you want to make objects hashable by value: use `@define(frozen=True)`.
- If you want hashing and equality by object identity: use `@define(eq=False)`

Setting `unsafe_hash` yourself can have unexpected consequences so we recommend to tinker with it only if you know exactly what you're doing.
:::

Under certain circumstances, it's necessary for objects to be *hashable*.
For example if you want to put them into a {class}`set` or if you want to use them as keys in a {class}`dict`.

The *hash* of an object is an integer that represents the contents of an object.
It can be obtained by calling {func}`hash` on an object and is implemented by writing a `__hash__` method for your class.

*attrs* will happily write a `__hash__` method for you [^fn1], however it will *not* do so by default.
Because according to the [definition](https://docs.python.org/3/glossary.html#term-hashable) from the official Python docs, the returned hash has to fulfill certain constraints:

[^fn1]: The hash is computed by hashing a tuple that consists of a unique id for the class plus all attribute values.

1. Two objects that are equal, **must** have the same hash.
   This means that if `x == y`, it *must* follow that `hash(x) == hash(y)`.

   By default, Python classes are compared *and* hashed by their `id`.
   That means that every instance of a class has a different hash, no matter what attributes it carries.

   It follows that the moment you (or *attrs*) change the way equality is handled by implementing `__eq__` which is based on attribute values, this constraint is broken.
   For that reason Python 3 will make a class that has customized equality unhashable.
   Python 2 on the other hand will happily let you shoot your foot off.
   Unfortunately, *attrs* still mimics (otherwise unsupported) Python 2's behavior for backward-compatibility reasons if you set `unsafe_hash=False`.

   The *correct way* to achieve hashing by id is to set `@define(eq=False)`.
   Setting `@define(unsafe_hash=False)` (which implies `eq=True`) is almost certainly a *bug*.

   :::{warning}
   Be careful when subclassing!
   Setting `eq=False` on a class whose base class has a non-default `__hash__` method will *not* make *attrs* remove that `__hash__` for you.

   It is part of *attrs*'s philosophy to only *add* to classes so you have the freedom to customize your classes as you wish.
   So if you want to *get rid* of methods, you'll have to do it by hand.

   The easiest way to reset `__hash__` on a class is adding `__hash__ = object.__hash__` in the class body.
   :::

2. If two objects are not equal, their hash **should** be different.

   While this isn't a requirement from a standpoint of correctness, sets and dicts become less effective if there are a lot of identical hashes.
   The worst case is when all objects have the same hash which turns a set into a list.

3. The hash of an object **must not** change.

   If you create a class with `@define(frozen=True)` this is fulfilled by definition, therefore *attrs* will write a `__hash__` function for you automatically.
   You can also force it to write one with `unsafe_hash=True` but then it's *your* responsibility to make sure that the object is not mutated.

   This point is the reason why mutable structures like lists, dictionaries, or sets aren't hashable while immutable ones like tuples or `frozenset`s are:
   point 1 and 2 require that the hash changes with the contents but point 3 forbids it.

For a more thorough explanation of this topic, please refer to this blog post: [*Python Hashes and Equality*](https://hynek.me/articles/hashes-and-equality/).

:::{note}
Please note that the `unsafe_hash` argument's original name was `hash` but was changed to conform with {pep}`681` in 22.2.0.
The old argument name is still around and will **not** be removed -- but setting `unsafe_hash` takes precedence over `hash`.
The field-level argument is still called `hash` and will remain so.
:::


## Hashing and Mutability

Changing any field involved in hash code computation after the first call to `__hash__` (typically this would be after its insertion into a hash-based collection) can result in silent bugs.
Therefore, it is strongly recommended that hashable classes be `frozen`.
Beware, however, that this is not a complete guarantee of safety:
if a field points to an object and that object is mutated, the hash code may change, but `frozen` will not protect you.


## Hash Code Caching

Some objects have hash codes which are expensive to compute.
If such objects are to be stored in hash-based collections, it can be useful to compute the hash codes only once and then store the result on the object to make future hash code requests fast.
To enable caching of hash codes, pass `@define(cache_hash=True)`.
This may only be done if *attrs* is already generating a hash function for the object.

---

## File: docs/how-does-it-work.md

(how)=

# How Does It Work?

## Boilerplate

*attrs* isn't the first library that aims to simplify class definition in Python.
But its **declarative** approach combined with **no runtime overhead** lets it stand out.

Once you apply the `@attrs.define` (or `@attr.s`) decorator to a class, *attrs* searches the class object for type hints and instances of `attrs.field`s.
Internally `field`s are a representation of the data passed, along with a counter to preserve the order of the attributes.

In order to ensure that subclassing works as you'd expect it to work, *attrs* also walks the class hierarchy and collects the attributes of all base classes.
Please note that *attrs* does *not* call `super()` *ever*.
It will write {term}`dunder methods` to work on *all* of those attributes which also has performance benefits due to fewer function calls.

Once *attrs* knows what attributes it has to work on, it writes the requested {term}`dunder methods` and -- depending on whether you wish to have a {term}`dict <dict classes>` or {term}`slotted <slotted classes>` class -- creates a new class for you (`slots=True`) or attaches them to the original class (`slots=False`).
While creating new classes is more elegant, we've run into several edge cases surrounding metaclasses that make it impossible to go this route unconditionally.

To be very clear: if you define a class with a single attribute without a default value, the generated `__init__` will look *exactly* how you'd expect:

```{doctest}
>>> import inspect
>>> from attrs import define
>>> @define
... class C:
...     x: int
>>> print(inspect.getsource(C.__init__))
def __init__(self, x):
    self.x = x

```

No magic, no meta programming, no expensive introspection at runtime.

---

Everything until this point happens exactly *once* when the class is defined.
As soon as a class is done, it's done.
And it's just a regular Python class like any other, except for a single `__attrs_attrs__` attribute that *attrs* uses internally.
Much of the information is accessible via {func}`attrs.inspect`, {func}`attrs.fields` and other functions which can be used for introspection or for writing your own tools and decorators on top of *attrs* (like {func}`attrs.asdict`).

And once you start instantiating your classes, *attrs* is out of your way completely.

This **static** approach was very much a design goal of *attrs* and what I strongly believe makes it distinct.

(how-frozen)=

## Immutability

In order to give you immutability, *attrs* will attach a `__setattr__` method to your class that raises an {class}`attrs.exceptions.FrozenInstanceError` whenever anyone tries to set an attribute.

The same is true if you choose to freeze individual attributes using the {obj}`attrs.setters.frozen` *on_setattr* hook -- except that the exception becomes {class}`attrs.exceptions.FrozenAttributeError`.

Both exceptions subclass {class}`attrs.exceptions.FrozenError`.

---

Depending on whether a class is a dict class or a slotted class, *attrs* uses a different technique to circumvent that limitation in the `__init__` method.

Once constructed, frozen instances don't differ in any way from regular ones except that you cannot change its attributes.


### Dict Classes

Dict classes -- that is: regular classes -- simply assign the value directly into the class' eponymous `__dict__` (and there's nothing we can do to stop the user to do the same).

The performance impact is negligible.


### Slotted Classes

Slotted classes are more complicated.
Here it uses (an aggressively cached) {meth}`object.__setattr__` to set your attributes.
This is (still) slower than a plain assignment:

```none
$ pyperf timeit --rigorous \
      -s "import attr; C = attr.make_class('C', ['x', 'y', 'z'], slots=True)" \
      "C(1, 2, 3)"
.........................................
Mean +- std dev: 228 ns +- 18 ns

$ pyperf timeit --rigorous \
      -s "import attr; C = attr.make_class('C', ['x', 'y', 'z'], slots=True, frozen=True)" \
      "C(1, 2, 3)"
.........................................
Mean +- std dev: 425 ns +- 16 ns
```

So on a laptop computer the difference is about 200 nanoseconds (1 second is 1,000,000,000 nanoseconds).
It's certainly something you'll feel in a hot loop but shouldn't matter in normal code.
Pick what's more important to you.

### Summary

You should avoid instantiating lots of frozen slotted classes (meaning: `@frozen`) in performance-critical code.

Frozen dict classes have barely a performance impact, unfrozen slotted classes are even *faster* than unfrozen dict classes (meaning: regular classes).


(how-slotted-cached_property)=

## Cached Properties on Slotted Classes

By default, the standard library {func}`functools.cached_property` decorator does not work on slotted classes, because it requires a `__dict__` to store the cached value.
This could be surprising when using *attrs*, as slotted classes are the default.
Therefore, *attrs* converts `cached_property`-decorated methods when constructing slotted classes.

Getting this working is achieved by:

* Adding names to `__slots__` for the wrapped methods.
* Adding a `__getattr__` method to set values on the wrapped methods.

For most users, this should mean that it works transparently.

:::{note}
The implementation does not guarantee that the wrapped method is called only once in multi-threaded usage.
This matches the implementation of `cached_property` in Python 3.12.
:::

---

## File: docs/index.md

# *attrs*: Classes Without Boilerplate

Release **{sub-ref}`release`**  ([What's new?](changelog.md))

```{include} ../README.md
:start-after: 'teaser-begin -->'
:end-before: '
[](https://www.variomedia.de/)
[](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek)
[](https://kraken.tech/)
[](https://privacy-solutions.org/)
[](https://klaviyo.com/)
[](https://filepreviews.io/)
[](https://www.testmuai.com/?utm_medium=sponsor&utm_source=structlog)
[](https://polar.sh/)


```{include} ../README.md
:start-after: 'sponsor-break-end -->'
:end-before: '<!-- teaser-end'
```
/* Detailed source-code truncated for AI context efficiency. */
```{include} ../README.md
:start-after: '### *attrs* for Enterprise'
```

---

## Full Table of Contents

```{toctree}
:maxdepth: 2
:caption: Getting Started

overview
why
examples
```

```{toctree}
:maxdepth: 2
:caption: Guides

types
init
comparison
hashing
```

```{toctree}
:maxdepth: 2
:caption: Reference

api
api-attr
glossary
```
```{toctree}
:maxdepth: 2
:caption: Advanced

extending
how-does-it-work
```

```{toctree}
:caption: Meta
:maxdepth: 1

names
license
changelog
PyPI <https://pypi.org/project/attrs/>
GitHub <https://github.com/python-attrs/attrs>
Third-party Extensions <https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs>
Contributing <https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md>
Funding <https://hynek.me/say-thanks/>
```

[Full Index](genindex)