123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- # 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 (
- NOTHING,
- _frozen_setattrs,
- _ng_default_on_setattr,
- 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"""
- Define an *attrs* class.
-
- Differences to the classic `attr.s` that it uses underneath:
-
- - Automatically detect whether or not *auto_attribs* should be `True` (c.f.
- *auto_attribs* parameter).
- - If *frozen* is `False`, run converters and validators when setting an
- attribute by default.
- - *slots=True*
-
- .. caution::
-
- Usually this has only upsides and few visible effects in everyday
- programming. But it *can* lead to some suprising 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.
-
- Please note that these are all defaults and you can change them as you
- wish.
-
- :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves
- exactly like `attr.s`. If left `None`, `attr.s` will try to guess:
-
- 1. If any attributes are annotated and no unannotated `attrs.fields`\ s
- are found, it assumes *auto_attribs=True*.
- 2. Otherwise it assumes *auto_attribs=False* and tries to collect
- `attrs.fields`\ s.
-
- For now, please refer to `attr.s` for the rest of the parameters.
-
- .. 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).
- """
-
- 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 = _ng_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:
- raise ValueError(
- "Frozen classes can't use on_setattr "
- "(frozen-ness was inherited)."
- )
-
- 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
- else:
- 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,
- ):
- """
- Identical to `attr.ib`, except keyword-only and with some arguments
- removed.
-
- .. versionadded:: 23.1.0
- The *type* parameter has been re-added; mostly for
- {func}`attrs.make_class`. Please note that type checkers ignore this
- metadata.
- .. versionadded:: 20.1.0
- """
- 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
- )
|