| | |
| |
|
| | """ |
| | 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) |
| |
|
| | |
| | if frozen is False and on_setattr is None: |
| | on_setattr = _ng_default_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) |
| |
|
| | |
| | |
| | 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 |
| | ) |
| |
|