|
|
|
|
|
""" |
|
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 |
|
) |
|
|