632 lines
24 KiB
Python
632 lines
24 KiB
Python
# SPDX-License-Identifier: MIT
|
|
|
|
"""
|
|
These are keyword-only APIs that call `attr.s` and `attr.ib` with different
|
|
default values.
|
|
"""
|
|
|
|
|
|
from functools import partial
|
|
|
|
from . import setters
|
|
from ._funcs import asdict as _asdict
|
|
from ._funcs import astuple as _astuple
|
|
from ._make import (
|
|
_DEFAULT_ON_SETATTR,
|
|
NOTHING,
|
|
_frozen_setattrs,
|
|
attrib,
|
|
attrs,
|
|
)
|
|
from .exceptions import UnannotatedAttributeError
|
|
|
|
|
|
def define(
|
|
maybe_cls=None,
|
|
*,
|
|
these=None,
|
|
repr=None,
|
|
unsafe_hash=None,
|
|
hash=None,
|
|
init=None,
|
|
slots=True,
|
|
frozen=False,
|
|
weakref_slot=True,
|
|
str=False,
|
|
auto_attribs=None,
|
|
kw_only=False,
|
|
cache_hash=False,
|
|
auto_exc=True,
|
|
eq=None,
|
|
order=False,
|
|
auto_detect=True,
|
|
getstate_setstate=None,
|
|
on_setattr=None,
|
|
field_transformer=None,
|
|
match_args=True,
|
|
):
|
|
r"""
|
|
A class decorator that adds :term:`dunder methods` according to
|
|
:term:`fields <field>` specified using :doc:`type annotations <types>`,
|
|
`field()` calls, or the *these* argument.
|
|
|
|
Since *attrs* patches or replaces an existing class, you cannot use
|
|
`object.__init_subclass__` with *attrs* classes, because it runs too early.
|
|
As a replacement, you can define ``__attrs_init_subclass__`` on your class.
|
|
It will be called by *attrs* classes that subclass it after they're
|
|
created. See also :ref:`init-subclass`.
|
|
|
|
Args:
|
|
slots (bool):
|
|
Create a :term:`slotted class <slotted classes>` that's more
|
|
memory-efficient. Slotted classes are generally superior to the
|
|
default dict classes, but have some gotchas you should know about,
|
|
so we encourage you to read the :term:`glossary entry <slotted
|
|
classes>`.
|
|
|
|
auto_detect (bool):
|
|
Instead of setting the *init*, *repr*, *eq*, and *hash* arguments
|
|
explicitly, assume they are set to True **unless any** of the
|
|
involved methods for one of the arguments is implemented in the
|
|
*current* class (meaning, it is *not* inherited from some base
|
|
class).
|
|
|
|
So, for example by implementing ``__eq__`` on a class yourself,
|
|
*attrs* will deduce ``eq=False`` and will create *neither*
|
|
``__eq__`` *nor* ``__ne__`` (but Python classes come with a
|
|
sensible ``__ne__`` by default, so it *should* be enough to only
|
|
implement ``__eq__`` in most cases).
|
|
|
|
Passing True or False` to *init*, *repr*, *eq*, *cmp*, or *hash*
|
|
overrides whatever *auto_detect* would determine.
|
|
|
|
auto_exc (bool):
|
|
If the class subclasses `BaseException` (which implicitly includes
|
|
any subclass of any exception), the following happens to behave
|
|
like a well-behaved Python exception class:
|
|
|
|
- the values for *eq*, *order*, and *hash* are ignored and the
|
|
instances compare and hash by the instance's ids [#]_ ,
|
|
- all attributes that are either passed into ``__init__`` or have a
|
|
default value are additionally available as a tuple in the
|
|
``args`` attribute,
|
|
- the value of *str* is ignored leaving ``__str__`` to base
|
|
classes.
|
|
|
|
.. [#]
|
|
Note that *attrs* will *not* remove existing implementations of
|
|
``__hash__`` or the equality methods. It just won't add own
|
|
ones.
|
|
|
|
on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]):
|
|
A callable that is run whenever the user attempts to set an
|
|
attribute (either by assignment like ``i.x = 42`` or by using
|
|
`setattr` like ``setattr(i, "x", 42)``). It receives the same
|
|
arguments as validators: the instance, the attribute that is being
|
|
modified, and the new value.
|
|
|
|
If no exception is raised, the attribute is set to the return value
|
|
of the callable.
|
|
|
|
If a list of callables is passed, they're automatically wrapped in
|
|
an `attrs.setters.pipe`.
|
|
|
|
If left None, the default behavior is to run converters and
|
|
validators whenever an attribute is set.
|
|
|
|
init (bool):
|
|
Create a ``__init__`` method that initializes the *attrs*
|
|
attributes. Leading underscores are stripped for the argument name,
|
|
unless an alias is set on the attribute.
|
|
|
|
.. seealso::
|
|
`init` shows advanced ways to customize the generated
|
|
``__init__`` method, including executing code before and after.
|
|
|
|
repr(bool):
|
|
Create a ``__repr__`` method with a human readable representation
|
|
of *attrs* attributes.
|
|
|
|
str (bool):
|
|
Create a ``__str__`` method that is identical to ``__repr__``. This
|
|
is usually not necessary except for `Exception`\ s.
|
|
|
|
eq (bool | None):
|
|
If True or None (default), add ``__eq__`` and ``__ne__`` methods
|
|
that check two instances for equality.
|
|
|
|
.. seealso::
|
|
`comparison` describes how to customize the comparison behavior
|
|
going as far comparing NumPy arrays.
|
|
|
|
order (bool | None):
|
|
If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__``
|
|
methods that behave like *eq* above and allow instances to be
|
|
ordered.
|
|
|
|
They compare the instances as if they were tuples of their *attrs*
|
|
attributes if and only if the types of both classes are
|
|
*identical*.
|
|
|
|
If `None` mirror value of *eq*.
|
|
|
|
.. seealso:: `comparison`
|
|
|
|
cmp (bool | None):
|
|
Setting *cmp* is equivalent to setting *eq* and *order* to the same
|
|
value. Must not be mixed with *eq* or *order*.
|
|
|
|
unsafe_hash (bool | None):
|
|
If None (default), the ``__hash__`` method is generated according
|
|
how *eq* and *frozen* are set.
|
|
|
|
1. If *both* are True, *attrs* will generate a ``__hash__`` for
|
|
you.
|
|
2. If *eq* is True and *frozen* is False, ``__hash__`` will be set
|
|
to None, marking it unhashable (which it is).
|
|
3. If *eq* is False, ``__hash__`` will be left untouched meaning
|
|
the ``__hash__`` method of the base class will be used. If the
|
|
base class is `object`, this means it will fall back to id-based
|
|
hashing.
|
|
|
|
Although not recommended, you can decide for yourself and force
|
|
*attrs* to create one (for example, if the class is immutable even
|
|
though you didn't freeze it programmatically) by passing True or
|
|
not. Both of these cases are rather special and should be used
|
|
carefully.
|
|
|
|
.. seealso::
|
|
|
|
- Our documentation on `hashing`,
|
|
- Python's documentation on `object.__hash__`,
|
|
- and the `GitHub issue that led to the default \ behavior
|
|
<https://github.com/python-attrs/attrs/issues/136>`_ for more
|
|
details.
|
|
|
|
hash (bool | None):
|
|
Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence.
|
|
|
|
cache_hash (bool):
|
|
Ensure that the object's hash code is computed only once and stored
|
|
on the object. If this is set to True, hashing must be either
|
|
explicitly or implicitly enabled for this class. If the hash code
|
|
is cached, avoid any reassignments of fields involved in hash code
|
|
computation or mutations of the objects those fields point to after
|
|
object creation. If such changes occur, the behavior of the
|
|
object's hash code is undefined.
|
|
|
|
frozen (bool):
|
|
Make instances immutable after initialization. If someone attempts
|
|
to modify a frozen instance, `attrs.exceptions.FrozenInstanceError`
|
|
is raised.
|
|
|
|
.. note::
|
|
|
|
1. This is achieved by installing a custom ``__setattr__``
|
|
method on your class, so you can't implement your own.
|
|
|
|
2. True immutability is impossible in Python.
|
|
|
|
3. This *does* have a minor a runtime performance `impact
|
|
<how-frozen>` when initializing new instances. In other
|
|
words: ``__init__`` is slightly slower with ``frozen=True``.
|
|
|
|
4. If a class is frozen, you cannot modify ``self`` in
|
|
``__attrs_post_init__`` or a self-written ``__init__``. You
|
|
can circumvent that limitation by using
|
|
``object.__setattr__(self, "attribute_name", value)``.
|
|
|
|
5. Subclasses of a frozen class are frozen too.
|
|
|
|
kw_only (bool):
|
|
Make all attributes keyword-only in the generated ``__init__`` (if
|
|
*init* is False, this parameter is ignored).
|
|
|
|
weakref_slot (bool):
|
|
Make instances weak-referenceable. This has no effect unless
|
|
*slots* is True.
|
|
|
|
field_transformer (~typing.Callable | None):
|
|
A function that is called with the original class object and all
|
|
fields right before *attrs* finalizes the class. You can use this,
|
|
for example, to automatically add converters or validators to
|
|
fields based on their types.
|
|
|
|
.. seealso:: `transform-fields`
|
|
|
|
match_args (bool):
|
|
If True (default), set ``__match_args__`` on the class to support
|
|
:pep:`634` (*Structural Pattern Matching*). It is a tuple of all
|
|
non-keyword-only ``__init__`` parameter names on Python 3.10 and
|
|
later. Ignored on older Python versions.
|
|
|
|
collect_by_mro (bool):
|
|
If True, *attrs* collects attributes from base classes correctly
|
|
according to the `method resolution order
|
|
<https://docs.python.org/3/howto/mro.html>`_. If False, *attrs*
|
|
will mimic the (wrong) behavior of `dataclasses` and :pep:`681`.
|
|
|
|
See also `issue #428
|
|
<https://github.com/python-attrs/attrs/issues/428>`_.
|
|
|
|
getstate_setstate (bool | None):
|
|
.. note::
|
|
|
|
This is usually only interesting for slotted classes and you
|
|
should probably just set *auto_detect* to True.
|
|
|
|
If True, ``__getstate__`` and ``__setstate__`` are generated and
|
|
attached to the class. This is necessary for slotted classes to be
|
|
pickleable. If left None, it's True by default for slotted classes
|
|
and False for dict classes.
|
|
|
|
If *auto_detect* is True, and *getstate_setstate* is left None, and
|
|
**either** ``__getstate__`` or ``__setstate__`` is detected
|
|
directly on the class (meaning: not inherited), it is set to False
|
|
(this is usually what you want).
|
|
|
|
auto_attribs (bool | None):
|
|
If True, look at type annotations to determine which attributes to
|
|
use, like `dataclasses`. If False, it will only look for explicit
|
|
:func:`field` class attributes, like classic *attrs*.
|
|
|
|
If left None, it will guess:
|
|
|
|
1. If any attributes are annotated and no unannotated
|
|
`attrs.field`\ s are found, it assumes *auto_attribs=True*.
|
|
2. Otherwise it assumes *auto_attribs=False* and tries to collect
|
|
`attrs.field`\ s.
|
|
|
|
If *attrs* decides to look at type annotations, **all** fields
|
|
**must** be annotated. If *attrs* encounters a field that is set to
|
|
a :func:`field` / `attr.ib` but lacks a type annotation, an
|
|
`attrs.exceptions.UnannotatedAttributeError` is raised. Use
|
|
``field_name: typing.Any = field(...)`` if you don't want to set a
|
|
type.
|
|
|
|
.. warning::
|
|
|
|
For features that use the attribute name to create decorators
|
|
(for example, :ref:`validators <validators>`), you still *must*
|
|
assign :func:`field` / `attr.ib` to them. Otherwise Python will
|
|
either not find the name or try to use the default value to
|
|
call, for example, ``validator`` on it.
|
|
|
|
Attributes annotated as `typing.ClassVar`, and attributes that are
|
|
neither annotated nor set to an `field()` are **ignored**.
|
|
|
|
these (dict[str, object]):
|
|
A dictionary of name to the (private) return value of `field()`
|
|
mappings. This is useful to avoid the definition of your attributes
|
|
within the class body because you can't (for example, if you want
|
|
to add ``__repr__`` methods to Django models) or don't want to.
|
|
|
|
If *these* is not `None`, *attrs* will *not* search the class body
|
|
for attributes and will *not* remove any attributes from it.
|
|
|
|
The order is deduced from the order of the attributes inside
|
|
*these*.
|
|
|
|
Arguably, this is a rather obscure feature.
|
|
|
|
.. versionadded:: 20.1.0
|
|
.. versionchanged:: 21.3.0 Converters are also run ``on_setattr``.
|
|
.. versionadded:: 22.2.0
|
|
*unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
|
|
.. versionchanged:: 24.1.0
|
|
Instances are not compared as tuples of attributes anymore, but using a
|
|
big ``and`` condition. This is faster and has more correct behavior for
|
|
uncomparable values like `math.nan`.
|
|
.. versionadded:: 24.1.0
|
|
If a class has an *inherited* classmethod called
|
|
``__attrs_init_subclass__``, it is executed after the class is created.
|
|
.. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*.
|
|
|
|
.. note::
|
|
|
|
The main differences to the classic `attr.s` are:
|
|
|
|
- Automatically detect whether or not *auto_attribs* should be `True`
|
|
(c.f. *auto_attribs* parameter).
|
|
- Converters and validators run when attributes are set by default --
|
|
if *frozen* is `False`.
|
|
- *slots=True*
|
|
|
|
Usually, this has only upsides and few visible effects in everyday
|
|
programming. But it *can* lead to some surprising behaviors, so
|
|
please make sure to read :term:`slotted classes`.
|
|
|
|
- *auto_exc=True*
|
|
- *auto_detect=True*
|
|
- *order=False*
|
|
- Some options that were only relevant on Python 2 or were kept around
|
|
for backwards-compatibility have been removed.
|
|
|
|
"""
|
|
|
|
def do_it(cls, auto_attribs):
|
|
return attrs(
|
|
maybe_cls=cls,
|
|
these=these,
|
|
repr=repr,
|
|
hash=hash,
|
|
unsafe_hash=unsafe_hash,
|
|
init=init,
|
|
slots=slots,
|
|
frozen=frozen,
|
|
weakref_slot=weakref_slot,
|
|
str=str,
|
|
auto_attribs=auto_attribs,
|
|
kw_only=kw_only,
|
|
cache_hash=cache_hash,
|
|
auto_exc=auto_exc,
|
|
eq=eq,
|
|
order=order,
|
|
auto_detect=auto_detect,
|
|
collect_by_mro=True,
|
|
getstate_setstate=getstate_setstate,
|
|
on_setattr=on_setattr,
|
|
field_transformer=field_transformer,
|
|
match_args=match_args,
|
|
)
|
|
|
|
def wrap(cls):
|
|
"""
|
|
Making this a wrapper ensures this code runs during class creation.
|
|
|
|
We also ensure that frozen-ness of classes is inherited.
|
|
"""
|
|
nonlocal frozen, on_setattr
|
|
|
|
had_on_setattr = on_setattr not in (None, setters.NO_OP)
|
|
|
|
# By default, mutable classes convert & validate on setattr.
|
|
if frozen is False and on_setattr is None:
|
|
on_setattr = _DEFAULT_ON_SETATTR
|
|
|
|
# However, if we subclass a frozen class, we inherit the immutability
|
|
# and disable on_setattr.
|
|
for base_cls in cls.__bases__:
|
|
if base_cls.__setattr__ is _frozen_setattrs:
|
|
if had_on_setattr:
|
|
msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)."
|
|
raise ValueError(msg)
|
|
|
|
on_setattr = setters.NO_OP
|
|
break
|
|
|
|
if auto_attribs is not None:
|
|
return do_it(cls, auto_attribs)
|
|
|
|
try:
|
|
return do_it(cls, True)
|
|
except UnannotatedAttributeError:
|
|
return do_it(cls, False)
|
|
|
|
# maybe_cls's type depends on the usage of the decorator. It's a class
|
|
# if it's used as `@attrs` but `None` if used as `@attrs()`.
|
|
if maybe_cls is None:
|
|
return wrap
|
|
|
|
return wrap(maybe_cls)
|
|
|
|
|
|
mutable = define
|
|
frozen = partial(define, frozen=True, on_setattr=None)
|
|
|
|
|
|
def field(
|
|
*,
|
|
default=NOTHING,
|
|
validator=None,
|
|
repr=True,
|
|
hash=None,
|
|
init=True,
|
|
metadata=None,
|
|
type=None,
|
|
converter=None,
|
|
factory=None,
|
|
kw_only=False,
|
|
eq=None,
|
|
order=None,
|
|
on_setattr=None,
|
|
alias=None,
|
|
):
|
|
"""
|
|
Create a new :term:`field` / :term:`attribute` on a class.
|
|
|
|
.. warning::
|
|
|
|
Does **nothing** unless the class is also decorated with
|
|
`attrs.define` (or similar)!
|
|
|
|
Args:
|
|
default:
|
|
A value that is used if an *attrs*-generated ``__init__`` is used
|
|
and no value is passed while instantiating or the attribute is
|
|
excluded using ``init=False``.
|
|
|
|
If the value is an instance of `attrs.Factory`, its callable will
|
|
be used to construct a new value (useful for mutable data types
|
|
like lists or dicts).
|
|
|
|
If a default is not set (or set manually to `attrs.NOTHING`), a
|
|
value *must* be supplied when instantiating; otherwise a
|
|
`TypeError` will be raised.
|
|
|
|
.. seealso:: `defaults`
|
|
|
|
factory (~typing.Callable):
|
|
Syntactic sugar for ``default=attr.Factory(factory)``.
|
|
|
|
validator (~typing.Callable | list[~typing.Callable]):
|
|
Callable that is called by *attrs*-generated ``__init__`` methods
|
|
after the instance has been initialized. They receive the
|
|
initialized instance, the :func:`~attrs.Attribute`, and the passed
|
|
value.
|
|
|
|
The return value is *not* inspected so the validator has to throw
|
|
an exception itself.
|
|
|
|
If a `list` is passed, its items are treated as validators and must
|
|
all pass.
|
|
|
|
Validators can be globally disabled and re-enabled using
|
|
`attrs.validators.get_disabled` / `attrs.validators.set_disabled`.
|
|
|
|
The validator can also be set using decorator notation as shown
|
|
below.
|
|
|
|
.. seealso:: :ref:`validators`
|
|
|
|
repr (bool | ~typing.Callable):
|
|
Include this attribute in the generated ``__repr__`` method. If
|
|
True, include the attribute; if False, omit it. By default, the
|
|
built-in ``repr()`` function is used. To override how the attribute
|
|
value is formatted, pass a ``callable`` that takes a single value
|
|
and returns a string. Note that the resulting string is used as-is,
|
|
which means it will be used directly *instead* of calling
|
|
``repr()`` (the default).
|
|
|
|
eq (bool | ~typing.Callable):
|
|
If True (default), include this attribute in the generated
|
|
``__eq__`` and ``__ne__`` methods that check two instances for
|
|
equality. To override how the attribute value is compared, pass a
|
|
callable that takes a single value and returns the value to be
|
|
compared.
|
|
|
|
.. seealso:: `comparison`
|
|
|
|
order (bool | ~typing.Callable):
|
|
If True (default), include this attributes in the generated
|
|
``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To
|
|
override how the attribute value is ordered, pass a callable that
|
|
takes a single value and returns the value to be ordered.
|
|
|
|
.. seealso:: `comparison`
|
|
|
|
cmp(bool | ~typing.Callable):
|
|
Setting *cmp* is equivalent to setting *eq* and *order* to the same
|
|
value. Must not be mixed with *eq* or *order*.
|
|
|
|
.. seealso:: `comparison`
|
|
|
|
hash (bool | None):
|
|
Include this attribute in the generated ``__hash__`` method. If
|
|
None (default), mirror *eq*'s value. This is the correct behavior
|
|
according the Python spec. Setting this value to anything else
|
|
than None is *discouraged*.
|
|
|
|
.. seealso:: `hashing`
|
|
|
|
init (bool):
|
|
Include this attribute in the generated ``__init__`` method.
|
|
|
|
It is possible to set this to False and set a default value. In
|
|
that case this attributed is unconditionally initialized with the
|
|
specified default value or factory.
|
|
|
|
.. seealso:: `init`
|
|
|
|
converter (typing.Callable | Converter):
|
|
A callable that is called by *attrs*-generated ``__init__`` methods
|
|
to convert attribute's value to the desired format.
|
|
|
|
If a vanilla callable is passed, it is given the passed-in value as
|
|
the only positional argument. It is possible to receive additional
|
|
arguments by wrapping the callable in a `Converter`.
|
|
|
|
Either way, the returned value will be used as the new value of the
|
|
attribute. The value is converted before being passed to the
|
|
validator, if any.
|
|
|
|
.. seealso:: :ref:`converters`
|
|
|
|
metadata (dict | None):
|
|
An arbitrary mapping, to be used by third-party code.
|
|
|
|
.. seealso:: `extending-metadata`.
|
|
|
|
type (type):
|
|
The type of the attribute. Nowadays, the preferred method to
|
|
specify the type is using a variable annotation (see :pep:`526`).
|
|
This argument is provided for backwards-compatibility and for usage
|
|
with `make_class`. Regardless of the approach used, the type will
|
|
be stored on ``Attribute.type``.
|
|
|
|
Please note that *attrs* doesn't do anything with this metadata by
|
|
itself. You can use it as part of your own code or for `static type
|
|
checking <types>`.
|
|
|
|
kw_only (bool):
|
|
Make this attribute keyword-only in the generated ``__init__`` (if
|
|
``init`` is False, this parameter is ignored).
|
|
|
|
on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]):
|
|
Allows to overwrite the *on_setattr* setting from `attr.s`. If left
|
|
None, the *on_setattr* value from `attr.s` is used. Set to
|
|
`attrs.setters.NO_OP` to run **no** `setattr` hooks for this
|
|
attribute -- regardless of the setting in `define()`.
|
|
|
|
alias (str | None):
|
|
Override this attribute's parameter name in the generated
|
|
``__init__`` method. If left None, default to ``name`` stripped
|
|
of leading underscores. See `private-attributes`.
|
|
|
|
.. versionadded:: 20.1.0
|
|
.. versionchanged:: 21.1.0
|
|
*eq*, *order*, and *cmp* also accept a custom callable
|
|
.. versionadded:: 22.2.0 *alias*
|
|
.. versionadded:: 23.1.0
|
|
The *type* parameter has been re-added; mostly for `attrs.make_class`.
|
|
Please note that type checkers ignore this metadata.
|
|
|
|
.. seealso::
|
|
|
|
`attr.ib`
|
|
"""
|
|
return attrib(
|
|
default=default,
|
|
validator=validator,
|
|
repr=repr,
|
|
hash=hash,
|
|
init=init,
|
|
metadata=metadata,
|
|
type=type,
|
|
converter=converter,
|
|
factory=factory,
|
|
kw_only=kw_only,
|
|
eq=eq,
|
|
order=order,
|
|
on_setattr=on_setattr,
|
|
alias=alias,
|
|
)
|
|
|
|
|
|
def asdict(inst, *, recurse=True, filter=None, value_serializer=None):
|
|
"""
|
|
Same as `attr.asdict`, except that collections types are always retained
|
|
and dict is always used as *dict_factory*.
|
|
|
|
.. versionadded:: 21.3.0
|
|
"""
|
|
return _asdict(
|
|
inst=inst,
|
|
recurse=recurse,
|
|
filter=filter,
|
|
value_serializer=value_serializer,
|
|
retain_collection_types=True,
|
|
)
|
|
|
|
|
|
def astuple(inst, *, recurse=True, filter=None):
|
|
"""
|
|
Same as `attr.astuple`, except that collections types are always retained
|
|
and `tuple` is always used as the *tuple_factory*.
|
|
|
|
.. versionadded:: 21.3.0
|
|
"""
|
|
return _astuple(
|
|
inst=inst, recurse=recurse, filter=filter, retain_collection_types=True
|
|
)
|