_next_gen.py
220 lines
| 5.7 KiB
| text/x-python
|
PythonLexer
Matt Harbison
|
r50538 | # 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* | ||||
.. 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``. | ||||
""" | ||||
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 | ||||
) | ||||