| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- # 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 NotAnAttrsClassError, 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,
- force_kw_only=False,
- ):
- 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 :data:`True` or :data:`False` to *init*, *repr*, *eq*, 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`
- 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 attributes keyword-only in the generated ``__init__`` (if
- *init* is False, this parameter is ignored). Attributes that
- explicitly set ``kw_only=False`` are not affected; base class
- attributes are also not affected.
- Also see *force_kw_only*.
- 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>`_.
- force_kw_only (bool):
- A back-compat flag for restoring pre-25.4.0 behavior. If True and
- ``kw_only=True``, all attributes are made keyword-only, including
- base class attributes, and those set to ``kw_only=False`` at the
- attribute level. Defaults to False.
- See also `issue #980
- <https://github.com/python-attrs/attrs/issues/980>`_.
- 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*.
- .. versionadded:: 24.3.0
- Unless already present, a ``__replace__`` method is automatically
- created for `copy.replace` (Python 3.13+ only).
- .. versionchanged:: 25.4.0
- *kw_only* now only applies to attributes defined in the current class,
- and respects attribute-level ``kw_only=False`` settings.
- .. versionadded:: 25.4.0
- Added *force_kw_only* to go back to the previous *kw_only* behavior.
- .. 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*
- - *force_kw_only=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,
- force_kw_only=force_kw_only,
- )
- 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=None,
- 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`
- 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 | None):
- Make this attribute keyword-only in the generated ``__init__`` (if
- *init* is False, this parameter is ignored). If None (default),
- mirror the setting from `attrs.define`.
- 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.
- .. versionchanged:: 25.4.0
- *kw_only* can now be None, and its default is also changed from False to
- None.
- .. 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
- )
- def inspect(cls):
- """
- Inspect the class and return its effective build parameters.
- Warning:
- This feature is currently **experimental** and is not covered by our
- strict backwards-compatibility guarantees.
- Args:
- cls: The *attrs*-decorated class to inspect.
- Returns:
- The effective build parameters of the class.
- Raises:
- NotAnAttrsClassError: If the class is not an *attrs*-decorated class.
- .. versionadded:: 25.4.0
- """
- try:
- return cls.__dict__["__attrs_props__"]
- except KeyError:
- msg = f"{cls!r} is not an attrs-decorated class."
- raise NotAnAttrsClassError(msg) from None
|