diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-02-10 16:44:30 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:30 +0300 |
commit | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (patch) | |
tree | 012bb94d777798f1f56ac1cec429509766d05181 /contrib/python/attrs/attr/_make.py | |
parent | 6751af0b0c1b952fede40b19b71da8025b5d8bcf (diff) | |
download | ydb-2598ef1d0aee359b4b6d5fdd1758916d5907d04f.tar.gz |
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/attrs/attr/_make.py')
-rw-r--r-- | contrib/python/attrs/attr/_make.py | 980 |
1 files changed, 490 insertions, 490 deletions
diff --git a/contrib/python/attrs/attr/_make.py b/contrib/python/attrs/attr/_make.py index a1912b1233..067a25d3f9 100644 --- a/contrib/python/attrs/attr/_make.py +++ b/contrib/python/attrs/attr/_make.py @@ -1,7 +1,7 @@ from __future__ import absolute_import, division, print_function import copy -import inspect +import inspect import linecache import sys import threading @@ -13,11 +13,11 @@ from operator import itemgetter from . import _config, setters from ._compat import ( PY2, - PYPY, + PYPY, isclass, iteritems, metadata_proxy, - new_class, + new_class, ordered_dict, set_closure_cell, ) @@ -30,10 +30,10 @@ from .exceptions import ( ) -if not PY2: - import typing - - +if not PY2: + import typing + + # This is used at least twice, so cache it here. _obj_setattr = object.__setattr__ _init_converter_pat = "__attr_converter_%s" @@ -41,12 +41,12 @@ _init_factory_pat = "__attr_factory_{}" _tuple_property_pat = ( " {attr_name} = _attrs_property(_attrs_itemgetter({index}))" ) -_classvar_prefixes = ( - "typing.ClassVar", - "t.ClassVar", - "ClassVar", - "typing_extensions.ClassVar", -) +_classvar_prefixes = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) # we don't use a double-underscore prefix because that triggers # name mangling when trying to create a slot for the field # (when slots=True) @@ -63,8 +63,8 @@ class _Nothing(object): Sentinel class to indicate the lack of a value when ``None`` is ambiguous. ``_Nothing`` is a singleton. There is only ever one of it. - - .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. """ _singleton = None @@ -77,13 +77,13 @@ class _Nothing(object): def __repr__(self): return "NOTHING" - def __bool__(self): - return False - - def __len__(self): - return 0 # __bool__ for Python 2 - + def __bool__(self): + return False + def __len__(self): + return 0 # __bool__ for Python 2 + + NOTHING = _Nothing() """ Sentinel to indicate the lack of a value when ``None`` is ambiguous. @@ -184,25 +184,25 @@ def attrib( as-is, i.e. it will be used directly *instead* of calling ``repr()`` (the default). :type repr: a `bool` or a `callable` to use a custom function. - - :param eq: If ``True`` (default), include this attribute in the + + :param eq: If ``True`` (default), include this attribute in the generated ``__eq__`` and ``__ne__`` methods that check two instances - for equality. To override how the attribute value is compared, - pass a ``callable`` that takes a single value and returns the value - to be compared. - :type eq: a `bool` or a `callable`. - - :param order: If ``True`` (default), include this attributes in the + for equality. To override how the attribute value is compared, + pass a ``callable`` that takes a single value and returns the value + to be compared. + :type eq: a `bool` or a `callable`. + + :param order: If ``True`` (default), include this attributes in the generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. - To override how the attribute value is ordered, - pass a ``callable`` that takes a single value and returns the value - to be ordered. - :type order: a `bool` or a `callable`. - - :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the - same value. Must not be mixed with *eq* or *order*. - :type cmp: a `bool` or a `callable`. - + To override how the attribute value is ordered, + pass a ``callable`` that takes a single value and returns the value + to be ordered. + :type order: a `bool` or a `callable`. + + :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the + same value. Must not be mixed with *eq* or *order*. + :type cmp: a `bool` or a `callable`. + :param Optional[bool] hash: Include this attribute in the generated ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This is the correct behavior according the Python spec. Setting this value @@ -250,19 +250,19 @@ def attrib( .. versionadded:: 18.1.0 ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. .. versionadded:: 18.2.0 *kw_only* - .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *convert* keyword argument removed. .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. .. versionadded:: 19.2.0 *eq* and *order* .. versionadded:: 20.1.0 *on_setattr* - .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 - .. versionchanged:: 21.1.0 - *eq*, *order*, and *cmp* also accept a custom callable - .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated """ - eq, eq_key, order, order_key = _determine_attrib_eq_order( - cmp, eq, order, True - ) + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) if hash is not None and hash is not True and hash is not False: raise TypeError( @@ -304,43 +304,43 @@ def attrib( type=type, kw_only=kw_only, eq=eq, - eq_key=eq_key, + eq_key=eq_key, order=order, - order_key=order_key, + order_key=order_key, on_setattr=on_setattr, ) -def _compile_and_eval(script, globs, locs=None, filename=""): - """ - "Exec" the script with the given global (globs) and local (locs) variables. - """ - bytecode = compile(script, filename, "exec") - eval(bytecode, globs, locs) - - -def _make_method(name, script, filename, globs=None): - """ - Create the method with the script given and return the method object. - """ - locs = {} - if globs is None: - globs = {} - - _compile_and_eval(script, globs, locs, filename) - - # In order of debuggers like PDB being able to step through the code, - # we add a fake linecache entry. - linecache.cache[filename] = ( - len(script), - None, - script.splitlines(True), - filename, - ) - - return locs[name] - - +def _compile_and_eval(script, globs, locs=None, filename=""): + """ + "Exec" the script with the given global (globs) and local (locs) variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _make_method(name, script, filename, globs=None): + """ + Create the method with the script given and return the method object. + """ + locs = {} + if globs is None: + globs = {} + + _compile_and_eval(script, globs, locs, filename) + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + linecache.cache[filename] = ( + len(script), + None, + script.splitlines(True), + filename, + ) + + return locs[name] + + def _make_attr_tuple_class(cls_name, attr_names): """ Create a tuple subclass to hold `Attribute`s for an `attrs` class. @@ -364,7 +364,7 @@ def _make_attr_tuple_class(cls_name, attr_names): else: attr_class_template.append(" pass") globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property} - _compile_and_eval("\n".join(attr_class_template), globs) + _compile_and_eval("\n".join(attr_class_template), globs) return globs[attr_class_name] @@ -391,15 +391,15 @@ def _is_class_var(annot): annotations which would put attrs-based classes at a performance disadvantage compared to plain old classes. """ - annot = str(annot) - - # Annotation can be quoted. - if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): - annot = annot[1:-1] - - return annot.startswith(_classvar_prefixes) + annot = str(annot) + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + return annot.startswith(_classvar_prefixes) + + def _has_own_attribute(cls, attrib_name): """ Check whether *cls* defines *attrib_name* (and doesn't just inherit it). @@ -448,7 +448,7 @@ def _collect_base_attrs(cls, taken_attr_names): if a.inherited or a.name in taken_attr_names: continue - a = a.evolve(inherited=True) + a = a.evolve(inherited=True) base_attrs.append(a) base_attr_map[a.name] = base_cls @@ -486,7 +486,7 @@ def _collect_base_attrs_broken(cls, taken_attr_names): if a.name in taken_attr_names: continue - a = a.evolve(inherited=True) + a = a.evolve(inherited=True) taken_attr_names.add(a.name) base_attrs.append(a) base_attr_map[a.name] = base_cls @@ -494,9 +494,9 @@ def _collect_base_attrs_broken(cls, taken_attr_names): return base_attrs, base_attr_map -def _transform_attrs( - cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer -): +def _transform_attrs( + cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer +): """ Transform all `_CountingAttr`s on a class into `Attribute`s. @@ -528,7 +528,7 @@ def _transform_attrs( continue annot_names.add(attr_name) a = cd.get(attr_name, NOTHING) - + if not isinstance(a, _CountingAttr): if a is NOTHING: a = attrib() @@ -576,8 +576,8 @@ def _transform_attrs( AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) if kw_only: - own_attrs = [a.evolve(kw_only=True) for a in own_attrs] - base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] attrs = AttrsClass(base_attrs + own_attrs) @@ -596,36 +596,36 @@ def _transform_attrs( if had_default is False and a.default is not NOTHING: had_default = True - if field_transformer is not None: - attrs = field_transformer(cls, attrs) + if field_transformer is not None: + attrs = field_transformer(cls, attrs) return _Attributes((attrs, base_attrs, base_attr_map)) -if PYPY: - - def _frozen_setattrs(self, name, value): - """ - Attached to frozen classes as __setattr__. - """ - if isinstance(self, BaseException) and name in ( - "__cause__", - "__context__", - ): - BaseException.__setattr__(self, name, value) - return - - raise FrozenInstanceError() - - -else: - - def _frozen_setattrs(self, name, value): - """ - Attached to frozen classes as __setattr__. - """ - raise FrozenInstanceError() - - +if PYPY: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError() + + +else: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + raise FrozenInstanceError() + + def _frozen_delattrs(self, name): """ Attached to frozen classes as __delattr__. @@ -648,9 +648,9 @@ class _ClassBuilder(object): "_cls_dict", "_delete_attribs", "_frozen", - "_has_pre_init", + "_has_pre_init", "_has_post_init", - "_is_exc", + "_is_exc", "_on_setattr", "_slots", "_weakref_slot", @@ -669,19 +669,19 @@ class _ClassBuilder(object): auto_attribs, kw_only, cache_hash, - is_exc, + is_exc, collect_by_mro, on_setattr, has_custom_setattr, - field_transformer, + field_transformer, ): attrs, base_attrs, base_map = _transform_attrs( - cls, - these, - auto_attribs, - kw_only, - collect_by_mro, - field_transformer, + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, ) self._cls = cls @@ -694,10 +694,10 @@ class _ClassBuilder(object): self._frozen = frozen self._weakref_slot = weakref_slot self._cache_hash = cache_hash - self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) self._delete_attribs = not bool(these) - self._is_exc = is_exc + self._is_exc = is_exc self._on_setattr = on_setattr self._has_custom_setattr = has_custom_setattr @@ -743,7 +743,7 @@ class _ClassBuilder(object): for name in self._attr_names: if ( name not in base_names - and getattr(cls, name, _sentinel) is not _sentinel + and getattr(cls, name, _sentinel) is not _sentinel ): try: delattr(cls, name) @@ -763,10 +763,10 @@ class _ClassBuilder(object): cls, "__attrs_own_setattr__", False ): cls.__attrs_own_setattr__ = False - + if not self._has_custom_setattr: cls.__setattr__ = object.__setattr__ - + return cls def _create_slots_class(self): @@ -796,22 +796,22 @@ class _ClassBuilder(object): cd["__setattr__"] = object.__setattr__ break - # Traverse the MRO to collect existing slots - # and check for an existing __weakref__. - existing_slots = dict() + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = dict() weakref_inherited = False for base_cls in self._cls.__mro__[1:-1]: if base_cls.__dict__.get("__weakref__", None) is not None: weakref_inherited = True - existing_slots.update( - { - name: getattr(base_cls, name) - for name in getattr(base_cls, "__slots__", []) - } - ) - - base_names = set(self._base_names) - + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + names = self._attr_names if ( self._weakref_slot @@ -824,17 +824,17 @@ class _ClassBuilder(object): # We only add the names of attributes that aren't inherited. # Setting __slots__ to inherited attributes wastes memory. slot_names = [name for name in names if name not in base_names] - # There are slots for attributes from current class - # that are defined in parent classes. - # As their descriptors may be overriden by a child class, - # we collect them here and update the class dict - reused_slots = { - slot: slot_descriptor - for slot, slot_descriptor in iteritems(existing_slots) - if slot in slot_names - } - slot_names = [name for name in slot_names if name not in reused_slots] - cd.update(reused_slots) + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overriden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in iteritems(existing_slots) + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) if self._cache_hash: slot_names.append(_hash_cache_field) cd["__slots__"] = tuple(slot_names) @@ -857,10 +857,10 @@ class _ClassBuilder(object): # Class- and staticmethods hide their functions inside. # These might need to be rewritten as well. closure_cells = getattr(item.__func__, "__closure__", None) - elif isinstance(item, property): - # Workaround for property `super()` shortcut (PY3-only). - # There is no universal way for other descriptors. - closure_cells = getattr(item.fget, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) else: closure_cells = getattr(item, "__closure__", None) @@ -951,41 +951,41 @@ class _ClassBuilder(object): _make_init( self._cls, self._attrs, - self._has_pre_init, + self._has_pre_init, self._has_post_init, self._frozen, self._slots, self._cache_hash, self._base_attr_map, - self._is_exc, + self._is_exc, self._on_setattr is not None and self._on_setattr is not setters.NO_OP, - attrs_init=False, - ) - ) - - return self - - def add_attrs_init(self): - self._cls_dict["__attrs_init__"] = self._add_method_dunders( - _make_init( - self._cls, - self._attrs, - self._has_pre_init, - self._has_post_init, - self._frozen, - self._slots, - self._cache_hash, - self._base_attr_map, - self._is_exc, - self._on_setattr is not None - and self._on_setattr is not setters.NO_OP, - attrs_init=True, + attrs_init=False, ) ) return self + def add_attrs_init(self): + self._cls_dict["__attrs_init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr is not None + and self._on_setattr is not setters.NO_OP, + attrs_init=True, + ) + ) + + return self + def add_eq(self): cd = self._cls_dict @@ -1074,7 +1074,7 @@ _CMP_DEPRECATION = ( ) -def _determine_attrs_eq_order(cmp, eq, order, default_eq): +def _determine_attrs_eq_order(cmp, eq, order, default_eq): """ Validate the combination of *cmp*, *eq*, and *order*. Derive the effective values of eq and order. If *eq* is None, set it to *default_eq*. @@ -1100,47 +1100,47 @@ def _determine_attrs_eq_order(cmp, eq, order, default_eq): return eq, order -def _determine_attrib_eq_order(cmp, eq, order, default_eq): - """ - Validate the combination of *cmp*, *eq*, and *order*. Derive the effective - values of eq and order. If *eq* is None, set it to *default_eq*. - """ - if cmp is not None and any((eq is not None, order is not None)): - raise ValueError("Don't mix `cmp` with `eq' and `order`.") - - def decide_callable_or_boolean(value): - """ - Decide whether a key function is used. - """ - if callable(value): - value, key = True, value - else: - key = None - return value, key - - # cmp takes precedence due to bw-compatibility. - if cmp is not None: - cmp, cmp_key = decide_callable_or_boolean(cmp) - return cmp, cmp_key, cmp, cmp_key - - # If left None, equality is set to the specified default and ordering - # mirrors equality. - if eq is None: - eq, eq_key = default_eq, None - else: - eq, eq_key = decide_callable_or_boolean(eq) - - if order is None: - order, order_key = eq, eq_key - else: - order, order_key = decide_callable_or_boolean(order) - - if eq is False and order is True: - raise ValueError("`order` can only be True if `eq` is True too.") - - return eq, eq_key, order, order_key - - +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + raise ValueError("Don't mix `cmp` with `eq' and `order`.") + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + raise ValueError("`order` can only be True if `eq` is True too.") + + return eq, eq_key, order, order_key + + def _determine_whether_to_implement( cls, flag, auto_detect, dunders, default=True ): @@ -1184,14 +1184,14 @@ def attrs( auto_attribs=False, kw_only=False, cache_hash=False, - auto_exc=False, + auto_exc=False, eq=None, order=None, auto_detect=False, collect_by_mro=False, getstate_setstate=None, on_setattr=None, - field_transformer=None, + field_transformer=None, ): r""" A class decorator that adds `dunder @@ -1223,7 +1223,7 @@ def attrs( inherited from some base class). So for example by implementing ``__eq__`` on a class yourself, - ``attrs`` will deduce ``eq=False`` and will create *neither* + ``attrs`` will deduce ``eq=False`` and will create *neither* ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible ``__ne__`` by default, so it *should* be enough to only implement ``__eq__`` in most cases). @@ -1256,8 +1256,8 @@ def attrs( ``__gt__``, and ``__ge__`` methods that behave like *eq* above and allow instances to be ordered. If ``None`` (default) mirror value of *eq*. - :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq* - and *order* to the same value. Must not be mixed with *eq* or *order*. + :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq* + and *order* to the same value. Must not be mixed with *eq* or *order*. :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method is generated according how *eq* and *frozen* are set. @@ -1278,28 +1278,28 @@ def attrs( behavior <https://github.com/python-attrs/attrs/issues/136>`_ for more details. :param bool init: Create a ``__init__`` method that initializes the - ``attrs`` attributes. Leading underscores are stripped for the argument - name. If a ``__attrs_pre_init__`` method exists on the class, it will - be called before the class is initialized. If a ``__attrs_post_init__`` - method exists on the class, it will be called after the class is fully - initialized. - - If ``init`` is ``False``, an ``__attrs_init__`` method will be - injected instead. This allows you to define a custom ``__init__`` - method that can do pre-init work such as ``super().__init__()``, - and then call ``__attrs_init__()`` and ``__attrs_post_init__()``. + ``attrs`` attributes. Leading underscores are stripped for the argument + name. If a ``__attrs_pre_init__`` method exists on the class, it will + be called before the class is initialized. If a ``__attrs_post_init__`` + method exists on the class, it will be called after the class is fully + initialized. + + If ``init`` is ``False``, an ``__attrs_init__`` method will be + injected instead. This allows you to define a custom ``__init__`` + method that can do pre-init work such as ``super().__init__()``, + and then call ``__attrs_init__()`` and ``__attrs_post_init__()``. :param bool slots: Create a `slotted class <slotted classes>` that's more - memory-efficient. Slotted classes are generally superior to the default - dict classes, but have some gotchas you should know about, so we - encourage you to read the `glossary entry <slotted classes>`. + memory-efficient. Slotted classes are generally superior to the default + dict classes, but have some gotchas you should know about, so we + encourage you to read the `glossary entry <slotted classes>`. :param bool frozen: Make instances immutable after initialization. If someone attempts to modify a frozen instance, `attr.exceptions.FrozenInstanceError` is raised. - .. note:: + .. note:: 1. This is achieved by installing a custom ``__setattr__`` method - on your class, so you can't implement your own. + on your class, so you can't implement your own. 2. True immutability is impossible in Python. @@ -1328,20 +1328,20 @@ def attrs( If you assign a value to those attributes (e.g. ``x: int = 42``), that value becomes the default value like if it were passed using ``attr.ib(default=42)``. Passing an instance of `Factory` also - works as expected in most cases (see warning below). + works as expected in most cases (see warning below). Attributes annotated as `typing.ClassVar`, and attributes that are neither annotated nor set to an `attr.ib` are **ignored**. - .. warning:: - For features that use the attribute name to create decorators (e.g. - `validators <validators>`), you still *must* assign `attr.ib` to - them. Otherwise Python will either not find the name or try to use - the default value to call e.g. ``validator`` on it. - - These errors can be quite confusing and probably the most common bug - report on our bug tracker. - + .. warning:: + For features that use the attribute name to create decorators (e.g. + `validators <validators>`), you still *must* assign `attr.ib` to + them. Otherwise Python will either not find the name or try to use + the default value to call e.g. ``validator`` on it. + + These errors can be quite confusing and probably the most common bug + report on our bug tracker. + .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/ :param bool kw_only: Make all attributes keyword-only (Python 3+) in the generated ``__init__`` (if ``init`` is ``False``, this @@ -1349,23 +1349,23 @@ def attrs( :param bool cache_hash: Ensure that the object's hash code is computed only once and stored on the object. If this is set to ``True``, hashing must be either explicitly or implicitly enabled for this - class. If the hash code is cached, avoid any reassignments of - fields involved in hash code computation or mutations of the objects - those fields point to after object creation. If such changes occur, - the behavior of the object's hash code is undefined. + class. If the hash code is cached, avoid any reassignments of + fields involved in hash code computation or mutations of the objects + those fields point to after object creation. If such changes occur, + the behavior of the object's hash code is undefined. :param bool auto_exc: If the class subclasses `BaseException` - (which implicitly includes any subclass of any exception), the - following happens to behave like a well-behaved Python exceptions - class: + (which implicitly includes any subclass of any exception), the + following happens to behave like a well-behaved Python exceptions + class: - the values for *eq*, *order*, and *hash* are ignored and the instances compare and hash by the instance's ids (N.B. ``attrs`` will *not* remove existing implementations of ``__hash__`` or the equality methods. It just won't add own ones.), - - all attributes that are either passed into ``__init__`` or have a - default value are additionally available as a tuple in the ``args`` - attribute, - - the value of *str* is ignored leaving ``__str__`` to base classes. + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the ``args`` + attribute, + - the value of *str* is ignored leaving ``__str__`` to base classes. :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs`` collects attributes from base classes. The default behavior is incorrect in certain cases of multiple inheritance. It should be on by @@ -1391,7 +1391,7 @@ def attrs( :param on_setattr: A callable that is run whenever the user attempts to set an attribute (either by assignment like ``i.x = 42`` or by using - `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments + `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments as validators: the instance, the attribute that is being modified, and the new value. @@ -1401,11 +1401,11 @@ def attrs( If a list of callables is passed, they're automatically wrapped in an `attr.setters.pipe`. - :param Optional[callable] field_transformer: - A function that is called with the original class object and all - fields right before ``attrs`` finalizes the class. You can use - this, e.g., to automatically add converters or validators to - fields based on their types. See `transform-fields` for more details. + :param Optional[callable] field_transformer: + A function that is called with the original class object and all + fields right before ``attrs`` finalizes the class. You can use + this, e.g., to automatically add converters or validators to + fields based on their types. See `transform-fields` for more details. .. versionadded:: 16.0.0 *slots* .. versionadded:: 16.1.0 *frozen* @@ -1428,41 +1428,41 @@ def attrs( subclasses comparable anymore. .. versionadded:: 18.2.0 *kw_only* .. versionadded:: 18.2.0 *cache_hash* - .. versionadded:: 19.1.0 *auto_exc* + .. versionadded:: 19.1.0 *auto_exc* .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. .. versionadded:: 19.2.0 *eq* and *order* .. versionadded:: 20.1.0 *auto_detect* .. versionadded:: 20.1.0 *collect_by_mro* .. versionadded:: 20.1.0 *getstate_setstate* .. versionadded:: 20.1.0 *on_setattr* - .. versionadded:: 20.3.0 *field_transformer* - .. versionchanged:: 21.1.0 - ``init=False`` injects ``__attrs_init__`` - .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` - .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated """ if auto_detect and PY2: raise PythonTooOldError( "auto_detect only works on Python 3 and later." ) - eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) hash_ = hash # work around the lack of nonlocal if isinstance(on_setattr, (list, tuple)): on_setattr = setters.pipe(*on_setattr) def wrap(cls): - + if getattr(cls, "__class__", None) is None: raise TypeError("attrs only works with new-style classes.") is_frozen = frozen or _has_frozen_base_class(cls) - is_exc = auto_exc is True and issubclass(cls, BaseException) + is_exc = auto_exc is True and issubclass(cls, BaseException) has_own_setattr = auto_detect and _has_own_attribute( cls, "__setattr__" ) - + if has_own_setattr and is_frozen: raise ValueError("Can't freeze a class with a custom __setattr__.") @@ -1482,11 +1482,11 @@ def attrs( auto_attribs, kw_only, cache_hash, - is_exc, + is_exc, collect_by_mro, on_setattr, has_own_setattr, - field_transformer, + field_transformer, ) if _determine_whether_to_implement( cls, repr, auto_detect, ("__repr__",) @@ -1549,7 +1549,7 @@ def attrs( ): builder.add_init() else: - builder.add_attrs_init() + builder.add_attrs_init() if cache_hash: raise TypeError( "Invalid value for cache_hash. To use hash caching," @@ -1690,7 +1690,7 @@ def _make_hash(cls, attrs, frozen, cache_hash): append_hash_computation_lines("return ", tab) script = "\n".join(method_lines) - return _make_method("__hash__", script, unique_filename) + return _make_method("__hash__", script, unique_filename) def _add_hash(cls, attrs): @@ -1732,36 +1732,36 @@ def _make_eq(cls, attrs): " if other.__class__ is not self.__class__:", " return NotImplemented", ] - + # We can't just do a big self.x = other.x and... clause due to # irregularities like nan == nan is false but (nan,) == (nan,) is true. - globs = {} + globs = {} if attrs: lines.append(" return (") others = [" ) == ("] for a in attrs: - if a.eq_key: - cmp_name = "_%s_key" % (a.name,) - # Add the key function to the global namespace - # of the evaluated function. - globs[cmp_name] = a.eq_key - lines.append( - " %s(self.%s)," - % ( - cmp_name, - a.name, - ) - ) - others.append( - " %s(other.%s)," - % ( - cmp_name, - a.name, - ) - ) - else: - lines.append(" self.%s," % (a.name,)) - others.append(" other.%s," % (a.name,)) + if a.eq_key: + cmp_name = "_%s_key" % (a.name,) + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append( + " %s(self.%s)," + % ( + cmp_name, + a.name, + ) + ) + others.append( + " %s(other.%s)," + % ( + cmp_name, + a.name, + ) + ) + else: + lines.append(" self.%s," % (a.name,)) + others.append(" other.%s," % (a.name,)) lines += others + [" )"] else: @@ -1769,7 +1769,7 @@ def _make_eq(cls, attrs): script = "\n".join(lines) - return _make_method("__eq__", script, unique_filename, globs) + return _make_method("__eq__", script, unique_filename, globs) def _make_order(cls, attrs): @@ -1782,12 +1782,12 @@ def _make_order(cls, attrs): """ Save us some typing. """ - return tuple( - key(value) if key else value - for value, key in ( - (getattr(obj, a.name), a.order_key) for a in attrs - ) - ) + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) def __lt__(self, other): """ @@ -2001,7 +2001,7 @@ def _is_slot_attr(a_name, base_attr_map): def _make_init( cls, attrs, - pre_init, + pre_init, post_init, frozen, slots, @@ -2009,7 +2009,7 @@ def _make_init( base_attr_map, is_exc, has_global_on_setattr, - attrs_init, + attrs_init, ): if frozen and has_global_on_setattr: raise ValueError("Frozen classes can't use on_setattr.") @@ -2040,19 +2040,19 @@ def _make_init( filtered_attrs, frozen, slots, - pre_init, + pre_init, post_init, cache_hash, base_attr_map, is_exc, needs_cached_setattr, has_global_on_setattr, - attrs_init, + attrs_init, ) - if cls.__module__ in sys.modules: - # This makes typing.get_type_hints(CLS.__init__) resolve string types. - globs.update(sys.modules[cls.__module__].__dict__) - + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) if needs_cached_setattr: @@ -2060,15 +2060,15 @@ def _make_init( # setattr hooks. globs["_cached_setattr"] = _obj_setattr - init = _make_method( - "__attrs_init__" if attrs_init else "__init__", - script, + init = _make_method( + "__attrs_init__" if attrs_init else "__init__", + script, unique_filename, - globs, + globs, ) - init.__annotations__ = annotations + init.__annotations__ = annotations - return init + return init def _setattr(attr_name, value_var, has_on_setattr): @@ -2135,12 +2135,12 @@ if PY2: def _unpack_kw_only_lines_py2(kw_only_args): """ Unpack all *kw_only_args* from _kw_only dict and handle errors. - + Given a list of strings "{attr_name}" and "{attr_name}={default}" generates list of lines of code that pop attrs from _kw_only dict and raise TypeError similar to builtin if required attr is missing or extra key is passed. - + >>> print("\n".join(_unpack_kw_only_lines_py2(["a", "b=42"]))) try: a = _kw_only.pop('a') @@ -2177,14 +2177,14 @@ def _attrs_to_init_script( attrs, frozen, slots, - pre_init, + pre_init, post_init, cache_hash, base_attr_map, is_exc, needs_cached_setattr, has_global_on_setattr, - attrs_init, + attrs_init, ): """ Return a script of an initializer for *attrs* and a dict of globals. @@ -2195,9 +2195,9 @@ def _attrs_to_init_script( a cached ``object.__setattr__``. """ lines = [] - if pre_init: - lines.append("self.__attrs_pre_init__()") - + if pre_init: + lines.append("self.__attrs_pre_init__()") + if needs_cached_setattr: lines.append( # Circumvent the __setattr__ descriptor to save one lookup per @@ -2387,24 +2387,24 @@ def _attrs_to_init_script( else: lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) - if a.init is True: - if a.type is not None and a.converter is None: - annotations[arg_name] = a.type - elif a.converter is not None and not PY2: - # Try to get the type from the converter. - sig = None - try: - sig = inspect.signature(a.converter) - except (ValueError, TypeError): # inspect failed - pass - if sig: - sig_params = list(sig.parameters.values()) - if ( - sig_params - and sig_params[0].annotation - is not inspect.Parameter.empty - ): - annotations[arg_name] = sig_params[0].annotation + if a.init is True: + if a.type is not None and a.converter is None: + annotations[arg_name] = a.type + elif a.converter is not None and not PY2: + # Try to get the type from the converter. + sig = None + try: + sig = inspect.signature(a.converter) + except (ValueError, TypeError): # inspect failed + pass + if sig: + sig_params = list(sig.parameters.values()) + if ( + sig_params + and sig_params[0].annotation + is not inspect.Parameter.empty + ): + annotations[arg_name] = sig_params[0].annotation if attrs_to_validate: # we can skip this if there are no validators. names_for_globals["_config"] = _config @@ -2438,13 +2438,13 @@ def _attrs_to_init_script( init_hash_cache = "self.%s = %s" lines.append(init_hash_cache % (_hash_cache_field, "None")) - # For exceptions we rely on BaseException.__init__ for proper - # initialization. - if is_exc: - vals = ",".join("self." + a.name for a in attrs if a.init) - - lines.append("BaseException.__init__(self, %s)" % (vals,)) - + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join("self." + a.name for a in attrs if a.init) + + lines.append("BaseException.__init__(self, %s)" % (vals,)) + args = ", ".join(args) if kw_only_args: if PY2: @@ -2458,12 +2458,12 @@ def _attrs_to_init_script( ) return ( """\ -def {init_name}(self, {args}): +def {init_name}(self, {args}): {lines} """.format( - init_name=("__attrs_init__" if attrs_init else "__init__"), - args=args, - lines="\n ".join(lines) if lines else "pass", + init_name=("__attrs_init__" if attrs_init else "__init__"), + args=args, + lines="\n ".join(lines) if lines else "pass", ), names_for_globals, annotations, @@ -2474,13 +2474,13 @@ class Attribute(object): """ *Read-only* representation of an attribute. - Instances of this class are frequently used for introspection purposes - like: - - - `fields` returns a tuple of them. - - Validators get them passed as the first argument. - - The *field transformer* hook receives a list of them. - + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The *field transformer* hook receives a list of them. + :attribute name: The name of the attribute. :attribute inherited: Whether or not that attribute has been inherited from a base class. @@ -2492,7 +2492,7 @@ class Attribute(object): .. versionadded:: 20.1.0 *on_setattr* .. versionchanged:: 20.2.0 *inherited* is not taken into account for equality checks and hashing anymore. - .. versionadded:: 21.1.0 *eq_key* and *order_key* + .. versionadded:: 21.1.0 *eq_key* and *order_key* For the full version history of the fields, see `attr.ib`. """ @@ -2503,9 +2503,9 @@ class Attribute(object): "validator", "repr", "eq", - "eq_key", + "eq_key", "order", - "order_key", + "order_key", "hash", "init", "metadata", @@ -2531,14 +2531,14 @@ class Attribute(object): converter=None, kw_only=False, eq=None, - eq_key=None, + eq_key=None, order=None, - order_key=None, + order_key=None, on_setattr=None, ): - eq, eq_key, order, order_key = _determine_attrib_eq_order( - cmp, eq_key or eq, order_key or order, True - ) + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) # Cache this descriptor here to speed things up later. bound_setattr = _obj_setattr.__get__(self, Attribute) @@ -2550,9 +2550,9 @@ class Attribute(object): bound_setattr("validator", validator) bound_setattr("repr", repr) bound_setattr("eq", eq) - bound_setattr("eq_key", eq_key) + bound_setattr("eq_key", eq_key) bound_setattr("order", order) - bound_setattr("order_key", order_key) + bound_setattr("order_key", order_key) bound_setattr("hash", hash) bound_setattr("init", init) bound_setattr("converter", converter) @@ -2612,17 +2612,17 @@ class Attribute(object): return self.eq and self.order - # Don't use attr.evolve since fields(Attribute) doesn't work - def evolve(self, **changes): + # Don't use attr.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): """ Copy *self* and apply *changes*. - - This works similarly to `attr.evolve` but that function does not work - with ``Attribute``. - - It is mainly meant to be used for `transform-fields`. - - .. versionadded:: 20.3.0 + + This works similarly to `attr.evolve` but that function does not work + with ``Attribute``. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 """ new = copy.copy(self) @@ -2699,9 +2699,9 @@ class _CountingAttr(object): "_default", "repr", "eq", - "eq_key", + "eq_key", "order", - "order_key", + "order_key", "hash", "init", "metadata", @@ -2722,9 +2722,9 @@ class _CountingAttr(object): init=True, kw_only=False, eq=True, - eq_key=None, + eq_key=None, order=False, - order_key=None, + order_key=None, inherited=False, on_setattr=None, ) @@ -2749,9 +2749,9 @@ class _CountingAttr(object): init=True, kw_only=False, eq=True, - eq_key=None, + eq_key=None, order=False, - order_key=None, + order_key=None, inherited=False, on_setattr=None, ), @@ -2763,7 +2763,7 @@ class _CountingAttr(object): default, validator, repr, - cmp, + cmp, hash, init, converter, @@ -2771,9 +2771,9 @@ class _CountingAttr(object): type, kw_only, eq, - eq_key, + eq_key, order, - order_key, + order_key, on_setattr, ): _CountingAttr.cls_counter += 1 @@ -2783,9 +2783,9 @@ class _CountingAttr(object): self.converter = converter self.repr = repr self.eq = eq - self.eq_key = eq_key + self.eq_key = eq_key self.order = order - self.order_key = order_key + self.order_key = order_key self.hash = hash self.init = init self.metadata = metadata @@ -2843,7 +2843,7 @@ class Factory(object): .. versionadded:: 17.1.0 *takes_self* """ - __slots__ = ("factory", "takes_self") + __slots__ = ("factory", "takes_self") def __init__(self, factory, takes_self=False): """ @@ -2853,39 +2853,39 @@ class Factory(object): self.factory = factory self.takes_self = takes_self - def __getstate__(self): - """ - Play nice with pickle. - """ - return tuple(getattr(self, name) for name in self.__slots__) - - def __setstate__(self, state): - """ - Play nice with pickle. - """ - for name, value in zip(self.__slots__, state): - setattr(self, name, value) - - -_f = [ - Attribute( - name=name, - default=NOTHING, - validator=None, - repr=True, - cmp=None, - eq=True, - order=False, - hash=True, - init=True, - inherited=False, - ) - for name in Factory.__slots__ -] - -Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) - - + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + def make_class(name, attrs, bases=(object,), **attributes_arguments): """ A quick way to create a new class called *name* with *attrs*. @@ -2918,20 +2918,20 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments): else: raise TypeError("attrs argument must be a dict or a list.") - pre_init = cls_dict.pop("__attrs_pre_init__", None) + pre_init = cls_dict.pop("__attrs_pre_init__", None) post_init = cls_dict.pop("__attrs_post_init__", None) - user_init = cls_dict.pop("__init__", None) - - body = {} - if pre_init is not None: - body["__attrs_pre_init__"] = pre_init - if post_init is not None: - body["__attrs_post_init__"] = post_init - if user_init is not None: - body["__init__"] = user_init - - type_ = new_class(name, bases, {}, lambda ns: ns.update(body)) - + user_init = cls_dict.pop("__init__", None) + + body = {} + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = new_class(name, bases, {}, lambda ns: ns.update(body)) + # For pickling to work, the __module__ variable needs to be set to the # frame where the class is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not @@ -2948,7 +2948,7 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments): ( attributes_arguments["eq"], attributes_arguments["order"], - ) = _determine_attrs_eq_order( + ) = _determine_attrs_eq_order( cmp, attributes_arguments.get("eq"), attributes_arguments.get("order"), @@ -3003,9 +3003,9 @@ def pipe(*converters): When called on a value, it runs all wrapped converters, returning the *last* value. - Type annotations will be inferred from the wrapped converters', if - they have any. - + Type annotations will be inferred from the wrapped converters', if + they have any. + :param callables converters: Arbitrary number of converters. .. versionadded:: 20.1.0 @@ -3017,36 +3017,36 @@ def pipe(*converters): return val - if not PY2: - if not converters: - # If the converter list is empty, pipe_converter is the identity. - A = typing.TypeVar("A") - pipe_converter.__annotations__ = {"val": A, "return": A} - else: - # Get parameter type. - sig = None - try: - sig = inspect.signature(converters[0]) - except (ValueError, TypeError): # inspect failed - pass - if sig: - params = list(sig.parameters.values()) - if ( - params - and params[0].annotation is not inspect.Parameter.empty - ): - pipe_converter.__annotations__["val"] = params[ - 0 - ].annotation - # Get return type. - sig = None - try: - sig = inspect.signature(converters[-1]) - except (ValueError, TypeError): # inspect failed - pass - if sig and sig.return_annotation is not inspect.Signature().empty: - pipe_converter.__annotations__[ - "return" - ] = sig.return_annotation - + if not PY2: + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = typing.TypeVar("A") + pipe_converter.__annotations__ = {"val": A, "return": A} + else: + # Get parameter type. + sig = None + try: + sig = inspect.signature(converters[0]) + except (ValueError, TypeError): # inspect failed + pass + if sig: + params = list(sig.parameters.values()) + if ( + params + and params[0].annotation is not inspect.Parameter.empty + ): + pipe_converter.__annotations__["val"] = params[ + 0 + ].annotation + # Get return type. + sig = None + try: + sig = inspect.signature(converters[-1]) + except (ValueError, TypeError): # inspect failed + pass + if sig and sig.return_annotation is not inspect.Signature().empty: + pipe_converter.__annotations__[ + "return" + ] = sig.return_annotation + return pipe_converter |