1 from __future__ import absolute_import, division, print_function |
1 # SPDX-License-Identifier: MIT |
2 |
2 |
3 import hashlib |
3 import copy |
4 import linecache |
4 import linecache |
|
5 import sys |
|
6 import types |
|
7 import typing |
5 |
8 |
6 from operator import itemgetter |
9 from operator import itemgetter |
7 |
10 |
8 from . import _config |
11 # We need to import _compat itself in addition to the _compat members to avoid |
9 from ._compat import PY2, iteritems, isclass, iterkeys, metadata_proxy |
12 # having the thread-local in the globals here. |
|
13 from . import _compat, _config, setters |
|
14 from ._compat import ( |
|
15 HAS_F_STRINGS, |
|
16 PY310, |
|
17 PYPY, |
|
18 _AnnotationExtractor, |
|
19 ordered_dict, |
|
20 set_closure_cell, |
|
21 ) |
10 from .exceptions import ( |
22 from .exceptions import ( |
11 DefaultAlreadySetError, |
23 DefaultAlreadySetError, |
12 FrozenInstanceError, |
24 FrozenInstanceError, |
13 NotAnAttrsClassError, |
25 NotAnAttrsClassError, |
|
26 UnannotatedAttributeError, |
14 ) |
27 ) |
15 |
28 |
16 |
29 |
17 # This is used at least twice, so cache it here. |
30 # This is used at least twice, so cache it here. |
18 _obj_setattr = object.__setattr__ |
31 _obj_setattr = object.__setattr__ |
19 _init_convert_pat = "__attr_convert_{}" |
32 _init_converter_pat = "__attr_converter_%s" |
20 _init_factory_pat = "__attr_factory_{}" |
33 _init_factory_pat = "__attr_factory_{}" |
21 _tuple_property_pat = " {attr_name} = property(itemgetter({index}))" |
34 _tuple_property_pat = ( |
22 _empty_metadata_singleton = metadata_proxy({}) |
35 " {attr_name} = _attrs_property(_attrs_itemgetter({index}))" |
23 |
36 ) |
24 |
37 _classvar_prefixes = ( |
25 class _Nothing(object): |
38 "typing.ClassVar", |
|
39 "t.ClassVar", |
|
40 "ClassVar", |
|
41 "typing_extensions.ClassVar", |
|
42 ) |
|
43 # we don't use a double-underscore prefix because that triggers |
|
44 # name mangling when trying to create a slot for the field |
|
45 # (when slots=True) |
|
46 _hash_cache_field = "_attrs_cached_hash" |
|
47 |
|
48 _empty_metadata_singleton = types.MappingProxyType({}) |
|
49 |
|
50 # Unique object for unequivocal getattr() defaults. |
|
51 _sentinel = object() |
|
52 |
|
53 _ng_default_on_setattr = setters.pipe(setters.convert, setters.validate) |
|
54 |
|
55 |
|
56 class _Nothing: |
26 """ |
57 """ |
27 Sentinel class to indicate the lack of a value when ``None`` is ambiguous. |
58 Sentinel class to indicate the lack of a value when ``None`` is ambiguous. |
28 |
59 |
29 All instances of `_Nothing` are equal. |
60 ``_Nothing`` is a singleton. There is only ever one of it. |
30 """ |
61 |
31 def __copy__(self): |
62 .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. |
32 return self |
63 """ |
33 |
64 |
34 def __deepcopy__(self, _): |
65 _singleton = None |
35 return self |
66 |
36 |
67 def __new__(cls): |
37 def __eq__(self, other): |
68 if _Nothing._singleton is None: |
38 return other.__class__ == _Nothing |
69 _Nothing._singleton = super().__new__(cls) |
39 |
70 return _Nothing._singleton |
40 def __ne__(self, other): |
|
41 return not self == other |
|
42 |
71 |
43 def __repr__(self): |
72 def __repr__(self): |
44 return "NOTHING" |
73 return "NOTHING" |
45 |
74 |
46 def __hash__(self): |
75 def __bool__(self): |
47 return 0xdeadbeef |
76 return False |
48 |
77 |
49 |
78 |
50 NOTHING = _Nothing() |
79 NOTHING = _Nothing() |
51 """ |
80 """ |
52 Sentinel to indicate the lack of a value when ``None`` is ambiguous. |
81 Sentinel to indicate the lack of a value when ``None`` is ambiguous. |
53 """ |
82 """ |
54 |
83 |
55 |
84 |
56 def attr(default=NOTHING, validator=None, |
85 class _CacheHashWrapper(int): |
57 repr=True, cmp=True, hash=None, init=True, |
86 """ |
58 convert=None, metadata={}): |
87 An integer subclass that pickles / copies as None |
59 r""" |
88 |
|
89 This is used for non-slots classes with ``cache_hash=True``, to avoid |
|
90 serializing a potentially (even likely) invalid hash value. Since ``None`` |
|
91 is the default value for uncalculated hashes, whenever this is copied, |
|
92 the copy's value for the hash should automatically reset. |
|
93 |
|
94 See GH #613 for more details. |
|
95 """ |
|
96 |
|
97 def __reduce__(self, _none_constructor=type(None), _args=()): |
|
98 return _none_constructor, _args |
|
99 |
|
100 |
|
101 def attrib( |
|
102 default=NOTHING, |
|
103 validator=None, |
|
104 repr=True, |
|
105 cmp=None, |
|
106 hash=None, |
|
107 init=True, |
|
108 metadata=None, |
|
109 type=None, |
|
110 converter=None, |
|
111 factory=None, |
|
112 kw_only=False, |
|
113 eq=None, |
|
114 order=None, |
|
115 on_setattr=None, |
|
116 ): |
|
117 """ |
60 Create a new attribute on a class. |
118 Create a new attribute on a class. |
61 |
119 |
62 .. warning:: |
120 .. warning:: |
63 |
121 |
64 Does *not* do anything unless the class is also decorated with |
122 Does *not* do anything unless the class is also decorated with |
65 :func:`attr.s`! |
123 `attr.s`! |
66 |
124 |
67 :param default: A value that is used if an ``attrs``-generated ``__init__`` |
125 :param default: A value that is used if an ``attrs``-generated ``__init__`` |
68 is used and no value is passed while instantiating or the attribute is |
126 is used and no value is passed while instantiating or the attribute is |
69 excluded using ``init=False``. |
127 excluded using ``init=False``. |
70 |
128 |
71 If the value is an instance of :class:`Factory`, its callable will be |
129 If the value is an instance of `attrs.Factory`, its callable will be |
72 used to construct a new value (useful for mutable datatypes like lists |
130 used to construct a new value (useful for mutable data types like lists |
73 or dicts). |
131 or dicts). |
74 |
132 |
75 If a default is not set (or set manually to ``attr.NOTHING``), a value |
133 If a default is not set (or set manually to `attrs.NOTHING`), a value |
76 *must* be supplied when instantiating; otherwise a :exc:`TypeError` |
134 *must* be supplied when instantiating; otherwise a `TypeError` |
77 will be raised. |
135 will be raised. |
78 |
136 |
79 The default can also be set using decorator notation as shown below. |
137 The default can also be set using decorator notation as shown below. |
80 |
138 |
81 :type default: Any value. |
139 :type default: Any value |
82 |
140 |
83 :param validator: :func:`callable` that is called by ``attrs``-generated |
141 :param callable factory: Syntactic sugar for |
|
142 ``default=attr.Factory(factory)``. |
|
143 |
|
144 :param validator: `callable` that is called by ``attrs``-generated |
84 ``__init__`` methods after the instance has been initialized. They |
145 ``__init__`` methods after the instance has been initialized. They |
85 receive the initialized instance, the :class:`Attribute`, and the |
146 receive the initialized instance, the :func:`~attrs.Attribute`, and the |
86 passed value. |
147 passed value. |
87 |
148 |
88 The return value is *not* inspected so the validator has to throw an |
149 The return value is *not* inspected so the validator has to throw an |
89 exception itself. |
150 exception itself. |
90 |
151 |
91 If a ``list`` is passed, its items are treated as validators and must |
152 If a `list` is passed, its items are treated as validators and must |
92 all pass. |
153 all pass. |
93 |
154 |
94 Validators can be globally disabled and re-enabled using |
155 Validators can be globally disabled and re-enabled using |
95 :func:`get_run_validators`. |
156 `get_run_validators`. |
96 |
157 |
97 The validator can also be set using decorator notation as shown below. |
158 The validator can also be set using decorator notation as shown below. |
98 |
159 |
99 :type validator: ``callable`` or a ``list`` of ``callable``\ s. |
160 :type validator: `callable` or a `list` of `callable`\\ s. |
100 |
161 |
101 :param bool repr: Include this attribute in the generated ``__repr__`` |
162 :param repr: Include this attribute in the generated ``__repr__`` |
102 method. |
163 method. If ``True``, include the attribute; if ``False``, omit it. By |
103 :param bool cmp: Include this attribute in the generated comparison methods |
164 default, the built-in ``repr()`` function is used. To override how the |
104 (``__eq__`` et al). |
165 attribute value is formatted, pass a ``callable`` that takes a single |
105 :param hash: Include this attribute in the generated ``__hash__`` |
166 value and returns a string. Note that the resulting string is used |
106 method. If ``None`` (default), mirror *cmp*'s value. This is the |
167 as-is, i.e. it will be used directly *instead* of calling ``repr()`` |
107 correct behavior according the Python spec. Setting this value to |
168 (the default). |
108 anything else than ``None`` is *discouraged*. |
169 :type repr: a `bool` or a `callable` to use a custom function. |
109 :type hash: ``bool`` or ``None`` |
170 |
|
171 :param eq: If ``True`` (default), include this attribute in the |
|
172 generated ``__eq__`` and ``__ne__`` methods that check two instances |
|
173 for equality. To override how the attribute value is compared, |
|
174 pass a ``callable`` that takes a single value and returns the value |
|
175 to be compared. |
|
176 :type eq: a `bool` or a `callable`. |
|
177 |
|
178 :param order: If ``True`` (default), include this attributes in the |
|
179 generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. |
|
180 To override how the attribute value is ordered, |
|
181 pass a ``callable`` that takes a single value and returns the value |
|
182 to be ordered. |
|
183 :type order: a `bool` or a `callable`. |
|
184 |
|
185 :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the |
|
186 same value. Must not be mixed with *eq* or *order*. |
|
187 :type cmp: a `bool` or a `callable`. |
|
188 |
|
189 :param Optional[bool] hash: Include this attribute in the generated |
|
190 ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This |
|
191 is the correct behavior according the Python spec. Setting this value |
|
192 to anything else than ``None`` is *discouraged*. |
110 :param bool init: Include this attribute in the generated ``__init__`` |
193 :param bool init: Include this attribute in the generated ``__init__`` |
111 method. It is possible to set this to ``False`` and set a default |
194 method. It is possible to set this to ``False`` and set a default |
112 value. In that case this attributed is unconditionally initialized |
195 value. In that case this attributed is unconditionally initialized |
113 with the specified default value or factory. |
196 with the specified default value or factory. |
114 :param callable convert: :func:`callable` that is called by |
197 :param callable converter: `callable` that is called by |
115 ``attrs``-generated ``__init__`` methods to convert attribute's value |
198 ``attrs``-generated ``__init__`` methods to convert attribute's value |
116 to the desired format. It is given the passed-in value, and the |
199 to the desired format. It is given the passed-in value, and the |
117 returned value will be used as the new value of the attribute. The |
200 returned value will be used as the new value of the attribute. The |
118 value is converted before being passed to the validator, if any. |
201 value is converted before being passed to the validator, if any. |
119 :param metadata: An arbitrary mapping, to be used by third-party |
202 :param metadata: An arbitrary mapping, to be used by third-party |
120 components. See :ref:`extending_metadata`. |
203 components. See `extending_metadata`. |
121 |
204 :param type: The type of the attribute. In Python 3.6 or greater, the |
122 .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. |
205 preferred method to specify the type is using a variable annotation |
123 .. versionchanged:: 17.1.0 |
206 (see :pep:`526`). |
124 *hash* is ``None`` and therefore mirrors *cmp* by default . |
207 This argument is provided for backward compatibility. |
125 """ |
208 Regardless of the approach used, the type will be stored on |
|
209 ``Attribute.type``. |
|
210 |
|
211 Please note that ``attrs`` doesn't do anything with this metadata by |
|
212 itself. You can use it as part of your own code or for |
|
213 `static type checking <types>`. |
|
214 :param kw_only: Make this attribute keyword-only (Python 3+) |
|
215 in the generated ``__init__`` (if ``init`` is ``False``, this |
|
216 parameter is ignored). |
|
217 :param on_setattr: Allows to overwrite the *on_setattr* setting from |
|
218 `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used. |
|
219 Set to `attrs.setters.NO_OP` to run **no** `setattr` hooks for this |
|
220 attribute -- regardless of the setting in `attr.s`. |
|
221 :type on_setattr: `callable`, or a list of callables, or `None`, or |
|
222 `attrs.setters.NO_OP` |
|
223 |
|
224 .. versionadded:: 15.2.0 *convert* |
|
225 .. versionadded:: 16.3.0 *metadata* |
|
226 .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. |
|
227 .. versionchanged:: 17.1.0 |
|
228 *hash* is ``None`` and therefore mirrors *eq* by default. |
|
229 .. versionadded:: 17.3.0 *type* |
|
230 .. deprecated:: 17.4.0 *convert* |
|
231 .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated |
|
232 *convert* to achieve consistency with other noun-based arguments. |
|
233 .. versionadded:: 18.1.0 |
|
234 ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. |
|
235 .. versionadded:: 18.2.0 *kw_only* |
|
236 .. versionchanged:: 19.2.0 *convert* keyword argument removed. |
|
237 .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. |
|
238 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. |
|
239 .. versionadded:: 19.2.0 *eq* and *order* |
|
240 .. versionadded:: 20.1.0 *on_setattr* |
|
241 .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 |
|
242 .. versionchanged:: 21.1.0 |
|
243 *eq*, *order*, and *cmp* also accept a custom callable |
|
244 .. versionchanged:: 21.1.0 *cmp* undeprecated |
|
245 """ |
|
246 eq, eq_key, order, order_key = _determine_attrib_eq_order( |
|
247 cmp, eq, order, True |
|
248 ) |
|
249 |
126 if hash is not None and hash is not True and hash is not False: |
250 if hash is not None and hash is not True and hash is not False: |
127 raise TypeError( |
251 raise TypeError( |
128 "Invalid value for hash. Must be True, False, or None." |
252 "Invalid value for hash. Must be True, False, or None." |
129 ) |
253 ) |
|
254 |
|
255 if factory is not None: |
|
256 if default is not NOTHING: |
|
257 raise ValueError( |
|
258 "The `default` and `factory` arguments are mutually " |
|
259 "exclusive." |
|
260 ) |
|
261 if not callable(factory): |
|
262 raise ValueError("The `factory` argument must be a callable.") |
|
263 default = Factory(factory) |
|
264 |
|
265 if metadata is None: |
|
266 metadata = {} |
|
267 |
|
268 # Apply syntactic sugar by auto-wrapping. |
|
269 if isinstance(on_setattr, (list, tuple)): |
|
270 on_setattr = setters.pipe(*on_setattr) |
|
271 |
|
272 if validator and isinstance(validator, (list, tuple)): |
|
273 validator = and_(*validator) |
|
274 |
|
275 if converter and isinstance(converter, (list, tuple)): |
|
276 converter = pipe(*converter) |
|
277 |
130 return _CountingAttr( |
278 return _CountingAttr( |
131 default=default, |
279 default=default, |
132 validator=validator, |
280 validator=validator, |
133 repr=repr, |
281 repr=repr, |
134 cmp=cmp, |
282 cmp=None, |
135 hash=hash, |
283 hash=hash, |
136 init=init, |
284 init=init, |
137 convert=convert, |
285 converter=converter, |
138 metadata=metadata, |
286 metadata=metadata, |
|
287 type=type, |
|
288 kw_only=kw_only, |
|
289 eq=eq, |
|
290 eq_key=eq_key, |
|
291 order=order, |
|
292 order_key=order_key, |
|
293 on_setattr=on_setattr, |
139 ) |
294 ) |
|
295 |
|
296 |
|
297 def _compile_and_eval(script, globs, locs=None, filename=""): |
|
298 """ |
|
299 "Exec" the script with the given global (globs) and local (locs) variables. |
|
300 """ |
|
301 bytecode = compile(script, filename, "exec") |
|
302 eval(bytecode, globs, locs) |
|
303 |
|
304 |
|
305 def _make_method(name, script, filename, globs): |
|
306 """ |
|
307 Create the method with the script given and return the method object. |
|
308 """ |
|
309 locs = {} |
|
310 |
|
311 # In order of debuggers like PDB being able to step through the code, |
|
312 # we add a fake linecache entry. |
|
313 count = 1 |
|
314 base_filename = filename |
|
315 while True: |
|
316 linecache_tuple = ( |
|
317 len(script), |
|
318 None, |
|
319 script.splitlines(True), |
|
320 filename, |
|
321 ) |
|
322 old_val = linecache.cache.setdefault(filename, linecache_tuple) |
|
323 if old_val == linecache_tuple: |
|
324 break |
|
325 else: |
|
326 filename = "{}-{}>".format(base_filename[:-1], count) |
|
327 count += 1 |
|
328 |
|
329 _compile_and_eval(script, globs, locs, filename) |
|
330 |
|
331 return locs[name] |
140 |
332 |
141 |
333 |
142 def _make_attr_tuple_class(cls_name, attr_names): |
334 def _make_attr_tuple_class(cls_name, attr_names): |
143 """ |
335 """ |
144 Create a tuple subclass to hold `Attribute`s for an `attrs` class. |
336 Create a tuple subclass to hold `Attribute`s for an `attrs` class. |
154 "class {}(tuple):".format(attr_class_name), |
346 "class {}(tuple):".format(attr_class_name), |
155 " __slots__ = ()", |
347 " __slots__ = ()", |
156 ] |
348 ] |
157 if attr_names: |
349 if attr_names: |
158 for i, attr_name in enumerate(attr_names): |
350 for i, attr_name in enumerate(attr_names): |
159 attr_class_template.append(_tuple_property_pat.format( |
351 attr_class_template.append( |
160 index=i, |
352 _tuple_property_pat.format(index=i, attr_name=attr_name) |
161 attr_name=attr_name, |
353 ) |
162 )) |
|
163 else: |
354 else: |
164 attr_class_template.append(" pass") |
355 attr_class_template.append(" pass") |
165 globs = {"itemgetter": itemgetter} |
356 globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property} |
166 eval(compile("\n".join(attr_class_template), "", "exec"), globs) |
357 _compile_and_eval("\n".join(attr_class_template), globs) |
167 return globs[attr_class_name] |
358 return globs[attr_class_name] |
168 |
359 |
169 |
360 |
170 def _transform_attrs(cls, these): |
361 # Tuple class for extracted attributes from a class definition. |
171 """ |
362 # `base_attrs` is a subset of `attrs`. |
172 Transforms all `_CountingAttr`s on a class into `Attribute`s and saves the |
363 _Attributes = _make_attr_tuple_class( |
173 list in `__attrs_attrs__`. |
364 "_Attributes", |
|
365 [ |
|
366 # all attributes to build dunder methods for |
|
367 "attrs", |
|
368 # attributes that have been inherited |
|
369 "base_attrs", |
|
370 # map inherited attributes to their originating classes |
|
371 "base_attrs_map", |
|
372 ], |
|
373 ) |
|
374 |
|
375 |
|
376 def _is_class_var(annot): |
|
377 """ |
|
378 Check whether *annot* is a typing.ClassVar. |
|
379 |
|
380 The string comparison hack is used to avoid evaluating all string |
|
381 annotations which would put attrs-based classes at a performance |
|
382 disadvantage compared to plain old classes. |
|
383 """ |
|
384 annot = str(annot) |
|
385 |
|
386 # Annotation can be quoted. |
|
387 if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): |
|
388 annot = annot[1:-1] |
|
389 |
|
390 return annot.startswith(_classvar_prefixes) |
|
391 |
|
392 |
|
393 def _has_own_attribute(cls, attrib_name): |
|
394 """ |
|
395 Check whether *cls* defines *attrib_name* (and doesn't just inherit it). |
|
396 |
|
397 Requires Python 3. |
|
398 """ |
|
399 attr = getattr(cls, attrib_name, _sentinel) |
|
400 if attr is _sentinel: |
|
401 return False |
|
402 |
|
403 for base_cls in cls.__mro__[1:]: |
|
404 a = getattr(base_cls, attrib_name, None) |
|
405 if attr is a: |
|
406 return False |
|
407 |
|
408 return True |
|
409 |
|
410 |
|
411 def _get_annotations(cls): |
|
412 """ |
|
413 Get annotations for *cls*. |
|
414 """ |
|
415 if _has_own_attribute(cls, "__annotations__"): |
|
416 return cls.__annotations__ |
|
417 |
|
418 return {} |
|
419 |
|
420 |
|
421 def _counter_getter(e): |
|
422 """ |
|
423 Key function for sorting to avoid re-creating a lambda for every class. |
|
424 """ |
|
425 return e[1].counter |
|
426 |
|
427 |
|
428 def _collect_base_attrs(cls, taken_attr_names): |
|
429 """ |
|
430 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. |
|
431 """ |
|
432 base_attrs = [] |
|
433 base_attr_map = {} # A dictionary of base attrs to their classes. |
|
434 |
|
435 # Traverse the MRO and collect attributes. |
|
436 for base_cls in reversed(cls.__mro__[1:-1]): |
|
437 for a in getattr(base_cls, "__attrs_attrs__", []): |
|
438 if a.inherited or a.name in taken_attr_names: |
|
439 continue |
|
440 |
|
441 a = a.evolve(inherited=True) |
|
442 base_attrs.append(a) |
|
443 base_attr_map[a.name] = base_cls |
|
444 |
|
445 # For each name, only keep the freshest definition i.e. the furthest at the |
|
446 # back. base_attr_map is fine because it gets overwritten with every new |
|
447 # instance. |
|
448 filtered = [] |
|
449 seen = set() |
|
450 for a in reversed(base_attrs): |
|
451 if a.name in seen: |
|
452 continue |
|
453 filtered.insert(0, a) |
|
454 seen.add(a.name) |
|
455 |
|
456 return filtered, base_attr_map |
|
457 |
|
458 |
|
459 def _collect_base_attrs_broken(cls, taken_attr_names): |
|
460 """ |
|
461 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. |
|
462 |
|
463 N.B. *taken_attr_names* will be mutated. |
|
464 |
|
465 Adhere to the old incorrect behavior. |
|
466 |
|
467 Notably it collects from the front and considers inherited attributes which |
|
468 leads to the buggy behavior reported in #428. |
|
469 """ |
|
470 base_attrs = [] |
|
471 base_attr_map = {} # A dictionary of base attrs to their classes. |
|
472 |
|
473 # Traverse the MRO and collect attributes. |
|
474 for base_cls in cls.__mro__[1:-1]: |
|
475 for a in getattr(base_cls, "__attrs_attrs__", []): |
|
476 if a.name in taken_attr_names: |
|
477 continue |
|
478 |
|
479 a = a.evolve(inherited=True) |
|
480 taken_attr_names.add(a.name) |
|
481 base_attrs.append(a) |
|
482 base_attr_map[a.name] = base_cls |
|
483 |
|
484 return base_attrs, base_attr_map |
|
485 |
|
486 |
|
487 def _transform_attrs( |
|
488 cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer |
|
489 ): |
|
490 """ |
|
491 Transform all `_CountingAttr`s on a class into `Attribute`s. |
174 |
492 |
175 If *these* is passed, use that and don't look for them on the class. |
493 If *these* is passed, use that and don't look for them on the class. |
176 """ |
494 |
177 super_cls = [] |
495 *collect_by_mro* is True, collect them in the correct MRO order, otherwise |
178 for c in reversed(cls.__mro__[1:-1]): |
496 use the old -- incorrect -- order. See #428. |
179 sub_attrs = getattr(c, "__attrs_attrs__", None) |
497 |
180 if sub_attrs is not None: |
498 Return an `_Attributes`. |
181 super_cls.extend(a for a in sub_attrs if a not in super_cls) |
499 """ |
182 if these is None: |
500 cd = cls.__dict__ |
183 ca_list = [(name, attr) |
501 anns = _get_annotations(cls) |
184 for name, attr |
502 |
185 in cls.__dict__.items() |
503 if these is not None: |
186 if isinstance(attr, _CountingAttr)] |
504 ca_list = [(name, ca) for name, ca in these.items()] |
|
505 |
|
506 if not isinstance(these, ordered_dict): |
|
507 ca_list.sort(key=_counter_getter) |
|
508 elif auto_attribs is True: |
|
509 ca_names = { |
|
510 name |
|
511 for name, attr in cd.items() |
|
512 if isinstance(attr, _CountingAttr) |
|
513 } |
|
514 ca_list = [] |
|
515 annot_names = set() |
|
516 for attr_name, type in anns.items(): |
|
517 if _is_class_var(type): |
|
518 continue |
|
519 annot_names.add(attr_name) |
|
520 a = cd.get(attr_name, NOTHING) |
|
521 |
|
522 if not isinstance(a, _CountingAttr): |
|
523 if a is NOTHING: |
|
524 a = attrib() |
|
525 else: |
|
526 a = attrib(default=a) |
|
527 ca_list.append((attr_name, a)) |
|
528 |
|
529 unannotated = ca_names - annot_names |
|
530 if len(unannotated) > 0: |
|
531 raise UnannotatedAttributeError( |
|
532 "The following `attr.ib`s lack a type annotation: " |
|
533 + ", ".join( |
|
534 sorted(unannotated, key=lambda n: cd.get(n).counter) |
|
535 ) |
|
536 + "." |
|
537 ) |
187 else: |
538 else: |
188 ca_list = [(name, ca) |
539 ca_list = sorted( |
189 for name, ca |
540 ( |
190 in iteritems(these)] |
541 (name, attr) |
191 |
542 for name, attr in cd.items() |
192 non_super_attrs = [ |
543 if isinstance(attr, _CountingAttr) |
193 Attribute.from_counting_attr(name=attr_name, ca=ca) |
544 ), |
194 for attr_name, ca |
545 key=lambda e: e[1].counter, |
195 in sorted(ca_list, key=lambda e: e[1].counter) |
546 ) |
|
547 |
|
548 own_attrs = [ |
|
549 Attribute.from_counting_attr( |
|
550 name=attr_name, ca=ca, type=anns.get(attr_name) |
|
551 ) |
|
552 for attr_name, ca in ca_list |
196 ] |
553 ] |
197 attr_names = [a.name for a in super_cls + non_super_attrs] |
554 |
198 |
555 if collect_by_mro: |
199 AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) |
556 base_attrs, base_attr_map = _collect_base_attrs( |
200 |
557 cls, {a.name for a in own_attrs} |
201 cls.__attrs_attrs__ = AttrsClass(super_cls + [ |
558 ) |
202 Attribute.from_counting_attr(name=attr_name, ca=ca) |
559 else: |
203 for attr_name, ca |
560 base_attrs, base_attr_map = _collect_base_attrs_broken( |
204 in sorted(ca_list, key=lambda e: e[1].counter) |
561 cls, {a.name for a in own_attrs} |
205 ]) |
562 ) |
206 |
563 |
|
564 if kw_only: |
|
565 own_attrs = [a.evolve(kw_only=True) for a in own_attrs] |
|
566 base_attrs = [a.evolve(kw_only=True) for a in base_attrs] |
|
567 |
|
568 attrs = base_attrs + own_attrs |
|
569 |
|
570 # Mandatory vs non-mandatory attr order only matters when they are part of |
|
571 # the __init__ signature and when they aren't kw_only (which are moved to |
|
572 # the end and can be mandatory or non-mandatory in any order, as they will |
|
573 # be specified as keyword args anyway). Check the order of those attrs: |
207 had_default = False |
574 had_default = False |
208 for a in cls.__attrs_attrs__: |
575 for a in (a for a in attrs if a.init is not False and a.kw_only is False): |
209 if these is None and a not in super_cls: |
576 if had_default is True and a.default is NOTHING: |
210 setattr(cls, a.name, a) |
|
211 if had_default is True and a.default is NOTHING and a.init is True: |
|
212 raise ValueError( |
577 raise ValueError( |
213 "No mandatory attributes allowed after an attribute with a " |
578 "No mandatory attributes allowed after an attribute with a " |
214 "default value or factory. Attribute in question: {a!r}" |
579 "default value or factory. Attribute in question: %r" % (a,) |
215 .format(a=a) |
|
216 ) |
580 ) |
217 elif had_default is False and \ |
581 |
218 a.default is not NOTHING and \ |
582 if had_default is False and a.default is not NOTHING: |
219 a.init is not False: |
|
220 had_default = True |
583 had_default = True |
221 |
584 |
222 |
585 if field_transformer is not None: |
223 def _frozen_setattrs(self, name, value): |
586 attrs = field_transformer(cls, attrs) |
224 """ |
587 |
225 Attached to frozen classes as __setattr__. |
588 # Create AttrsClass *after* applying the field_transformer since it may |
|
589 # add or remove attributes! |
|
590 attr_names = [a.name for a in attrs] |
|
591 AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) |
|
592 |
|
593 return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map)) |
|
594 |
|
595 |
|
596 if PYPY: |
|
597 |
|
598 def _frozen_setattrs(self, name, value): |
|
599 """ |
|
600 Attached to frozen classes as __setattr__. |
|
601 """ |
|
602 if isinstance(self, BaseException) and name in ( |
|
603 "__cause__", |
|
604 "__context__", |
|
605 ): |
|
606 BaseException.__setattr__(self, name, value) |
|
607 return |
|
608 |
|
609 raise FrozenInstanceError() |
|
610 |
|
611 else: |
|
612 |
|
613 def _frozen_setattrs(self, name, value): |
|
614 """ |
|
615 Attached to frozen classes as __setattr__. |
|
616 """ |
|
617 raise FrozenInstanceError() |
|
618 |
|
619 |
|
620 def _frozen_delattrs(self, name): |
|
621 """ |
|
622 Attached to frozen classes as __delattr__. |
226 """ |
623 """ |
227 raise FrozenInstanceError() |
624 raise FrozenInstanceError() |
228 |
625 |
229 |
626 |
230 def _frozen_delattrs(self, name): |
627 class _ClassBuilder: |
231 """ |
628 """ |
232 Attached to frozen classes as __delattr__. |
629 Iteratively build *one* class. |
233 """ |
630 """ |
234 raise FrozenInstanceError() |
631 |
235 |
632 __slots__ = ( |
236 |
633 "_attr_names", |
237 def attributes(maybe_cls=None, these=None, repr_ns=None, |
634 "_attrs", |
238 repr=True, cmp=True, hash=None, init=True, |
635 "_base_attr_map", |
239 slots=False, frozen=False, str=False): |
636 "_base_names", |
|
637 "_cache_hash", |
|
638 "_cls", |
|
639 "_cls_dict", |
|
640 "_delete_attribs", |
|
641 "_frozen", |
|
642 "_has_pre_init", |
|
643 "_has_post_init", |
|
644 "_is_exc", |
|
645 "_on_setattr", |
|
646 "_slots", |
|
647 "_weakref_slot", |
|
648 "_wrote_own_setattr", |
|
649 "_has_custom_setattr", |
|
650 ) |
|
651 |
|
652 def __init__( |
|
653 self, |
|
654 cls, |
|
655 these, |
|
656 slots, |
|
657 frozen, |
|
658 weakref_slot, |
|
659 getstate_setstate, |
|
660 auto_attribs, |
|
661 kw_only, |
|
662 cache_hash, |
|
663 is_exc, |
|
664 collect_by_mro, |
|
665 on_setattr, |
|
666 has_custom_setattr, |
|
667 field_transformer, |
|
668 ): |
|
669 attrs, base_attrs, base_map = _transform_attrs( |
|
670 cls, |
|
671 these, |
|
672 auto_attribs, |
|
673 kw_only, |
|
674 collect_by_mro, |
|
675 field_transformer, |
|
676 ) |
|
677 |
|
678 self._cls = cls |
|
679 self._cls_dict = dict(cls.__dict__) if slots else {} |
|
680 self._attrs = attrs |
|
681 self._base_names = {a.name for a in base_attrs} |
|
682 self._base_attr_map = base_map |
|
683 self._attr_names = tuple(a.name for a in attrs) |
|
684 self._slots = slots |
|
685 self._frozen = frozen |
|
686 self._weakref_slot = weakref_slot |
|
687 self._cache_hash = cache_hash |
|
688 self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) |
|
689 self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) |
|
690 self._delete_attribs = not bool(these) |
|
691 self._is_exc = is_exc |
|
692 self._on_setattr = on_setattr |
|
693 |
|
694 self._has_custom_setattr = has_custom_setattr |
|
695 self._wrote_own_setattr = False |
|
696 |
|
697 self._cls_dict["__attrs_attrs__"] = self._attrs |
|
698 |
|
699 if frozen: |
|
700 self._cls_dict["__setattr__"] = _frozen_setattrs |
|
701 self._cls_dict["__delattr__"] = _frozen_delattrs |
|
702 |
|
703 self._wrote_own_setattr = True |
|
704 elif on_setattr in ( |
|
705 _ng_default_on_setattr, |
|
706 setters.validate, |
|
707 setters.convert, |
|
708 ): |
|
709 has_validator = has_converter = False |
|
710 for a in attrs: |
|
711 if a.validator is not None: |
|
712 has_validator = True |
|
713 if a.converter is not None: |
|
714 has_converter = True |
|
715 |
|
716 if has_validator and has_converter: |
|
717 break |
|
718 if ( |
|
719 ( |
|
720 on_setattr == _ng_default_on_setattr |
|
721 and not (has_validator or has_converter) |
|
722 ) |
|
723 or (on_setattr == setters.validate and not has_validator) |
|
724 or (on_setattr == setters.convert and not has_converter) |
|
725 ): |
|
726 # If class-level on_setattr is set to convert + validate, but |
|
727 # there's no field to convert or validate, pretend like there's |
|
728 # no on_setattr. |
|
729 self._on_setattr = None |
|
730 |
|
731 if getstate_setstate: |
|
732 ( |
|
733 self._cls_dict["__getstate__"], |
|
734 self._cls_dict["__setstate__"], |
|
735 ) = self._make_getstate_setstate() |
|
736 |
|
737 def __repr__(self): |
|
738 return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__) |
|
739 |
|
740 def build_class(self): |
|
741 """ |
|
742 Finalize class based on the accumulated configuration. |
|
743 |
|
744 Builder cannot be used after calling this method. |
|
745 """ |
|
746 if self._slots is True: |
|
747 return self._create_slots_class() |
|
748 else: |
|
749 return self._patch_original_class() |
|
750 |
|
751 def _patch_original_class(self): |
|
752 """ |
|
753 Apply accumulated methods and return the class. |
|
754 """ |
|
755 cls = self._cls |
|
756 base_names = self._base_names |
|
757 |
|
758 # Clean class of attribute definitions (`attr.ib()`s). |
|
759 if self._delete_attribs: |
|
760 for name in self._attr_names: |
|
761 if ( |
|
762 name not in base_names |
|
763 and getattr(cls, name, _sentinel) is not _sentinel |
|
764 ): |
|
765 try: |
|
766 delattr(cls, name) |
|
767 except AttributeError: |
|
768 # This can happen if a base class defines a class |
|
769 # variable and we want to set an attribute with the |
|
770 # same name by using only a type annotation. |
|
771 pass |
|
772 |
|
773 # Attach our dunder methods. |
|
774 for name, value in self._cls_dict.items(): |
|
775 setattr(cls, name, value) |
|
776 |
|
777 # If we've inherited an attrs __setattr__ and don't write our own, |
|
778 # reset it to object's. |
|
779 if not self._wrote_own_setattr and getattr( |
|
780 cls, "__attrs_own_setattr__", False |
|
781 ): |
|
782 cls.__attrs_own_setattr__ = False |
|
783 |
|
784 if not self._has_custom_setattr: |
|
785 cls.__setattr__ = _obj_setattr |
|
786 |
|
787 return cls |
|
788 |
|
789 def _create_slots_class(self): |
|
790 """ |
|
791 Build and return a new class with a `__slots__` attribute. |
|
792 """ |
|
793 cd = { |
|
794 k: v |
|
795 for k, v in self._cls_dict.items() |
|
796 if k not in tuple(self._attr_names) + ("__dict__", "__weakref__") |
|
797 } |
|
798 |
|
799 # If our class doesn't have its own implementation of __setattr__ |
|
800 # (either from the user or by us), check the bases, if one of them has |
|
801 # an attrs-made __setattr__, that needs to be reset. We don't walk the |
|
802 # MRO because we only care about our immediate base classes. |
|
803 # XXX: This can be confused by subclassing a slotted attrs class with |
|
804 # XXX: a non-attrs class and subclass the resulting class with an attrs |
|
805 # XXX: class. See `test_slotted_confused` for details. For now that's |
|
806 # XXX: OK with us. |
|
807 if not self._wrote_own_setattr: |
|
808 cd["__attrs_own_setattr__"] = False |
|
809 |
|
810 if not self._has_custom_setattr: |
|
811 for base_cls in self._cls.__bases__: |
|
812 if base_cls.__dict__.get("__attrs_own_setattr__", False): |
|
813 cd["__setattr__"] = _obj_setattr |
|
814 break |
|
815 |
|
816 # Traverse the MRO to collect existing slots |
|
817 # and check for an existing __weakref__. |
|
818 existing_slots = dict() |
|
819 weakref_inherited = False |
|
820 for base_cls in self._cls.__mro__[1:-1]: |
|
821 if base_cls.__dict__.get("__weakref__", None) is not None: |
|
822 weakref_inherited = True |
|
823 existing_slots.update( |
|
824 { |
|
825 name: getattr(base_cls, name) |
|
826 for name in getattr(base_cls, "__slots__", []) |
|
827 } |
|
828 ) |
|
829 |
|
830 base_names = set(self._base_names) |
|
831 |
|
832 names = self._attr_names |
|
833 if ( |
|
834 self._weakref_slot |
|
835 and "__weakref__" not in getattr(self._cls, "__slots__", ()) |
|
836 and "__weakref__" not in names |
|
837 and not weakref_inherited |
|
838 ): |
|
839 names += ("__weakref__",) |
|
840 |
|
841 # We only add the names of attributes that aren't inherited. |
|
842 # Setting __slots__ to inherited attributes wastes memory. |
|
843 slot_names = [name for name in names if name not in base_names] |
|
844 # There are slots for attributes from current class |
|
845 # that are defined in parent classes. |
|
846 # As their descriptors may be overridden by a child class, |
|
847 # we collect them here and update the class dict |
|
848 reused_slots = { |
|
849 slot: slot_descriptor |
|
850 for slot, slot_descriptor in existing_slots.items() |
|
851 if slot in slot_names |
|
852 } |
|
853 slot_names = [name for name in slot_names if name not in reused_slots] |
|
854 cd.update(reused_slots) |
|
855 if self._cache_hash: |
|
856 slot_names.append(_hash_cache_field) |
|
857 cd["__slots__"] = tuple(slot_names) |
|
858 |
|
859 cd["__qualname__"] = self._cls.__qualname__ |
|
860 |
|
861 # Create new class based on old class and our methods. |
|
862 cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) |
|
863 |
|
864 # The following is a fix for |
|
865 # <https://github.com/python-attrs/attrs/issues/102>. On Python 3, |
|
866 # if a method mentions `__class__` or uses the no-arg super(), the |
|
867 # compiler will bake a reference to the class in the method itself |
|
868 # as `method.__closure__`. Since we replace the class with a |
|
869 # clone, we rewrite these references so it keeps working. |
|
870 for item in cls.__dict__.values(): |
|
871 if isinstance(item, (classmethod, staticmethod)): |
|
872 # Class- and staticmethods hide their functions inside. |
|
873 # These might need to be rewritten as well. |
|
874 closure_cells = getattr(item.__func__, "__closure__", None) |
|
875 elif isinstance(item, property): |
|
876 # Workaround for property `super()` shortcut (PY3-only). |
|
877 # There is no universal way for other descriptors. |
|
878 closure_cells = getattr(item.fget, "__closure__", None) |
|
879 else: |
|
880 closure_cells = getattr(item, "__closure__", None) |
|
881 |
|
882 if not closure_cells: # Catch None or the empty list. |
|
883 continue |
|
884 for cell in closure_cells: |
|
885 try: |
|
886 match = cell.cell_contents is self._cls |
|
887 except ValueError: # ValueError: Cell is empty |
|
888 pass |
|
889 else: |
|
890 if match: |
|
891 set_closure_cell(cell, cls) |
|
892 |
|
893 return cls |
|
894 |
|
895 def add_repr(self, ns): |
|
896 self._cls_dict["__repr__"] = self._add_method_dunders( |
|
897 _make_repr(self._attrs, ns, self._cls) |
|
898 ) |
|
899 return self |
|
900 |
|
901 def add_str(self): |
|
902 repr = self._cls_dict.get("__repr__") |
|
903 if repr is None: |
|
904 raise ValueError( |
|
905 "__str__ can only be generated if a __repr__ exists." |
|
906 ) |
|
907 |
|
908 def __str__(self): |
|
909 return self.__repr__() |
|
910 |
|
911 self._cls_dict["__str__"] = self._add_method_dunders(__str__) |
|
912 return self |
|
913 |
|
914 def _make_getstate_setstate(self): |
|
915 """ |
|
916 Create custom __setstate__ and __getstate__ methods. |
|
917 """ |
|
918 # __weakref__ is not writable. |
|
919 state_attr_names = tuple( |
|
920 an for an in self._attr_names if an != "__weakref__" |
|
921 ) |
|
922 |
|
923 def slots_getstate(self): |
|
924 """ |
|
925 Automatically created by attrs. |
|
926 """ |
|
927 return tuple(getattr(self, name) for name in state_attr_names) |
|
928 |
|
929 hash_caching_enabled = self._cache_hash |
|
930 |
|
931 def slots_setstate(self, state): |
|
932 """ |
|
933 Automatically created by attrs. |
|
934 """ |
|
935 __bound_setattr = _obj_setattr.__get__(self, Attribute) |
|
936 for name, value in zip(state_attr_names, state): |
|
937 __bound_setattr(name, value) |
|
938 |
|
939 # The hash code cache is not included when the object is |
|
940 # serialized, but it still needs to be initialized to None to |
|
941 # indicate that the first call to __hash__ should be a cache |
|
942 # miss. |
|
943 if hash_caching_enabled: |
|
944 __bound_setattr(_hash_cache_field, None) |
|
945 |
|
946 return slots_getstate, slots_setstate |
|
947 |
|
948 def make_unhashable(self): |
|
949 self._cls_dict["__hash__"] = None |
|
950 return self |
|
951 |
|
952 def add_hash(self): |
|
953 self._cls_dict["__hash__"] = self._add_method_dunders( |
|
954 _make_hash( |
|
955 self._cls, |
|
956 self._attrs, |
|
957 frozen=self._frozen, |
|
958 cache_hash=self._cache_hash, |
|
959 ) |
|
960 ) |
|
961 |
|
962 return self |
|
963 |
|
964 def add_init(self): |
|
965 self._cls_dict["__init__"] = self._add_method_dunders( |
|
966 _make_init( |
|
967 self._cls, |
|
968 self._attrs, |
|
969 self._has_pre_init, |
|
970 self._has_post_init, |
|
971 self._frozen, |
|
972 self._slots, |
|
973 self._cache_hash, |
|
974 self._base_attr_map, |
|
975 self._is_exc, |
|
976 self._on_setattr, |
|
977 attrs_init=False, |
|
978 ) |
|
979 ) |
|
980 |
|
981 return self |
|
982 |
|
983 def add_match_args(self): |
|
984 self._cls_dict["__match_args__"] = tuple( |
|
985 field.name |
|
986 for field in self._attrs |
|
987 if field.init and not field.kw_only |
|
988 ) |
|
989 |
|
990 def add_attrs_init(self): |
|
991 self._cls_dict["__attrs_init__"] = self._add_method_dunders( |
|
992 _make_init( |
|
993 self._cls, |
|
994 self._attrs, |
|
995 self._has_pre_init, |
|
996 self._has_post_init, |
|
997 self._frozen, |
|
998 self._slots, |
|
999 self._cache_hash, |
|
1000 self._base_attr_map, |
|
1001 self._is_exc, |
|
1002 self._on_setattr, |
|
1003 attrs_init=True, |
|
1004 ) |
|
1005 ) |
|
1006 |
|
1007 return self |
|
1008 |
|
1009 def add_eq(self): |
|
1010 cd = self._cls_dict |
|
1011 |
|
1012 cd["__eq__"] = self._add_method_dunders( |
|
1013 _make_eq(self._cls, self._attrs) |
|
1014 ) |
|
1015 cd["__ne__"] = self._add_method_dunders(_make_ne()) |
|
1016 |
|
1017 return self |
|
1018 |
|
1019 def add_order(self): |
|
1020 cd = self._cls_dict |
|
1021 |
|
1022 cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( |
|
1023 self._add_method_dunders(meth) |
|
1024 for meth in _make_order(self._cls, self._attrs) |
|
1025 ) |
|
1026 |
|
1027 return self |
|
1028 |
|
1029 def add_setattr(self): |
|
1030 if self._frozen: |
|
1031 return self |
|
1032 |
|
1033 sa_attrs = {} |
|
1034 for a in self._attrs: |
|
1035 on_setattr = a.on_setattr or self._on_setattr |
|
1036 if on_setattr and on_setattr is not setters.NO_OP: |
|
1037 sa_attrs[a.name] = a, on_setattr |
|
1038 |
|
1039 if not sa_attrs: |
|
1040 return self |
|
1041 |
|
1042 if self._has_custom_setattr: |
|
1043 # We need to write a __setattr__ but there already is one! |
|
1044 raise ValueError( |
|
1045 "Can't combine custom __setattr__ with on_setattr hooks." |
|
1046 ) |
|
1047 |
|
1048 # docstring comes from _add_method_dunders |
|
1049 def __setattr__(self, name, val): |
|
1050 try: |
|
1051 a, hook = sa_attrs[name] |
|
1052 except KeyError: |
|
1053 nval = val |
|
1054 else: |
|
1055 nval = hook(self, a, val) |
|
1056 |
|
1057 _obj_setattr(self, name, nval) |
|
1058 |
|
1059 self._cls_dict["__attrs_own_setattr__"] = True |
|
1060 self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) |
|
1061 self._wrote_own_setattr = True |
|
1062 |
|
1063 return self |
|
1064 |
|
1065 def _add_method_dunders(self, method): |
|
1066 """ |
|
1067 Add __module__ and __qualname__ to a *method* if possible. |
|
1068 """ |
|
1069 try: |
|
1070 method.__module__ = self._cls.__module__ |
|
1071 except AttributeError: |
|
1072 pass |
|
1073 |
|
1074 try: |
|
1075 method.__qualname__ = ".".join( |
|
1076 (self._cls.__qualname__, method.__name__) |
|
1077 ) |
|
1078 except AttributeError: |
|
1079 pass |
|
1080 |
|
1081 try: |
|
1082 method.__doc__ = "Method generated by attrs for class %s." % ( |
|
1083 self._cls.__qualname__, |
|
1084 ) |
|
1085 except AttributeError: |
|
1086 pass |
|
1087 |
|
1088 return method |
|
1089 |
|
1090 |
|
1091 def _determine_attrs_eq_order(cmp, eq, order, default_eq): |
|
1092 """ |
|
1093 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective |
|
1094 values of eq and order. If *eq* is None, set it to *default_eq*. |
|
1095 """ |
|
1096 if cmp is not None and any((eq is not None, order is not None)): |
|
1097 raise ValueError("Don't mix `cmp` with `eq' and `order`.") |
|
1098 |
|
1099 # cmp takes precedence due to bw-compatibility. |
|
1100 if cmp is not None: |
|
1101 return cmp, cmp |
|
1102 |
|
1103 # If left None, equality is set to the specified default and ordering |
|
1104 # mirrors equality. |
|
1105 if eq is None: |
|
1106 eq = default_eq |
|
1107 |
|
1108 if order is None: |
|
1109 order = eq |
|
1110 |
|
1111 if eq is False and order is True: |
|
1112 raise ValueError("`order` can only be True if `eq` is True too.") |
|
1113 |
|
1114 return eq, order |
|
1115 |
|
1116 |
|
1117 def _determine_attrib_eq_order(cmp, eq, order, default_eq): |
|
1118 """ |
|
1119 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective |
|
1120 values of eq and order. If *eq* is None, set it to *default_eq*. |
|
1121 """ |
|
1122 if cmp is not None and any((eq is not None, order is not None)): |
|
1123 raise ValueError("Don't mix `cmp` with `eq' and `order`.") |
|
1124 |
|
1125 def decide_callable_or_boolean(value): |
|
1126 """ |
|
1127 Decide whether a key function is used. |
|
1128 """ |
|
1129 if callable(value): |
|
1130 value, key = True, value |
|
1131 else: |
|
1132 key = None |
|
1133 return value, key |
|
1134 |
|
1135 # cmp takes precedence due to bw-compatibility. |
|
1136 if cmp is not None: |
|
1137 cmp, cmp_key = decide_callable_or_boolean(cmp) |
|
1138 return cmp, cmp_key, cmp, cmp_key |
|
1139 |
|
1140 # If left None, equality is set to the specified default and ordering |
|
1141 # mirrors equality. |
|
1142 if eq is None: |
|
1143 eq, eq_key = default_eq, None |
|
1144 else: |
|
1145 eq, eq_key = decide_callable_or_boolean(eq) |
|
1146 |
|
1147 if order is None: |
|
1148 order, order_key = eq, eq_key |
|
1149 else: |
|
1150 order, order_key = decide_callable_or_boolean(order) |
|
1151 |
|
1152 if eq is False and order is True: |
|
1153 raise ValueError("`order` can only be True if `eq` is True too.") |
|
1154 |
|
1155 return eq, eq_key, order, order_key |
|
1156 |
|
1157 |
|
1158 def _determine_whether_to_implement( |
|
1159 cls, flag, auto_detect, dunders, default=True |
|
1160 ): |
|
1161 """ |
|
1162 Check whether we should implement a set of methods for *cls*. |
|
1163 |
|
1164 *flag* is the argument passed into @attr.s like 'init', *auto_detect* the |
|
1165 same as passed into @attr.s and *dunders* is a tuple of attribute names |
|
1166 whose presence signal that the user has implemented it themselves. |
|
1167 |
|
1168 Return *default* if no reason for either for or against is found. |
|
1169 """ |
|
1170 if flag is True or flag is False: |
|
1171 return flag |
|
1172 |
|
1173 if flag is None and auto_detect is False: |
|
1174 return default |
|
1175 |
|
1176 # Logically, flag is None and auto_detect is True here. |
|
1177 for dunder in dunders: |
|
1178 if _has_own_attribute(cls, dunder): |
|
1179 return False |
|
1180 |
|
1181 return default |
|
1182 |
|
1183 |
|
1184 def attrs( |
|
1185 maybe_cls=None, |
|
1186 these=None, |
|
1187 repr_ns=None, |
|
1188 repr=None, |
|
1189 cmp=None, |
|
1190 hash=None, |
|
1191 init=None, |
|
1192 slots=False, |
|
1193 frozen=False, |
|
1194 weakref_slot=True, |
|
1195 str=False, |
|
1196 auto_attribs=False, |
|
1197 kw_only=False, |
|
1198 cache_hash=False, |
|
1199 auto_exc=False, |
|
1200 eq=None, |
|
1201 order=None, |
|
1202 auto_detect=False, |
|
1203 collect_by_mro=False, |
|
1204 getstate_setstate=None, |
|
1205 on_setattr=None, |
|
1206 field_transformer=None, |
|
1207 match_args=True, |
|
1208 ): |
240 r""" |
1209 r""" |
241 A class decorator that adds `dunder |
1210 A class decorator that adds `dunder |
242 <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the |
1211 <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the |
243 specified attributes using :func:`attr.ib` or the *these* argument. |
1212 specified attributes using `attr.ib` or the *these* argument. |
244 |
1213 |
245 :param these: A dictionary of name to :func:`attr.ib` mappings. This is |
1214 :param these: A dictionary of name to `attr.ib` mappings. This is |
246 useful to avoid the definition of your attributes within the class body |
1215 useful to avoid the definition of your attributes within the class body |
247 because you can't (e.g. if you want to add ``__repr__`` methods to |
1216 because you can't (e.g. if you want to add ``__repr__`` methods to |
248 Django models) or don't want to. |
1217 Django models) or don't want to. |
249 |
1218 |
250 If *these* is not ``None``, ``attrs`` will *not* search the class body |
1219 If *these* is not ``None``, ``attrs`` will *not* search the class body |
251 for attributes. |
1220 for attributes and will *not* remove any attributes from it. |
252 |
1221 |
253 :type these: :class:`dict` of :class:`str` to :func:`attr.ib` |
1222 If *these* is an ordered dict (`dict` on Python 3.6+, |
|
1223 `collections.OrderedDict` otherwise), the order is deduced from |
|
1224 the order of the attributes inside *these*. Otherwise the order |
|
1225 of the definition of the attributes is used. |
|
1226 |
|
1227 :type these: `dict` of `str` to `attr.ib` |
254 |
1228 |
255 :param str repr_ns: When using nested classes, there's no way in Python 2 |
1229 :param str repr_ns: When using nested classes, there's no way in Python 2 |
256 to automatically detect that. Therefore it's possible to set the |
1230 to automatically detect that. Therefore it's possible to set the |
257 namespace explicitly for a more meaningful ``repr`` output. |
1231 namespace explicitly for a more meaningful ``repr`` output. |
|
1232 :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*, |
|
1233 *order*, and *hash* arguments explicitly, assume they are set to |
|
1234 ``True`` **unless any** of the involved methods for one of the |
|
1235 arguments is implemented in the *current* class (i.e. it is *not* |
|
1236 inherited from some base class). |
|
1237 |
|
1238 So for example by implementing ``__eq__`` on a class yourself, |
|
1239 ``attrs`` will deduce ``eq=False`` and will create *neither* |
|
1240 ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible |
|
1241 ``__ne__`` by default, so it *should* be enough to only implement |
|
1242 ``__eq__`` in most cases). |
|
1243 |
|
1244 .. warning:: |
|
1245 |
|
1246 If you prevent ``attrs`` from creating the ordering methods for you |
|
1247 (``order=False``, e.g. by implementing ``__le__``), it becomes |
|
1248 *your* responsibility to make sure its ordering is sound. The best |
|
1249 way is to use the `functools.total_ordering` decorator. |
|
1250 |
|
1251 |
|
1252 Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, |
|
1253 *cmp*, or *hash* overrides whatever *auto_detect* would determine. |
|
1254 |
|
1255 *auto_detect* requires Python 3. Setting it ``True`` on Python 2 raises |
|
1256 an `attrs.exceptions.PythonTooOldError`. |
|
1257 |
258 :param bool repr: Create a ``__repr__`` method with a human readable |
1258 :param bool repr: Create a ``__repr__`` method with a human readable |
259 represantation of ``attrs`` attributes.. |
1259 representation of ``attrs`` attributes.. |
260 :param bool str: Create a ``__str__`` method that is identical to |
1260 :param bool str: Create a ``__str__`` method that is identical to |
261 ``__repr__``. This is usually not necessary except for |
1261 ``__repr__``. This is usually not necessary except for |
262 :class:`Exception`\ s. |
1262 `Exception`\ s. |
263 :param bool cmp: Create ``__eq__``, ``__ne__``, ``__lt__``, ``__le__``, |
1263 :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__`` |
264 ``__gt__``, and ``__ge__`` methods that compare the class as if it were |
1264 and ``__ne__`` methods that check two instances for equality. |
265 a tuple of its ``attrs`` attributes. But the attributes are *only* |
1265 |
266 compared, if the type of both classes is *identical*! |
1266 They compare the instances as if they were tuples of their ``attrs`` |
267 :param hash: If ``None`` (default), the ``__hash__`` method is generated |
1267 attributes if and only if the types of both classes are *identical*! |
268 according how *cmp* and *frozen* are set. |
1268 :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``, |
|
1269 ``__gt__``, and ``__ge__`` methods that behave like *eq* above and |
|
1270 allow instances to be ordered. If ``None`` (default) mirror value of |
|
1271 *eq*. |
|
1272 :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq* |
|
1273 and *order* to the same value. Must not be mixed with *eq* or *order*. |
|
1274 :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method |
|
1275 is generated according how *eq* and *frozen* are set. |
269 |
1276 |
270 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you. |
1277 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you. |
271 2. If *cmp* is True and *frozen* is False, ``__hash__`` will be set to |
1278 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to |
272 None, marking it unhashable (which it is). |
1279 None, marking it unhashable (which it is). |
273 3. If *cmp* is False, ``__hash__`` will be left untouched meaning the |
1280 3. If *eq* is False, ``__hash__`` will be left untouched meaning the |
274 ``__hash__`` method of the superclass will be used (if superclass is |
1281 ``__hash__`` method of the base class will be used (if base class is |
275 ``object``, this means it will fall back to id-based hashing.). |
1282 ``object``, this means it will fall back to id-based hashing.). |
276 |
1283 |
277 Although not recommended, you can decide for yourself and force |
1284 Although not recommended, you can decide for yourself and force |
278 ``attrs`` to create one (e.g. if the class is immutable even though you |
1285 ``attrs`` to create one (e.g. if the class is immutable even though you |
279 didn't freeze it programmatically) by passing ``True`` or not. Both of |
1286 didn't freeze it programmatically) by passing ``True`` or not. Both of |
280 these cases are rather special and should be used carefully. |
1287 these cases are rather special and should be used carefully. |
281 |
1288 |
282 See the `Python documentation \ |
1289 See our documentation on `hashing`, Python's documentation on |
283 <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`_ |
1290 `object.__hash__`, and the `GitHub issue that led to the default \ |
284 and the `GitHub issue that led to the default behavior \ |
1291 behavior <https://github.com/python-attrs/attrs/issues/136>`_ for more |
285 <https://github.com/python-attrs/attrs/issues/136>`_ for more details. |
1292 details. |
286 :type hash: ``bool`` or ``None`` |
1293 :param bool init: Create a ``__init__`` method that initializes the |
287 :param bool init: Create a ``__init__`` method that initialiazes the |
1294 ``attrs`` attributes. Leading underscores are stripped for the argument |
288 ``attrs`` attributes. Leading underscores are stripped for the |
1295 name. If a ``__attrs_pre_init__`` method exists on the class, it will |
289 argument name. If a ``__attrs_post_init__`` method exists on the |
1296 be called before the class is initialized. If a ``__attrs_post_init__`` |
290 class, it will be called after the class is fully initialized. |
1297 method exists on the class, it will be called after the class is fully |
291 :param bool slots: Create a slots_-style class that's more |
1298 initialized. |
292 memory-efficient. See :ref:`slots` for further ramifications. |
1299 |
|
1300 If ``init`` is ``False``, an ``__attrs_init__`` method will be |
|
1301 injected instead. This allows you to define a custom ``__init__`` |
|
1302 method that can do pre-init work such as ``super().__init__()``, |
|
1303 and then call ``__attrs_init__()`` and ``__attrs_post_init__()``. |
|
1304 :param bool slots: Create a `slotted class <slotted classes>` that's more |
|
1305 memory-efficient. Slotted classes are generally superior to the default |
|
1306 dict classes, but have some gotchas you should know about, so we |
|
1307 encourage you to read the `glossary entry <slotted classes>`. |
293 :param bool frozen: Make instances immutable after initialization. If |
1308 :param bool frozen: Make instances immutable after initialization. If |
294 someone attempts to modify a frozen instance, |
1309 someone attempts to modify a frozen instance, |
295 :exc:`attr.exceptions.FrozenInstanceError` is raised. |
1310 `attr.exceptions.FrozenInstanceError` is raised. |
296 |
1311 |
297 Please note: |
1312 .. note:: |
298 |
1313 |
299 1. This is achieved by installing a custom ``__setattr__`` method |
1314 1. This is achieved by installing a custom ``__setattr__`` method |
300 on your class so you can't implement an own one. |
1315 on your class, so you can't implement your own. |
301 |
1316 |
302 2. True immutability is impossible in Python. |
1317 2. True immutability is impossible in Python. |
303 |
1318 |
304 3. This *does* have a minor a runtime performance :ref:`impact |
1319 3. This *does* have a minor a runtime performance `impact |
305 <how-frozen>` when initializing new instances. In other words: |
1320 <how-frozen>` when initializing new instances. In other words: |
306 ``__init__`` is slightly slower with ``frozen=True``. |
1321 ``__init__`` is slightly slower with ``frozen=True``. |
307 |
1322 |
308 4. If a class is frozen, you cannot modify ``self`` in |
1323 4. If a class is frozen, you cannot modify ``self`` in |
309 ``__attrs_post_init__`` or a self-written ``__init__``. You can |
1324 ``__attrs_post_init__`` or a self-written ``__init__``. You can |
310 circumvent that limitation by using |
1325 circumvent that limitation by using |
311 ``object.__setattr__(self, "attribute_name", value)``. |
1326 ``object.__setattr__(self, "attribute_name", value)``. |
312 |
1327 |
313 .. _slots: https://docs.python.org/3.5/reference/datamodel.html#slots |
1328 5. Subclasses of a frozen class are frozen too. |
314 |
1329 |
315 .. versionadded:: 16.0.0 *slots* |
1330 :param bool weakref_slot: Make instances weak-referenceable. This has no |
316 .. versionadded:: 16.1.0 *frozen* |
1331 effect unless ``slots`` is also enabled. |
317 .. versionadded:: 16.3.0 *str*, and support for ``__attrs_post_init__``. |
1332 :param bool auto_attribs: If ``True``, collect :pep:`526`-annotated |
318 .. versionchanged:: |
1333 attributes (Python 3.6 and later only) from the class body. |
319 17.1.0 *hash* supports ``None`` as value which is also the default |
1334 |
320 now. |
1335 In this case, you **must** annotate every field. If ``attrs`` |
321 """ |
1336 encounters a field that is set to an `attr.ib` but lacks a type |
|
1337 annotation, an `attr.exceptions.UnannotatedAttributeError` is |
|
1338 raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't |
|
1339 want to set a type. |
|
1340 |
|
1341 If you assign a value to those attributes (e.g. ``x: int = 42``), that |
|
1342 value becomes the default value like if it were passed using |
|
1343 ``attr.ib(default=42)``. Passing an instance of `attrs.Factory` also |
|
1344 works as expected in most cases (see warning below). |
|
1345 |
|
1346 Attributes annotated as `typing.ClassVar`, and attributes that are |
|
1347 neither annotated nor set to an `attr.ib` are **ignored**. |
|
1348 |
|
1349 .. warning:: |
|
1350 For features that use the attribute name to create decorators (e.g. |
|
1351 `validators <validators>`), you still *must* assign `attr.ib` to |
|
1352 them. Otherwise Python will either not find the name or try to use |
|
1353 the default value to call e.g. ``validator`` on it. |
|
1354 |
|
1355 These errors can be quite confusing and probably the most common bug |
|
1356 report on our bug tracker. |
|
1357 |
|
1358 :param bool kw_only: Make all attributes keyword-only (Python 3+) |
|
1359 in the generated ``__init__`` (if ``init`` is ``False``, this |
|
1360 parameter is ignored). |
|
1361 :param bool cache_hash: Ensure that the object's hash code is computed |
|
1362 only once and stored on the object. If this is set to ``True``, |
|
1363 hashing must be either explicitly or implicitly enabled for this |
|
1364 class. If the hash code is cached, avoid any reassignments of |
|
1365 fields involved in hash code computation or mutations of the objects |
|
1366 those fields point to after object creation. If such changes occur, |
|
1367 the behavior of the object's hash code is undefined. |
|
1368 :param bool auto_exc: If the class subclasses `BaseException` |
|
1369 (which implicitly includes any subclass of any exception), the |
|
1370 following happens to behave like a well-behaved Python exceptions |
|
1371 class: |
|
1372 |
|
1373 - the values for *eq*, *order*, and *hash* are ignored and the |
|
1374 instances compare and hash by the instance's ids (N.B. ``attrs`` will |
|
1375 *not* remove existing implementations of ``__hash__`` or the equality |
|
1376 methods. It just won't add own ones.), |
|
1377 - all attributes that are either passed into ``__init__`` or have a |
|
1378 default value are additionally available as a tuple in the ``args`` |
|
1379 attribute, |
|
1380 - the value of *str* is ignored leaving ``__str__`` to base classes. |
|
1381 :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs`` |
|
1382 collects attributes from base classes. The default behavior is |
|
1383 incorrect in certain cases of multiple inheritance. It should be on by |
|
1384 default but is kept off for backward-compatibility. |
|
1385 |
|
1386 See issue `#428 <https://github.com/python-attrs/attrs/issues/428>`_ for |
|
1387 more details. |
|
1388 |
|
1389 :param Optional[bool] getstate_setstate: |
|
1390 .. note:: |
|
1391 This is usually only interesting for slotted classes and you should |
|
1392 probably just set *auto_detect* to `True`. |
|
1393 |
|
1394 If `True`, ``__getstate__`` and |
|
1395 ``__setstate__`` are generated and attached to the class. This is |
|
1396 necessary for slotted classes to be pickleable. If left `None`, it's |
|
1397 `True` by default for slotted classes and ``False`` for dict classes. |
|
1398 |
|
1399 If *auto_detect* is `True`, and *getstate_setstate* is left `None`, |
|
1400 and **either** ``__getstate__`` or ``__setstate__`` is detected directly |
|
1401 on the class (i.e. not inherited), it is set to `False` (this is usually |
|
1402 what you want). |
|
1403 |
|
1404 :param on_setattr: A callable that is run whenever the user attempts to set |
|
1405 an attribute (either by assignment like ``i.x = 42`` or by using |
|
1406 `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments |
|
1407 as validators: the instance, the attribute that is being modified, and |
|
1408 the new value. |
|
1409 |
|
1410 If no exception is raised, the attribute is set to the return value of |
|
1411 the callable. |
|
1412 |
|
1413 If a list of callables is passed, they're automatically wrapped in an |
|
1414 `attrs.setters.pipe`. |
|
1415 :type on_setattr: `callable`, or a list of callables, or `None`, or |
|
1416 `attrs.setters.NO_OP` |
|
1417 |
|
1418 :param Optional[callable] field_transformer: |
|
1419 A function that is called with the original class object and all |
|
1420 fields right before ``attrs`` finalizes the class. You can use |
|
1421 this, e.g., to automatically add converters or validators to |
|
1422 fields based on their types. See `transform-fields` for more details. |
|
1423 |
|
1424 :param bool match_args: |
|
1425 If `True` (default), set ``__match_args__`` on the class to support |
|
1426 :pep:`634` (Structural Pattern Matching). It is a tuple of all |
|
1427 non-keyword-only ``__init__`` parameter names on Python 3.10 and later. |
|
1428 Ignored on older Python versions. |
|
1429 |
|
1430 .. versionadded:: 16.0.0 *slots* |
|
1431 .. versionadded:: 16.1.0 *frozen* |
|
1432 .. versionadded:: 16.3.0 *str* |
|
1433 .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. |
|
1434 .. versionchanged:: 17.1.0 |
|
1435 *hash* supports ``None`` as value which is also the default now. |
|
1436 .. versionadded:: 17.3.0 *auto_attribs* |
|
1437 .. versionchanged:: 18.1.0 |
|
1438 If *these* is passed, no attributes are deleted from the class body. |
|
1439 .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. |
|
1440 .. versionadded:: 18.2.0 *weakref_slot* |
|
1441 .. deprecated:: 18.2.0 |
|
1442 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a |
|
1443 `DeprecationWarning` if the classes compared are subclasses of |
|
1444 each other. ``__eq`` and ``__ne__`` never tried to compared subclasses |
|
1445 to each other. |
|
1446 .. versionchanged:: 19.2.0 |
|
1447 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider |
|
1448 subclasses comparable anymore. |
|
1449 .. versionadded:: 18.2.0 *kw_only* |
|
1450 .. versionadded:: 18.2.0 *cache_hash* |
|
1451 .. versionadded:: 19.1.0 *auto_exc* |
|
1452 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. |
|
1453 .. versionadded:: 19.2.0 *eq* and *order* |
|
1454 .. versionadded:: 20.1.0 *auto_detect* |
|
1455 .. versionadded:: 20.1.0 *collect_by_mro* |
|
1456 .. versionadded:: 20.1.0 *getstate_setstate* |
|
1457 .. versionadded:: 20.1.0 *on_setattr* |
|
1458 .. versionadded:: 20.3.0 *field_transformer* |
|
1459 .. versionchanged:: 21.1.0 |
|
1460 ``init=False`` injects ``__attrs_init__`` |
|
1461 .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` |
|
1462 .. versionchanged:: 21.1.0 *cmp* undeprecated |
|
1463 .. versionadded:: 21.3.0 *match_args* |
|
1464 """ |
|
1465 eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) |
|
1466 hash_ = hash # work around the lack of nonlocal |
|
1467 |
|
1468 if isinstance(on_setattr, (list, tuple)): |
|
1469 on_setattr = setters.pipe(*on_setattr) |
|
1470 |
322 def wrap(cls): |
1471 def wrap(cls): |
323 if getattr(cls, "__class__", None) is None: |
1472 is_frozen = frozen or _has_frozen_base_class(cls) |
324 raise TypeError("attrs only works with new-style classes.") |
1473 is_exc = auto_exc is True and issubclass(cls, BaseException) |
325 |
1474 has_own_setattr = auto_detect and _has_own_attribute( |
326 if repr is False and str is True: |
1475 cls, "__setattr__" |
327 raise ValueError( |
1476 ) |
328 "__str__ can only be generated if a __repr__ exists." |
1477 |
329 ) |
1478 if has_own_setattr and is_frozen: |
330 |
1479 raise ValueError("Can't freeze a class with a custom __setattr__.") |
331 if slots: |
1480 |
332 # Only need this later if we're using slots. |
1481 builder = _ClassBuilder( |
333 if these is None: |
1482 cls, |
334 ca_list = [name |
1483 these, |
335 for name, attr |
1484 slots, |
336 in cls.__dict__.items() |
1485 is_frozen, |
337 if isinstance(attr, _CountingAttr)] |
1486 weakref_slot, |
338 else: |
1487 _determine_whether_to_implement( |
339 ca_list = list(iterkeys(these)) |
1488 cls, |
340 _transform_attrs(cls, these) |
1489 getstate_setstate, |
341 |
1490 auto_detect, |
342 # Can't just re-use frozen name because Python's scoping. :( |
1491 ("__getstate__", "__setstate__"), |
343 # Can't compare function objects because Python 2 is terrible. :( |
1492 default=slots, |
344 effectively_frozen = _has_frozen_superclass(cls) or frozen |
1493 ), |
345 if repr is True: |
1494 auto_attribs, |
346 cls = _add_repr(cls, ns=repr_ns) |
1495 kw_only, |
|
1496 cache_hash, |
|
1497 is_exc, |
|
1498 collect_by_mro, |
|
1499 on_setattr, |
|
1500 has_own_setattr, |
|
1501 field_transformer, |
|
1502 ) |
|
1503 if _determine_whether_to_implement( |
|
1504 cls, repr, auto_detect, ("__repr__",) |
|
1505 ): |
|
1506 builder.add_repr(repr_ns) |
347 if str is True: |
1507 if str is True: |
348 cls.__str__ = cls.__repr__ |
1508 builder.add_str() |
349 if cmp is True: |
1509 |
350 cls = _add_cmp(cls) |
1510 eq = _determine_whether_to_implement( |
351 |
1511 cls, eq_, auto_detect, ("__eq__", "__ne__") |
|
1512 ) |
|
1513 if not is_exc and eq is True: |
|
1514 builder.add_eq() |
|
1515 if not is_exc and _determine_whether_to_implement( |
|
1516 cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__") |
|
1517 ): |
|
1518 builder.add_order() |
|
1519 |
|
1520 builder.add_setattr() |
|
1521 |
|
1522 if ( |
|
1523 hash_ is None |
|
1524 and auto_detect is True |
|
1525 and _has_own_attribute(cls, "__hash__") |
|
1526 ): |
|
1527 hash = False |
|
1528 else: |
|
1529 hash = hash_ |
352 if hash is not True and hash is not False and hash is not None: |
1530 if hash is not True and hash is not False and hash is not None: |
|
1531 # Can't use `hash in` because 1 == True for example. |
353 raise TypeError( |
1532 raise TypeError( |
354 "Invalid value for hash. Must be True, False, or None." |
1533 "Invalid value for hash. Must be True, False, or None." |
355 ) |
1534 ) |
356 elif hash is False or (hash is None and cmp is False): |
1535 elif hash is False or (hash is None and eq is False) or is_exc: |
357 pass |
1536 # Don't do anything. Should fall back to __object__'s __hash__ |
358 elif hash is True or (hash is None and cmp is True and frozen is True): |
1537 # which is by id. |
359 cls = _add_hash(cls) |
1538 if cache_hash: |
|
1539 raise TypeError( |
|
1540 "Invalid value for cache_hash. To use hash caching," |
|
1541 " hashing must be either explicitly or implicitly " |
|
1542 "enabled." |
|
1543 ) |
|
1544 elif hash is True or ( |
|
1545 hash is None and eq is True and is_frozen is True |
|
1546 ): |
|
1547 # Build a __hash__ if told so, or if it's safe. |
|
1548 builder.add_hash() |
360 else: |
1549 else: |
361 cls.__hash__ = None |
1550 # Raise TypeError on attempts to hash. |
362 |
1551 if cache_hash: |
363 if init is True: |
1552 raise TypeError( |
364 cls = _add_init(cls, effectively_frozen) |
1553 "Invalid value for cache_hash. To use hash caching," |
365 if effectively_frozen is True: |
1554 " hashing must be either explicitly or implicitly " |
366 cls.__setattr__ = _frozen_setattrs |
1555 "enabled." |
367 cls.__delattr__ = _frozen_delattrs |
1556 ) |
368 if slots is True: |
1557 builder.make_unhashable() |
369 # slots and frozen require __getstate__/__setstate__ to work |
1558 |
370 cls = _add_pickle(cls) |
1559 if _determine_whether_to_implement( |
371 if slots is True: |
1560 cls, init, auto_detect, ("__init__",) |
372 cls_dict = dict(cls.__dict__) |
1561 ): |
373 cls_dict["__slots__"] = tuple(ca_list) |
1562 builder.add_init() |
374 for ca_name in ca_list: |
1563 else: |
375 # It might not actually be in there, e.g. if using 'these'. |
1564 builder.add_attrs_init() |
376 cls_dict.pop(ca_name, None) |
1565 if cache_hash: |
377 cls_dict.pop("__dict__", None) |
1566 raise TypeError( |
378 |
1567 "Invalid value for cache_hash. To use hash caching," |
379 qualname = getattr(cls, "__qualname__", None) |
1568 " init must be True." |
380 cls = type(cls)(cls.__name__, cls.__bases__, cls_dict) |
1569 ) |
381 if qualname is not None: |
1570 |
382 cls.__qualname__ = qualname |
1571 if ( |
383 |
1572 PY310 |
384 return cls |
1573 and match_args |
385 |
1574 and not _has_own_attribute(cls, "__match_args__") |
386 # attrs_or class type depends on the usage of the decorator. It's a class |
1575 ): |
387 # if it's used as `@attributes` but ``None`` if used # as `@attributes()`. |
1576 builder.add_match_args() |
|
1577 |
|
1578 return builder.build_class() |
|
1579 |
|
1580 # maybe_cls's type depends on the usage of the decorator. It's a class |
|
1581 # if it's used as `@attrs` but ``None`` if used as `@attrs()`. |
388 if maybe_cls is None: |
1582 if maybe_cls is None: |
389 return wrap |
1583 return wrap |
390 else: |
1584 else: |
391 return wrap(maybe_cls) |
1585 return wrap(maybe_cls) |
392 |
1586 |
393 |
1587 |
394 if PY2: |
1588 _attrs = attrs |
395 def _has_frozen_superclass(cls): |
1589 """ |
396 """ |
1590 Internal alias so we can use it in functions that take an argument called |
397 Check whether *cls* has a frozen ancestor by looking at its |
1591 *attrs*. |
398 __setattr__. |
1592 """ |
399 """ |
1593 |
400 return ( |
1594 |
401 getattr( |
1595 def _has_frozen_base_class(cls): |
402 cls.__setattr__, "__module__", None |
1596 """ |
403 ) == _frozen_setattrs.__module__ and |
1597 Check whether *cls* has a frozen ancestor by looking at its |
404 cls.__setattr__.__name__ == _frozen_setattrs.__name__ |
1598 __setattr__. |
405 ) |
1599 """ |
406 else: |
1600 return cls.__setattr__ is _frozen_setattrs |
407 def _has_frozen_superclass(cls): |
1601 |
408 """ |
1602 |
409 Check whether *cls* has a frozen ancestor by looking at its |
1603 def _generate_unique_filename(cls, func_name): |
410 __setattr__. |
1604 """ |
411 """ |
1605 Create a "filename" suitable for a function being generated. |
412 return cls.__setattr__ == _frozen_setattrs |
1606 """ |
413 |
1607 unique_filename = "<attrs generated {} {}.{}>".format( |
414 |
1608 func_name, |
415 def _attrs_to_tuple(obj, attrs): |
1609 cls.__module__, |
416 """ |
1610 getattr(cls, "__qualname__", cls.__name__), |
417 Create a tuple of all values of *obj*'s *attrs*. |
1611 ) |
418 """ |
1612 return unique_filename |
419 return tuple(getattr(obj, a.name) for a in attrs) |
1613 |
420 |
1614 |
421 |
1615 def _make_hash(cls, attrs, frozen, cache_hash): |
422 def _add_hash(cls, attrs=None): |
1616 attrs = tuple( |
|
1617 a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) |
|
1618 ) |
|
1619 |
|
1620 tab = " " |
|
1621 |
|
1622 unique_filename = _generate_unique_filename(cls, "hash") |
|
1623 type_hash = hash(unique_filename) |
|
1624 # If eq is custom generated, we need to include the functions in globs |
|
1625 globs = {} |
|
1626 |
|
1627 hash_def = "def __hash__(self" |
|
1628 hash_func = "hash((" |
|
1629 closing_braces = "))" |
|
1630 if not cache_hash: |
|
1631 hash_def += "):" |
|
1632 else: |
|
1633 hash_def += ", *" |
|
1634 |
|
1635 hash_def += ( |
|
1636 ", _cache_wrapper=" |
|
1637 + "__import__('attr._make')._make._CacheHashWrapper):" |
|
1638 ) |
|
1639 hash_func = "_cache_wrapper(" + hash_func |
|
1640 closing_braces += ")" |
|
1641 |
|
1642 method_lines = [hash_def] |
|
1643 |
|
1644 def append_hash_computation_lines(prefix, indent): |
|
1645 """ |
|
1646 Generate the code for actually computing the hash code. |
|
1647 Below this will either be returned directly or used to compute |
|
1648 a value which is then cached, depending on the value of cache_hash |
|
1649 """ |
|
1650 |
|
1651 method_lines.extend( |
|
1652 [ |
|
1653 indent + prefix + hash_func, |
|
1654 indent + " %d," % (type_hash,), |
|
1655 ] |
|
1656 ) |
|
1657 |
|
1658 for a in attrs: |
|
1659 if a.eq_key: |
|
1660 cmp_name = "_%s_key" % (a.name,) |
|
1661 globs[cmp_name] = a.eq_key |
|
1662 method_lines.append( |
|
1663 indent + " %s(self.%s)," % (cmp_name, a.name) |
|
1664 ) |
|
1665 else: |
|
1666 method_lines.append(indent + " self.%s," % a.name) |
|
1667 |
|
1668 method_lines.append(indent + " " + closing_braces) |
|
1669 |
|
1670 if cache_hash: |
|
1671 method_lines.append(tab + "if self.%s is None:" % _hash_cache_field) |
|
1672 if frozen: |
|
1673 append_hash_computation_lines( |
|
1674 "object.__setattr__(self, '%s', " % _hash_cache_field, tab * 2 |
|
1675 ) |
|
1676 method_lines.append(tab * 2 + ")") # close __setattr__ |
|
1677 else: |
|
1678 append_hash_computation_lines( |
|
1679 "self.%s = " % _hash_cache_field, tab * 2 |
|
1680 ) |
|
1681 method_lines.append(tab + "return self.%s" % _hash_cache_field) |
|
1682 else: |
|
1683 append_hash_computation_lines("return ", tab) |
|
1684 |
|
1685 script = "\n".join(method_lines) |
|
1686 return _make_method("__hash__", script, unique_filename, globs) |
|
1687 |
|
1688 |
|
1689 def _add_hash(cls, attrs): |
423 """ |
1690 """ |
424 Add a hash method to *cls*. |
1691 Add a hash method to *cls*. |
425 """ |
1692 """ |
426 if attrs is None: |
1693 cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False) |
427 attrs = [a |
|
428 for a in cls.__attrs_attrs__ |
|
429 if a.hash is True or (a.hash is None and a.cmp is True)] |
|
430 |
|
431 def hash_(self): |
|
432 """ |
|
433 Automatically created by attrs. |
|
434 """ |
|
435 return hash(_attrs_to_tuple(self, attrs)) |
|
436 |
|
437 cls.__hash__ = hash_ |
|
438 return cls |
1694 return cls |
439 |
1695 |
440 |
1696 |
441 def _add_cmp(cls, attrs=None): |
1697 def _make_ne(): |
442 """ |
1698 """ |
443 Add comparison methods to *cls*. |
1699 Create __ne__ method. |
444 """ |
1700 """ |
445 if attrs is None: |
1701 |
446 attrs = [a for a in cls.__attrs_attrs__ if a.cmp] |
1702 def __ne__(self, other): |
447 |
1703 """ |
448 def attrs_to_tuple(obj): |
1704 Check equality and either forward a NotImplemented or |
449 """ |
1705 return the result negated. |
450 Save us some typing. |
1706 """ |
451 """ |
1707 result = self.__eq__(other) |
452 return _attrs_to_tuple(obj, attrs) |
|
453 |
|
454 def eq(self, other): |
|
455 """ |
|
456 Automatically created by attrs. |
|
457 """ |
|
458 if other.__class__ is self.__class__: |
|
459 return attrs_to_tuple(self) == attrs_to_tuple(other) |
|
460 else: |
|
461 return NotImplemented |
|
462 |
|
463 def ne(self, other): |
|
464 """ |
|
465 Automatically created by attrs. |
|
466 """ |
|
467 result = eq(self, other) |
|
468 if result is NotImplemented: |
1708 if result is NotImplemented: |
469 return NotImplemented |
1709 return NotImplemented |
|
1710 |
|
1711 return not result |
|
1712 |
|
1713 return __ne__ |
|
1714 |
|
1715 |
|
1716 def _make_eq(cls, attrs): |
|
1717 """ |
|
1718 Create __eq__ method for *cls* with *attrs*. |
|
1719 """ |
|
1720 attrs = [a for a in attrs if a.eq] |
|
1721 |
|
1722 unique_filename = _generate_unique_filename(cls, "eq") |
|
1723 lines = [ |
|
1724 "def __eq__(self, other):", |
|
1725 " if other.__class__ is not self.__class__:", |
|
1726 " return NotImplemented", |
|
1727 ] |
|
1728 |
|
1729 # We can't just do a big self.x = other.x and... clause due to |
|
1730 # irregularities like nan == nan is false but (nan,) == (nan,) is true. |
|
1731 globs = {} |
|
1732 if attrs: |
|
1733 lines.append(" return (") |
|
1734 others = [" ) == ("] |
|
1735 for a in attrs: |
|
1736 if a.eq_key: |
|
1737 cmp_name = "_%s_key" % (a.name,) |
|
1738 # Add the key function to the global namespace |
|
1739 # of the evaluated function. |
|
1740 globs[cmp_name] = a.eq_key |
|
1741 lines.append( |
|
1742 " %s(self.%s)," |
|
1743 % ( |
|
1744 cmp_name, |
|
1745 a.name, |
|
1746 ) |
|
1747 ) |
|
1748 others.append( |
|
1749 " %s(other.%s)," |
|
1750 % ( |
|
1751 cmp_name, |
|
1752 a.name, |
|
1753 ) |
|
1754 ) |
|
1755 else: |
|
1756 lines.append(" self.%s," % (a.name,)) |
|
1757 others.append(" other.%s," % (a.name,)) |
|
1758 |
|
1759 lines += others + [" )"] |
|
1760 else: |
|
1761 lines.append(" return True") |
|
1762 |
|
1763 script = "\n".join(lines) |
|
1764 |
|
1765 return _make_method("__eq__", script, unique_filename, globs) |
|
1766 |
|
1767 |
|
1768 def _make_order(cls, attrs): |
|
1769 """ |
|
1770 Create ordering methods for *cls* with *attrs*. |
|
1771 """ |
|
1772 attrs = [a for a in attrs if a.order] |
|
1773 |
|
1774 def attrs_to_tuple(obj): |
|
1775 """ |
|
1776 Save us some typing. |
|
1777 """ |
|
1778 return tuple( |
|
1779 key(value) if key else value |
|
1780 for value, key in ( |
|
1781 (getattr(obj, a.name), a.order_key) for a in attrs |
|
1782 ) |
|
1783 ) |
|
1784 |
|
1785 def __lt__(self, other): |
|
1786 """ |
|
1787 Automatically created by attrs. |
|
1788 """ |
|
1789 if other.__class__ is self.__class__: |
|
1790 return attrs_to_tuple(self) < attrs_to_tuple(other) |
|
1791 |
|
1792 return NotImplemented |
|
1793 |
|
1794 def __le__(self, other): |
|
1795 """ |
|
1796 Automatically created by attrs. |
|
1797 """ |
|
1798 if other.__class__ is self.__class__: |
|
1799 return attrs_to_tuple(self) <= attrs_to_tuple(other) |
|
1800 |
|
1801 return NotImplemented |
|
1802 |
|
1803 def __gt__(self, other): |
|
1804 """ |
|
1805 Automatically created by attrs. |
|
1806 """ |
|
1807 if other.__class__ is self.__class__: |
|
1808 return attrs_to_tuple(self) > attrs_to_tuple(other) |
|
1809 |
|
1810 return NotImplemented |
|
1811 |
|
1812 def __ge__(self, other): |
|
1813 """ |
|
1814 Automatically created by attrs. |
|
1815 """ |
|
1816 if other.__class__ is self.__class__: |
|
1817 return attrs_to_tuple(self) >= attrs_to_tuple(other) |
|
1818 |
|
1819 return NotImplemented |
|
1820 |
|
1821 return __lt__, __le__, __gt__, __ge__ |
|
1822 |
|
1823 |
|
1824 def _add_eq(cls, attrs=None): |
|
1825 """ |
|
1826 Add equality methods to *cls* with *attrs*. |
|
1827 """ |
|
1828 if attrs is None: |
|
1829 attrs = cls.__attrs_attrs__ |
|
1830 |
|
1831 cls.__eq__ = _make_eq(cls, attrs) |
|
1832 cls.__ne__ = _make_ne() |
|
1833 |
|
1834 return cls |
|
1835 |
|
1836 |
|
1837 if HAS_F_STRINGS: |
|
1838 |
|
1839 def _make_repr(attrs, ns, cls): |
|
1840 unique_filename = _generate_unique_filename(cls, "repr") |
|
1841 # Figure out which attributes to include, and which function to use to |
|
1842 # format them. The a.repr value can be either bool or a custom |
|
1843 # callable. |
|
1844 attr_names_with_reprs = tuple( |
|
1845 (a.name, (repr if a.repr is True else a.repr), a.init) |
|
1846 for a in attrs |
|
1847 if a.repr is not False |
|
1848 ) |
|
1849 globs = { |
|
1850 name + "_repr": r |
|
1851 for name, r, _ in attr_names_with_reprs |
|
1852 if r != repr |
|
1853 } |
|
1854 globs["_compat"] = _compat |
|
1855 globs["AttributeError"] = AttributeError |
|
1856 globs["NOTHING"] = NOTHING |
|
1857 attribute_fragments = [] |
|
1858 for name, r, i in attr_names_with_reprs: |
|
1859 accessor = ( |
|
1860 "self." + name |
|
1861 if i |
|
1862 else 'getattr(self, "' + name + '", NOTHING)' |
|
1863 ) |
|
1864 fragment = ( |
|
1865 "%s={%s!r}" % (name, accessor) |
|
1866 if r == repr |
|
1867 else "%s={%s_repr(%s)}" % (name, name, accessor) |
|
1868 ) |
|
1869 attribute_fragments.append(fragment) |
|
1870 repr_fragment = ", ".join(attribute_fragments) |
|
1871 |
|
1872 if ns is None: |
|
1873 cls_name_fragment = ( |
|
1874 '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}' |
|
1875 ) |
470 else: |
1876 else: |
471 return not result |
1877 cls_name_fragment = ns + ".{self.__class__.__name__}" |
472 |
1878 |
473 def lt(self, other): |
1879 lines = [ |
474 """ |
1880 "def __repr__(self):", |
475 Automatically created by attrs. |
1881 " try:", |
476 """ |
1882 " already_repring = _compat.repr_context.already_repring", |
477 if isinstance(other, self.__class__): |
1883 " except AttributeError:", |
478 return attrs_to_tuple(self) < attrs_to_tuple(other) |
1884 " already_repring = {id(self),}", |
479 else: |
1885 " _compat.repr_context.already_repring = already_repring", |
480 return NotImplemented |
1886 " else:", |
481 |
1887 " if id(self) in already_repring:", |
482 def le(self, other): |
1888 " return '...'", |
483 """ |
1889 " else:", |
484 Automatically created by attrs. |
1890 " already_repring.add(id(self))", |
485 """ |
1891 " try:", |
486 if isinstance(other, self.__class__): |
1892 " return f'%s(%s)'" % (cls_name_fragment, repr_fragment), |
487 return attrs_to_tuple(self) <= attrs_to_tuple(other) |
1893 " finally:", |
488 else: |
1894 " already_repring.remove(id(self))", |
489 return NotImplemented |
1895 ] |
490 |
1896 |
491 def gt(self, other): |
1897 return _make_method( |
492 """ |
1898 "__repr__", "\n".join(lines), unique_filename, globs=globs |
493 Automatically created by attrs. |
1899 ) |
494 """ |
1900 |
495 if isinstance(other, self.__class__): |
1901 else: |
496 return attrs_to_tuple(self) > attrs_to_tuple(other) |
1902 |
497 else: |
1903 def _make_repr(attrs, ns, _): |
498 return NotImplemented |
1904 """ |
499 |
1905 Make a repr method that includes relevant *attrs*, adding *ns* to the |
500 def ge(self, other): |
1906 full name. |
501 """ |
1907 """ |
502 Automatically created by attrs. |
1908 |
503 """ |
1909 # Figure out which attributes to include, and which function to use to |
504 if isinstance(other, self.__class__): |
1910 # format them. The a.repr value can be either bool or a custom |
505 return attrs_to_tuple(self) >= attrs_to_tuple(other) |
1911 # callable. |
506 else: |
1912 attr_names_with_reprs = tuple( |
507 return NotImplemented |
1913 (a.name, repr if a.repr is True else a.repr) |
508 |
1914 for a in attrs |
509 cls.__eq__ = eq |
1915 if a.repr is not False |
510 cls.__ne__ = ne |
1916 ) |
511 cls.__lt__ = lt |
1917 |
512 cls.__le__ = le |
1918 def __repr__(self): |
513 cls.__gt__ = gt |
1919 """ |
514 cls.__ge__ = ge |
1920 Automatically created by attrs. |
515 |
1921 """ |
|
1922 try: |
|
1923 already_repring = _compat.repr_context.already_repring |
|
1924 except AttributeError: |
|
1925 already_repring = set() |
|
1926 _compat.repr_context.already_repring = already_repring |
|
1927 |
|
1928 if id(self) in already_repring: |
|
1929 return "..." |
|
1930 real_cls = self.__class__ |
|
1931 if ns is None: |
|
1932 class_name = real_cls.__qualname__.rsplit(">.", 1)[-1] |
|
1933 else: |
|
1934 class_name = ns + "." + real_cls.__name__ |
|
1935 |
|
1936 # Since 'self' remains on the stack (i.e.: strongly referenced) |
|
1937 # for the duration of this call, it's safe to depend on id(...) |
|
1938 # stability, and not need to track the instance and therefore |
|
1939 # worry about properties like weakref- or hash-ability. |
|
1940 already_repring.add(id(self)) |
|
1941 try: |
|
1942 result = [class_name, "("] |
|
1943 first = True |
|
1944 for name, attr_repr in attr_names_with_reprs: |
|
1945 if first: |
|
1946 first = False |
|
1947 else: |
|
1948 result.append(", ") |
|
1949 result.extend( |
|
1950 (name, "=", attr_repr(getattr(self, name, NOTHING))) |
|
1951 ) |
|
1952 return "".join(result) + ")" |
|
1953 finally: |
|
1954 already_repring.remove(id(self)) |
|
1955 |
|
1956 return __repr__ |
|
1957 |
|
1958 |
|
1959 def _add_repr(cls, ns=None, attrs=None): |
|
1960 """ |
|
1961 Add a repr method to *cls*. |
|
1962 """ |
|
1963 if attrs is None: |
|
1964 attrs = cls.__attrs_attrs__ |
|
1965 |
|
1966 cls.__repr__ = _make_repr(attrs, ns, cls) |
516 return cls |
1967 return cls |
517 |
1968 |
518 |
1969 |
519 def _add_repr(cls, ns=None, attrs=None): |
|
520 """ |
|
521 Add a repr method to *cls*. |
|
522 """ |
|
523 if attrs is None: |
|
524 attrs = [a for a in cls.__attrs_attrs__ if a.repr] |
|
525 |
|
526 def repr_(self): |
|
527 """ |
|
528 Automatically created by attrs. |
|
529 """ |
|
530 real_cls = self.__class__ |
|
531 if ns is None: |
|
532 qualname = getattr(real_cls, "__qualname__", None) |
|
533 if qualname is not None: |
|
534 class_name = qualname.rsplit(">.", 1)[-1] |
|
535 else: |
|
536 class_name = real_cls.__name__ |
|
537 else: |
|
538 class_name = ns + "." + real_cls.__name__ |
|
539 |
|
540 return "{0}({1})".format( |
|
541 class_name, |
|
542 ", ".join(a.name + "=" + repr(getattr(self, a.name)) |
|
543 for a in attrs) |
|
544 ) |
|
545 cls.__repr__ = repr_ |
|
546 return cls |
|
547 |
|
548 |
|
549 def _add_init(cls, frozen): |
|
550 """ |
|
551 Add a __init__ method to *cls*. If *frozen* is True, make it immutable. |
|
552 """ |
|
553 attrs = [a for a in cls.__attrs_attrs__ |
|
554 if a.init or a.default is not NOTHING] |
|
555 |
|
556 # We cache the generated init methods for the same kinds of attributes. |
|
557 sha1 = hashlib.sha1() |
|
558 r = repr(attrs) |
|
559 if not isinstance(r, bytes): |
|
560 r = r.encode('utf-8') |
|
561 sha1.update(r) |
|
562 unique_filename = "<attrs generated init {0}>".format( |
|
563 sha1.hexdigest() |
|
564 ) |
|
565 |
|
566 script, globs = _attrs_to_script( |
|
567 attrs, |
|
568 frozen, |
|
569 getattr(cls, "__attrs_post_init__", False), |
|
570 ) |
|
571 locs = {} |
|
572 bytecode = compile(script, unique_filename, "exec") |
|
573 attr_dict = dict((a.name, a) for a in attrs) |
|
574 globs.update({ |
|
575 "NOTHING": NOTHING, |
|
576 "attr_dict": attr_dict, |
|
577 }) |
|
578 if frozen is True: |
|
579 # Save the lookup overhead in __init__ if we need to circumvent |
|
580 # immutability. |
|
581 globs["_cached_setattr"] = _obj_setattr |
|
582 eval(bytecode, globs, locs) |
|
583 init = locs["__init__"] |
|
584 |
|
585 # In order of debuggers like PDB being able to step through the code, |
|
586 # we add a fake linecache entry. |
|
587 linecache.cache[unique_filename] = ( |
|
588 len(script), |
|
589 None, |
|
590 script.splitlines(True), |
|
591 unique_filename |
|
592 ) |
|
593 cls.__init__ = init |
|
594 return cls |
|
595 |
|
596 |
|
597 def _add_pickle(cls): |
|
598 """ |
|
599 Add pickle helpers, needed for frozen and slotted classes |
|
600 """ |
|
601 def _slots_getstate__(obj): |
|
602 """ |
|
603 Play nice with pickle. |
|
604 """ |
|
605 return tuple(getattr(obj, a.name) for a in fields(obj.__class__)) |
|
606 |
|
607 def _slots_setstate__(obj, state): |
|
608 """ |
|
609 Play nice with pickle. |
|
610 """ |
|
611 __bound_setattr = _obj_setattr.__get__(obj, Attribute) |
|
612 for a, value in zip(fields(obj.__class__), state): |
|
613 __bound_setattr(a.name, value) |
|
614 |
|
615 cls.__getstate__ = _slots_getstate__ |
|
616 cls.__setstate__ = _slots_setstate__ |
|
617 return cls |
|
618 |
|
619 |
|
620 def fields(cls): |
1970 def fields(cls): |
621 """ |
1971 """ |
622 Returns the tuple of ``attrs`` attributes for a class. |
1972 Return the tuple of ``attrs`` attributes for a class. |
623 |
1973 |
624 The tuple also allows accessing the fields by their names (see below for |
1974 The tuple also allows accessing the fields by their names (see below for |
625 examples). |
1975 examples). |
626 |
1976 |
627 :param type cls: Class to introspect. |
1977 :param type cls: Class to introspect. |
628 |
1978 |
629 :raise TypeError: If *cls* is not a class. |
1979 :raise TypeError: If *cls* is not a class. |
630 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` |
1980 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` |
631 class. |
1981 class. |
632 |
1982 |
633 :rtype: tuple (with name accesors) of :class:`attr.Attribute` |
1983 :rtype: tuple (with name accessors) of `attrs.Attribute` |
634 |
1984 |
635 .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields |
1985 .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields |
636 by name. |
1986 by name. |
637 """ |
1987 """ |
638 if not isclass(cls): |
1988 if not isinstance(cls, type): |
639 raise TypeError("Passed object must be a class.") |
1989 raise TypeError("Passed object must be a class.") |
640 attrs = getattr(cls, "__attrs_attrs__", None) |
1990 attrs = getattr(cls, "__attrs_attrs__", None) |
641 if attrs is None: |
1991 if attrs is None: |
642 raise NotAnAttrsClassError( |
1992 raise NotAnAttrsClassError( |
643 "{cls!r} is not an attrs-decorated class.".format(cls=cls) |
1993 "{cls!r} is not an attrs-decorated class.".format(cls=cls) |
644 ) |
1994 ) |
645 return attrs |
1995 return attrs |
646 |
1996 |
647 |
1997 |
|
1998 def fields_dict(cls): |
|
1999 """ |
|
2000 Return an ordered dictionary of ``attrs`` attributes for a class, whose |
|
2001 keys are the attribute names. |
|
2002 |
|
2003 :param type cls: Class to introspect. |
|
2004 |
|
2005 :raise TypeError: If *cls* is not a class. |
|
2006 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` |
|
2007 class. |
|
2008 |
|
2009 :rtype: an ordered dict where keys are attribute names and values are |
|
2010 `attrs.Attribute`\\ s. This will be a `dict` if it's |
|
2011 naturally ordered like on Python 3.6+ or an |
|
2012 :class:`~collections.OrderedDict` otherwise. |
|
2013 |
|
2014 .. versionadded:: 18.1.0 |
|
2015 """ |
|
2016 if not isinstance(cls, type): |
|
2017 raise TypeError("Passed object must be a class.") |
|
2018 attrs = getattr(cls, "__attrs_attrs__", None) |
|
2019 if attrs is None: |
|
2020 raise NotAnAttrsClassError( |
|
2021 "{cls!r} is not an attrs-decorated class.".format(cls=cls) |
|
2022 ) |
|
2023 return ordered_dict((a.name, a) for a in attrs) |
|
2024 |
|
2025 |
648 def validate(inst): |
2026 def validate(inst): |
649 """ |
2027 """ |
650 Validate all attributes on *inst* that have a validator. |
2028 Validate all attributes on *inst* that have a validator. |
651 |
2029 |
652 Leaves all exceptions through. |
2030 Leaves all exceptions through. |
660 v = a.validator |
2038 v = a.validator |
661 if v is not None: |
2039 if v is not None: |
662 v(inst, a, getattr(inst, a.name)) |
2040 v(inst, a, getattr(inst, a.name)) |
663 |
2041 |
664 |
2042 |
665 def _attrs_to_script(attrs, frozen, post_init): |
2043 def _is_slot_cls(cls): |
|
2044 return "__slots__" in cls.__dict__ |
|
2045 |
|
2046 |
|
2047 def _is_slot_attr(a_name, base_attr_map): |
|
2048 """ |
|
2049 Check if the attribute name comes from a slot class. |
|
2050 """ |
|
2051 return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name]) |
|
2052 |
|
2053 |
|
2054 def _make_init( |
|
2055 cls, |
|
2056 attrs, |
|
2057 pre_init, |
|
2058 post_init, |
|
2059 frozen, |
|
2060 slots, |
|
2061 cache_hash, |
|
2062 base_attr_map, |
|
2063 is_exc, |
|
2064 cls_on_setattr, |
|
2065 attrs_init, |
|
2066 ): |
|
2067 has_cls_on_setattr = ( |
|
2068 cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP |
|
2069 ) |
|
2070 |
|
2071 if frozen and has_cls_on_setattr: |
|
2072 raise ValueError("Frozen classes can't use on_setattr.") |
|
2073 |
|
2074 needs_cached_setattr = cache_hash or frozen |
|
2075 filtered_attrs = [] |
|
2076 attr_dict = {} |
|
2077 for a in attrs: |
|
2078 if not a.init and a.default is NOTHING: |
|
2079 continue |
|
2080 |
|
2081 filtered_attrs.append(a) |
|
2082 attr_dict[a.name] = a |
|
2083 |
|
2084 if a.on_setattr is not None: |
|
2085 if frozen is True: |
|
2086 raise ValueError("Frozen classes can't use on_setattr.") |
|
2087 |
|
2088 needs_cached_setattr = True |
|
2089 elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: |
|
2090 needs_cached_setattr = True |
|
2091 |
|
2092 unique_filename = _generate_unique_filename(cls, "init") |
|
2093 |
|
2094 script, globs, annotations = _attrs_to_init_script( |
|
2095 filtered_attrs, |
|
2096 frozen, |
|
2097 slots, |
|
2098 pre_init, |
|
2099 post_init, |
|
2100 cache_hash, |
|
2101 base_attr_map, |
|
2102 is_exc, |
|
2103 has_cls_on_setattr, |
|
2104 attrs_init, |
|
2105 ) |
|
2106 if cls.__module__ in sys.modules: |
|
2107 # This makes typing.get_type_hints(CLS.__init__) resolve string types. |
|
2108 globs.update(sys.modules[cls.__module__].__dict__) |
|
2109 |
|
2110 globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) |
|
2111 |
|
2112 if needs_cached_setattr: |
|
2113 # Save the lookup overhead in __init__ if we need to circumvent |
|
2114 # setattr hooks. |
|
2115 globs["_setattr"] = _obj_setattr |
|
2116 |
|
2117 init = _make_method( |
|
2118 "__attrs_init__" if attrs_init else "__init__", |
|
2119 script, |
|
2120 unique_filename, |
|
2121 globs, |
|
2122 ) |
|
2123 init.__annotations__ = annotations |
|
2124 |
|
2125 return init |
|
2126 |
|
2127 |
|
2128 def _setattr(attr_name, value_var, has_on_setattr): |
|
2129 """ |
|
2130 Use the cached object.setattr to set *attr_name* to *value_var*. |
|
2131 """ |
|
2132 return "_setattr(self, '%s', %s)" % (attr_name, value_var) |
|
2133 |
|
2134 |
|
2135 def _setattr_with_converter(attr_name, value_var, has_on_setattr): |
|
2136 """ |
|
2137 Use the cached object.setattr to set *attr_name* to *value_var*, but run |
|
2138 its converter first. |
|
2139 """ |
|
2140 return "_setattr(self, '%s', %s(%s))" % ( |
|
2141 attr_name, |
|
2142 _init_converter_pat % (attr_name,), |
|
2143 value_var, |
|
2144 ) |
|
2145 |
|
2146 |
|
2147 def _assign(attr_name, value, has_on_setattr): |
|
2148 """ |
|
2149 Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise |
|
2150 relegate to _setattr. |
|
2151 """ |
|
2152 if has_on_setattr: |
|
2153 return _setattr(attr_name, value, True) |
|
2154 |
|
2155 return "self.%s = %s" % (attr_name, value) |
|
2156 |
|
2157 |
|
2158 def _assign_with_converter(attr_name, value_var, has_on_setattr): |
|
2159 """ |
|
2160 Unless *attr_name* has an on_setattr hook, use normal assignment after |
|
2161 conversion. Otherwise relegate to _setattr_with_converter. |
|
2162 """ |
|
2163 if has_on_setattr: |
|
2164 return _setattr_with_converter(attr_name, value_var, True) |
|
2165 |
|
2166 return "self.%s = %s(%s)" % ( |
|
2167 attr_name, |
|
2168 _init_converter_pat % (attr_name,), |
|
2169 value_var, |
|
2170 ) |
|
2171 |
|
2172 |
|
2173 def _attrs_to_init_script( |
|
2174 attrs, |
|
2175 frozen, |
|
2176 slots, |
|
2177 pre_init, |
|
2178 post_init, |
|
2179 cache_hash, |
|
2180 base_attr_map, |
|
2181 is_exc, |
|
2182 has_cls_on_setattr, |
|
2183 attrs_init, |
|
2184 ): |
666 """ |
2185 """ |
667 Return a script of an initializer for *attrs* and a dict of globals. |
2186 Return a script of an initializer for *attrs* and a dict of globals. |
668 |
2187 |
669 The globals are expected by the generated script. |
2188 The globals are expected by the generated script. |
670 |
2189 |
671 If *frozen* is True, we cannot set the attributes directly so we use |
2190 If *frozen* is True, we cannot set the attributes directly so we use |
672 a cached ``object.__setattr__``. |
2191 a cached ``object.__setattr__``. |
673 """ |
2192 """ |
674 lines = [] |
2193 lines = [] |
|
2194 if pre_init: |
|
2195 lines.append("self.__attrs_pre_init__()") |
|
2196 |
675 if frozen is True: |
2197 if frozen is True: |
676 lines.append( |
2198 if slots is True: |
677 # Circumvent the __setattr__ descriptor to save one lookup per |
2199 fmt_setter = _setattr |
678 # assignment. |
2200 fmt_setter_with_converter = _setattr_with_converter |
679 "_setattr = _cached_setattr.__get__(self, self.__class__)" |
2201 else: |
680 ) |
2202 # Dict frozen classes assign directly to __dict__. |
681 |
2203 # But only if the attribute doesn't come from an ancestor slot |
682 def fmt_setter(attr_name, value_var): |
2204 # class. |
683 return "_setattr('%(attr_name)s', %(value_var)s)" % { |
2205 # Note _inst_dict will be used again below if cache_hash is True |
684 "attr_name": attr_name, |
2206 lines.append("_inst_dict = self.__dict__") |
685 "value_var": value_var, |
2207 |
686 } |
2208 def fmt_setter(attr_name, value_var, has_on_setattr): |
687 |
2209 if _is_slot_attr(attr_name, base_attr_map): |
688 def fmt_setter_with_converter(attr_name, value_var): |
2210 return _setattr(attr_name, value_var, has_on_setattr) |
689 conv_name = _init_convert_pat.format(attr_name) |
2211 |
690 return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % { |
2212 return "_inst_dict['%s'] = %s" % (attr_name, value_var) |
691 "attr_name": attr_name, |
2213 |
692 "value_var": value_var, |
2214 def fmt_setter_with_converter( |
693 "conv": conv_name, |
2215 attr_name, value_var, has_on_setattr |
694 } |
2216 ): |
|
2217 if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): |
|
2218 return _setattr_with_converter( |
|
2219 attr_name, value_var, has_on_setattr |
|
2220 ) |
|
2221 |
|
2222 return "_inst_dict['%s'] = %s(%s)" % ( |
|
2223 attr_name, |
|
2224 _init_converter_pat % (attr_name,), |
|
2225 value_var, |
|
2226 ) |
|
2227 |
695 else: |
2228 else: |
696 def fmt_setter(attr_name, value): |
2229 # Not frozen. |
697 return "self.%(attr_name)s = %(value)s" % { |
2230 fmt_setter = _assign |
698 "attr_name": attr_name, |
2231 fmt_setter_with_converter = _assign_with_converter |
699 "value": value, |
|
700 } |
|
701 |
|
702 def fmt_setter_with_converter(attr_name, value_var): |
|
703 conv_name = _init_convert_pat.format(attr_name) |
|
704 return "self.%(attr_name)s = %(conv)s(%(value_var)s)" % { |
|
705 "attr_name": attr_name, |
|
706 "value_var": value_var, |
|
707 "conv": conv_name, |
|
708 } |
|
709 |
2232 |
710 args = [] |
2233 args = [] |
|
2234 kw_only_args = [] |
711 attrs_to_validate = [] |
2235 attrs_to_validate = [] |
712 |
2236 |
713 # This is a dictionary of names to validator and converter callables. |
2237 # This is a dictionary of names to validator and converter callables. |
714 # Injecting this into __init__ globals lets us avoid lookups. |
2238 # Injecting this into __init__ globals lets us avoid lookups. |
715 names_for_globals = {} |
2239 names_for_globals = {} |
|
2240 annotations = {"return": None} |
716 |
2241 |
717 for a in attrs: |
2242 for a in attrs: |
718 if a.validator: |
2243 if a.validator: |
719 attrs_to_validate.append(a) |
2244 attrs_to_validate.append(a) |
|
2245 |
720 attr_name = a.name |
2246 attr_name = a.name |
|
2247 has_on_setattr = a.on_setattr is not None or ( |
|
2248 a.on_setattr is not setters.NO_OP and has_cls_on_setattr |
|
2249 ) |
721 arg_name = a.name.lstrip("_") |
2250 arg_name = a.name.lstrip("_") |
|
2251 |
722 has_factory = isinstance(a.default, Factory) |
2252 has_factory = isinstance(a.default, Factory) |
723 if has_factory and a.default.takes_self: |
2253 if has_factory and a.default.takes_self: |
724 maybe_self = "self" |
2254 maybe_self = "self" |
725 else: |
2255 else: |
726 maybe_self = "" |
2256 maybe_self = "" |
|
2257 |
727 if a.init is False: |
2258 if a.init is False: |
728 if has_factory: |
2259 if has_factory: |
729 init_factory_name = _init_factory_pat.format(a.name) |
2260 init_factory_name = _init_factory_pat.format(a.name) |
730 if a.convert is not None: |
2261 if a.converter is not None: |
731 lines.append(fmt_setter_with_converter( |
2262 lines.append( |
732 attr_name, |
2263 fmt_setter_with_converter( |
733 init_factory_name + "({0})".format(maybe_self))) |
2264 attr_name, |
734 conv_name = _init_convert_pat.format(a.name) |
2265 init_factory_name + "(%s)" % (maybe_self,), |
735 names_for_globals[conv_name] = a.convert |
2266 has_on_setattr, |
|
2267 ) |
|
2268 ) |
|
2269 conv_name = _init_converter_pat % (a.name,) |
|
2270 names_for_globals[conv_name] = a.converter |
736 else: |
2271 else: |
737 lines.append(fmt_setter( |
2272 lines.append( |
738 attr_name, |
2273 fmt_setter( |
739 init_factory_name + "({0})".format(maybe_self) |
2274 attr_name, |
740 )) |
2275 init_factory_name + "(%s)" % (maybe_self,), |
|
2276 has_on_setattr, |
|
2277 ) |
|
2278 ) |
741 names_for_globals[init_factory_name] = a.default.factory |
2279 names_for_globals[init_factory_name] = a.default.factory |
742 else: |
2280 else: |
743 if a.convert is not None: |
2281 if a.converter is not None: |
744 lines.append(fmt_setter_with_converter( |
2282 lines.append( |
|
2283 fmt_setter_with_converter( |
|
2284 attr_name, |
|
2285 "attr_dict['%s'].default" % (attr_name,), |
|
2286 has_on_setattr, |
|
2287 ) |
|
2288 ) |
|
2289 conv_name = _init_converter_pat % (a.name,) |
|
2290 names_for_globals[conv_name] = a.converter |
|
2291 else: |
|
2292 lines.append( |
|
2293 fmt_setter( |
|
2294 attr_name, |
|
2295 "attr_dict['%s'].default" % (attr_name,), |
|
2296 has_on_setattr, |
|
2297 ) |
|
2298 ) |
|
2299 elif a.default is not NOTHING and not has_factory: |
|
2300 arg = "%s=attr_dict['%s'].default" % (arg_name, attr_name) |
|
2301 if a.kw_only: |
|
2302 kw_only_args.append(arg) |
|
2303 else: |
|
2304 args.append(arg) |
|
2305 |
|
2306 if a.converter is not None: |
|
2307 lines.append( |
|
2308 fmt_setter_with_converter( |
|
2309 attr_name, arg_name, has_on_setattr |
|
2310 ) |
|
2311 ) |
|
2312 names_for_globals[ |
|
2313 _init_converter_pat % (a.name,) |
|
2314 ] = a.converter |
|
2315 else: |
|
2316 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) |
|
2317 |
|
2318 elif has_factory: |
|
2319 arg = "%s=NOTHING" % (arg_name,) |
|
2320 if a.kw_only: |
|
2321 kw_only_args.append(arg) |
|
2322 else: |
|
2323 args.append(arg) |
|
2324 lines.append("if %s is not NOTHING:" % (arg_name,)) |
|
2325 |
|
2326 init_factory_name = _init_factory_pat.format(a.name) |
|
2327 if a.converter is not None: |
|
2328 lines.append( |
|
2329 " " |
|
2330 + fmt_setter_with_converter( |
|
2331 attr_name, arg_name, has_on_setattr |
|
2332 ) |
|
2333 ) |
|
2334 lines.append("else:") |
|
2335 lines.append( |
|
2336 " " |
|
2337 + fmt_setter_with_converter( |
745 attr_name, |
2338 attr_name, |
746 "attr_dict['{attr_name}'].default" |
2339 init_factory_name + "(" + maybe_self + ")", |
747 .format(attr_name=attr_name) |
2340 has_on_setattr, |
748 )) |
2341 ) |
749 conv_name = _init_convert_pat.format(a.name) |
2342 ) |
750 names_for_globals[conv_name] = a.convert |
2343 names_for_globals[ |
751 else: |
2344 _init_converter_pat % (a.name,) |
752 lines.append(fmt_setter( |
2345 ] = a.converter |
|
2346 else: |
|
2347 lines.append( |
|
2348 " " + fmt_setter(attr_name, arg_name, has_on_setattr) |
|
2349 ) |
|
2350 lines.append("else:") |
|
2351 lines.append( |
|
2352 " " |
|
2353 + fmt_setter( |
753 attr_name, |
2354 attr_name, |
754 "attr_dict['{attr_name}'].default" |
2355 init_factory_name + "(" + maybe_self + ")", |
755 .format(attr_name=attr_name) |
2356 has_on_setattr, |
756 )) |
2357 ) |
757 elif a.default is not NOTHING and not has_factory: |
|
758 args.append( |
|
759 "{arg_name}=attr_dict['{attr_name}'].default".format( |
|
760 arg_name=arg_name, |
|
761 attr_name=attr_name, |
|
762 ) |
2358 ) |
763 ) |
|
764 if a.convert is not None: |
|
765 lines.append(fmt_setter_with_converter(attr_name, arg_name)) |
|
766 names_for_globals[_init_convert_pat.format(a.name)] = a.convert |
|
767 else: |
|
768 lines.append(fmt_setter(attr_name, arg_name)) |
|
769 elif has_factory: |
|
770 args.append("{arg_name}=NOTHING".format(arg_name=arg_name)) |
|
771 lines.append("if {arg_name} is not NOTHING:" |
|
772 .format(arg_name=arg_name)) |
|
773 init_factory_name = _init_factory_pat.format(a.name) |
|
774 if a.convert is not None: |
|
775 lines.append(" " + fmt_setter_with_converter(attr_name, |
|
776 arg_name)) |
|
777 lines.append("else:") |
|
778 lines.append(" " + fmt_setter_with_converter( |
|
779 attr_name, |
|
780 init_factory_name + "({0})".format(maybe_self) |
|
781 )) |
|
782 names_for_globals[_init_convert_pat.format(a.name)] = a.convert |
|
783 else: |
|
784 lines.append(" " + fmt_setter(attr_name, arg_name)) |
|
785 lines.append("else:") |
|
786 lines.append(" " + fmt_setter( |
|
787 attr_name, |
|
788 init_factory_name + "({0})".format(maybe_self) |
|
789 )) |
|
790 names_for_globals[init_factory_name] = a.default.factory |
2359 names_for_globals[init_factory_name] = a.default.factory |
791 else: |
2360 else: |
792 args.append(arg_name) |
2361 if a.kw_only: |
793 if a.convert is not None: |
2362 kw_only_args.append(arg_name) |
794 lines.append(fmt_setter_with_converter(attr_name, arg_name)) |
|
795 names_for_globals[_init_convert_pat.format(a.name)] = a.convert |
|
796 else: |
2363 else: |
797 lines.append(fmt_setter(attr_name, arg_name)) |
2364 args.append(arg_name) |
|
2365 |
|
2366 if a.converter is not None: |
|
2367 lines.append( |
|
2368 fmt_setter_with_converter( |
|
2369 attr_name, arg_name, has_on_setattr |
|
2370 ) |
|
2371 ) |
|
2372 names_for_globals[ |
|
2373 _init_converter_pat % (a.name,) |
|
2374 ] = a.converter |
|
2375 else: |
|
2376 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) |
|
2377 |
|
2378 if a.init is True: |
|
2379 if a.type is not None and a.converter is None: |
|
2380 annotations[arg_name] = a.type |
|
2381 elif a.converter is not None: |
|
2382 # Try to get the type from the converter. |
|
2383 t = _AnnotationExtractor(a.converter).get_first_param_type() |
|
2384 if t: |
|
2385 annotations[arg_name] = t |
798 |
2386 |
799 if attrs_to_validate: # we can skip this if there are no validators. |
2387 if attrs_to_validate: # we can skip this if there are no validators. |
800 names_for_globals["_config"] = _config |
2388 names_for_globals["_config"] = _config |
801 lines.append("if _config._run_validators is True:") |
2389 lines.append("if _config._run_validators is True:") |
802 for a in attrs_to_validate: |
2390 for a in attrs_to_validate: |
803 val_name = "__attr_validator_{}".format(a.name) |
2391 val_name = "__attr_validator_" + a.name |
804 attr_name = "__attr_{}".format(a.name) |
2392 attr_name = "__attr_" + a.name |
805 lines.append(" {}(self, {}, self.{})".format( |
2393 lines.append( |
806 val_name, attr_name, a.name)) |
2394 " %s(self, %s, self.%s)" % (val_name, attr_name, a.name) |
|
2395 ) |
807 names_for_globals[val_name] = a.validator |
2396 names_for_globals[val_name] = a.validator |
808 names_for_globals[attr_name] = a |
2397 names_for_globals[attr_name] = a |
|
2398 |
809 if post_init: |
2399 if post_init: |
810 lines.append("self.__attrs_post_init__()") |
2400 lines.append("self.__attrs_post_init__()") |
811 |
2401 |
812 return """\ |
2402 # because this is set only after __attrs_post_init__ is called, a crash |
813 def __init__(self, {args}): |
2403 # will result if post-init tries to access the hash code. This seemed |
|
2404 # preferable to setting this beforehand, in which case alteration to |
|
2405 # field values during post-init combined with post-init accessing the |
|
2406 # hash code would result in silent bugs. |
|
2407 if cache_hash: |
|
2408 if frozen: |
|
2409 if slots: |
|
2410 # if frozen and slots, then _setattr defined above |
|
2411 init_hash_cache = "_setattr(self, '%s', %s)" |
|
2412 else: |
|
2413 # if frozen and not slots, then _inst_dict defined above |
|
2414 init_hash_cache = "_inst_dict['%s'] = %s" |
|
2415 else: |
|
2416 init_hash_cache = "self.%s = %s" |
|
2417 lines.append(init_hash_cache % (_hash_cache_field, "None")) |
|
2418 |
|
2419 # For exceptions we rely on BaseException.__init__ for proper |
|
2420 # initialization. |
|
2421 if is_exc: |
|
2422 vals = ",".join("self." + a.name for a in attrs if a.init) |
|
2423 |
|
2424 lines.append("BaseException.__init__(self, %s)" % (vals,)) |
|
2425 |
|
2426 args = ", ".join(args) |
|
2427 if kw_only_args: |
|
2428 args += "%s*, %s" % ( |
|
2429 ", " if args else "", # leading comma |
|
2430 ", ".join(kw_only_args), # kw_only args |
|
2431 ) |
|
2432 return ( |
|
2433 """\ |
|
2434 def {init_name}(self, {args}): |
814 {lines} |
2435 {lines} |
815 """.format( |
2436 """.format( |
816 args=", ".join(args), |
2437 init_name=("__attrs_init__" if attrs_init else "__init__"), |
817 lines="\n ".join(lines) if lines else "pass", |
2438 args=args, |
818 ), names_for_globals |
2439 lines="\n ".join(lines) if lines else "pass", |
819 |
2440 ), |
820 |
2441 names_for_globals, |
821 class Attribute(object): |
2442 annotations, |
|
2443 ) |
|
2444 |
|
2445 |
|
2446 class Attribute: |
822 """ |
2447 """ |
823 *Read-only* representation of an attribute. |
2448 *Read-only* representation of an attribute. |
824 |
2449 |
825 :attribute name: The name of the attribute. |
2450 The class has *all* arguments of `attr.ib` (except for ``factory`` |
826 |
2451 which is only syntactic sugar for ``default=Factory(...)`` plus the |
827 Plus *all* arguments of :func:`attr.ib`. |
2452 following: |
828 """ |
2453 |
|
2454 - ``name`` (`str`): The name of the attribute. |
|
2455 - ``inherited`` (`bool`): Whether or not that attribute has been inherited |
|
2456 from a base class. |
|
2457 - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The callables |
|
2458 that are used for comparing and ordering objects by this attribute, |
|
2459 respectively. These are set by passing a callable to `attr.ib`'s ``eq``, |
|
2460 ``order``, or ``cmp`` arguments. See also :ref:`comparison customization |
|
2461 <custom-comparison>`. |
|
2462 |
|
2463 Instances of this class are frequently used for introspection purposes |
|
2464 like: |
|
2465 |
|
2466 - `fields` returns a tuple of them. |
|
2467 - Validators get them passed as the first argument. |
|
2468 - The :ref:`field transformer <transform-fields>` hook receives a list of |
|
2469 them. |
|
2470 |
|
2471 .. versionadded:: 20.1.0 *inherited* |
|
2472 .. versionadded:: 20.1.0 *on_setattr* |
|
2473 .. versionchanged:: 20.2.0 *inherited* is not taken into account for |
|
2474 equality checks and hashing anymore. |
|
2475 .. versionadded:: 21.1.0 *eq_key* and *order_key* |
|
2476 |
|
2477 For the full version history of the fields, see `attr.ib`. |
|
2478 """ |
|
2479 |
829 __slots__ = ( |
2480 __slots__ = ( |
830 "name", "default", "validator", "repr", "cmp", "hash", "init", |
2481 "name", |
831 "convert", "metadata", |
2482 "default", |
|
2483 "validator", |
|
2484 "repr", |
|
2485 "eq", |
|
2486 "eq_key", |
|
2487 "order", |
|
2488 "order_key", |
|
2489 "hash", |
|
2490 "init", |
|
2491 "metadata", |
|
2492 "type", |
|
2493 "converter", |
|
2494 "kw_only", |
|
2495 "inherited", |
|
2496 "on_setattr", |
832 ) |
2497 ) |
833 |
2498 |
834 def __init__(self, name, default, validator, repr, cmp, hash, init, |
2499 def __init__( |
835 convert=None, metadata=None): |
2500 self, |
|
2501 name, |
|
2502 default, |
|
2503 validator, |
|
2504 repr, |
|
2505 cmp, # XXX: unused, remove along with other cmp code. |
|
2506 hash, |
|
2507 init, |
|
2508 inherited, |
|
2509 metadata=None, |
|
2510 type=None, |
|
2511 converter=None, |
|
2512 kw_only=False, |
|
2513 eq=None, |
|
2514 eq_key=None, |
|
2515 order=None, |
|
2516 order_key=None, |
|
2517 on_setattr=None, |
|
2518 ): |
|
2519 eq, eq_key, order, order_key = _determine_attrib_eq_order( |
|
2520 cmp, eq_key or eq, order_key or order, True |
|
2521 ) |
|
2522 |
836 # Cache this descriptor here to speed things up later. |
2523 # Cache this descriptor here to speed things up later. |
837 bound_setattr = _obj_setattr.__get__(self, Attribute) |
2524 bound_setattr = _obj_setattr.__get__(self, Attribute) |
838 |
2525 |
|
2526 # Despite the big red warning, people *do* instantiate `Attribute` |
|
2527 # themselves. |
839 bound_setattr("name", name) |
2528 bound_setattr("name", name) |
840 bound_setattr("default", default) |
2529 bound_setattr("default", default) |
841 bound_setattr("validator", validator) |
2530 bound_setattr("validator", validator) |
842 bound_setattr("repr", repr) |
2531 bound_setattr("repr", repr) |
843 bound_setattr("cmp", cmp) |
2532 bound_setattr("eq", eq) |
|
2533 bound_setattr("eq_key", eq_key) |
|
2534 bound_setattr("order", order) |
|
2535 bound_setattr("order_key", order_key) |
844 bound_setattr("hash", hash) |
2536 bound_setattr("hash", hash) |
845 bound_setattr("init", init) |
2537 bound_setattr("init", init) |
846 bound_setattr("convert", convert) |
2538 bound_setattr("converter", converter) |
847 bound_setattr("metadata", (metadata_proxy(metadata) if metadata |
2539 bound_setattr( |
848 else _empty_metadata_singleton)) |
2540 "metadata", |
|
2541 ( |
|
2542 types.MappingProxyType(dict(metadata)) # Shallow copy |
|
2543 if metadata |
|
2544 else _empty_metadata_singleton |
|
2545 ), |
|
2546 ) |
|
2547 bound_setattr("type", type) |
|
2548 bound_setattr("kw_only", kw_only) |
|
2549 bound_setattr("inherited", inherited) |
|
2550 bound_setattr("on_setattr", on_setattr) |
849 |
2551 |
850 def __setattr__(self, name, value): |
2552 def __setattr__(self, name, value): |
851 raise FrozenInstanceError() |
2553 raise FrozenInstanceError() |
852 |
2554 |
853 @classmethod |
2555 @classmethod |
854 def from_counting_attr(cls, name, ca): |
2556 def from_counting_attr(cls, name, ca, type=None): |
|
2557 # type holds the annotated value. deal with conflicts: |
|
2558 if type is None: |
|
2559 type = ca.type |
|
2560 elif ca.type is not None: |
|
2561 raise ValueError( |
|
2562 "Type annotation and type argument cannot both be present" |
|
2563 ) |
855 inst_dict = { |
2564 inst_dict = { |
856 k: getattr(ca, k) |
2565 k: getattr(ca, k) |
857 for k |
2566 for k in Attribute.__slots__ |
858 in Attribute.__slots__ |
2567 if k |
859 if k not in ( |
2568 not in ( |
860 "name", "validator", "default", |
2569 "name", |
861 ) # exclude methods |
2570 "validator", |
|
2571 "default", |
|
2572 "type", |
|
2573 "inherited", |
|
2574 ) # exclude methods and deprecated alias |
862 } |
2575 } |
863 return cls(name=name, validator=ca._validator, default=ca._default, |
2576 return cls( |
864 **inst_dict) |
2577 name=name, |
|
2578 validator=ca._validator, |
|
2579 default=ca._default, |
|
2580 type=type, |
|
2581 cmp=None, |
|
2582 inherited=False, |
|
2583 **inst_dict |
|
2584 ) |
|
2585 |
|
2586 # Don't use attr.evolve since fields(Attribute) doesn't work |
|
2587 def evolve(self, **changes): |
|
2588 """ |
|
2589 Copy *self* and apply *changes*. |
|
2590 |
|
2591 This works similarly to `attr.evolve` but that function does not work |
|
2592 with ``Attribute``. |
|
2593 |
|
2594 It is mainly meant to be used for `transform-fields`. |
|
2595 |
|
2596 .. versionadded:: 20.3.0 |
|
2597 """ |
|
2598 new = copy.copy(self) |
|
2599 |
|
2600 new._setattrs(changes.items()) |
|
2601 |
|
2602 return new |
865 |
2603 |
866 # Don't use _add_pickle since fields(Attribute) doesn't work |
2604 # Don't use _add_pickle since fields(Attribute) doesn't work |
867 def __getstate__(self): |
2605 def __getstate__(self): |
868 """ |
2606 """ |
869 Play nice with pickle. |
2607 Play nice with pickle. |
870 """ |
2608 """ |
871 return tuple(getattr(self, name) if name != "metadata" |
2609 return tuple( |
872 else dict(self.metadata) |
2610 getattr(self, name) if name != "metadata" else dict(self.metadata) |
873 for name in self.__slots__) |
2611 for name in self.__slots__ |
|
2612 ) |
874 |
2613 |
875 def __setstate__(self, state): |
2614 def __setstate__(self, state): |
876 """ |
2615 """ |
877 Play nice with pickle. |
2616 Play nice with pickle. |
878 """ |
2617 """ |
|
2618 self._setattrs(zip(self.__slots__, state)) |
|
2619 |
|
2620 def _setattrs(self, name_values_pairs): |
879 bound_setattr = _obj_setattr.__get__(self, Attribute) |
2621 bound_setattr = _obj_setattr.__get__(self, Attribute) |
880 for name, value in zip(self.__slots__, state): |
2622 for name, value in name_values_pairs: |
881 if name != "metadata": |
2623 if name != "metadata": |
882 bound_setattr(name, value) |
2624 bound_setattr(name, value) |
883 else: |
2625 else: |
884 bound_setattr(name, metadata_proxy(value) if value else |
2626 bound_setattr( |
885 _empty_metadata_singleton) |
2627 name, |
886 |
2628 types.MappingProxyType(dict(value)) |
887 |
2629 if value |
888 _a = [Attribute(name=name, default=NOTHING, validator=None, |
2630 else _empty_metadata_singleton, |
889 repr=True, cmp=True, hash=(name != "metadata"), init=True) |
2631 ) |
890 for name in Attribute.__slots__] |
2632 |
|
2633 |
|
2634 _a = [ |
|
2635 Attribute( |
|
2636 name=name, |
|
2637 default=NOTHING, |
|
2638 validator=None, |
|
2639 repr=True, |
|
2640 cmp=None, |
|
2641 eq=True, |
|
2642 order=False, |
|
2643 hash=(name != "metadata"), |
|
2644 init=True, |
|
2645 inherited=False, |
|
2646 ) |
|
2647 for name in Attribute.__slots__ |
|
2648 ] |
891 |
2649 |
892 Attribute = _add_hash( |
2650 Attribute = _add_hash( |
893 _add_cmp(_add_repr(Attribute, attrs=_a), attrs=_a), |
2651 _add_eq( |
894 attrs=[a for a in _a if a.hash] |
2652 _add_repr(Attribute, attrs=_a), |
|
2653 attrs=[a for a in _a if a.name != "inherited"], |
|
2654 ), |
|
2655 attrs=[a for a in _a if a.hash and a.name != "inherited"], |
895 ) |
2656 ) |
896 |
2657 |
897 |
2658 |
898 class _CountingAttr(object): |
2659 class _CountingAttr: |
899 """ |
2660 """ |
900 Intermediate representation of attributes that uses a counter to preserve |
2661 Intermediate representation of attributes that uses a counter to preserve |
901 the order in which the attributes have been defined. |
2662 the order in which the attributes have been defined. |
902 |
2663 |
903 *Internal* data structure of the attrs library. Running into is most |
2664 *Internal* data structure of the attrs library. Running into is most |
904 likely the result of a bug like a forgotten `@attr.s` decorator. |
2665 likely the result of a bug like a forgotten `@attr.s` decorator. |
905 """ |
2666 """ |
906 __slots__ = ("counter", "_default", "repr", "cmp", "hash", "init", |
2667 |
907 "metadata", "_validator", "convert") |
2668 __slots__ = ( |
|
2669 "counter", |
|
2670 "_default", |
|
2671 "repr", |
|
2672 "eq", |
|
2673 "eq_key", |
|
2674 "order", |
|
2675 "order_key", |
|
2676 "hash", |
|
2677 "init", |
|
2678 "metadata", |
|
2679 "_validator", |
|
2680 "converter", |
|
2681 "type", |
|
2682 "kw_only", |
|
2683 "on_setattr", |
|
2684 ) |
908 __attrs_attrs__ = tuple( |
2685 __attrs_attrs__ = tuple( |
909 Attribute(name=name, default=NOTHING, validator=None, |
2686 Attribute( |
910 repr=True, cmp=True, hash=True, init=True) |
2687 name=name, |
911 for name |
2688 default=NOTHING, |
912 in ("counter", "_default", "repr", "cmp", "hash", "init",) |
2689 validator=None, |
|
2690 repr=True, |
|
2691 cmp=None, |
|
2692 hash=True, |
|
2693 init=True, |
|
2694 kw_only=False, |
|
2695 eq=True, |
|
2696 eq_key=None, |
|
2697 order=False, |
|
2698 order_key=None, |
|
2699 inherited=False, |
|
2700 on_setattr=None, |
|
2701 ) |
|
2702 for name in ( |
|
2703 "counter", |
|
2704 "_default", |
|
2705 "repr", |
|
2706 "eq", |
|
2707 "order", |
|
2708 "hash", |
|
2709 "init", |
|
2710 "on_setattr", |
|
2711 ) |
913 ) + ( |
2712 ) + ( |
914 Attribute(name="metadata", default=None, validator=None, |
2713 Attribute( |
915 repr=True, cmp=True, hash=False, init=True), |
2714 name="metadata", |
|
2715 default=None, |
|
2716 validator=None, |
|
2717 repr=True, |
|
2718 cmp=None, |
|
2719 hash=False, |
|
2720 init=True, |
|
2721 kw_only=False, |
|
2722 eq=True, |
|
2723 eq_key=None, |
|
2724 order=False, |
|
2725 order_key=None, |
|
2726 inherited=False, |
|
2727 on_setattr=None, |
|
2728 ), |
916 ) |
2729 ) |
917 cls_counter = 0 |
2730 cls_counter = 0 |
918 |
2731 |
919 def __init__(self, default, validator, repr, cmp, hash, init, convert, |
2732 def __init__( |
920 metadata): |
2733 self, |
|
2734 default, |
|
2735 validator, |
|
2736 repr, |
|
2737 cmp, |
|
2738 hash, |
|
2739 init, |
|
2740 converter, |
|
2741 metadata, |
|
2742 type, |
|
2743 kw_only, |
|
2744 eq, |
|
2745 eq_key, |
|
2746 order, |
|
2747 order_key, |
|
2748 on_setattr, |
|
2749 ): |
921 _CountingAttr.cls_counter += 1 |
2750 _CountingAttr.cls_counter += 1 |
922 self.counter = _CountingAttr.cls_counter |
2751 self.counter = _CountingAttr.cls_counter |
923 self._default = default |
2752 self._default = default |
924 # If validator is a list/tuple, wrap it using helper validator. |
2753 self._validator = validator |
925 if validator and isinstance(validator, (list, tuple)): |
2754 self.converter = converter |
926 self._validator = and_(*validator) |
|
927 else: |
|
928 self._validator = validator |
|
929 self.repr = repr |
2755 self.repr = repr |
930 self.cmp = cmp |
2756 self.eq = eq |
|
2757 self.eq_key = eq_key |
|
2758 self.order = order |
|
2759 self.order_key = order_key |
931 self.hash = hash |
2760 self.hash = hash |
932 self.init = init |
2761 self.init = init |
933 self.convert = convert |
|
934 self.metadata = metadata |
2762 self.metadata = metadata |
|
2763 self.type = type |
|
2764 self.kw_only = kw_only |
|
2765 self.on_setattr = on_setattr |
935 |
2766 |
936 def validator(self, meth): |
2767 def validator(self, meth): |
937 """ |
2768 """ |
938 Decorator that adds *meth* to the list of validators. |
2769 Decorator that adds *meth* to the list of validators. |
939 |
2770 |