Skip to content

Latest commit

 

History

History
768 lines (548 loc) · 23 KB

api.rst

File metadata and controls

768 lines (548 loc) · 23 KB

API Reference

attr

attrs works by decorating a class using attr.define or attr.s and then optionally defining attributes on the class using attr.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.

Core

Note

attrs 20.1.0 added a bunch of nicer APIs (sometimes referred to as next generation -- or NG -- APIs) that were intended to become the main way of defining classes in the future. As of 21.1.0, they are not provisional anymore and are the recommended way to use attrs! The next step will be adding an importable attrs namespace. The documentation will be updated successively.

Please have a look at next-gen!

attr.NOTHING

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:

>>> import attr >>> @attr.s ... class C(object): ... _private = attr.ib() >>> C(private=42) C(_private=42) >>> class D(object): ... 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)

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:

>>> @attr.s ... class C(object): ... 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

attr.Attribute

For example:

>>> import attr >>> @attr.s ... class C(object): ... 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)

attr.make_class

This is handy if you want to programmatically create classes.

For example:

>>> 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=[])

attr.Factory

For example:

>>> @attr.s ... class C(object): ... 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})

Exceptions

attr.exceptions.PythonTooOldError

attr.exceptions.FrozenError

attr.exceptions.FrozenInstanceError

attr.exceptions.FrozenAttributeError

attr.exceptions.AttrsAttributeNotFoundError

attr.exceptions.NotAnAttrsClassError

attr.exceptions.DefaultAlreadySetError

attr.exceptions.UnannotatedAttributeError

attr.exceptions.NotCallableError

For example:

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

Helpers

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

attr.cmp_using

attr.fields

For example:

>>> @attr.s ... class C(object): ... x = attr.ib() ... y = attr.ib() >>> attr.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), 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)) >>> attr.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) >>> attr.fields(C).y is attr.fields(C)[1] True

attr.fields_dict

For example:

>>> @attr.s ... class C(object): ... x = attr.ib() ... y = attr.ib() >>> attr.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), '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)} >>> 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) >>> attr.fields_dict(C)['y'] is attr.fields(C).y True

attr.has

For example:

>>> @attr.s ... class C(object): ... pass >>> attr.has(C) True >>> attr.has(object) False

attr.resolve_types

For example:

>>> import typing >>> @attr.s(auto_attribs=True) ... class A: ... a: typing.List['A'] ... b: 'B' ... >>> @attr.s(auto_attribs=True) ... class B: ... a: A ... >>> attr.fields(A).a.type typing.List[ForwardRef('A')] >>> attr.fields(A).b.type 'B' >>> attr.resolve_types(A, globals(), locals()) <class 'A'> >>> attr.fields(A).a.type typing.List[A] >>> attr.fields(A).b.type <class 'B'>

attr.asdict

For example:

>>> @attr.s ... class C(object): ... x = attr.ib() ... y = attr.ib() >>> attr.asdict(C(1, C(2, 3))) {'x': 1, 'y': {'x': 2, 'y': 3}}

attr.astuple

For example:

>>> @attr.s ... class C(object): ... x = attr.ib() ... y = attr.ib() >>> attr.astuple(C(1,2)) (1, 2)

attrs includes some handy helpers for filtering the attributes in attr.asdict and `attr.astuple`:

attr.filters.include

attr.filters.exclude

See asdict for examples.

attr.evolve

For example:

>>> @attr.s ... class C(object): ... x = attr.ib() ... y = attr.ib() >>> i1 = C(1, 2) >>> i1 C(x=1, y=2) >>> i2 = attr.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.

validate

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.instance_of(int)) >>> i = C(1) >>> i.x = "1" >>> attr.validate(i) Traceback (most recent call last): ... TypeError: ("'x' must be <class 'int'> (got '1' that is a <class 'str'>).", ...)

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:

set_run_validators

get_run_validators

Validators

attrs comes with some common validators in the attrs.validators module:

attr.validators.lt

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.lt(42)) >>> C(41) C(x=41) >>> C(42) Traceback (most recent call last): ... ValueError: ("'x' must be < 42: 42")

