.. 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 is that as of version 21.3.0, attrs consists of two top-level package names:
- The classic
attrthat powers the venerable attr.s and attr.ib. - The newer
attrsthat 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 inattrhave 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>.
.. 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, reuse
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:
.. 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:
>>> 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!
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'>).", ...)
.. 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.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=<instance_of validator for type <type 'int'>>, 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=<instance_of validator for type <type 'int'>>, 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=<in_ validator with options <enum 'State'>>, 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=<in_ validator with options [1, 2, 3]>, 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 (<instance_of validator for type <class 'int'>>, <deep_iterable validator for iterables of <instance_of validator for type <class 'int'>>>) 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 <in_ validator with options {'id', 'time', 'source'}>, capturing (<class 'ValueError'>, <class 'TypeError'>)>, type=None, kw_only=False), <in_ validator with options {'id', 'time', 'source'}>, {'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=<instance_of validator for type <type 'int'>>, 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=<built-in function isinstance>)
>>> 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 <instance_of validator for type <class 'list'>> iterables of <instance_of validator for type <class 'int'>>>, 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 <instance_of validator for type <class 'list'>> iterables of <instance_of validator for type <class 'int'>>>, 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 <instance_of validator for type <class 'str'>> to <instance_of validator for type <class 'int'>>>, 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 <instance_of validator for type <class 'str'>> to <instance_of validator for type <class 'int'>>>, 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 <instance_of validator for type <class 'str'>> to <instance_of validator for type <class 'int'>>>, 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
.. 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
.. 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.