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