49643
|
1 |
# SPDX-License-Identifier: MIT |
|
2 |
|
|
3 |
""" |
|
4 |
These are Python 3.6+-only and keyword-only APIs that call `attr.s` and |
|
5 |
`attr.ib` with different default values. |
|
6 |
""" |
|
7 |
|
|
8 |
|
|
9 |
from functools import partial |
|
10 |
|
|
11 |
from . import setters |
|
12 |
from ._funcs import asdict as _asdict |
|
13 |
from ._funcs import astuple as _astuple |
|
14 |
from ._make import ( |
|
15 |
NOTHING, |
|
16 |
_frozen_setattrs, |
|
17 |
_ng_default_on_setattr, |
|
18 |
attrib, |
|
19 |
attrs, |
|
20 |
) |
|
21 |
from .exceptions import UnannotatedAttributeError |
|
22 |
|
|
23 |
|
|
24 |
def define( |
|
25 |
maybe_cls=None, |
|
26 |
*, |
|
27 |
these=None, |
|
28 |
repr=None, |
|
29 |
hash=None, |
|
30 |
init=None, |
|
31 |
slots=True, |
|
32 |
frozen=False, |
|
33 |
weakref_slot=True, |
|
34 |
str=False, |
|
35 |
auto_attribs=None, |
|
36 |
kw_only=False, |
|
37 |
cache_hash=False, |
|
38 |
auto_exc=True, |
|
39 |
eq=None, |
|
40 |
order=False, |
|
41 |
auto_detect=True, |
|
42 |
getstate_setstate=None, |
|
43 |
on_setattr=None, |
|
44 |
field_transformer=None, |
|
45 |
match_args=True, |
|
46 |
): |
|
47 |
r""" |
|
48 |
Define an ``attrs`` class. |
|
49 |
|
|
50 |
Differences to the classic `attr.s` that it uses underneath: |
|
51 |
|
|
52 |
- Automatically detect whether or not *auto_attribs* should be `True` (c.f. |
|
53 |
*auto_attribs* parameter). |
|
54 |
- If *frozen* is `False`, run converters and validators when setting an |
|
55 |
attribute by default. |
|
56 |
- *slots=True* |
|
57 |
|
|
58 |
.. caution:: |
|
59 |
|
|
60 |
Usually this has only upsides and few visible effects in everyday |
|
61 |
programming. But it *can* lead to some suprising behaviors, so please |
|
62 |
make sure to read :term:`slotted classes`. |
|
63 |
- *auto_exc=True* |
|
64 |
- *auto_detect=True* |
|
65 |
- *order=False* |
|
66 |
- Some options that were only relevant on Python 2 or were kept around for |
|
67 |
backwards-compatibility have been removed. |
|
68 |
|
|
69 |
Please note that these are all defaults and you can change them as you |
|
70 |
wish. |
|
71 |
|
|
72 |
:param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves |
|
73 |
exactly like `attr.s`. If left `None`, `attr.s` will try to guess: |
|
74 |
|
|
75 |
1. If any attributes are annotated and no unannotated `attrs.fields`\ s |
|
76 |
are found, it assumes *auto_attribs=True*. |
|
77 |
2. Otherwise it assumes *auto_attribs=False* and tries to collect |
|
78 |
`attrs.fields`\ s. |
|
79 |
|
|
80 |
For now, please refer to `attr.s` for the rest of the parameters. |
|
81 |
|
|
82 |
.. versionadded:: 20.1.0 |
|
83 |
.. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. |
|
84 |
""" |
|
85 |
|
|
86 |
def do_it(cls, auto_attribs): |
|
87 |
return attrs( |
|
88 |
maybe_cls=cls, |
|
89 |
these=these, |
|
90 |
repr=repr, |
|
91 |
hash=hash, |
|
92 |
init=init, |
|
93 |
slots=slots, |
|
94 |
frozen=frozen, |
|
95 |
weakref_slot=weakref_slot, |
|
96 |
str=str, |
|
97 |
auto_attribs=auto_attribs, |
|
98 |
kw_only=kw_only, |
|
99 |
cache_hash=cache_hash, |
|
100 |
auto_exc=auto_exc, |
|
101 |
eq=eq, |
|
102 |
order=order, |
|
103 |
auto_detect=auto_detect, |
|
104 |
collect_by_mro=True, |
|
105 |
getstate_setstate=getstate_setstate, |
|
106 |
on_setattr=on_setattr, |
|
107 |
field_transformer=field_transformer, |
|
108 |
match_args=match_args, |
|
109 |
) |
|
110 |
|
|
111 |
def wrap(cls): |
|
112 |
""" |
|
113 |
Making this a wrapper ensures this code runs during class creation. |
|
114 |
|
|
115 |
We also ensure that frozen-ness of classes is inherited. |
|
116 |
""" |
|
117 |
nonlocal frozen, on_setattr |
|
118 |
|
|
119 |
had_on_setattr = on_setattr not in (None, setters.NO_OP) |
|
120 |
|
|
121 |
# By default, mutable classes convert & validate on setattr. |
|
122 |
if frozen is False and on_setattr is None: |
|
123 |
on_setattr = _ng_default_on_setattr |
|
124 |
|
|
125 |
# However, if we subclass a frozen class, we inherit the immutability |
|
126 |
# and disable on_setattr. |
|
127 |
for base_cls in cls.__bases__: |
|
128 |
if base_cls.__setattr__ is _frozen_setattrs: |
|
129 |
if had_on_setattr: |
|
130 |
raise ValueError( |
|
131 |
"Frozen classes can't use on_setattr " |
|
132 |
"(frozen-ness was inherited)." |
|
133 |
) |
|
134 |
|
|
135 |
on_setattr = setters.NO_OP |
|
136 |
break |
|
137 |
|
|
138 |
if auto_attribs is not None: |
|
139 |
return do_it(cls, auto_attribs) |
|
140 |
|
|
141 |
try: |
|
142 |
return do_it(cls, True) |
|
143 |
except UnannotatedAttributeError: |
|
144 |
return do_it(cls, False) |
|
145 |
|
|
146 |
# maybe_cls's type depends on the usage of the decorator. It's a class |
|
147 |
# if it's used as `@attrs` but ``None`` if used as `@attrs()`. |
|
148 |
if maybe_cls is None: |
|
149 |
return wrap |
|
150 |
else: |
|
151 |
return wrap(maybe_cls) |
|
152 |
|
|
153 |
|
|
154 |
mutable = define |
|
155 |
frozen = partial(define, frozen=True, on_setattr=None) |
|
156 |
|
|
157 |
|
|
158 |
def field( |
|
159 |
*, |
|
160 |
default=NOTHING, |
|
161 |
validator=None, |
|
162 |
repr=True, |
|
163 |
hash=None, |
|
164 |
init=True, |
|
165 |
metadata=None, |
|
166 |
converter=None, |
|
167 |
factory=None, |
|
168 |
kw_only=False, |
|
169 |
eq=None, |
|
170 |
order=None, |
|
171 |
on_setattr=None, |
|
172 |
): |
|
173 |
""" |
|
174 |
Identical to `attr.ib`, except keyword-only and with some arguments |
|
175 |
removed. |
|
176 |
|
|
177 |
.. versionadded:: 20.1.0 |
|
178 |
""" |
|
179 |
return attrib( |
|
180 |
default=default, |
|
181 |
validator=validator, |
|
182 |
repr=repr, |
|
183 |
hash=hash, |
|
184 |
init=init, |
|
185 |
metadata=metadata, |
|
186 |
converter=converter, |
|
187 |
factory=factory, |
|
188 |
kw_only=kw_only, |
|
189 |
eq=eq, |
|
190 |
order=order, |
|
191 |
on_setattr=on_setattr, |
|
192 |
) |
|
193 |
|
|
194 |
|
|
195 |
def asdict(inst, *, recurse=True, filter=None, value_serializer=None): |
|
196 |
""" |
|
197 |
Same as `attr.asdict`, except that collections types are always retained |
|
198 |
and dict is always used as *dict_factory*. |
|
199 |
|
|
200 |
.. versionadded:: 21.3.0 |
|
201 |
""" |
|
202 |
return _asdict( |
|
203 |
inst=inst, |
|
204 |
recurse=recurse, |
|
205 |
filter=filter, |
|
206 |
value_serializer=value_serializer, |
|
207 |
retain_collection_types=True, |
|
208 |
) |
|
209 |
|
|
210 |
|
|
211 |
def astuple(inst, *, recurse=True, filter=None): |
|
212 |
""" |
|
213 |
Same as `attr.astuple`, except that collections types are always retained |
|
214 |
and `tuple` is always used as the *tuple_factory*. |
|
215 |
|
|
216 |
.. versionadded:: 21.3.0 |
|
217 |
""" |
|
218 |
return _astuple( |
|
219 |
inst=inst, recurse=recurse, filter=filter, retain_collection_types=True |
|
220 |
) |