attr.validators.le

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.le(42)) >>> C(42) C(x=42) >>> C(43) Traceback (most recent call last): ... ValueError: ("'x' must be <= 42: 43")

attr.validators.ge

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.ge(42)) >>> C(42) C(x=42) >>> C(41) Traceback (most recent call last): ... ValueError: ("'x' must be => 42: 41")

attr.validators.gt

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.gt(42)) >>> C(43) C(x=43) >>> C(42) Traceback (most recent call last): ... ValueError: ("'x' must be > 42: 42")

attr.validators.max_len

For example:

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

attr.validators.instance_of

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.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)

attr.validators.in

For example:

>>> import enum >>> class State(enum.Enum): ... ON = "on" ... OFF = "off" >>> @attr.s ... class C(object): ... state = attr.ib(validator=attr.validators.in(State)) ... val = attr.ib(validator=attr.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') >>> C(State.ON, 4) Traceback (most recent call last): ... ValueError: 'val' must be in [1, 2, 3] (got 4)

attr.validators.provides

attr.validators.and

For convenience, it's also possible to pass a list to attr.ib's validator argument.

Thus the following two statements are equivalent:

x = attr.ib(validator=attr.validators.and_(v1, v2, v3))
x = attr.ib(validator=[v1, v2, v3])

attr.validators.optional

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.optional(attr.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)

attr.validators.is_callable

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.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'>).

attr.validators.matches_re

For example:

>>> @attr.s ... class User(object): ... email = attr.ib(validator=attr.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=<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')

attr.validators.deep_iterable

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.deep_iterable( ... member_validator=attr.validators.instance_of(int), ... iterable_validator=attr.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')

attr.validators.deep_mapping

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.deep_mapping( ... key_validator=attr.validators.instance_of(str), ... value_validator=attr.validators.instance_of(int), ... mapping_validator=attr.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:

attr.validators.set_disabled

attr.validators.get_disabled

attr.validators.disabled

Converters

attr.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])

attr.converters.optional

For example:

>>> @attr.s ... class C(object): ... x = attr.ib(converter=attr.converters.optional(int)) >>> C(None) C(x=None) >>> C(42) C(x=42)

attr.converters.default_if_none

For example:

>>> @attr.s ... class C(object): ... x = attr.ib( ... converter=attr.converters.default_if_none("") ... ) >>> C(None) C(x='')

attr.converters.to_bool

For example:

>>> @attr.s ... class C(object): ... 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 "<stdin>", line 1, in <module> ValueError: Cannot convert value to bool: foo

Setters

These are helpers that you can use together with attr.s's and attr.ib's on_setattr arguments.

attr.setters.frozen

attr.setters.validate

attr.setters.convert

attr.setters.pipe

attr.setters.NO_OP

For example, only x is frozen here:

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

N.B. Please use attr.s's frozen argument to freeze whole classes; it is more efficient.

Next Generation APIs

These are Python 3.6 and later-only, and keyword-only APIs that call attr.s with different default values.

The most notable differences are:

  • automatically detect whether or not auto_attribs should be True
  • slots=True (see slotted classes for potentially surprising behaviors)
  • auto_exc=True
  • auto_detect=True
  • eq=True, but order=False
  • Converters and validators are run when you set an attribute (on_setattr=[attr.setters.convert, attr.setters.validate]).
  • Some options that aren't relevant to Python 3 have been dropped.

Please note that these are defaults and you're free to override them, just like before.

Since the Python ecosystem has settled on the term field for defining attributes, we have also added attr.field as a substitute for attr.ib.

21.3.0 Converters are also run on_setattr.

Note

attr.s and attr.ib (and their serious business cousins) aren't going anywhere. The new APIs build on top of them.

attr.define

mutable(same_as_define)

Alias for attr.define.

20.1.0

frozen(same_as_define)

Behaves the same as attr.define but sets frozen=True and on_setattr=None.

20.1.0

attr.field

Deprecated APIs

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`:

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(object): ... 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.

assoc