API Reference ============= .. currentmodule:: attr ``attrs`` works by decorating a class using `attrs.define` or `attr.s` and then optionally defining attributes on the class using `attrs.field`, `attr.ib`, or a type annotation. If you're confused by the many names, please check out `names` for clarification. What follows is the API explanation, if you'd like a more hands-on introduction, have a look at `examples`. As of version 21.3.0, ``attrs`` consists of **two** top-level package names: - The classic ``attr`` that powered the venerable `attr.s` and `attr.ib` - The modern ``attrs`` that only contains most modern APIs and relies on `attrs.define` and `attrs.field` to define your classes. Additionally it offers some ``attr`` APIs with nicer defaults (e.g. `attrs.asdict`). The ``attrs`` namespace is built *on top of* ``attr`` which will *never* go away. Core ---- .. note:: Please note that the ``attrs`` namespace has been added in version 21.3.0. Most of the objects are simply re-imported from ``attr``. Therefore if a class, method, or function claims that it has been added in an older version, it is only available in the ``attr`` namespace. .. autodata:: attrs.NOTHING :no-value: .. autofunction:: attrs.define .. function:: attrs.mutable(same_as_define) Alias for `attrs.define`. .. versionadded:: 20.1.0 .. function:: attrs.frozen(same_as_define) Behaves the same as `attrs.define` but sets *frozen=True* and *on_setattr=None*. .. versionadded:: 20.1.0 .. autofunction:: attrs.field .. function:: define Old import path for `attrs.define`. .. function:: mutable Old import path for `attrs.mutable`. .. function:: frozen Old import path for `attrs.frozen`. .. function:: field Old import path for `attrs.field`. .. autoclass:: attrs.Attribute :members: evolve For example: .. doctest:: >>> import attr >>> @attr.s ... class C: ... x = attr.ib() >>> attr.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:: attrs.make_class This is handy if you want to programmatically create classes. For example: .. doctest:: >>> C1 = attr.make_class("C1", ["x", "y"]) >>> C1(1, 2) C1(x=1, y=2) >>> C2 = attr.make_class("C2", {"x": attr.ib(default=42), ... "y": attr.ib(default=attr.Factory(list))}) >>> C2() C2(x=42, y=[]) .. autoclass:: attrs.Factory For example: .. doctest:: >>> @attr.s ... class C: ... x = attr.ib(default=attr.Factory(list)) ... y = attr.ib(default=attr.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}) Classic ~~~~~~~ .. data:: attr.NOTHING Same as `attrs.NOTHING`. .. 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) .. note:: ``attrs`` also comes with a serious-business alias ``attr.attrs``. 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 = 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 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:: attrs.exceptions.PythonTooOldError .. autoexception:: attrs.exceptions.FrozenError .. autoexception:: attrs.exceptions.FrozenInstanceError .. autoexception:: attrs.exceptions.FrozenAttributeError .. autoexception:: attrs.exceptions.AttrsAttributeNotFoundError .. autoexception:: attrs.exceptions.NotAnAttrsClassError .. autoexception:: attrs.exceptions.DefaultAlreadySetError .. autoexception:: attrs.exceptions.UnannotatedAttributeError .. autoexception:: attrs.exceptions.NotCallableError 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: .. autofunction:: attrs.cmp_using .. function:: attr.cmp_using Same as `attrs.cmp_using`. .. autofunction:: attrs.fields For example: .. doctest:: >>> @attr.s ... class C: ... x = attr.ib() ... y = attr.ib() >>> 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 .. function:: attr.fields Same as `attrs.fields`. .. 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 .. function:: attr.fields_dict Same as `attrs.fields_dict`. .. autofunction:: attrs.has For example: .. doctest:: >>> @attr.s ... class C: ... pass >>> attr.has(C) True >>> attr.has(object) False .. function:: attr.has Same as `attrs.has`. .. autofunction:: attrs.resolve_types For example: .. doctest:: >>> import typing >>> @attrs.define ... class A: ... a: typing.List['A'] ... b: 'B' ... >>> @attrs.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 .. function:: attr.resolve_types Same as `attrs.resolve_types`. .. autofunction:: attrs.asdict For example: .. doctest:: >>> @attrs.define ... class C: ... x: int ... y: int >>> attrs.asdict(C(1, C(2, 3))) {'x': 1, 'y': {'x': 2, 'y': 3}} .. autofunction:: attr.asdict .. autofunction:: attrs.astuple For example: .. doctest:: >>> @attrs.define ... class C: ... x = attr.field() ... y = attr.field() >>> attrs.astuple(C(1,2)) (1, 2) .. autofunction:: attr.astuple ``attrs`` includes some handy helpers for filtering the attributes in `attrs.asdict` and `attrs.astuple`: .. autofunction:: attrs.filters.include .. autofunction:: attrs.filters.exclude .. function:: attr.filters.include Same as `attrs.filters.include`. .. function:: attr.filters.exclude Same as `attrs.filters.exclude`. See :func:`attrs.asdict` for examples. All objects from ``attrs.filters`` are also available from ``attr.filters``. ---- .. autofunction:: attrs.evolve For example: .. doctest:: >>> @attrs.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. .. function:: attr.evolve Same as `attrs.evolve`. .. autofunction:: attrs.validate For example: .. doctest:: >>> @attrs.define(on_setattr=attrs.setters.NO_OP) ... class C: ... x = attrs.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 ).", ...) .. function:: attr.validate Same as `attrs.validate`. Validators can be globally disabled if you want to run them only in development and tests but not in production because you fear their performance impact: .. autofunction:: set_run_validators .. autofunction:: get_run_validators .. _api_validators: Validators ---------- ``attrs`` comes with some common validators in the ``attrs.validators`` module. All objects from ``attrs.validators`` are also available from ``attr.validators``. .. autofunction:: attrs.validators.lt For example: .. doctest:: >>> @attrs.define ... class C: ... x = attrs.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:: >>> @attrs.define ... class C: ... x = attrs.field(validator=attr.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:: >>> @attrs.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:: >>> @attrs.define ... class C: ... x = attr.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:: >>> @attrs.define ... class C: ... x = attrs.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:: >>> @attrs.define ... class C: ... x = attrs.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:: >>> @attrs.define ... class C: ... x = attrs.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" >>> @attrs.define ... class C: ... state = attrs.field(validator=attrs.validators.in_(State)) ... val = attrs.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.provides .. 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 = attrs.field(validator=attrs.validators.and_(v1, v2, v3)) x = attrs.field(validator=[v1, v2, v3]) .. autofunction:: attrs.validators.not_ For example: .. doctest:: >>> reserved_names = {"id", "time", "source"} >>> @attrs.define ... class Measurement: ... tags = attrs.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:: >>> @attrs.define ... class C: ... x = attrs.field(validator=attrs.validators.optional(attr.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:: >>> @attrs.define ... class C: ... x = attrs.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:: >>> @attrs.define ... class User: ... email = attrs.field(validator=attrs.validators.matches_re( ... "(^[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:: >>> @attrs.define ... class C: ... x = attrs.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:: >>> @attrs.define ... class C: ... x = attrs.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 ---------- All objects from ``attrs.converters`` are also available from ``attr.converters``. .. autofunction:: attrs.converters.pipe For convenience, it's also possible to pass a list to `attr.ib`'s converter argument. Thus the following two statements are equivalent:: x = attr.ib(converter=attr.converter.pipe(c1, c2, c3)) x = attr.ib(converter=[c1, c2, c3]) .. autofunction:: attrs.converters.optional For example: .. doctest:: >>> @attr.s ... class C: ... x = attr.ib(converter=attr.converters.optional(int)) >>> C(None) C(x=None) >>> C(42) C(x=42) .. autofunction:: attrs.converters.default_if_none For example: .. doctest:: >>> @attr.s ... class C: ... x = attr.ib( ... converter=attr.converters.default_if_none("") ... ) >>> C(None) C(x='') .. autofunction:: attrs.converters.to_bool For example: .. doctest:: >>> @attr.s ... class C: ... x = attr.ib( ... converter=attr.converters.to_bool ... ) >>> C("yes") C(x=True) >>> C(0) C(x=False) >>> C("foo") Traceback (most recent call last): File "", line 1, in ValueError: Cannot convert value to bool: foo .. _api_setters: Setters ------- These are helpers that you can use together with `attrs.define`'s and `attrs.fields`'s ``on_setattr`` arguments. All setters in ``attrs.setters`` are also available from ``attr.setters``. .. autofunction:: attrs.setters.frozen .. autofunction:: attrs.setters.validate .. autofunction:: attrs.setters.convert .. autofunction:: attrs.setters.pipe .. data:: attrs.setters.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:: >>> @attrs.define(on_setattr=attr.setters.frozen) ... class C: ... x = attr.field() ... y = attr.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: () N.B. Please use `attrs.define`'s *frozen* argument (or `attrs.frozen`) to freeze whole classes; it is more efficient. Deprecated APIs --------------- .. _version-info: 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 `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 ---- 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. .. autofunction:: assoc