| # SPDX-License-Identifier: MIT |
| |
| """ |
| These are Python 3.6+-only and 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, |
| 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* (see :term:`slotted classes` for potentially surprising |
| behaviors) |
| - *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``. |
| """ |
| |
| def do_it(cls, auto_attribs): |
| return attrs( |
| maybe_cls=cls, |
| these=these, |
| repr=repr, |
| hash=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, |
| converter=None, |
| factory=None, |
| kw_only=False, |
| eq=None, |
| order=None, |
| on_setattr=None, |
| ): |
| """ |
| Identical to `attr.ib`, except keyword-only and with some arguments |
| removed. |
| |
| .. versionadded:: 20.1.0 |
| """ |
| return attrib( |
| default=default, |
| validator=validator, |
| repr=repr, |
| hash=hash, |
| init=init, |
| metadata=metadata, |
| converter=converter, |
| factory=factory, |
| kw_only=kw_only, |
| eq=eq, |
| order=order, |
| on_setattr=on_setattr, |
| ) |
| |
| |
| 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 |
| ) |