.. 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
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 inattr
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>.
.. 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:
.. 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] 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:: >>> @attr.s ... class C: ... pass >>> attr.has(C) True >>> attr.has(object) False
.. 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="[email protected]") User(email='[email protected]') >>> User(email="[email protected]@test.com") Traceback (most recent call last): ... ValueError: ("'email' must match regex '(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\\\.[a-zA-Z0-9-.]+$)' ('[email protected]@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-.]+$)'), '[email protected]@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.