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