764 lines
23 KiB
ReStructuredText
764 lines
23 KiB
ReStructuredText
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 <https://en.wikipedia.org/wiki/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 <api-attr>`.
|
|
|
|
|
|
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())
|
|
<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'>).", ...)
|
|
|
|
|
|
.. _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 <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
|
|
|
|
|
|
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 "<stdin>", line 1, in <module>
|
|
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.
|