Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/attr/_make.py: 69%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: MIT
3import contextlib
4import copy
5import enum
6import functools
7import inspect
8import itertools
9import linecache
10import sys
11import types
12import typing
14from operator import itemgetter
16# We need to import _compat itself in addition to the _compat members to avoid
17# having the thread-local in the globals here.
18from . import _compat, _config, setters
19from ._compat import (
20 PY310,
21 PY_3_8_PLUS,
22 _AnnotationExtractor,
23 get_generic_base,
24)
25from .exceptions import (
26 DefaultAlreadySetError,
27 FrozenInstanceError,
28 NotAnAttrsClassError,
29 UnannotatedAttributeError,
30)
33# This is used at least twice, so cache it here.
34_obj_setattr = object.__setattr__
35_init_converter_pat = "__attr_converter_%s"
36_init_factory_pat = "__attr_factory_%s"
37_classvar_prefixes = (
38 "typing.ClassVar",
39 "t.ClassVar",
40 "ClassVar",
41 "typing_extensions.ClassVar",
42)
43# we don't use a double-underscore prefix because that triggers
44# name mangling when trying to create a slot for the field
45# (when slots=True)
46_hash_cache_field = "_attrs_cached_hash"
48_empty_metadata_singleton = types.MappingProxyType({})
50# Unique object for unequivocal getattr() defaults.
51_sentinel = object()
53_ng_default_on_setattr = setters.pipe(setters.convert, setters.validate)
56class _Nothing(enum.Enum):
57 """
58 Sentinel to indicate the lack of a value when ``None`` is ambiguous.
60 If extending attrs, you can use ``typing.Literal[NOTHING]`` to show
61 that a value may be ``NOTHING``.
63 .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False.
64 .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant.
65 """
67 NOTHING = enum.auto()
69 def __repr__(self):
70 return "NOTHING"
72 def __bool__(self):
73 return False
76NOTHING = _Nothing.NOTHING
77"""
78Sentinel to indicate the lack of a value when ``None`` is ambiguous.
79"""
82class _CacheHashWrapper(int):
83 """
84 An integer subclass that pickles / copies as None
86 This is used for non-slots classes with ``cache_hash=True``, to avoid
87 serializing a potentially (even likely) invalid hash value. Since ``None``
88 is the default value for uncalculated hashes, whenever this is copied,
89 the copy's value for the hash should automatically reset.
91 See GH #613 for more details.
92 """
94 def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008
95 return _none_constructor, _args
98def attrib(
99 default=NOTHING,
100 validator=None,
101 repr=True,
102 cmp=None,
103 hash=None,
104 init=True,
105 metadata=None,
106 type=None,
107 converter=None,
108 factory=None,
109 kw_only=False,
110 eq=None,
111 order=None,
112 on_setattr=None,
113 alias=None,
114):
115 """
116 Create a new attribute on a class.
118 .. warning::
120 Does *not* do anything unless the class is also decorated with `attr.s`
121 / `attrs.define` / and so on!
123 Please consider using `attrs.field` in new code (``attr.ib`` will *never*
124 go away, though).
126 :param default: A value that is used if an *attrs*-generated ``__init__``
127 is used and no value is passed while instantiating or the attribute is
128 excluded using ``init=False``.
130 If the value is an instance of `attrs.Factory`, its callable will be
131 used to construct a new value (useful for mutable data types like lists
132 or dicts).
134 If a default is not set (or set manually to `attrs.NOTHING`), a value
135 *must* be supplied when instantiating; otherwise a `TypeError` will be
136 raised.
138 The default can also be set using decorator notation as shown below.
140 .. seealso:: `defaults`
142 :param callable factory: Syntactic sugar for
143 ``default=attr.Factory(factory)``.
145 :param validator: `callable` that is called by *attrs*-generated
146 ``__init__`` methods after the instance has been initialized. They
147 receive the initialized instance, the :func:`~attrs.Attribute`, and the
148 passed value.
150 The return value is *not* inspected so the validator has to throw an
151 exception itself.
153 If a `list` is passed, its items are treated as validators and must all
154 pass.
156 Validators can be globally disabled and re-enabled using
157 `attrs.validators.get_disabled` / `attrs.validators.set_disabled`.
159 The validator can also be set using decorator notation as shown below.
161 .. seealso:: :ref:`validators`
163 :type validator: `callable` or a `list` of `callable`\\ s.
165 :param repr: Include this attribute in the generated ``__repr__`` method.
166 If ``True``, include the attribute; if ``False``, omit it. By default,
167 the built-in ``repr()`` function is used. To override how the attribute
168 value is formatted, pass a ``callable`` that takes a single value and
169 returns a string. Note that the resulting string is used as-is, i.e. it
170 will be used directly *instead* of calling ``repr()`` (the default).
171 :type repr: a `bool` or a `callable` to use a custom function.
173 :param eq: If ``True`` (default), include this attribute in the generated
174 ``__eq__`` and ``__ne__`` methods that check two instances for
175 equality. To override how the attribute value is compared, pass a
176 ``callable`` that takes a single value and returns the value to be
177 compared.
179 .. seealso:: `comparison`
180 :type eq: a `bool` or a `callable`.
182 :param order: If ``True`` (default), include this attributes in the
183 generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To
184 override how the attribute value is ordered, pass a ``callable`` that
185 takes a single value and returns the value to be ordered.
187 .. seealso:: `comparison`
188 :type order: a `bool` or a `callable`.
190 :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the
191 same value. Must not be mixed with *eq* or *order*.
193 .. seealso:: `comparison`
194 :type cmp: a `bool` or a `callable`.
196 :param bool | None hash: Include this attribute in the generated
197 ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This
198 is the correct behavior according the Python spec. Setting this value
199 to anything else than ``None`` is *discouraged*.
201 .. seealso:: `hashing`
202 :param bool init: Include this attribute in the generated ``__init__``
203 method. It is possible to set this to ``False`` and set a default
204 value. In that case this attributed is unconditionally initialized
205 with the specified default value or factory.
207 .. seealso:: `init`
208 :param callable converter: `callable` that is called by *attrs*-generated
209 ``__init__`` methods to convert attribute's value to the desired
210 format. It is given the passed-in value, and the returned value will
211 be used as the new value of the attribute. The value is converted
212 before being passed to the validator, if any.
214 .. seealso:: :ref:`converters`
215 :param dict | None metadata: An arbitrary mapping, to be used by
216 third-party components. See `extending-metadata`.
218 :param type: The type of the attribute. Nowadays, the preferred method to
219 specify the type is using a variable annotation (see :pep:`526`). This
220 argument is provided for backward compatibility. Regardless of the
221 approach used, the type will be stored on ``Attribute.type``.
223 Please note that *attrs* doesn't do anything with this metadata by
224 itself. You can use it as part of your own code or for `static type
225 checking <types>`.
226 :param bool kw_only: Make this attribute keyword-only in the generated
227 ``__init__`` (if ``init`` is ``False``, this parameter is ignored).
228 :param on_setattr: Allows to overwrite the *on_setattr* setting from
229 `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used.
230 Set to `attrs.setters.NO_OP` to run **no** `setattr` hooks for this
231 attribute -- regardless of the setting in `attr.s`.
232 :type on_setattr: `callable`, or a list of callables, or `None`, or
233 `attrs.setters.NO_OP`
234 :param str | None alias: Override this attribute's parameter name in the
235 generated ``__init__`` method. If left `None`, default to ``name``
236 stripped of leading underscores. See `private-attributes`.
238 .. versionadded:: 15.2.0 *convert*
239 .. versionadded:: 16.3.0 *metadata*
240 .. versionchanged:: 17.1.0 *validator* can be a ``list`` now.
241 .. versionchanged:: 17.1.0
242 *hash* is ``None`` and therefore mirrors *eq* by default.
243 .. versionadded:: 17.3.0 *type*
244 .. deprecated:: 17.4.0 *convert*
245 .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated
246 *convert* to achieve consistency with other noun-based arguments.
247 .. versionadded:: 18.1.0
248 ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``.
249 .. versionadded:: 18.2.0 *kw_only*
250 .. versionchanged:: 19.2.0 *convert* keyword argument removed.
251 .. versionchanged:: 19.2.0 *repr* also accepts a custom callable.
252 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
253 .. versionadded:: 19.2.0 *eq* and *order*
254 .. versionadded:: 20.1.0 *on_setattr*
255 .. versionchanged:: 20.3.0 *kw_only* backported to Python 2
256 .. versionchanged:: 21.1.0
257 *eq*, *order*, and *cmp* also accept a custom callable
258 .. versionchanged:: 21.1.0 *cmp* undeprecated
259 .. versionadded:: 22.2.0 *alias*
260 """
261 eq, eq_key, order, order_key = _determine_attrib_eq_order(
262 cmp, eq, order, True
263 )
265 if hash is not None and hash is not True and hash is not False:
266 msg = "Invalid value for hash. Must be True, False, or None."
267 raise TypeError(msg)
269 if factory is not None:
270 if default is not NOTHING:
271 msg = (
272 "The `default` and `factory` arguments are mutually exclusive."
273 )
274 raise ValueError(msg)
275 if not callable(factory):
276 msg = "The `factory` argument must be a callable."
277 raise ValueError(msg)
278 default = Factory(factory)
280 if metadata is None:
281 metadata = {}
283 # Apply syntactic sugar by auto-wrapping.
284 if isinstance(on_setattr, (list, tuple)):
285 on_setattr = setters.pipe(*on_setattr)
287 if validator and isinstance(validator, (list, tuple)):
288 validator = and_(*validator)
290 if converter and isinstance(converter, (list, tuple)):
291 converter = pipe(*converter)
293 return _CountingAttr(
294 default=default,
295 validator=validator,
296 repr=repr,
297 cmp=None,
298 hash=hash,
299 init=init,
300 converter=converter,
301 metadata=metadata,
302 type=type,
303 kw_only=kw_only,
304 eq=eq,
305 eq_key=eq_key,
306 order=order,
307 order_key=order_key,
308 on_setattr=on_setattr,
309 alias=alias,
310 )
313def _compile_and_eval(script, globs, locs=None, filename=""):
314 """
315 "Exec" the script with the given global (globs) and local (locs) variables.
316 """
317 bytecode = compile(script, filename, "exec")
318 eval(bytecode, globs, locs)
321def _make_method(name, script, filename, globs):
322 """
323 Create the method with the script given and return the method object.
324 """
325 locs = {}
327 # In order of debuggers like PDB being able to step through the code,
328 # we add a fake linecache entry.
329 count = 1
330 base_filename = filename
331 while True:
332 linecache_tuple = (
333 len(script),
334 None,
335 script.splitlines(True),
336 filename,
337 )
338 old_val = linecache.cache.setdefault(filename, linecache_tuple)
339 if old_val == linecache_tuple:
340 break
342 filename = f"{base_filename[:-1]}-{count}>"
343 count += 1
345 _compile_and_eval(script, globs, locs, filename)
347 return locs[name]
350def _make_attr_tuple_class(cls_name, attr_names):
351 """
352 Create a tuple subclass to hold `Attribute`s for an `attrs` class.
354 The subclass is a bare tuple with properties for names.
356 class MyClassAttributes(tuple):
357 __slots__ = ()
358 x = property(itemgetter(0))
359 """
360 attr_class_name = f"{cls_name}Attributes"
361 attr_class_template = [
362 f"class {attr_class_name}(tuple):",
363 " __slots__ = ()",
364 ]
365 if attr_names:
366 for i, attr_name in enumerate(attr_names):
367 attr_class_template.append(
368 f" {attr_name} = _attrs_property(_attrs_itemgetter({i}))"
369 )
370 else:
371 attr_class_template.append(" pass")
372 globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property}
373 _compile_and_eval("\n".join(attr_class_template), globs)
374 return globs[attr_class_name]
377# Tuple class for extracted attributes from a class definition.
378# `base_attrs` is a subset of `attrs`.
379_Attributes = _make_attr_tuple_class(
380 "_Attributes",
381 [
382 # all attributes to build dunder methods for
383 "attrs",
384 # attributes that have been inherited
385 "base_attrs",
386 # map inherited attributes to their originating classes
387 "base_attrs_map",
388 ],
389)
392def _is_class_var(annot):
393 """
394 Check whether *annot* is a typing.ClassVar.
396 The string comparison hack is used to avoid evaluating all string
397 annotations which would put attrs-based classes at a performance
398 disadvantage compared to plain old classes.
399 """
400 annot = str(annot)
402 # Annotation can be quoted.
403 if annot.startswith(("'", '"')) and annot.endswith(("'", '"')):
404 annot = annot[1:-1]
406 return annot.startswith(_classvar_prefixes)
409def _has_own_attribute(cls, attrib_name):
410 """
411 Check whether *cls* defines *attrib_name* (and doesn't just inherit it).
412 """
413 attr = getattr(cls, attrib_name, _sentinel)
414 if attr is _sentinel:
415 return False
417 for base_cls in cls.__mro__[1:]:
418 a = getattr(base_cls, attrib_name, None)
419 if attr is a:
420 return False
422 return True
425def _get_annotations(cls):
426 """
427 Get annotations for *cls*.
428 """
429 if _has_own_attribute(cls, "__annotations__"):
430 return cls.__annotations__
432 return {}
435def _collect_base_attrs(cls, taken_attr_names):
436 """
437 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.
438 """
439 base_attrs = []
440 base_attr_map = {} # A dictionary of base attrs to their classes.
442 # Traverse the MRO and collect attributes.
443 for base_cls in reversed(cls.__mro__[1:-1]):
444 for a in getattr(base_cls, "__attrs_attrs__", []):
445 if a.inherited or a.name in taken_attr_names:
446 continue
448 a = a.evolve(inherited=True) # noqa: PLW2901
449 base_attrs.append(a)
450 base_attr_map[a.name] = base_cls
452 # For each name, only keep the freshest definition i.e. the furthest at the
453 # back. base_attr_map is fine because it gets overwritten with every new
454 # instance.
455 filtered = []
456 seen = set()
457 for a in reversed(base_attrs):
458 if a.name in seen:
459 continue
460 filtered.insert(0, a)
461 seen.add(a.name)
463 return filtered, base_attr_map
466def _collect_base_attrs_broken(cls, taken_attr_names):
467 """
468 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.
470 N.B. *taken_attr_names* will be mutated.
472 Adhere to the old incorrect behavior.
474 Notably it collects from the front and considers inherited attributes which
475 leads to the buggy behavior reported in #428.
476 """
477 base_attrs = []
478 base_attr_map = {} # A dictionary of base attrs to their classes.
480 # Traverse the MRO and collect attributes.
481 for base_cls in cls.__mro__[1:-1]:
482 for a in getattr(base_cls, "__attrs_attrs__", []):
483 if a.name in taken_attr_names:
484 continue
486 a = a.evolve(inherited=True) # noqa: PLW2901
487 taken_attr_names.add(a.name)
488 base_attrs.append(a)
489 base_attr_map[a.name] = base_cls
491 return base_attrs, base_attr_map
494def _transform_attrs(
495 cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer
496):
497 """
498 Transform all `_CountingAttr`s on a class into `Attribute`s.
500 If *these* is passed, use that and don't look for them on the class.
502 *collect_by_mro* is True, collect them in the correct MRO order, otherwise
503 use the old -- incorrect -- order. See #428.
505 Return an `_Attributes`.
506 """
507 cd = cls.__dict__
508 anns = _get_annotations(cls)
510 if these is not None:
511 ca_list = list(these.items())
512 elif auto_attribs is True:
513 ca_names = {
514 name
515 for name, attr in cd.items()
516 if isinstance(attr, _CountingAttr)
517 }
518 ca_list = []
519 annot_names = set()
520 for attr_name, type in anns.items():
521 if _is_class_var(type):
522 continue
523 annot_names.add(attr_name)
524 a = cd.get(attr_name, NOTHING)
526 if not isinstance(a, _CountingAttr):
527 a = attrib() if a is NOTHING else attrib(default=a)
528 ca_list.append((attr_name, a))
530 unannotated = ca_names - annot_names
531 if len(unannotated) > 0:
532 raise UnannotatedAttributeError(
533 "The following `attr.ib`s lack a type annotation: "
534 + ", ".join(
535 sorted(unannotated, key=lambda n: cd.get(n).counter)
536 )
537 + "."
538 )
539 else:
540 ca_list = sorted(
541 (
542 (name, attr)
543 for name, attr in cd.items()
544 if isinstance(attr, _CountingAttr)
545 ),
546 key=lambda e: e[1].counter,
547 )
549 own_attrs = [
550 Attribute.from_counting_attr(
551 name=attr_name, ca=ca, type=anns.get(attr_name)
552 )
553 for attr_name, ca in ca_list
554 ]
556 if collect_by_mro:
557 base_attrs, base_attr_map = _collect_base_attrs(
558 cls, {a.name for a in own_attrs}
559 )
560 else:
561 base_attrs, base_attr_map = _collect_base_attrs_broken(
562 cls, {a.name for a in own_attrs}
563 )
565 if kw_only:
566 own_attrs = [a.evolve(kw_only=True) for a in own_attrs]
567 base_attrs = [a.evolve(kw_only=True) for a in base_attrs]
569 attrs = base_attrs + own_attrs
571 # Mandatory vs non-mandatory attr order only matters when they are part of
572 # the __init__ signature and when they aren't kw_only (which are moved to
573 # the end and can be mandatory or non-mandatory in any order, as they will
574 # be specified as keyword args anyway). Check the order of those attrs:
575 had_default = False
576 for a in (a for a in attrs if a.init is not False and a.kw_only is False):
577 if had_default is True and a.default is NOTHING:
578 msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}"
579 raise ValueError(msg)
581 if had_default is False and a.default is not NOTHING:
582 had_default = True
584 if field_transformer is not None:
585 attrs = field_transformer(cls, attrs)
587 # Resolve default field alias after executing field_transformer.
588 # This allows field_transformer to differentiate between explicit vs
589 # default aliases and supply their own defaults.
590 attrs = [
591 a.evolve(alias=_default_init_alias_for(a.name)) if not a.alias else a
592 for a in attrs
593 ]
595 # Create AttrsClass *after* applying the field_transformer since it may
596 # add or remove attributes!
597 attr_names = [a.name for a in attrs]
598 AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
600 return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map))
603def _make_cached_property_getattr(
604 cached_properties,
605 original_getattr,
606 cls,
607):
608 lines = [
609 # Wrapped to get `__class__` into closure cell for super()
610 # (It will be replaced with the newly constructed class after construction).
611 "def wrapper():",
612 " __class__ = _cls",
613 " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):",
614 " func = cached_properties.get(item)",
615 " if func is not None:",
616 " result = func(self)",
617 " _setter = _cached_setattr_get(self)",
618 " _setter(item, result)",
619 " return result",
620 ]
621 if original_getattr is not None:
622 lines.append(
623 " return original_getattr(self, item)",
624 )
625 else:
626 lines.extend(
627 [
628 " if hasattr(super(), '__getattr__'):",
629 " return super().__getattr__(item)",
630 " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"",
631 " raise AttributeError(original_error)",
632 ]
633 )
635 lines.extend(
636 [
637 " return __getattr__",
638 "__getattr__ = wrapper()",
639 ]
640 )
642 unique_filename = _generate_unique_filename(cls, "getattr")
644 glob = {
645 "cached_properties": cached_properties,
646 "_cached_setattr_get": _obj_setattr.__get__,
647 "_cls": cls,
648 "original_getattr": original_getattr,
649 }
651 return _make_method(
652 "__getattr__",
653 "\n".join(lines),
654 unique_filename,
655 glob,
656 )
659def _frozen_setattrs(self, name, value):
660 """
661 Attached to frozen classes as __setattr__.
662 """
663 if isinstance(self, BaseException) and name in (
664 "__cause__",
665 "__context__",
666 "__traceback__",
667 ):
668 BaseException.__setattr__(self, name, value)
669 return
671 raise FrozenInstanceError()
674def _frozen_delattrs(self, name):
675 """
676 Attached to frozen classes as __delattr__.
677 """
678 raise FrozenInstanceError()
681class _ClassBuilder:
682 """
683 Iteratively build *one* class.
684 """
686 __slots__ = (
687 "_attr_names",
688 "_attrs",
689 "_base_attr_map",
690 "_base_names",
691 "_cache_hash",
692 "_cls",
693 "_cls_dict",
694 "_delete_attribs",
695 "_frozen",
696 "_has_pre_init",
697 "_pre_init_has_args",
698 "_has_post_init",
699 "_is_exc",
700 "_on_setattr",
701 "_slots",
702 "_weakref_slot",
703 "_wrote_own_setattr",
704 "_has_custom_setattr",
705 )
707 def __init__(
708 self,
709 cls,
710 these,
711 slots,
712 frozen,
713 weakref_slot,
714 getstate_setstate,
715 auto_attribs,
716 kw_only,
717 cache_hash,
718 is_exc,
719 collect_by_mro,
720 on_setattr,
721 has_custom_setattr,
722 field_transformer,
723 ):
724 attrs, base_attrs, base_map = _transform_attrs(
725 cls,
726 these,
727 auto_attribs,
728 kw_only,
729 collect_by_mro,
730 field_transformer,
731 )
733 self._cls = cls
734 self._cls_dict = dict(cls.__dict__) if slots else {}
735 self._attrs = attrs
736 self._base_names = {a.name for a in base_attrs}
737 self._base_attr_map = base_map
738 self._attr_names = tuple(a.name for a in attrs)
739 self._slots = slots
740 self._frozen = frozen
741 self._weakref_slot = weakref_slot
742 self._cache_hash = cache_hash
743 self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False))
744 self._pre_init_has_args = False
745 if self._has_pre_init:
746 # Check if the pre init method has more arguments than just `self`
747 # We want to pass arguments if pre init expects arguments
748 pre_init_func = cls.__attrs_pre_init__
749 pre_init_signature = inspect.signature(pre_init_func)
750 self._pre_init_has_args = len(pre_init_signature.parameters) > 1
751 self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))
752 self._delete_attribs = not bool(these)
753 self._is_exc = is_exc
754 self._on_setattr = on_setattr
756 self._has_custom_setattr = has_custom_setattr
757 self._wrote_own_setattr = False
759 self._cls_dict["__attrs_attrs__"] = self._attrs
761 if frozen:
762 self._cls_dict["__setattr__"] = _frozen_setattrs
763 self._cls_dict["__delattr__"] = _frozen_delattrs
765 self._wrote_own_setattr = True
766 elif on_setattr in (
767 _ng_default_on_setattr,
768 setters.validate,
769 setters.convert,
770 ):
771 has_validator = has_converter = False
772 for a in attrs:
773 if a.validator is not None:
774 has_validator = True
775 if a.converter is not None:
776 has_converter = True
778 if has_validator and has_converter:
779 break
780 if (
781 (
782 on_setattr == _ng_default_on_setattr
783 and not (has_validator or has_converter)
784 )
785 or (on_setattr == setters.validate and not has_validator)
786 or (on_setattr == setters.convert and not has_converter)
787 ):
788 # If class-level on_setattr is set to convert + validate, but
789 # there's no field to convert or validate, pretend like there's
790 # no on_setattr.
791 self._on_setattr = None
793 if getstate_setstate:
794 (
795 self._cls_dict["__getstate__"],
796 self._cls_dict["__setstate__"],
797 ) = self._make_getstate_setstate()
799 def __repr__(self):
800 return f"<_ClassBuilder(cls={self._cls.__name__})>"
802 if PY310:
803 import abc
805 def build_class(self):
806 """
807 Finalize class based on the accumulated configuration.
809 Builder cannot be used after calling this method.
810 """
811 if self._slots is True:
812 return self._create_slots_class()
814 return self.abc.update_abstractmethods(
815 self._patch_original_class()
816 )
818 else:
820 def build_class(self):
821 """
822 Finalize class based on the accumulated configuration.
824 Builder cannot be used after calling this method.
825 """
826 if self._slots is True:
827 return self._create_slots_class()
829 return self._patch_original_class()
831 def _patch_original_class(self):
832 """
833 Apply accumulated methods and return the class.
834 """
835 cls = self._cls
836 base_names = self._base_names
838 # Clean class of attribute definitions (`attr.ib()`s).
839 if self._delete_attribs:
840 for name in self._attr_names:
841 if (
842 name not in base_names
843 and getattr(cls, name, _sentinel) is not _sentinel
844 ):
845 # An AttributeError can happen if a base class defines a
846 # class variable and we want to set an attribute with the
847 # same name by using only a type annotation.
848 with contextlib.suppress(AttributeError):
849 delattr(cls, name)
851 # Attach our dunder methods.
852 for name, value in self._cls_dict.items():
853 setattr(cls, name, value)
855 # If we've inherited an attrs __setattr__ and don't write our own,
856 # reset it to object's.
857 if not self._wrote_own_setattr and getattr(
858 cls, "__attrs_own_setattr__", False
859 ):
860 cls.__attrs_own_setattr__ = False
862 if not self._has_custom_setattr:
863 cls.__setattr__ = _obj_setattr
865 return cls
867 def _create_slots_class(self):
868 """
869 Build and return a new class with a `__slots__` attribute.
870 """
871 cd = {
872 k: v
873 for k, v in self._cls_dict.items()
874 if k not in (*tuple(self._attr_names), "__dict__", "__weakref__")
875 }
877 # If our class doesn't have its own implementation of __setattr__
878 # (either from the user or by us), check the bases, if one of them has
879 # an attrs-made __setattr__, that needs to be reset. We don't walk the
880 # MRO because we only care about our immediate base classes.
881 # XXX: This can be confused by subclassing a slotted attrs class with
882 # XXX: a non-attrs class and subclass the resulting class with an attrs
883 # XXX: class. See `test_slotted_confused` for details. For now that's
884 # XXX: OK with us.
885 if not self._wrote_own_setattr:
886 cd["__attrs_own_setattr__"] = False
888 if not self._has_custom_setattr:
889 for base_cls in self._cls.__bases__:
890 if base_cls.__dict__.get("__attrs_own_setattr__", False):
891 cd["__setattr__"] = _obj_setattr
892 break
894 # Traverse the MRO to collect existing slots
895 # and check for an existing __weakref__.
896 existing_slots = {}
897 weakref_inherited = False
898 for base_cls in self._cls.__mro__[1:-1]:
899 if base_cls.__dict__.get("__weakref__", None) is not None:
900 weakref_inherited = True
901 existing_slots.update(
902 {
903 name: getattr(base_cls, name)
904 for name in getattr(base_cls, "__slots__", [])
905 }
906 )
908 base_names = set(self._base_names)
910 names = self._attr_names
911 if (
912 self._weakref_slot
913 and "__weakref__" not in getattr(self._cls, "__slots__", ())
914 and "__weakref__" not in names
915 and not weakref_inherited
916 ):
917 names += ("__weakref__",)
919 if PY_3_8_PLUS:
920 cached_properties = {
921 name: cached_property.func
922 for name, cached_property in cd.items()
923 if isinstance(cached_property, functools.cached_property)
924 }
925 else:
926 # `functools.cached_property` was introduced in 3.8.
927 # So can't be used before this.
928 cached_properties = {}
930 # Collect methods with a `__class__` reference that are shadowed in the new class.
931 # To know to update them.
932 additional_closure_functions_to_update = []
933 if cached_properties:
934 # Add cached properties to names for slotting.
935 names += tuple(cached_properties.keys())
937 for name in cached_properties:
938 # Clear out function from class to avoid clashing.
939 del cd[name]
941 class_annotations = _get_annotations(self._cls)
942 for name, func in cached_properties.items():
943 annotation = inspect.signature(func).return_annotation
944 if annotation is not inspect.Parameter.empty:
945 class_annotations[name] = annotation
947 original_getattr = cd.get("__getattr__")
948 if original_getattr is not None:
949 additional_closure_functions_to_update.append(original_getattr)
951 cd["__getattr__"] = _make_cached_property_getattr(
952 cached_properties, original_getattr, self._cls
953 )
955 # We only add the names of attributes that aren't inherited.
956 # Setting __slots__ to inherited attributes wastes memory.
957 slot_names = [name for name in names if name not in base_names]
959 # There are slots for attributes from current class
960 # that are defined in parent classes.
961 # As their descriptors may be overridden by a child class,
962 # we collect them here and update the class dict
963 reused_slots = {
964 slot: slot_descriptor
965 for slot, slot_descriptor in existing_slots.items()
966 if slot in slot_names
967 }
968 slot_names = [name for name in slot_names if name not in reused_slots]
969 cd.update(reused_slots)
970 if self._cache_hash:
971 slot_names.append(_hash_cache_field)
973 cd["__slots__"] = tuple(slot_names)
975 cd["__qualname__"] = self._cls.__qualname__
977 # Create new class based on old class and our methods.
978 cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd)
980 # The following is a fix for
981 # <https://github.com/python-attrs/attrs/issues/102>.
982 # If a method mentions `__class__` or uses the no-arg super(), the
983 # compiler will bake a reference to the class in the method itself
984 # as `method.__closure__`. Since we replace the class with a
985 # clone, we rewrite these references so it keeps working.
986 for item in itertools.chain(
987 cls.__dict__.values(), additional_closure_functions_to_update
988 ):
989 if isinstance(item, (classmethod, staticmethod)):
990 # Class- and staticmethods hide their functions inside.
991 # These might need to be rewritten as well.
992 closure_cells = getattr(item.__func__, "__closure__", None)
993 elif isinstance(item, property):
994 # Workaround for property `super()` shortcut (PY3-only).
995 # There is no universal way for other descriptors.
996 closure_cells = getattr(item.fget, "__closure__", None)
997 else:
998 closure_cells = getattr(item, "__closure__", None)
1000 if not closure_cells: # Catch None or the empty list.
1001 continue
1002 for cell in closure_cells:
1003 try:
1004 match = cell.cell_contents is self._cls
1005 except ValueError: # noqa: PERF203
1006 # ValueError: Cell is empty
1007 pass
1008 else:
1009 if match:
1010 cell.cell_contents = cls
1011 return cls
1013 def add_repr(self, ns):
1014 self._cls_dict["__repr__"] = self._add_method_dunders(
1015 _make_repr(self._attrs, ns, self._cls)
1016 )
1017 return self
1019 def add_str(self):
1020 repr = self._cls_dict.get("__repr__")
1021 if repr is None:
1022 msg = "__str__ can only be generated if a __repr__ exists."
1023 raise ValueError(msg)
1025 def __str__(self):
1026 return self.__repr__()
1028 self._cls_dict["__str__"] = self._add_method_dunders(__str__)
1029 return self
1031 def _make_getstate_setstate(self):
1032 """
1033 Create custom __setstate__ and __getstate__ methods.
1034 """
1035 # __weakref__ is not writable.
1036 state_attr_names = tuple(
1037 an for an in self._attr_names if an != "__weakref__"
1038 )
1040 def slots_getstate(self):
1041 """
1042 Automatically created by attrs.
1043 """
1044 return {name: getattr(self, name) for name in state_attr_names}
1046 hash_caching_enabled = self._cache_hash
1048 def slots_setstate(self, state):
1049 """
1050 Automatically created by attrs.
1051 """
1052 __bound_setattr = _obj_setattr.__get__(self)
1053 if isinstance(state, tuple):
1054 # Backward compatibility with attrs instances pickled with
1055 # attrs versions before v22.2.0 which stored tuples.
1056 for name, value in zip(state_attr_names, state):
1057 __bound_setattr(name, value)
1058 else:
1059 for name in state_attr_names:
1060 if name in state:
1061 __bound_setattr(name, state[name])
1063 # The hash code cache is not included when the object is
1064 # serialized, but it still needs to be initialized to None to
1065 # indicate that the first call to __hash__ should be a cache
1066 # miss.
1067 if hash_caching_enabled:
1068 __bound_setattr(_hash_cache_field, None)
1070 return slots_getstate, slots_setstate
1072 def make_unhashable(self):
1073 self._cls_dict["__hash__"] = None
1074 return self
1076 def add_hash(self):
1077 self._cls_dict["__hash__"] = self._add_method_dunders(
1078 _make_hash(
1079 self._cls,
1080 self._attrs,
1081 frozen=self._frozen,
1082 cache_hash=self._cache_hash,
1083 )
1084 )
1086 return self
1088 def add_init(self):
1089 self._cls_dict["__init__"] = self._add_method_dunders(
1090 _make_init(
1091 self._cls,
1092 self._attrs,
1093 self._has_pre_init,
1094 self._pre_init_has_args,
1095 self._has_post_init,
1096 self._frozen,
1097 self._slots,
1098 self._cache_hash,
1099 self._base_attr_map,
1100 self._is_exc,
1101 self._on_setattr,
1102 attrs_init=False,
1103 )
1104 )
1106 return self
1108 def add_match_args(self):
1109 self._cls_dict["__match_args__"] = tuple(
1110 field.name
1111 for field in self._attrs
1112 if field.init and not field.kw_only
1113 )
1115 def add_attrs_init(self):
1116 self._cls_dict["__attrs_init__"] = self._add_method_dunders(
1117 _make_init(
1118 self._cls,
1119 self._attrs,
1120 self._has_pre_init,
1121 self._pre_init_has_args,
1122 self._has_post_init,
1123 self._frozen,
1124 self._slots,
1125 self._cache_hash,
1126 self._base_attr_map,
1127 self._is_exc,
1128 self._on_setattr,
1129 attrs_init=True,
1130 )
1131 )
1133 return self
1135 def add_eq(self):
1136 cd = self._cls_dict
1138 cd["__eq__"] = self._add_method_dunders(
1139 _make_eq(self._cls, self._attrs)
1140 )
1141 cd["__ne__"] = self._add_method_dunders(_make_ne())
1143 return self
1145 def add_order(self):
1146 cd = self._cls_dict
1148 cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = (
1149 self._add_method_dunders(meth)
1150 for meth in _make_order(self._cls, self._attrs)
1151 )
1153 return self
1155 def add_setattr(self):
1156 if self._frozen:
1157 return self
1159 sa_attrs = {}
1160 for a in self._attrs:
1161 on_setattr = a.on_setattr or self._on_setattr
1162 if on_setattr and on_setattr is not setters.NO_OP:
1163 sa_attrs[a.name] = a, on_setattr
1165 if not sa_attrs:
1166 return self
1168 if self._has_custom_setattr:
1169 # We need to write a __setattr__ but there already is one!
1170 msg = "Can't combine custom __setattr__ with on_setattr hooks."
1171 raise ValueError(msg)
1173 # docstring comes from _add_method_dunders
1174 def __setattr__(self, name, val):
1175 try:
1176 a, hook = sa_attrs[name]
1177 except KeyError:
1178 nval = val
1179 else:
1180 nval = hook(self, a, val)
1182 _obj_setattr(self, name, nval)
1184 self._cls_dict["__attrs_own_setattr__"] = True
1185 self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__)
1186 self._wrote_own_setattr = True
1188 return self
1190 def _add_method_dunders(self, method):
1191 """
1192 Add __module__ and __qualname__ to a *method* if possible.
1193 """
1194 with contextlib.suppress(AttributeError):
1195 method.__module__ = self._cls.__module__
1197 with contextlib.suppress(AttributeError):
1198 method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}"
1200 with contextlib.suppress(AttributeError):
1201 method.__doc__ = (
1202 "Method generated by attrs for class "
1203 f"{self._cls.__qualname__}."
1204 )
1206 return method
1209def _determine_attrs_eq_order(cmp, eq, order, default_eq):
1210 """
1211 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
1212 values of eq and order. If *eq* is None, set it to *default_eq*.
1213 """
1214 if cmp is not None and any((eq is not None, order is not None)):
1215 msg = "Don't mix `cmp` with `eq' and `order`."
1216 raise ValueError(msg)
1218 # cmp takes precedence due to bw-compatibility.
1219 if cmp is not None:
1220 return cmp, cmp
1222 # If left None, equality is set to the specified default and ordering
1223 # mirrors equality.
1224 if eq is None:
1225 eq = default_eq
1227 if order is None:
1228 order = eq
1230 if eq is False and order is True:
1231 msg = "`order` can only be True if `eq` is True too."
1232 raise ValueError(msg)
1234 return eq, order
1237def _determine_attrib_eq_order(cmp, eq, order, default_eq):
1238 """
1239 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
1240 values of eq and order. If *eq* is None, set it to *default_eq*.
1241 """
1242 if cmp is not None and any((eq is not None, order is not None)):
1243 msg = "Don't mix `cmp` with `eq' and `order`."
1244 raise ValueError(msg)
1246 def decide_callable_or_boolean(value):
1247 """
1248 Decide whether a key function is used.
1249 """
1250 if callable(value):
1251 value, key = True, value
1252 else:
1253 key = None
1254 return value, key
1256 # cmp takes precedence due to bw-compatibility.
1257 if cmp is not None:
1258 cmp, cmp_key = decide_callable_or_boolean(cmp)
1259 return cmp, cmp_key, cmp, cmp_key
1261 # If left None, equality is set to the specified default and ordering
1262 # mirrors equality.
1263 if eq is None:
1264 eq, eq_key = default_eq, None
1265 else:
1266 eq, eq_key = decide_callable_or_boolean(eq)
1268 if order is None:
1269 order, order_key = eq, eq_key
1270 else:
1271 order, order_key = decide_callable_or_boolean(order)
1273 if eq is False and order is True:
1274 msg = "`order` can only be True if `eq` is True too."
1275 raise ValueError(msg)
1277 return eq, eq_key, order, order_key
1280def _determine_whether_to_implement(
1281 cls, flag, auto_detect, dunders, default=True
1282):
1283 """
1284 Check whether we should implement a set of methods for *cls*.
1286 *flag* is the argument passed into @attr.s like 'init', *auto_detect* the
1287 same as passed into @attr.s and *dunders* is a tuple of attribute names
1288 whose presence signal that the user has implemented it themselves.
1290 Return *default* if no reason for either for or against is found.
1291 """
1292 if flag is True or flag is False:
1293 return flag
1295 if flag is None and auto_detect is False:
1296 return default
1298 # Logically, flag is None and auto_detect is True here.
1299 for dunder in dunders:
1300 if _has_own_attribute(cls, dunder):
1301 return False
1303 return default
1306def attrs(
1307 maybe_cls=None,
1308 these=None,
1309 repr_ns=None,
1310 repr=None,
1311 cmp=None,
1312 hash=None,
1313 init=None,
1314 slots=False,
1315 frozen=False,
1316 weakref_slot=True,
1317 str=False,
1318 auto_attribs=False,
1319 kw_only=False,
1320 cache_hash=False,
1321 auto_exc=False,
1322 eq=None,
1323 order=None,
1324 auto_detect=False,
1325 collect_by_mro=False,
1326 getstate_setstate=None,
1327 on_setattr=None,
1328 field_transformer=None,
1329 match_args=True,
1330 unsafe_hash=None,
1331):
1332 r"""
1333 A class decorator that adds :term:`dunder methods` according to the
1334 specified attributes using `attr.ib` or the *these* argument.
1336 Please consider using `attrs.define` / `attrs.frozen` in new code
1337 (``attr.s`` will *never* go away, though).
1339 :param these: A dictionary of name to `attr.ib` mappings. This is useful
1340 to avoid the definition of your attributes within the class body
1341 because you can't (e.g. if you want to add ``__repr__`` methods to
1342 Django models) or don't want to.
1344 If *these* is not ``None``, *attrs* will *not* search the class body
1345 for attributes and will *not* remove any attributes from it.
1347 The order is deduced from the order of the attributes inside *these*.
1349 :type these: `dict` of `str` to `attr.ib`
1351 :param str repr_ns: When using nested classes, there's no way in Python 2
1352 to automatically detect that. Therefore it's possible to set the
1353 namespace explicitly for a more meaningful ``repr`` output.
1354 :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*,
1355 *order*, and *hash* arguments explicitly, assume they are set to
1356 ``True`` **unless any** of the involved methods for one of the
1357 arguments is implemented in the *current* class (i.e. it is *not*
1358 inherited from some base class).
1360 So for example by implementing ``__eq__`` on a class yourself, *attrs*
1361 will deduce ``eq=False`` and will create *neither* ``__eq__`` *nor*
1362 ``__ne__`` (but Python classes come with a sensible ``__ne__`` by
1363 default, so it *should* be enough to only implement ``__eq__`` in most
1364 cases).
1366 .. warning::
1368 If you prevent *attrs* from creating the ordering methods for you
1369 (``order=False``, e.g. by implementing ``__le__``), it becomes
1370 *your* responsibility to make sure its ordering is sound. The best
1371 way is to use the `functools.total_ordering` decorator.
1374 Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, *cmp*,
1375 or *hash* overrides whatever *auto_detect* would determine.
1377 :param bool repr: Create a ``__repr__`` method with a human readable
1378 representation of *attrs* attributes..
1379 :param bool str: Create a ``__str__`` method that is identical to
1380 ``__repr__``. This is usually not necessary except for `Exception`\ s.
1381 :param bool | None eq: If ``True`` or ``None`` (default), add ``__eq__``
1382 and ``__ne__`` methods that check two instances for equality.
1384 They compare the instances as if they were tuples of their *attrs*
1385 attributes if and only if the types of both classes are *identical*!
1387 .. seealso:: `comparison`
1388 :param bool | None order: If ``True``, add ``__lt__``, ``__le__``,
1389 ``__gt__``, and ``__ge__`` methods that behave like *eq* above and
1390 allow instances to be ordered. If ``None`` (default) mirror value of
1391 *eq*.
1393 .. seealso:: `comparison`
1394 :param bool | None cmp: Setting *cmp* is equivalent to setting *eq* and
1395 *order* to the same value. Must not be mixed with *eq* or *order*.
1397 .. seealso:: `comparison`
1398 :param bool | None unsafe_hash: If ``None`` (default), the ``__hash__``
1399 method is generated according how *eq* and *frozen* are set.
1401 1. If *both* are True, *attrs* will generate a ``__hash__`` for you.
1402 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to
1403 None, marking it unhashable (which it is).
1404 3. If *eq* is False, ``__hash__`` will be left untouched meaning the
1405 ``__hash__`` method of the base class will be used (if base class is
1406 ``object``, this means it will fall back to id-based hashing.).
1408 Although not recommended, you can decide for yourself and force *attrs*
1409 to create one (e.g. if the class is immutable even though you didn't
1410 freeze it programmatically) by passing ``True`` or not. Both of these
1411 cases are rather special and should be used carefully.
1413 .. seealso::
1415 - Our documentation on `hashing`,
1416 - Python's documentation on `object.__hash__`,
1417 - and the `GitHub issue that led to the default \
1418 behavior <https://github.com/python-attrs/attrs/issues/136>`_ for
1419 more details.
1421 :param bool | None hash: Alias for *unsafe_hash*. *unsafe_hash* takes
1422 precedence.
1423 :param bool init: Create a ``__init__`` method that initializes the *attrs*
1424 attributes. Leading underscores are stripped for the argument name. If
1425 a ``__attrs_pre_init__`` method exists on the class, it will be called
1426 before the class is initialized. If a ``__attrs_post_init__`` method
1427 exists on the class, it will be called after the class is fully
1428 initialized.
1430 If ``init`` is ``False``, an ``__attrs_init__`` method will be injected
1431 instead. This allows you to define a custom ``__init__`` method that
1432 can do pre-init work such as ``super().__init__()``, and then call
1433 ``__attrs_init__()`` and ``__attrs_post_init__()``.
1435 .. seealso:: `init`
1436 :param bool slots: Create a :term:`slotted class <slotted classes>` that's
1437 more memory-efficient. Slotted classes are generally superior to the
1438 default dict classes, but have some gotchas you should know about, so
1439 we encourage you to read the :term:`glossary entry <slotted classes>`.
1440 :param bool frozen: Make instances immutable after initialization. If
1441 someone attempts to modify a frozen instance,
1442 `attrs.exceptions.FrozenInstanceError` is raised.
1444 .. note::
1446 1. This is achieved by installing a custom ``__setattr__`` method
1447 on your class, so you can't implement your own.
1449 2. True immutability is impossible in Python.
1451 3. This *does* have a minor a runtime performance `impact
1452 <how-frozen>` when initializing new instances. In other words:
1453 ``__init__`` is slightly slower with ``frozen=True``.
1455 4. If a class is frozen, you cannot modify ``self`` in
1456 ``__attrs_post_init__`` or a self-written ``__init__``. You can
1457 circumvent that limitation by using ``object.__setattr__(self,
1458 "attribute_name", value)``.
1460 5. Subclasses of a frozen class are frozen too.
1462 :param bool weakref_slot: Make instances weak-referenceable. This has no
1463 effect unless ``slots`` is also enabled.
1464 :param bool auto_attribs: If ``True``, collect :pep:`526`-annotated
1465 attributes from the class body.
1467 In this case, you **must** annotate every field. If *attrs* encounters
1468 a field that is set to an `attr.ib` but lacks a type annotation, an
1469 `attr.exceptions.UnannotatedAttributeError` is raised. Use
1470 ``field_name: typing.Any = attr.ib(...)`` if you don't want to set a
1471 type.
1473 If you assign a value to those attributes (e.g. ``x: int = 42``), that
1474 value becomes the default value like if it were passed using
1475 ``attr.ib(default=42)``. Passing an instance of `attrs.Factory` also
1476 works as expected in most cases (see warning below).
1478 Attributes annotated as `typing.ClassVar`, and attributes that are
1479 neither annotated nor set to an `attr.ib` are **ignored**.
1481 .. warning::
1482 For features that use the attribute name to create decorators (e.g.
1483 :ref:`validators <validators>`), you still *must* assign `attr.ib`
1484 to them. Otherwise Python will either not find the name or try to
1485 use the default value to call e.g. ``validator`` on it.
1487 These errors can be quite confusing and probably the most common bug
1488 report on our bug tracker.
1490 :param bool kw_only: Make all attributes keyword-only in the generated
1491 ``__init__`` (if ``init`` is ``False``, this parameter is ignored).
1492 :param bool cache_hash: Ensure that the object's hash code is computed only
1493 once and stored on the object. If this is set to ``True``, hashing
1494 must be either explicitly or implicitly enabled for this class. If the
1495 hash code is cached, avoid any reassignments of fields involved in hash
1496 code computation or mutations of the objects those fields point to
1497 after object creation. If such changes occur, the behavior of the
1498 object's hash code is undefined.
1499 :param bool auto_exc: If the class subclasses `BaseException` (which
1500 implicitly includes any subclass of any exception), the following
1501 happens to behave like a well-behaved Python exceptions class:
1503 - the values for *eq*, *order*, and *hash* are ignored and the
1504 instances compare and hash by the instance's ids (N.B. *attrs* will
1505 *not* remove existing implementations of ``__hash__`` or the equality
1506 methods. It just won't add own ones.),
1507 - all attributes that are either passed into ``__init__`` or have a
1508 default value are additionally available as a tuple in the ``args``
1509 attribute,
1510 - the value of *str* is ignored leaving ``__str__`` to base classes.
1511 :param bool collect_by_mro: Setting this to `True` fixes the way *attrs*
1512 collects attributes from base classes. The default behavior is
1513 incorrect in certain cases of multiple inheritance. It should be on by
1514 default but is kept off for backward-compatibility.
1516 .. seealso::
1517 Issue `#428 <https://github.com/python-attrs/attrs/issues/428>`_
1519 :param bool | None getstate_setstate:
1520 .. note::
1521 This is usually only interesting for slotted classes and you should
1522 probably just set *auto_detect* to `True`.
1524 If `True`, ``__getstate__`` and ``__setstate__`` are generated and
1525 attached to the class. This is necessary for slotted classes to be
1526 pickleable. If left `None`, it's `True` by default for slotted classes
1527 and ``False`` for dict classes.
1529 If *auto_detect* is `True`, and *getstate_setstate* is left `None`, and
1530 **either** ``__getstate__`` or ``__setstate__`` is detected directly on
1531 the class (i.e. not inherited), it is set to `False` (this is usually
1532 what you want).
1534 :param on_setattr: A callable that is run whenever the user attempts to set
1535 an attribute (either by assignment like ``i.x = 42`` or by using
1536 `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments
1537 as validators: the instance, the attribute that is being modified, and
1538 the new value.
1540 If no exception is raised, the attribute is set to the return value of
1541 the callable.
1543 If a list of callables is passed, they're automatically wrapped in an
1544 `attrs.setters.pipe`.
1545 :type on_setattr: `callable`, or a list of callables, or `None`, or
1546 `attrs.setters.NO_OP`
1548 :param callable | None field_transformer:
1549 A function that is called with the original class object and all fields
1550 right before *attrs* finalizes the class. You can use this, e.g., to
1551 automatically add converters or validators to fields based on their
1552 types.
1554 .. seealso:: `transform-fields`
1556 :param bool match_args:
1557 If `True` (default), set ``__match_args__`` on the class to support
1558 :pep:`634` (Structural Pattern Matching). It is a tuple of all
1559 non-keyword-only ``__init__`` parameter names on Python 3.10 and later.
1560 Ignored on older Python versions.
1562 .. versionadded:: 16.0.0 *slots*
1563 .. versionadded:: 16.1.0 *frozen*
1564 .. versionadded:: 16.3.0 *str*
1565 .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``.
1566 .. versionchanged:: 17.1.0
1567 *hash* supports ``None`` as value which is also the default now.
1568 .. versionadded:: 17.3.0 *auto_attribs*
1569 .. versionchanged:: 18.1.0
1570 If *these* is passed, no attributes are deleted from the class body.
1571 .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained.
1572 .. versionadded:: 18.2.0 *weakref_slot*
1573 .. deprecated:: 18.2.0
1574 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a
1575 `DeprecationWarning` if the classes compared are subclasses of
1576 each other. ``__eq`` and ``__ne__`` never tried to compared subclasses
1577 to each other.
1578 .. versionchanged:: 19.2.0
1579 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider
1580 subclasses comparable anymore.
1581 .. versionadded:: 18.2.0 *kw_only*
1582 .. versionadded:: 18.2.0 *cache_hash*
1583 .. versionadded:: 19.1.0 *auto_exc*
1584 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
1585 .. versionadded:: 19.2.0 *eq* and *order*
1586 .. versionadded:: 20.1.0 *auto_detect*
1587 .. versionadded:: 20.1.0 *collect_by_mro*
1588 .. versionadded:: 20.1.0 *getstate_setstate*
1589 .. versionadded:: 20.1.0 *on_setattr*
1590 .. versionadded:: 20.3.0 *field_transformer*
1591 .. versionchanged:: 21.1.0
1592 ``init=False`` injects ``__attrs_init__``
1593 .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__``
1594 .. versionchanged:: 21.1.0 *cmp* undeprecated
1595 .. versionadded:: 21.3.0 *match_args*
1596 .. versionadded:: 22.2.0
1597 *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
1598 """
1599 eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None)
1601 # unsafe_hash takes precedence due to PEP 681.
1602 if unsafe_hash is not None:
1603 hash = unsafe_hash
1605 if isinstance(on_setattr, (list, tuple)):
1606 on_setattr = setters.pipe(*on_setattr)
1608 def wrap(cls):
1609 is_frozen = frozen or _has_frozen_base_class(cls)
1610 is_exc = auto_exc is True and issubclass(cls, BaseException)
1611 has_own_setattr = auto_detect and _has_own_attribute(
1612 cls, "__setattr__"
1613 )
1615 if has_own_setattr and is_frozen:
1616 msg = "Can't freeze a class with a custom __setattr__."
1617 raise ValueError(msg)
1619 builder = _ClassBuilder(
1620 cls,
1621 these,
1622 slots,
1623 is_frozen,
1624 weakref_slot,
1625 _determine_whether_to_implement(
1626 cls,
1627 getstate_setstate,
1628 auto_detect,
1629 ("__getstate__", "__setstate__"),
1630 default=slots,
1631 ),
1632 auto_attribs,
1633 kw_only,
1634 cache_hash,
1635 is_exc,
1636 collect_by_mro,
1637 on_setattr,
1638 has_own_setattr,
1639 field_transformer,
1640 )
1641 if _determine_whether_to_implement(
1642 cls, repr, auto_detect, ("__repr__",)
1643 ):
1644 builder.add_repr(repr_ns)
1645 if str is True:
1646 builder.add_str()
1648 eq = _determine_whether_to_implement(
1649 cls, eq_, auto_detect, ("__eq__", "__ne__")
1650 )
1651 if not is_exc and eq is True:
1652 builder.add_eq()
1653 if not is_exc and _determine_whether_to_implement(
1654 cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__")
1655 ):
1656 builder.add_order()
1658 builder.add_setattr()
1660 nonlocal hash
1661 if (
1662 hash is None
1663 and auto_detect is True
1664 and _has_own_attribute(cls, "__hash__")
1665 ):
1666 hash = False
1668 if hash is not True and hash is not False and hash is not None:
1669 # Can't use `hash in` because 1 == True for example.
1670 msg = "Invalid value for hash. Must be True, False, or None."
1671 raise TypeError(msg)
1673 if hash is False or (hash is None and eq is False) or is_exc:
1674 # Don't do anything. Should fall back to __object__'s __hash__
1675 # which is by id.
1676 if cache_hash:
1677 msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
1678 raise TypeError(msg)
1679 elif hash is True or (
1680 hash is None and eq is True and is_frozen is True
1681 ):
1682 # Build a __hash__ if told so, or if it's safe.
1683 builder.add_hash()
1684 else:
1685 # Raise TypeError on attempts to hash.
1686 if cache_hash:
1687 msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
1688 raise TypeError(msg)
1689 builder.make_unhashable()
1691 if _determine_whether_to_implement(
1692 cls, init, auto_detect, ("__init__",)
1693 ):
1694 builder.add_init()
1695 else:
1696 builder.add_attrs_init()
1697 if cache_hash:
1698 msg = "Invalid value for cache_hash. To use hash caching, init must be True."
1699 raise TypeError(msg)
1701 if (
1702 PY310
1703 and match_args
1704 and not _has_own_attribute(cls, "__match_args__")
1705 ):
1706 builder.add_match_args()
1708 return builder.build_class()
1710 # maybe_cls's type depends on the usage of the decorator. It's a class
1711 # if it's used as `@attrs` but ``None`` if used as `@attrs()`.
1712 if maybe_cls is None:
1713 return wrap
1715 return wrap(maybe_cls)
1718_attrs = attrs
1719"""
1720Internal alias so we can use it in functions that take an argument called
1721*attrs*.
1722"""
1725def _has_frozen_base_class(cls):
1726 """
1727 Check whether *cls* has a frozen ancestor by looking at its
1728 __setattr__.
1729 """
1730 return cls.__setattr__ is _frozen_setattrs
1733def _generate_unique_filename(cls, func_name):
1734 """
1735 Create a "filename" suitable for a function being generated.
1736 """
1737 return (
1738 f"<attrs generated {func_name} {cls.__module__}."
1739 f"{getattr(cls, '__qualname__', cls.__name__)}>"
1740 )
1743def _make_hash(cls, attrs, frozen, cache_hash):
1744 attrs = tuple(
1745 a for a in attrs if a.hash is True or (a.hash is None and a.eq is True)
1746 )
1748 tab = " "
1750 unique_filename = _generate_unique_filename(cls, "hash")
1751 type_hash = hash(unique_filename)
1752 # If eq is custom generated, we need to include the functions in globs
1753 globs = {}
1755 hash_def = "def __hash__(self"
1756 hash_func = "hash(("
1757 closing_braces = "))"
1758 if not cache_hash:
1759 hash_def += "):"
1760 else:
1761 hash_def += ", *"
1763 hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):"
1764 hash_func = "_cache_wrapper(" + hash_func
1765 closing_braces += ")"
1767 method_lines = [hash_def]
1769 def append_hash_computation_lines(prefix, indent):
1770 """
1771 Generate the code for actually computing the hash code.
1772 Below this will either be returned directly or used to compute
1773 a value which is then cached, depending on the value of cache_hash
1774 """
1776 method_lines.extend(
1777 [
1778 indent + prefix + hash_func,
1779 indent + f" {type_hash},",
1780 ]
1781 )
1783 for a in attrs:
1784 if a.eq_key:
1785 cmp_name = f"_{a.name}_key"
1786 globs[cmp_name] = a.eq_key
1787 method_lines.append(
1788 indent + f" {cmp_name}(self.{a.name}),"
1789 )
1790 else:
1791 method_lines.append(indent + f" self.{a.name},")
1793 method_lines.append(indent + " " + closing_braces)
1795 if cache_hash:
1796 method_lines.append(tab + f"if self.{_hash_cache_field} is None:")
1797 if frozen:
1798 append_hash_computation_lines(
1799 f"object.__setattr__(self, '{_hash_cache_field}', ", tab * 2
1800 )
1801 method_lines.append(tab * 2 + ")") # close __setattr__
1802 else:
1803 append_hash_computation_lines(
1804 f"self.{_hash_cache_field} = ", tab * 2
1805 )
1806 method_lines.append(tab + f"return self.{_hash_cache_field}")
1807 else:
1808 append_hash_computation_lines("return ", tab)
1810 script = "\n".join(method_lines)
1811 return _make_method("__hash__", script, unique_filename, globs)
1814def _add_hash(cls, attrs):
1815 """
1816 Add a hash method to *cls*.
1817 """
1818 cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False)
1819 return cls
1822def _make_ne():
1823 """
1824 Create __ne__ method.
1825 """
1827 def __ne__(self, other):
1828 """
1829 Check equality and either forward a NotImplemented or
1830 return the result negated.
1831 """
1832 result = self.__eq__(other)
1833 if result is NotImplemented:
1834 return NotImplemented
1836 return not result
1838 return __ne__
1841def _make_eq(cls, attrs):
1842 """
1843 Create __eq__ method for *cls* with *attrs*.
1844 """
1845 attrs = [a for a in attrs if a.eq]
1847 unique_filename = _generate_unique_filename(cls, "eq")
1848 lines = [
1849 "def __eq__(self, other):",
1850 " if other.__class__ is not self.__class__:",
1851 " return NotImplemented",
1852 ]
1854 # We can't just do a big self.x = other.x and... clause due to
1855 # irregularities like nan == nan is false but (nan,) == (nan,) is true.
1856 globs = {}
1857 if attrs:
1858 lines.append(" return (")
1859 others = [" ) == ("]
1860 for a in attrs:
1861 if a.eq_key:
1862 cmp_name = f"_{a.name}_key"
1863 # Add the key function to the global namespace
1864 # of the evaluated function.
1865 globs[cmp_name] = a.eq_key
1866 lines.append(f" {cmp_name}(self.{a.name}),")
1867 others.append(f" {cmp_name}(other.{a.name}),")
1868 else:
1869 lines.append(f" self.{a.name},")
1870 others.append(f" other.{a.name},")
1872 lines += [*others, " )"]
1873 else:
1874 lines.append(" return True")
1876 script = "\n".join(lines)
1878 return _make_method("__eq__", script, unique_filename, globs)
1881def _make_order(cls, attrs):
1882 """
1883 Create ordering methods for *cls* with *attrs*.
1884 """
1885 attrs = [a for a in attrs if a.order]
1887 def attrs_to_tuple(obj):
1888 """
1889 Save us some typing.
1890 """
1891 return tuple(
1892 key(value) if key else value
1893 for value, key in (
1894 (getattr(obj, a.name), a.order_key) for a in attrs
1895 )
1896 )
1898 def __lt__(self, other):
1899 """
1900 Automatically created by attrs.
1901 """
1902 if other.__class__ is self.__class__:
1903 return attrs_to_tuple(self) < attrs_to_tuple(other)
1905 return NotImplemented
1907 def __le__(self, other):
1908 """
1909 Automatically created by attrs.
1910 """
1911 if other.__class__ is self.__class__:
1912 return attrs_to_tuple(self) <= attrs_to_tuple(other)
1914 return NotImplemented
1916 def __gt__(self, other):
1917 """
1918 Automatically created by attrs.
1919 """
1920 if other.__class__ is self.__class__:
1921 return attrs_to_tuple(self) > attrs_to_tuple(other)
1923 return NotImplemented
1925 def __ge__(self, other):
1926 """
1927 Automatically created by attrs.
1928 """
1929 if other.__class__ is self.__class__:
1930 return attrs_to_tuple(self) >= attrs_to_tuple(other)
1932 return NotImplemented
1934 return __lt__, __le__, __gt__, __ge__
1937def _add_eq(cls, attrs=None):
1938 """
1939 Add equality methods to *cls* with *attrs*.
1940 """
1941 if attrs is None:
1942 attrs = cls.__attrs_attrs__
1944 cls.__eq__ = _make_eq(cls, attrs)
1945 cls.__ne__ = _make_ne()
1947 return cls
1950def _make_repr(attrs, ns, cls):
1951 unique_filename = _generate_unique_filename(cls, "repr")
1952 # Figure out which attributes to include, and which function to use to
1953 # format them. The a.repr value can be either bool or a custom
1954 # callable.
1955 attr_names_with_reprs = tuple(
1956 (a.name, (repr if a.repr is True else a.repr), a.init)
1957 for a in attrs
1958 if a.repr is not False
1959 )
1960 globs = {
1961 name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr
1962 }
1963 globs["_compat"] = _compat
1964 globs["AttributeError"] = AttributeError
1965 globs["NOTHING"] = NOTHING
1966 attribute_fragments = []
1967 for name, r, i in attr_names_with_reprs:
1968 accessor = (
1969 "self." + name if i else 'getattr(self, "' + name + '", NOTHING)'
1970 )
1971 fragment = (
1972 "%s={%s!r}" % (name, accessor)
1973 if r == repr
1974 else "%s={%s_repr(%s)}" % (name, name, accessor)
1975 )
1976 attribute_fragments.append(fragment)
1977 repr_fragment = ", ".join(attribute_fragments)
1979 if ns is None:
1980 cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}'
1981 else:
1982 cls_name_fragment = ns + ".{self.__class__.__name__}"
1984 lines = [
1985 "def __repr__(self):",
1986 " try:",
1987 " already_repring = _compat.repr_context.already_repring",
1988 " except AttributeError:",
1989 " already_repring = {id(self),}",
1990 " _compat.repr_context.already_repring = already_repring",
1991 " else:",
1992 " if id(self) in already_repring:",
1993 " return '...'",
1994 " else:",
1995 " already_repring.add(id(self))",
1996 " try:",
1997 f" return f'{cls_name_fragment}({repr_fragment})'",
1998 " finally:",
1999 " already_repring.remove(id(self))",
2000 ]
2002 return _make_method(
2003 "__repr__", "\n".join(lines), unique_filename, globs=globs
2004 )
2007def _add_repr(cls, ns=None, attrs=None):
2008 """
2009 Add a repr method to *cls*.
2010 """
2011 if attrs is None:
2012 attrs = cls.__attrs_attrs__
2014 cls.__repr__ = _make_repr(attrs, ns, cls)
2015 return cls
2018def fields(cls):
2019 """
2020 Return the tuple of *attrs* attributes for a class.
2022 The tuple also allows accessing the fields by their names (see below for
2023 examples).
2025 :param type cls: Class to introspect.
2027 :raise TypeError: If *cls* is not a class.
2028 :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
2029 class.
2031 :rtype: tuple (with name accessors) of `attrs.Attribute`
2033 .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
2034 by name.
2035 .. versionchanged:: 23.1.0 Add support for generic classes.
2036 """
2037 generic_base = get_generic_base(cls)
2039 if generic_base is None and not isinstance(cls, type):
2040 msg = "Passed object must be a class."
2041 raise TypeError(msg)
2043 attrs = getattr(cls, "__attrs_attrs__", None)
2045 if attrs is None:
2046 if generic_base is not None:
2047 attrs = getattr(generic_base, "__attrs_attrs__", None)
2048 if attrs is not None:
2049 # Even though this is global state, stick it on here to speed
2050 # it up. We rely on `cls` being cached for this to be
2051 # efficient.
2052 cls.__attrs_attrs__ = attrs
2053 return attrs
2054 msg = f"{cls!r} is not an attrs-decorated class."
2055 raise NotAnAttrsClassError(msg)
2057 return attrs
2060def fields_dict(cls):
2061 """
2062 Return an ordered dictionary of *attrs* attributes for a class, whose
2063 keys are the attribute names.
2065 :param type cls: Class to introspect.
2067 :raise TypeError: If *cls* is not a class.
2068 :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
2069 class.
2071 :rtype: dict
2073 .. versionadded:: 18.1.0
2074 """
2075 if not isinstance(cls, type):
2076 msg = "Passed object must be a class."
2077 raise TypeError(msg)
2078 attrs = getattr(cls, "__attrs_attrs__", None)
2079 if attrs is None:
2080 msg = f"{cls!r} is not an attrs-decorated class."
2081 raise NotAnAttrsClassError(msg)
2082 return {a.name: a for a in attrs}
2085def validate(inst):
2086 """
2087 Validate all attributes on *inst* that have a validator.
2089 Leaves all exceptions through.
2091 :param inst: Instance of a class with *attrs* attributes.
2092 """
2093 if _config._run_validators is False:
2094 return
2096 for a in fields(inst.__class__):
2097 v = a.validator
2098 if v is not None:
2099 v(inst, a, getattr(inst, a.name))
2102def _is_slot_cls(cls):
2103 return "__slots__" in cls.__dict__
2106def _is_slot_attr(a_name, base_attr_map):
2107 """
2108 Check if the attribute name comes from a slot class.
2109 """
2110 return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name])
2113def _make_init(
2114 cls,
2115 attrs,
2116 pre_init,
2117 pre_init_has_args,
2118 post_init,
2119 frozen,
2120 slots,
2121 cache_hash,
2122 base_attr_map,
2123 is_exc,
2124 cls_on_setattr,
2125 attrs_init,
2126):
2127 has_cls_on_setattr = (
2128 cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP
2129 )
2131 if frozen and has_cls_on_setattr:
2132 msg = "Frozen classes can't use on_setattr."
2133 raise ValueError(msg)
2135 needs_cached_setattr = cache_hash or frozen
2136 filtered_attrs = []
2137 attr_dict = {}
2138 for a in attrs:
2139 if not a.init and a.default is NOTHING:
2140 continue
2142 filtered_attrs.append(a)
2143 attr_dict[a.name] = a
2145 if a.on_setattr is not None:
2146 if frozen is True:
2147 msg = "Frozen classes can't use on_setattr."
2148 raise ValueError(msg)
2150 needs_cached_setattr = True
2151 elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP:
2152 needs_cached_setattr = True
2154 unique_filename = _generate_unique_filename(cls, "init")
2156 script, globs, annotations = _attrs_to_init_script(
2157 filtered_attrs,
2158 frozen,
2159 slots,
2160 pre_init,
2161 pre_init_has_args,
2162 post_init,
2163 cache_hash,
2164 base_attr_map,
2165 is_exc,
2166 needs_cached_setattr,
2167 has_cls_on_setattr,
2168 attrs_init,
2169 )
2170 if cls.__module__ in sys.modules:
2171 # This makes typing.get_type_hints(CLS.__init__) resolve string types.
2172 globs.update(sys.modules[cls.__module__].__dict__)
2174 globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict})
2176 if needs_cached_setattr:
2177 # Save the lookup overhead in __init__ if we need to circumvent
2178 # setattr hooks.
2179 globs["_cached_setattr_get"] = _obj_setattr.__get__
2181 init = _make_method(
2182 "__attrs_init__" if attrs_init else "__init__",
2183 script,
2184 unique_filename,
2185 globs,
2186 )
2187 init.__annotations__ = annotations
2189 return init
2192def _setattr(attr_name, value_var, has_on_setattr):
2193 """
2194 Use the cached object.setattr to set *attr_name* to *value_var*.
2195 """
2196 return f"_setattr('{attr_name}', {value_var})"
2199def _setattr_with_converter(attr_name, value_var, has_on_setattr):
2200 """
2201 Use the cached object.setattr to set *attr_name* to *value_var*, but run
2202 its converter first.
2203 """
2204 return "_setattr('%s', %s(%s))" % (
2205 attr_name,
2206 _init_converter_pat % (attr_name,),
2207 value_var,
2208 )
2211def _assign(attr_name, value, has_on_setattr):
2212 """
2213 Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise
2214 relegate to _setattr.
2215 """
2216 if has_on_setattr:
2217 return _setattr(attr_name, value, True)
2219 return f"self.{attr_name} = {value}"
2222def _assign_with_converter(attr_name, value_var, has_on_setattr):
2223 """
2224 Unless *attr_name* has an on_setattr hook, use normal assignment after
2225 conversion. Otherwise relegate to _setattr_with_converter.
2226 """
2227 if has_on_setattr:
2228 return _setattr_with_converter(attr_name, value_var, True)
2230 return "self.%s = %s(%s)" % (
2231 attr_name,
2232 _init_converter_pat % (attr_name,),
2233 value_var,
2234 )
2237def _attrs_to_init_script(
2238 attrs,
2239 frozen,
2240 slots,
2241 pre_init,
2242 pre_init_has_args,
2243 post_init,
2244 cache_hash,
2245 base_attr_map,
2246 is_exc,
2247 needs_cached_setattr,
2248 has_cls_on_setattr,
2249 attrs_init,
2250):
2251 """
2252 Return a script of an initializer for *attrs* and a dict of globals.
2254 The globals are expected by the generated script.
2256 If *frozen* is True, we cannot set the attributes directly so we use
2257 a cached ``object.__setattr__``.
2258 """
2259 lines = []
2260 if pre_init:
2261 lines.append("self.__attrs_pre_init__()")
2263 if needs_cached_setattr:
2264 lines.append(
2265 # Circumvent the __setattr__ descriptor to save one lookup per
2266 # assignment.
2267 # Note _setattr will be used again below if cache_hash is True
2268 "_setattr = _cached_setattr_get(self)"
2269 )
2271 if frozen is True:
2272 if slots is True:
2273 fmt_setter = _setattr
2274 fmt_setter_with_converter = _setattr_with_converter
2275 else:
2276 # Dict frozen classes assign directly to __dict__.
2277 # But only if the attribute doesn't come from an ancestor slot
2278 # class.
2279 # Note _inst_dict will be used again below if cache_hash is True
2280 lines.append("_inst_dict = self.__dict__")
2282 def fmt_setter(attr_name, value_var, has_on_setattr):
2283 if _is_slot_attr(attr_name, base_attr_map):
2284 return _setattr(attr_name, value_var, has_on_setattr)
2286 return f"_inst_dict['{attr_name}'] = {value_var}"
2288 def fmt_setter_with_converter(
2289 attr_name, value_var, has_on_setattr
2290 ):
2291 if has_on_setattr or _is_slot_attr(attr_name, base_attr_map):
2292 return _setattr_with_converter(
2293 attr_name, value_var, has_on_setattr
2294 )
2296 return "_inst_dict['%s'] = %s(%s)" % (
2297 attr_name,
2298 _init_converter_pat % (attr_name,),
2299 value_var,
2300 )
2302 else:
2303 # Not frozen.
2304 fmt_setter = _assign
2305 fmt_setter_with_converter = _assign_with_converter
2307 args = []
2308 kw_only_args = []
2309 attrs_to_validate = []
2311 # This is a dictionary of names to validator and converter callables.
2312 # Injecting this into __init__ globals lets us avoid lookups.
2313 names_for_globals = {}
2314 annotations = {"return": None}
2316 for a in attrs:
2317 if a.validator:
2318 attrs_to_validate.append(a)
2320 attr_name = a.name
2321 has_on_setattr = a.on_setattr is not None or (
2322 a.on_setattr is not setters.NO_OP and has_cls_on_setattr
2323 )
2324 # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not
2325 # explicitly provided
2326 arg_name = a.alias
2328 has_factory = isinstance(a.default, Factory)
2329 maybe_self = "self" if has_factory and a.default.takes_self else ""
2331 if a.init is False:
2332 if has_factory:
2333 init_factory_name = _init_factory_pat % (a.name,)
2334 if a.converter is not None:
2335 lines.append(
2336 fmt_setter_with_converter(
2337 attr_name,
2338 init_factory_name + f"({maybe_self})",
2339 has_on_setattr,
2340 )
2341 )
2342 conv_name = _init_converter_pat % (a.name,)
2343 names_for_globals[conv_name] = a.converter
2344 else:
2345 lines.append(
2346 fmt_setter(
2347 attr_name,
2348 init_factory_name + f"({maybe_self})",
2349 has_on_setattr,
2350 )
2351 )
2352 names_for_globals[init_factory_name] = a.default.factory
2353 elif a.converter is not None:
2354 lines.append(
2355 fmt_setter_with_converter(
2356 attr_name,
2357 f"attr_dict['{attr_name}'].default",
2358 has_on_setattr,
2359 )
2360 )
2361 conv_name = _init_converter_pat % (a.name,)
2362 names_for_globals[conv_name] = a.converter
2363 else:
2364 lines.append(
2365 fmt_setter(
2366 attr_name,
2367 f"attr_dict['{attr_name}'].default",
2368 has_on_setattr,
2369 )
2370 )
2371 elif a.default is not NOTHING and not has_factory:
2372 arg = f"{arg_name}=attr_dict['{attr_name}'].default"
2373 if a.kw_only:
2374 kw_only_args.append(arg)
2375 else:
2376 args.append(arg)
2378 if a.converter is not None:
2379 lines.append(
2380 fmt_setter_with_converter(
2381 attr_name, arg_name, has_on_setattr
2382 )
2383 )
2384 names_for_globals[
2385 _init_converter_pat % (a.name,)
2386 ] = a.converter
2387 else:
2388 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))
2390 elif has_factory:
2391 arg = f"{arg_name}=NOTHING"
2392 if a.kw_only:
2393 kw_only_args.append(arg)
2394 else:
2395 args.append(arg)
2396 lines.append(f"if {arg_name} is not NOTHING:")
2398 init_factory_name = _init_factory_pat % (a.name,)
2399 if a.converter is not None:
2400 lines.append(
2401 " "
2402 + fmt_setter_with_converter(
2403 attr_name, arg_name, has_on_setattr
2404 )
2405 )
2406 lines.append("else:")
2407 lines.append(
2408 " "
2409 + fmt_setter_with_converter(
2410 attr_name,
2411 init_factory_name + "(" + maybe_self + ")",
2412 has_on_setattr,
2413 )
2414 )
2415 names_for_globals[
2416 _init_converter_pat % (a.name,)
2417 ] = a.converter
2418 else:
2419 lines.append(
2420 " " + fmt_setter(attr_name, arg_name, has_on_setattr)
2421 )
2422 lines.append("else:")
2423 lines.append(
2424 " "
2425 + fmt_setter(
2426 attr_name,
2427 init_factory_name + "(" + maybe_self + ")",
2428 has_on_setattr,
2429 )
2430 )
2431 names_for_globals[init_factory_name] = a.default.factory
2432 else:
2433 if a.kw_only:
2434 kw_only_args.append(arg_name)
2435 else:
2436 args.append(arg_name)
2438 if a.converter is not None:
2439 lines.append(
2440 fmt_setter_with_converter(
2441 attr_name, arg_name, has_on_setattr
2442 )
2443 )
2444 names_for_globals[
2445 _init_converter_pat % (a.name,)
2446 ] = a.converter
2447 else:
2448 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))
2450 if a.init is True:
2451 if a.type is not None and a.converter is None:
2452 annotations[arg_name] = a.type
2453 elif a.converter is not None:
2454 # Try to get the type from the converter.
2455 t = _AnnotationExtractor(a.converter).get_first_param_type()
2456 if t:
2457 annotations[arg_name] = t
2459 if attrs_to_validate: # we can skip this if there are no validators.
2460 names_for_globals["_config"] = _config
2461 lines.append("if _config._run_validators is True:")
2462 for a in attrs_to_validate:
2463 val_name = "__attr_validator_" + a.name
2464 attr_name = "__attr_" + a.name
2465 lines.append(f" {val_name}(self, {attr_name}, self.{a.name})")
2466 names_for_globals[val_name] = a.validator
2467 names_for_globals[attr_name] = a
2469 if post_init:
2470 lines.append("self.__attrs_post_init__()")
2472 # because this is set only after __attrs_post_init__ is called, a crash
2473 # will result if post-init tries to access the hash code. This seemed
2474 # preferable to setting this beforehand, in which case alteration to
2475 # field values during post-init combined with post-init accessing the
2476 # hash code would result in silent bugs.
2477 if cache_hash:
2478 if frozen:
2479 if slots: # noqa: SIM108
2480 # if frozen and slots, then _setattr defined above
2481 init_hash_cache = "_setattr('%s', %s)"
2482 else:
2483 # if frozen and not slots, then _inst_dict defined above
2484 init_hash_cache = "_inst_dict['%s'] = %s"
2485 else:
2486 init_hash_cache = "self.%s = %s"
2487 lines.append(init_hash_cache % (_hash_cache_field, "None"))
2489 # For exceptions we rely on BaseException.__init__ for proper
2490 # initialization.
2491 if is_exc:
2492 vals = ",".join(f"self.{a.name}" for a in attrs if a.init)
2494 lines.append(f"BaseException.__init__(self, {vals})")
2496 args = ", ".join(args)
2497 pre_init_args = args
2498 if kw_only_args:
2499 args += "%s*, %s" % (
2500 ", " if args else "", # leading comma
2501 ", ".join(kw_only_args), # kw_only args
2502 )
2503 pre_init_kw_only_args = ", ".join(
2504 ["%s=%s" % (kw_arg, kw_arg) for kw_arg in kw_only_args]
2505 )
2506 pre_init_args += (
2507 ", " if pre_init_args else ""
2508 ) # handle only kwargs and no regular args
2509 pre_init_args += pre_init_kw_only_args
2511 if pre_init and pre_init_has_args:
2512 # If pre init method has arguments, pass same arguments as `__init__`
2513 lines[0] = "self.__attrs_pre_init__(%s)" % pre_init_args
2515 return (
2516 "def %s(self, %s):\n %s\n"
2517 % (
2518 ("__attrs_init__" if attrs_init else "__init__"),
2519 args,
2520 "\n ".join(lines) if lines else "pass",
2521 ),
2522 names_for_globals,
2523 annotations,
2524 )
2527def _default_init_alias_for(name: str) -> str:
2528 """
2529 The default __init__ parameter name for a field.
2531 This performs private-name adjustment via leading-unscore stripping,
2532 and is the default value of Attribute.alias if not provided.
2533 """
2535 return name.lstrip("_")
2538class Attribute:
2539 """
2540 *Read-only* representation of an attribute.
2542 .. warning::
2544 You should never instantiate this class yourself.
2546 The class has *all* arguments of `attr.ib` (except for ``factory``
2547 which is only syntactic sugar for ``default=Factory(...)`` plus the
2548 following:
2550 - ``name`` (`str`): The name of the attribute.
2551 - ``alias`` (`str`): The __init__ parameter name of the attribute, after
2552 any explicit overrides and default private-attribute-name handling.
2553 - ``inherited`` (`bool`): Whether or not that attribute has been inherited
2554 from a base class.
2555 - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The callables
2556 that are used for comparing and ordering objects by this attribute,
2557 respectively. These are set by passing a callable to `attr.ib`'s ``eq``,
2558 ``order``, or ``cmp`` arguments. See also :ref:`comparison customization
2559 <custom-comparison>`.
2561 Instances of this class are frequently used for introspection purposes
2562 like:
2564 - `fields` returns a tuple of them.
2565 - Validators get them passed as the first argument.
2566 - The :ref:`field transformer <transform-fields>` hook receives a list of
2567 them.
2568 - The ``alias`` property exposes the __init__ parameter name of the field,
2569 with any overrides and default private-attribute handling applied.
2572 .. versionadded:: 20.1.0 *inherited*
2573 .. versionadded:: 20.1.0 *on_setattr*
2574 .. versionchanged:: 20.2.0 *inherited* is not taken into account for
2575 equality checks and hashing anymore.
2576 .. versionadded:: 21.1.0 *eq_key* and *order_key*
2577 .. versionadded:: 22.2.0 *alias*
2579 For the full version history of the fields, see `attr.ib`.
2580 """
2582 __slots__ = (
2583 "name",
2584 "default",
2585 "validator",
2586 "repr",
2587 "eq",
2588 "eq_key",
2589 "order",
2590 "order_key",
2591 "hash",
2592 "init",
2593 "metadata",
2594 "type",
2595 "converter",
2596 "kw_only",
2597 "inherited",
2598 "on_setattr",
2599 "alias",
2600 )
2602 def __init__(
2603 self,
2604 name,
2605 default,
2606 validator,
2607 repr,
2608 cmp, # XXX: unused, remove along with other cmp code.
2609 hash,
2610 init,
2611 inherited,
2612 metadata=None,
2613 type=None,
2614 converter=None,
2615 kw_only=False,
2616 eq=None,
2617 eq_key=None,
2618 order=None,
2619 order_key=None,
2620 on_setattr=None,
2621 alias=None,
2622 ):
2623 eq, eq_key, order, order_key = _determine_attrib_eq_order(
2624 cmp, eq_key or eq, order_key or order, True
2625 )
2627 # Cache this descriptor here to speed things up later.
2628 bound_setattr = _obj_setattr.__get__(self)
2630 # Despite the big red warning, people *do* instantiate `Attribute`
2631 # themselves.
2632 bound_setattr("name", name)
2633 bound_setattr("default", default)
2634 bound_setattr("validator", validator)
2635 bound_setattr("repr", repr)
2636 bound_setattr("eq", eq)
2637 bound_setattr("eq_key", eq_key)
2638 bound_setattr("order", order)
2639 bound_setattr("order_key", order_key)
2640 bound_setattr("hash", hash)
2641 bound_setattr("init", init)
2642 bound_setattr("converter", converter)
2643 bound_setattr(
2644 "metadata",
2645 (
2646 types.MappingProxyType(dict(metadata)) # Shallow copy
2647 if metadata
2648 else _empty_metadata_singleton
2649 ),
2650 )
2651 bound_setattr("type", type)
2652 bound_setattr("kw_only", kw_only)
2653 bound_setattr("inherited", inherited)
2654 bound_setattr("on_setattr", on_setattr)
2655 bound_setattr("alias", alias)
2657 def __setattr__(self, name, value):
2658 raise FrozenInstanceError()
2660 @classmethod
2661 def from_counting_attr(cls, name, ca, type=None):
2662 # type holds the annotated value. deal with conflicts:
2663 if type is None:
2664 type = ca.type
2665 elif ca.type is not None:
2666 msg = "Type annotation and type argument cannot both be present"
2667 raise ValueError(msg)
2668 inst_dict = {
2669 k: getattr(ca, k)
2670 for k in Attribute.__slots__
2671 if k
2672 not in (
2673 "name",
2674 "validator",
2675 "default",
2676 "type",
2677 "inherited",
2678 ) # exclude methods and deprecated alias
2679 }
2680 return cls(
2681 name=name,
2682 validator=ca._validator,
2683 default=ca._default,
2684 type=type,
2685 cmp=None,
2686 inherited=False,
2687 **inst_dict,
2688 )
2690 # Don't use attrs.evolve since fields(Attribute) doesn't work
2691 def evolve(self, **changes):
2692 """
2693 Copy *self* and apply *changes*.
2695 This works similarly to `attrs.evolve` but that function does not work
2696 with `Attribute`.
2698 It is mainly meant to be used for `transform-fields`.
2700 .. versionadded:: 20.3.0
2701 """
2702 new = copy.copy(self)
2704 new._setattrs(changes.items())
2706 return new
2708 # Don't use _add_pickle since fields(Attribute) doesn't work
2709 def __getstate__(self):
2710 """
2711 Play nice with pickle.
2712 """
2713 return tuple(
2714 getattr(self, name) if name != "metadata" else dict(self.metadata)
2715 for name in self.__slots__
2716 )
2718 def __setstate__(self, state):
2719 """
2720 Play nice with pickle.
2721 """
2722 self._setattrs(zip(self.__slots__, state))
2724 def _setattrs(self, name_values_pairs):
2725 bound_setattr = _obj_setattr.__get__(self)
2726 for name, value in name_values_pairs:
2727 if name != "metadata":
2728 bound_setattr(name, value)
2729 else:
2730 bound_setattr(
2731 name,
2732 types.MappingProxyType(dict(value))
2733 if value
2734 else _empty_metadata_singleton,
2735 )
2738_a = [
2739 Attribute(
2740 name=name,
2741 default=NOTHING,
2742 validator=None,
2743 repr=True,
2744 cmp=None,
2745 eq=True,
2746 order=False,
2747 hash=(name != "metadata"),
2748 init=True,
2749 inherited=False,
2750 alias=_default_init_alias_for(name),
2751 )
2752 for name in Attribute.__slots__
2753]
2755Attribute = _add_hash(
2756 _add_eq(
2757 _add_repr(Attribute, attrs=_a),
2758 attrs=[a for a in _a if a.name != "inherited"],
2759 ),
2760 attrs=[a for a in _a if a.hash and a.name != "inherited"],
2761)
2764class _CountingAttr:
2765 """
2766 Intermediate representation of attributes that uses a counter to preserve
2767 the order in which the attributes have been defined.
2769 *Internal* data structure of the attrs library. Running into is most
2770 likely the result of a bug like a forgotten `@attr.s` decorator.
2771 """
2773 __slots__ = (
2774 "counter",
2775 "_default",
2776 "repr",
2777 "eq",
2778 "eq_key",
2779 "order",
2780 "order_key",
2781 "hash",
2782 "init",
2783 "metadata",
2784 "_validator",
2785 "converter",
2786 "type",
2787 "kw_only",
2788 "on_setattr",
2789 "alias",
2790 )
2791 __attrs_attrs__ = (
2792 *tuple(
2793 Attribute(
2794 name=name,
2795 alias=_default_init_alias_for(name),
2796 default=NOTHING,
2797 validator=None,
2798 repr=True,
2799 cmp=None,
2800 hash=True,
2801 init=True,
2802 kw_only=False,
2803 eq=True,
2804 eq_key=None,
2805 order=False,
2806 order_key=None,
2807 inherited=False,
2808 on_setattr=None,
2809 )
2810 for name in (
2811 "counter",
2812 "_default",
2813 "repr",
2814 "eq",
2815 "order",
2816 "hash",
2817 "init",
2818 "on_setattr",
2819 "alias",
2820 )
2821 ),
2822 Attribute(
2823 name="metadata",
2824 alias="metadata",
2825 default=None,
2826 validator=None,
2827 repr=True,
2828 cmp=None,
2829 hash=False,
2830 init=True,
2831 kw_only=False,
2832 eq=True,
2833 eq_key=None,
2834 order=False,
2835 order_key=None,
2836 inherited=False,
2837 on_setattr=None,
2838 ),
2839 )
2840 cls_counter = 0
2842 def __init__(
2843 self,
2844 default,
2845 validator,
2846 repr,
2847 cmp,
2848 hash,
2849 init,
2850 converter,
2851 metadata,
2852 type,
2853 kw_only,
2854 eq,
2855 eq_key,
2856 order,
2857 order_key,
2858 on_setattr,
2859 alias,
2860 ):
2861 _CountingAttr.cls_counter += 1
2862 self.counter = _CountingAttr.cls_counter
2863 self._default = default
2864 self._validator = validator
2865 self.converter = converter
2866 self.repr = repr
2867 self.eq = eq
2868 self.eq_key = eq_key
2869 self.order = order
2870 self.order_key = order_key
2871 self.hash = hash
2872 self.init = init
2873 self.metadata = metadata
2874 self.type = type
2875 self.kw_only = kw_only
2876 self.on_setattr = on_setattr
2877 self.alias = alias
2879 def validator(self, meth):
2880 """
2881 Decorator that adds *meth* to the list of validators.
2883 Returns *meth* unchanged.
2885 .. versionadded:: 17.1.0
2886 """
2887 if self._validator is None:
2888 self._validator = meth
2889 else:
2890 self._validator = and_(self._validator, meth)
2891 return meth
2893 def default(self, meth):
2894 """
2895 Decorator that allows to set the default for an attribute.
2897 Returns *meth* unchanged.
2899 :raises DefaultAlreadySetError: If default has been set before.
2901 .. versionadded:: 17.1.0
2902 """
2903 if self._default is not NOTHING:
2904 raise DefaultAlreadySetError()
2906 self._default = Factory(meth, takes_self=True)
2908 return meth
2911_CountingAttr = _add_eq(_add_repr(_CountingAttr))
2914class Factory:
2915 """
2916 Stores a factory callable.
2918 If passed as the default value to `attrs.field`, the factory is used to
2919 generate a new value.
2921 :param callable factory: A callable that takes either none or exactly one
2922 mandatory positional argument depending on *takes_self*.
2923 :param bool takes_self: Pass the partially initialized instance that is
2924 being initialized as a positional argument.
2926 .. versionadded:: 17.1.0 *takes_self*
2927 """
2929 __slots__ = ("factory", "takes_self")
2931 def __init__(self, factory, takes_self=False):
2932 self.factory = factory
2933 self.takes_self = takes_self
2935 def __getstate__(self):
2936 """
2937 Play nice with pickle.
2938 """
2939 return tuple(getattr(self, name) for name in self.__slots__)
2941 def __setstate__(self, state):
2942 """
2943 Play nice with pickle.
2944 """
2945 for name, value in zip(self.__slots__, state):
2946 setattr(self, name, value)
2949_f = [
2950 Attribute(
2951 name=name,
2952 default=NOTHING,
2953 validator=None,
2954 repr=True,
2955 cmp=None,
2956 eq=True,
2957 order=False,
2958 hash=True,
2959 init=True,
2960 inherited=False,
2961 )
2962 for name in Factory.__slots__
2963]
2965Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)
2968def make_class(
2969 name, attrs, bases=(object,), class_body=None, **attributes_arguments
2970):
2971 r"""
2972 A quick way to create a new class called *name* with *attrs*.
2974 :param str name: The name for the new class.
2976 :param attrs: A list of names or a dictionary of mappings of names to
2977 `attr.ib`\ s / `attrs.field`\ s.
2979 The order is deduced from the order of the names or attributes inside
2980 *attrs*. Otherwise the order of the definition of the attributes is
2981 used.
2982 :type attrs: `list` or `dict`
2984 :param tuple bases: Classes that the new class will subclass.
2986 :param dict class_body: An optional dictionary of class attributes for the new class.
2988 :param attributes_arguments: Passed unmodified to `attr.s`.
2990 :return: A new class with *attrs*.
2991 :rtype: type
2993 .. versionadded:: 17.1.0 *bases*
2994 .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
2995 .. versionchanged:: 23.2.0 *class_body*
2996 """
2997 if isinstance(attrs, dict):
2998 cls_dict = attrs
2999 elif isinstance(attrs, (list, tuple)):
3000 cls_dict = {a: attrib() for a in attrs}
3001 else:
3002 msg = "attrs argument must be a dict or a list."
3003 raise TypeError(msg)
3005 pre_init = cls_dict.pop("__attrs_pre_init__", None)
3006 post_init = cls_dict.pop("__attrs_post_init__", None)
3007 user_init = cls_dict.pop("__init__", None)
3009 body = {}
3010 if class_body is not None:
3011 body.update(class_body)
3012 if pre_init is not None:
3013 body["__attrs_pre_init__"] = pre_init
3014 if post_init is not None:
3015 body["__attrs_post_init__"] = post_init
3016 if user_init is not None:
3017 body["__init__"] = user_init
3019 type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body))
3021 # For pickling to work, the __module__ variable needs to be set to the
3022 # frame where the class is created. Bypass this step in environments where
3023 # sys._getframe is not defined (Jython for example) or sys._getframe is not
3024 # defined for arguments greater than 0 (IronPython).
3025 with contextlib.suppress(AttributeError, ValueError):
3026 type_.__module__ = sys._getframe(1).f_globals.get(
3027 "__name__", "__main__"
3028 )
3030 # We do it here for proper warnings with meaningful stacklevel.
3031 cmp = attributes_arguments.pop("cmp", None)
3032 (
3033 attributes_arguments["eq"],
3034 attributes_arguments["order"],
3035 ) = _determine_attrs_eq_order(
3036 cmp,
3037 attributes_arguments.get("eq"),
3038 attributes_arguments.get("order"),
3039 True,
3040 )
3042 return _attrs(these=cls_dict, **attributes_arguments)(type_)
3045# These are required by within this module so we define them here and merely
3046# import into .validators / .converters.
3049@attrs(slots=True, hash=True)
3050class _AndValidator:
3051 """
3052 Compose many validators to a single one.
3053 """
3055 _validators = attrib()
3057 def __call__(self, inst, attr, value):
3058 for v in self._validators:
3059 v(inst, attr, value)
3062def and_(*validators):
3063 """
3064 A validator that composes multiple validators into one.
3066 When called on a value, it runs all wrapped validators.
3068 :param callables validators: Arbitrary number of validators.
3070 .. versionadded:: 17.1.0
3071 """
3072 vals = []
3073 for validator in validators:
3074 vals.extend(
3075 validator._validators
3076 if isinstance(validator, _AndValidator)
3077 else [validator]
3078 )
3080 return _AndValidator(tuple(vals))
3083def pipe(*converters):
3084 """
3085 A converter that composes multiple converters into one.
3087 When called on a value, it runs all wrapped converters, returning the
3088 *last* value.
3090 Type annotations will be inferred from the wrapped converters', if
3091 they have any.
3093 :param callables converters: Arbitrary number of converters.
3095 .. versionadded:: 20.1.0
3096 """
3098 def pipe_converter(val):
3099 for converter in converters:
3100 val = converter(val)
3102 return val
3104 if not converters:
3105 # If the converter list is empty, pipe_converter is the identity.
3106 A = typing.TypeVar("A")
3107 pipe_converter.__annotations__ = {"val": A, "return": A}
3108 else:
3109 # Get parameter type from first converter.
3110 t = _AnnotationExtractor(converters[0]).get_first_param_type()
3111 if t:
3112 pipe_converter.__annotations__["val"] = t
3114 # Get return type from last converter.
3115 rt = _AnnotationExtractor(converters[-1]).get_return_type()
3116 if rt:
3117 pipe_converter.__annotations__["return"] = rt
3119 return pipe_converter