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 `_ 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 `. 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] 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()) >>> attrs.fields(A).a.type typing.List[A] >>> attrs.fields(A).b.type .. 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 (got '1' that is a ).", ...) .. _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.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 (got '42' that is a ).", Attribute(name='x', default=NOTHING, validator=>, type=None, kw_only=False), , '42') >>> C(None) Traceback (most recent call last): ... TypeError: ("'x' must be (got None that is a ).", Attribute(name='x', default=NOTHING, validator=>, repr=True, cmp=True, hash=None, init=True, type=None, kw_only=False), , 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=, val=1) >>> C("On", 1) Traceback (most recent call last): ... ValueError: 'state' must be in (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), , '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 (>, >>) 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=, capturing (, )>, type=None, kw_only=False), , {'source_': 'universe'}, (, )) >>> 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 (got '42' that is a ).", Attribute(name='x', default=NOTHING, validator=>, type=None, kw_only=False), , '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 ). .. 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=, 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 (got {1, 2, 3} that is a ).", Attribute(name='x', default=NOTHING, validator=> iterables of >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), , {1, 2, 3}) >>> C(x=[1, 2, "3"]) Traceback (most recent call last): ... TypeError: ("'x' must be (got '3' that is a ).", Attribute(name='x', default=NOTHING, validator=> iterables of >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), , '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 (got None that is a ).", Attribute(name='x', default=NOTHING, validator=> to >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), , None) >>> C(x={"a": 1.0, "b": 2}) Traceback (most recent call last): ... TypeError: ("'x' must be (got 1.0 that is a ).", Attribute(name='x', default=NOTHING, validator=> to >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), , 1.0) >>> C(x={"a": 1, 7: 2}) Traceback (most recent call last): ... TypeError: ("'x' must be (got 7 that is a ).", Attribute(name='x', default=NOTHING, validator=> to >>, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), , 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 "", line 1, in 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.