The attrs package allows defining namedtuple-like classes with no weird
behavior and no runtime performance cost.
This patch vendors in attrs 17.2.0.
- no-check-commit
| durin42 |
| hg-reviewers |
The attrs package allows defining namedtuple-like classes with no weird
behavior and no runtime performance cost.
This patch vendors in attrs 17.2.0.
| Automatic diff as part of commit; lint not applicable. |
| Automatic diff as part of commit; unit tests not applicable. |
| Path | Packages | |||
|---|---|---|---|---|
| A | M | mercurial/thirdparty/__init__.py | ||
| A | M | mercurial/thirdparty/attr/LICENSE.txt (21 lines) | ||
| A | M | mercurial/thirdparty/attr/__init__.py (71 lines) | ||
| A | M | mercurial/thirdparty/attr/_compat.py (90 lines) | ||
| A | M | mercurial/thirdparty/attr/_config.py (23 lines) | ||
| A | M | mercurial/thirdparty/attr/_funcs.py (212 lines) | ||
| A | M | mercurial/thirdparty/attr/_make.py (1059 lines) | ||
| A | M | mercurial/thirdparty/attr/converters.py (24 lines) | ||
| A | M | mercurial/thirdparty/attr/exceptions.py (39 lines) | ||
| A | M | mercurial/thirdparty/attr/filters.py (52 lines) | ||
| A | M | mercurial/thirdparty/attr/validators.py (166 lines) | ||
| M | setup.py (2 lines) |
| The MIT License (MIT) | |||||
| Copyright (c) 2015 Hynek Schlawack | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights | |||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| copies of the Software, and to permit persons to whom the Software is | |||||
| furnished to do so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in all | |||||
| copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| SOFTWARE. | |||||
| from __future__ import absolute_import, division, print_function | |||||
| from ._funcs import ( | |||||
| asdict, | |||||
| assoc, | |||||
| astuple, | |||||
| evolve, | |||||
| has, | |||||
| ) | |||||
| from ._make import ( | |||||
| Attribute, | |||||
| Factory, | |||||
| NOTHING, | |||||
| attr, | |||||
| attributes, | |||||
| fields, | |||||
| make_class, | |||||
| validate, | |||||
| ) | |||||
| from ._config import ( | |||||
| get_run_validators, | |||||
| set_run_validators, | |||||
| ) | |||||
| from . import exceptions | |||||
| from . import filters | |||||
| from . import converters | |||||
| from . import validators | |||||
| __version__ = "17.2.0" | |||||
| __title__ = "attrs" | |||||
| __description__ = "Classes Without Boilerplate" | |||||
| __uri__ = "http://www.attrs.org/" | |||||
| __doc__ = __description__ + " <" + __uri__ + ">" | |||||
| __author__ = "Hynek Schlawack" | |||||
| __email__ = "hs@ox.cx" | |||||
| __license__ = "MIT" | |||||
| __copyright__ = "Copyright (c) 2015 Hynek Schlawack" | |||||
| s = attrs = attributes | |||||
| ib = attrib = attr | |||||
| __all__ = [ | |||||
| "Attribute", | |||||
| "Factory", | |||||
| "NOTHING", | |||||
| "asdict", | |||||
| "assoc", | |||||
| "astuple", | |||||
| "attr", | |||||
| "attrib", | |||||
| "attributes", | |||||
| "attrs", | |||||
| "converters", | |||||
| "evolve", | |||||
| "exceptions", | |||||
| "fields", | |||||
| "filters", | |||||
| "get_run_validators", | |||||
| "has", | |||||
| "ib", | |||||
| "make_class", | |||||
| "s", | |||||
| "set_run_validators", | |||||
| "validate", | |||||
| "validators", | |||||
| ] | |||||
| from __future__ import absolute_import, division, print_function | |||||
| import sys | |||||
| import types | |||||
| PY2 = sys.version_info[0] == 2 | |||||
| if PY2: | |||||
| from UserDict import IterableUserDict | |||||
| # We 'bundle' isclass instead of using inspect as importing inspect is | |||||
| # fairly expensive (order of 10-15 ms for a modern machine in 2016) | |||||
| def isclass(klass): | |||||
| return isinstance(klass, (type, types.ClassType)) | |||||
| # TYPE is used in exceptions, repr(int) is different on Python 2 and 3. | |||||
| TYPE = "type" | |||||
| def iteritems(d): | |||||
| return d.iteritems() | |||||
| def iterkeys(d): | |||||
| return d.iterkeys() | |||||
| # Python 2 is bereft of a read-only dict proxy, so we make one! | |||||
| class ReadOnlyDict(IterableUserDict): | |||||
| """ | |||||
| Best-effort read-only dict wrapper. | |||||
| """ | |||||
| def __setitem__(self, key, val): | |||||
| # We gently pretend we're a Python 3 mappingproxy. | |||||
| raise TypeError("'mappingproxy' object does not support item " | |||||
| "assignment") | |||||
| def update(self, _): | |||||
| # We gently pretend we're a Python 3 mappingproxy. | |||||
| raise AttributeError("'mappingproxy' object has no attribute " | |||||
| "'update'") | |||||
| def __delitem__(self, _): | |||||
| # We gently pretend we're a Python 3 mappingproxy. | |||||
| raise TypeError("'mappingproxy' object does not support item " | |||||
| "deletion") | |||||
| def clear(self): | |||||
| # We gently pretend we're a Python 3 mappingproxy. | |||||
| raise AttributeError("'mappingproxy' object has no attribute " | |||||
| "'clear'") | |||||
| def pop(self, key, default=None): | |||||
| # We gently pretend we're a Python 3 mappingproxy. | |||||
| raise AttributeError("'mappingproxy' object has no attribute " | |||||
| "'pop'") | |||||
| def popitem(self): | |||||
| # We gently pretend we're a Python 3 mappingproxy. | |||||
| raise AttributeError("'mappingproxy' object has no attribute " | |||||
| "'popitem'") | |||||
| def setdefault(self, key, default=None): | |||||
| # We gently pretend we're a Python 3 mappingproxy. | |||||
| raise AttributeError("'mappingproxy' object has no attribute " | |||||
| "'setdefault'") | |||||
| def __repr__(self): | |||||
| # Override to be identical to the Python 3 version. | |||||
| return "mappingproxy(" + repr(self.data) + ")" | |||||
| def metadata_proxy(d): | |||||
| res = ReadOnlyDict() | |||||
| res.data.update(d) # We blocked update, so we have to do it like this. | |||||
| return res | |||||
| else: | |||||
| def isclass(klass): | |||||
| return isinstance(klass, type) | |||||
| TYPE = "class" | |||||
| def iteritems(d): | |||||
| return d.items() | |||||
| def iterkeys(d): | |||||
| return d.keys() | |||||
| def metadata_proxy(d): | |||||
| return types.MappingProxyType(dict(d)) | |||||
| from __future__ import absolute_import, division, print_function | |||||
| __all__ = ["set_run_validators", "get_run_validators"] | |||||
| _run_validators = True | |||||
| def set_run_validators(run): | |||||
| """ | |||||
| Set whether or not validators are run. By default, they are run. | |||||
| """ | |||||
| if not isinstance(run, bool): | |||||
| raise TypeError("'run' must be bool.") | |||||
| global _run_validators | |||||
| _run_validators = run | |||||
| def get_run_validators(): | |||||
| """ | |||||
| Return whether or not validators are run. | |||||
| """ | |||||
| return _run_validators | |||||
| from __future__ import absolute_import, division, print_function | |||||
| import copy | |||||
| from ._compat import iteritems | |||||
| from ._make import NOTHING, fields, _obj_setattr | |||||
| from .exceptions import AttrsAttributeNotFoundError | |||||
| def asdict(inst, recurse=True, filter=None, dict_factory=dict, | |||||
| retain_collection_types=False): | |||||
| """ | |||||
| Return the ``attrs`` attribute values of *inst* as a dict. | |||||
| Optionally recurse into other ``attrs``-decorated classes. | |||||
| :param inst: Instance of an ``attrs``-decorated class. | |||||
| :param bool recurse: Recurse into classes that are also | |||||
| ``attrs``-decorated. | |||||
| :param callable filter: A callable whose return code deteremines whether an | |||||
| attribute or element is included (``True``) or dropped (``False``). Is | |||||
| called with the :class:`attr.Attribute` as the first argument and the | |||||
| value as the second argument. | |||||
| :param callable dict_factory: A callable to produce dictionaries from. For | |||||
| example, to produce ordered dictionaries instead of normal Python | |||||
| dictionaries, pass in ``collections.OrderedDict``. | |||||
| :param bool retain_collection_types: Do not convert to ``list`` when | |||||
| encountering an attribute whose type is ``tuple`` or ``set``. Only | |||||
| meaningful if ``recurse`` is ``True``. | |||||
| :rtype: return type of *dict_factory* | |||||
| :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |||||
| class. | |||||
| .. versionadded:: 16.0.0 *dict_factory* | |||||
| .. versionadded:: 16.1.0 *retain_collection_types* | |||||
| """ | |||||
| attrs = fields(inst.__class__) | |||||
| rv = dict_factory() | |||||
| for a in attrs: | |||||
| v = getattr(inst, a.name) | |||||
| if filter is not None and not filter(a, v): | |||||
| continue | |||||
| if recurse is True: | |||||
| if has(v.__class__): | |||||
| rv[a.name] = asdict(v, recurse=True, filter=filter, | |||||
| dict_factory=dict_factory) | |||||
| elif isinstance(v, (tuple, list, set)): | |||||
| cf = v.__class__ if retain_collection_types is True else list | |||||
| rv[a.name] = cf([ | |||||
| asdict(i, recurse=True, filter=filter, | |||||
| dict_factory=dict_factory) | |||||
| if has(i.__class__) else i | |||||
| for i in v | |||||
| ]) | |||||
| elif isinstance(v, dict): | |||||
| df = dict_factory | |||||
| rv[a.name] = df(( | |||||
| asdict(kk, dict_factory=df) if has(kk.__class__) else kk, | |||||
| asdict(vv, dict_factory=df) if has(vv.__class__) else vv) | |||||
| for kk, vv in iteritems(v)) | |||||
| else: | |||||
| rv[a.name] = v | |||||
| else: | |||||
| rv[a.name] = v | |||||
| return rv | |||||
| def astuple(inst, recurse=True, filter=None, tuple_factory=tuple, | |||||
| retain_collection_types=False): | |||||
| """ | |||||
| Return the ``attrs`` attribute values of *inst* as a tuple. | |||||
| Optionally recurse into other ``attrs``-decorated classes. | |||||
| :param inst: Instance of an ``attrs``-decorated class. | |||||
| :param bool recurse: Recurse into classes that are also | |||||
| ``attrs``-decorated. | |||||
| :param callable filter: A callable whose return code determines whether an | |||||
| attribute or element is included (``True``) or dropped (``False``). Is | |||||
| called with the :class:`attr.Attribute` as the first argument and the | |||||
| value as the second argument. | |||||
| :param callable tuple_factory: A callable to produce tuples from. For | |||||
| example, to produce lists instead of tuples. | |||||
| :param bool retain_collection_types: Do not convert to ``list`` | |||||
| or ``dict`` when encountering an attribute which type is | |||||
| ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is | |||||
| ``True``. | |||||
| :rtype: return type of *tuple_factory* | |||||
| :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |||||
| class. | |||||
| .. versionadded:: 16.2.0 | |||||
| """ | |||||
| attrs = fields(inst.__class__) | |||||
| rv = [] | |||||
| retain = retain_collection_types # Very long. :/ | |||||
| for a in attrs: | |||||
| v = getattr(inst, a.name) | |||||
| if filter is not None and not filter(a, v): | |||||
| continue | |||||
| if recurse is True: | |||||
| if has(v.__class__): | |||||
| rv.append(astuple(v, recurse=True, filter=filter, | |||||
| tuple_factory=tuple_factory, | |||||
| retain_collection_types=retain)) | |||||
| elif isinstance(v, (tuple, list, set)): | |||||
| cf = v.__class__ if retain is True else list | |||||
| rv.append(cf([ | |||||
| astuple(j, recurse=True, filter=filter, | |||||
| tuple_factory=tuple_factory, | |||||
| retain_collection_types=retain) | |||||
| if has(j.__class__) else j | |||||
| for j in v | |||||
| ])) | |||||
| elif isinstance(v, dict): | |||||
| df = v.__class__ if retain is True else dict | |||||
| rv.append(df( | |||||
| ( | |||||
| astuple( | |||||
| kk, | |||||
| tuple_factory=tuple_factory, | |||||
| retain_collection_types=retain | |||||
| ) if has(kk.__class__) else kk, | |||||
| astuple( | |||||
| vv, | |||||
| tuple_factory=tuple_factory, | |||||
| retain_collection_types=retain | |||||
| ) if has(vv.__class__) else vv | |||||
| ) | |||||
| for kk, vv in iteritems(v))) | |||||
| else: | |||||
| rv.append(v) | |||||
| else: | |||||
| rv.append(v) | |||||
| return rv if tuple_factory is list else tuple_factory(rv) | |||||
| def has(cls): | |||||
| """ | |||||
| Check whether *cls* is a class with ``attrs`` attributes. | |||||
| :param type cls: Class to introspect. | |||||
| :raise TypeError: If *cls* is not a class. | |||||
| :rtype: :class:`bool` | |||||
| """ | |||||
| return getattr(cls, "__attrs_attrs__", None) is not None | |||||
| def assoc(inst, **changes): | |||||
| """ | |||||
| Copy *inst* and apply *changes*. | |||||
| :param inst: Instance of a class with ``attrs`` attributes. | |||||
| :param changes: Keyword changes in the new copy. | |||||
| :return: A copy of inst with *changes* incorporated. | |||||
| :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't | |||||
| be found on *cls*. | |||||
| :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |||||
| class. | |||||
| .. deprecated:: 17.1.0 | |||||
| Use :func:`evolve` instead. | |||||
| """ | |||||
| import warnings | |||||
| warnings.warn("assoc is deprecated and will be removed after 2018/01.", | |||||
| DeprecationWarning) | |||||
| new = copy.copy(inst) | |||||
| attrs = fields(inst.__class__) | |||||
| for k, v in iteritems(changes): | |||||
| a = getattr(attrs, k, NOTHING) | |||||
| if a is NOTHING: | |||||
| raise AttrsAttributeNotFoundError( | |||||
| "{k} is not an attrs attribute on {cl}." | |||||
| .format(k=k, cl=new.__class__) | |||||
| ) | |||||
| _obj_setattr(new, k, v) | |||||
| return new | |||||
| def evolve(inst, **changes): | |||||
| """ | |||||
| Create a new instance, based on *inst* with *changes* applied. | |||||
| :param inst: Instance of a class with ``attrs`` attributes. | |||||
| :param changes: Keyword changes in the new copy. | |||||
| :return: A copy of inst with *changes* incorporated. | |||||
| :raise TypeError: If *attr_name* couldn't be found in the class | |||||
| ``__init__``. | |||||
| :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |||||
| class. | |||||
| .. versionadded:: 17.1.0 | |||||
| """ | |||||
| cls = inst.__class__ | |||||
| attrs = fields(cls) | |||||
| for a in attrs: | |||||
| if not a.init: | |||||
| continue | |||||
| attr_name = a.name # To deal with private attributes. | |||||
| init_name = attr_name if attr_name[0] != "_" else attr_name[1:] | |||||
| if init_name not in changes: | |||||
| changes[init_name] = getattr(inst, attr_name) | |||||
| return cls(**changes) | |||||
| from __future__ import absolute_import, division, print_function | |||||
| import hashlib | |||||
| import linecache | |||||
| from operator import itemgetter | |||||
| from . import _config | |||||
| from ._compat import PY2, iteritems, isclass, iterkeys, metadata_proxy | |||||
| from .exceptions import ( | |||||
| DefaultAlreadySetError, | |||||
| FrozenInstanceError, | |||||
| NotAnAttrsClassError, | |||||
| ) | |||||
| # This is used at least twice, so cache it here. | |||||
| _obj_setattr = object.__setattr__ | |||||
| _init_convert_pat = "__attr_convert_{}" | |||||
| _init_factory_pat = "__attr_factory_{}" | |||||
| _tuple_property_pat = " {attr_name} = property(itemgetter({index}))" | |||||
| _empty_metadata_singleton = metadata_proxy({}) | |||||
| class _Nothing(object): | |||||
| """ | |||||
| Sentinel class to indicate the lack of a value when ``None`` is ambiguous. | |||||
| All instances of `_Nothing` are equal. | |||||
| """ | |||||
| def __copy__(self): | |||||
| return self | |||||
| def __deepcopy__(self, _): | |||||
| return self | |||||
| def __eq__(self, other): | |||||
| return other.__class__ == _Nothing | |||||
| def __ne__(self, other): | |||||
| return not self == other | |||||
| def __repr__(self): | |||||
| return "NOTHING" | |||||
| def __hash__(self): | |||||
| return 0xdeadbeef | |||||
| NOTHING = _Nothing() | |||||
| """ | |||||
| Sentinel to indicate the lack of a value when ``None`` is ambiguous. | |||||
| """ | |||||
| def attr(default=NOTHING, validator=None, | |||||
| repr=True, cmp=True, hash=None, init=True, | |||||
| convert=None, metadata={}): | |||||
| """ | |||||
| Create a new attribute on a class. | |||||
| .. warning:: | |||||
| Does *not* do anything unless the class is also decorated with | |||||
| :func:`attr.s`! | |||||
| :param default: A value that is used if an ``attrs``-generated ``__init__`` | |||||
| is used and no value is passed while instantiating or the attribute is | |||||
| excluded using ``init=False``. | |||||
| If the value is an instance of :class:`Factory`, its callable will be | |||||
| used to construct a new value (useful for mutable datatypes like lists | |||||
| or dicts). | |||||
| If a default is not set (or set manually to ``attr.NOTHING``), a value | |||||
| *must* be supplied when instantiating; otherwise a :exc:`TypeError` | |||||
| will be raised. | |||||
| The default can also be set using decorator notation as shown below. | |||||
| :type default: Any value. | |||||
| :param validator: :func:`callable` that is called by ``attrs``-generated | |||||
| ``__init__`` methods after the instance has been initialized. They | |||||
| receive the initialized instance, the :class:`Attribute`, and the | |||||
| passed value. | |||||
| The return value is *not* inspected so the validator has to throw an | |||||
| exception itself. | |||||
| If a ``list`` is passed, its items are treated as validators and must | |||||
| all pass. | |||||
| Validators can be globally disabled and re-enabled using | |||||
| :func:`get_run_validators`. | |||||
| The validator can also be set using decorator notation as shown below. | |||||
| :type validator: ``callable`` or a ``list`` of ``callable``\ s. | |||||
| :param bool repr: Include this attribute in the generated ``__repr__`` | |||||
| method. | |||||
| :param bool cmp: Include this attribute in the generated comparison methods | |||||
| (``__eq__`` et al). | |||||
| :param hash: Include this attribute in the generated ``__hash__`` | |||||
| method. If ``None`` (default), mirror *cmp*'s value. This is the | |||||
| correct behavior according the Python spec. Setting this value to | |||||
| anything else than ``None`` is *discouraged*. | |||||
| :type hash: ``bool`` or ``None`` | |||||
| :param bool init: Include this attribute in the generated ``__init__`` | |||||
| method. It is possible to set this to ``False`` and set a default | |||||
| value. In that case this attributed is unconditionally initialized | |||||
| with the specified default value or factory. | |||||
| :param callable convert: :func:`callable` that is called by | |||||
| ``attrs``-generated ``__init__`` methods to convert attribute's value | |||||
| to the desired format. It is given the passed-in value, and the | |||||
| returned value will be used as the new value of the attribute. The | |||||
| value is converted before being passed to the validator, if any. | |||||
| :param metadata: An arbitrary mapping, to be used by third-party | |||||
| components. See :ref:`extending_metadata`. | |||||
| .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. | |||||
| .. versionchanged:: 17.1.0 | |||||
| *hash* is ``None`` and therefore mirrors *cmp* by default . | |||||
| """ | |||||
| if hash is not None and hash is not True and hash is not False: | |||||
| raise TypeError( | |||||
| "Invalid value for hash. Must be True, False, or None." | |||||
| ) | |||||
| return _CountingAttr( | |||||
| default=default, | |||||
| validator=validator, | |||||
| repr=repr, | |||||
| cmp=cmp, | |||||
| hash=hash, | |||||
| init=init, | |||||
| convert=convert, | |||||
| metadata=metadata, | |||||
| ) | |||||
| def _make_attr_tuple_class(cls_name, attr_names): | |||||
| """ | |||||
| Create a tuple subclass to hold `Attribute`s for an `attrs` class. | |||||
| The subclass is a bare tuple with properties for names. | |||||
| class MyClassAttributes(tuple): | |||||
| __slots__ = () | |||||
| x = property(itemgetter(0)) | |||||
| """ | |||||
| attr_class_name = "{}Attributes".format(cls_name) | |||||
| attr_class_template = [ | |||||
| "class {}(tuple):".format(attr_class_name), | |||||
| " __slots__ = ()", | |||||
| ] | |||||
| if attr_names: | |||||
| for i, attr_name in enumerate(attr_names): | |||||
| attr_class_template.append(_tuple_property_pat.format( | |||||
| index=i, | |||||
| attr_name=attr_name, | |||||
| )) | |||||
| else: | |||||
| attr_class_template.append(" pass") | |||||
| globs = {"itemgetter": itemgetter} | |||||
| eval(compile("\n".join(attr_class_template), "", "exec"), globs) | |||||
| return globs[attr_class_name] | |||||
| def _transform_attrs(cls, these): | |||||
| """ | |||||
| Transforms all `_CountingAttr`s on a class into `Attribute`s and saves the | |||||
| list in `__attrs_attrs__`. | |||||
| If *these* is passed, use that and don't look for them on the class. | |||||
| """ | |||||
| super_cls = [] | |||||
| for c in reversed(cls.__mro__[1:-1]): | |||||
| sub_attrs = getattr(c, "__attrs_attrs__", None) | |||||
| if sub_attrs is not None: | |||||
| super_cls.extend(a for a in sub_attrs if a not in super_cls) | |||||
| if these is None: | |||||
| ca_list = [(name, attr) | |||||
| for name, attr | |||||
| in cls.__dict__.items() | |||||
| if isinstance(attr, _CountingAttr)] | |||||
| else: | |||||
| ca_list = [(name, ca) | |||||
| for name, ca | |||||
| in iteritems(these)] | |||||
| non_super_attrs = [ | |||||
| Attribute.from_counting_attr(name=attr_name, ca=ca) | |||||
| for attr_name, ca | |||||
| in sorted(ca_list, key=lambda e: e[1].counter) | |||||
| ] | |||||
| attr_names = [a.name for a in super_cls + non_super_attrs] | |||||
| AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) | |||||
| cls.__attrs_attrs__ = AttrsClass(super_cls + [ | |||||
| Attribute.from_counting_attr(name=attr_name, ca=ca) | |||||
| for attr_name, ca | |||||
| in sorted(ca_list, key=lambda e: e[1].counter) | |||||
| ]) | |||||
| had_default = False | |||||
| for a in cls.__attrs_attrs__: | |||||
| if these is None and a not in super_cls: | |||||
| setattr(cls, a.name, a) | |||||
| if had_default is True and a.default is NOTHING and a.init is True: | |||||
| raise ValueError( | |||||
| "No mandatory attributes allowed after an attribute with a " | |||||
| "default value or factory. Attribute in question: {a!r}" | |||||
| .format(a=a) | |||||
| ) | |||||
| elif had_default is False and \ | |||||
| a.default is not NOTHING and \ | |||||
| a.init is not False: | |||||
| had_default = True | |||||
| 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__. | |||||
| """ | |||||
| raise FrozenInstanceError() | |||||
| def attributes(maybe_cls=None, these=None, repr_ns=None, | |||||
| repr=True, cmp=True, hash=None, init=True, | |||||
| slots=False, frozen=False, str=False): | |||||
| r""" | |||||
| A class decorator that adds `dunder | |||||
| <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the | |||||
| specified attributes using :func:`attr.ib` or the *these* argument. | |||||
| :param these: A dictionary of name to :func:`attr.ib` mappings. This is | |||||
| useful to avoid the definition of your attributes within the class body | |||||
| because you can't (e.g. if you want to add ``__repr__`` methods to | |||||
| Django models) or don't want to. | |||||
| If *these* is not ``None``, ``attrs`` will *not* search the class body | |||||
| for attributes. | |||||
| :type these: :class:`dict` of :class:`str` to :func:`attr.ib` | |||||
| :param str repr_ns: When using nested classes, there's no way in Python 2 | |||||
| to automatically detect that. Therefore it's possible to set the | |||||
| namespace explicitly for a more meaningful ``repr`` output. | |||||
| :param bool repr: Create a ``__repr__`` method with a human readable | |||||
| represantation of ``attrs`` attributes.. | |||||
| :param bool str: Create a ``__str__`` method that is identical to | |||||
| ``__repr__``. This is usually not necessary except for | |||||
| :class:`Exception`\ s. | |||||
| :param bool cmp: Create ``__eq__``, ``__ne__``, ``__lt__``, ``__le__``, | |||||
| ``__gt__``, and ``__ge__`` methods that compare the class as if it were | |||||
| a tuple of its ``attrs`` attributes. But the attributes are *only* | |||||
| compared, if the type of both classes is *identical*! | |||||
| :param hash: If ``None`` (default), the ``__hash__`` method is generated | |||||
| according how *cmp* and *frozen* are set. | |||||
| 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you. | |||||
| 2. If *cmp* is True and *frozen* is False, ``__hash__`` will be set to | |||||
| None, marking it unhashable (which it is). | |||||
| 3. If *cmp* is False, ``__hash__`` will be left untouched meaning the | |||||
| ``__hash__`` method of the superclass will be used (if superclass is | |||||
| ``object``, this means it will fall back to id-based hashing.). | |||||
| Although not recommended, you can decide for yourself and force | |||||
| ``attrs`` to create one (e.g. if the class is immutable even though you | |||||
| didn't freeze it programmatically) by passing ``True`` or not. Both of | |||||
| these cases are rather special and should be used carefully. | |||||
| See the `Python documentation \ | |||||
| <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`_ | |||||
| and the `GitHub issue that led to the default behavior \ | |||||
| <https://github.com/python-attrs/attrs/issues/136>`_ for more details. | |||||
| :type hash: ``bool`` or ``None`` | |||||
| :param bool init: Create a ``__init__`` method that initialiazes the | |||||
| ``attrs`` attributes. Leading underscores are stripped for the | |||||
| argument name. If a ``__attrs_post_init__`` method exists on the | |||||
| class, it will be called after the class is fully initialized. | |||||
| :param bool slots: Create a slots_-style class that's more | |||||
| memory-efficient. See :ref:`slots` for further ramifications. | |||||
| :param bool frozen: Make instances immutable after initialization. If | |||||
| someone attempts to modify a frozen instance, | |||||
| :exc:`attr.exceptions.FrozenInstanceError` is raised. | |||||
| Please note: | |||||
| 1. This is achieved by installing a custom ``__setattr__`` method | |||||
| on your class so you can't implement an own one. | |||||
| 2. True immutability is impossible in Python. | |||||
| 3. This *does* have a minor a runtime performance :ref:`impact | |||||
| <how-frozen>` when initializing new instances. In other words: | |||||
| ``__init__`` is slightly slower with ``frozen=True``. | |||||
| 4. If a class is frozen, you cannot modify ``self`` in | |||||
| ``__attrs_post_init__`` or a self-written ``__init__``. You can | |||||
| circumvent that limitation by using | |||||
| ``object.__setattr__(self, "attribute_name", value)``. | |||||
| .. _slots: https://docs.python.org/3.5/reference/datamodel.html#slots | |||||
| .. versionadded:: 16.0.0 *slots* | |||||
| .. versionadded:: 16.1.0 *frozen* | |||||
| .. versionadded:: 16.3.0 *str*, and support for ``__attrs_post_init__``. | |||||
| .. versionchanged:: | |||||
| 17.1.0 *hash* supports ``None`` as value which is also the default | |||||
| now. | |||||
| """ | |||||
| def wrap(cls): | |||||
| if getattr(cls, "__class__", None) is None: | |||||
| raise TypeError("attrs only works with new-style classes.") | |||||
| if repr is False and str is True: | |||||
| raise ValueError( | |||||
| "__str__ can only be generated if a __repr__ exists." | |||||
| ) | |||||
| if slots: | |||||
| # Only need this later if we're using slots. | |||||
| if these is None: | |||||
| ca_list = [name | |||||
| for name, attr | |||||
| in cls.__dict__.items() | |||||
| if isinstance(attr, _CountingAttr)] | |||||
| else: | |||||
| ca_list = list(iterkeys(these)) | |||||
| _transform_attrs(cls, these) | |||||
| # Can't just re-use frozen name because Python's scoping. :( | |||||
| # Can't compare function objects because Python 2 is terrible. :( | |||||
| effectively_frozen = _has_frozen_superclass(cls) or frozen | |||||
| if repr is True: | |||||
| cls = _add_repr(cls, ns=repr_ns) | |||||
| if str is True: | |||||
| cls.__str__ = cls.__repr__ | |||||
| if cmp is True: | |||||
| cls = _add_cmp(cls) | |||||
| if hash is not True and hash is not False and hash is not None: | |||||
| raise TypeError( | |||||
| "Invalid value for hash. Must be True, False, or None." | |||||
| ) | |||||
| elif hash is False or (hash is None and cmp is False): | |||||
| pass | |||||
| elif hash is True or (hash is None and cmp is True and frozen is True): | |||||
| cls = _add_hash(cls) | |||||
| else: | |||||
| cls.__hash__ = None | |||||
| if init is True: | |||||
| cls = _add_init(cls, effectively_frozen) | |||||
| if effectively_frozen is True: | |||||
| cls.__setattr__ = _frozen_setattrs | |||||
| cls.__delattr__ = _frozen_delattrs | |||||
| if slots is True: | |||||
| # slots and frozen require __getstate__/__setstate__ to work | |||||
| cls = _add_pickle(cls) | |||||
| if slots is True: | |||||
| cls_dict = dict(cls.__dict__) | |||||
| cls_dict["__slots__"] = tuple(ca_list) | |||||
| for ca_name in ca_list: | |||||
| # It might not actually be in there, e.g. if using 'these'. | |||||
| cls_dict.pop(ca_name, None) | |||||
| cls_dict.pop("__dict__", None) | |||||
| qualname = getattr(cls, "__qualname__", None) | |||||
| cls = type(cls)(cls.__name__, cls.__bases__, cls_dict) | |||||
| if qualname is not None: | |||||
| cls.__qualname__ = qualname | |||||
| return cls | |||||
| # attrs_or class type depends on the usage of the decorator. It's a class | |||||
| # if it's used as `@attributes` but ``None`` if used # as `@attributes()`. | |||||
| if maybe_cls is None: | |||||
| return wrap | |||||
| else: | |||||
| return wrap(maybe_cls) | |||||
| if PY2: | |||||
| def _has_frozen_superclass(cls): | |||||
| """ | |||||
| Check whether *cls* has a frozen ancestor by looking at its | |||||
| __setattr__. | |||||
| """ | |||||
| return ( | |||||
| getattr( | |||||
| cls.__setattr__, "__module__", None | |||||
| ) == _frozen_setattrs.__module__ and | |||||
| cls.__setattr__.__name__ == _frozen_setattrs.__name__ | |||||
| ) | |||||
| else: | |||||
| def _has_frozen_superclass(cls): | |||||
| """ | |||||
| Check whether *cls* has a frozen ancestor by looking at its | |||||
| __setattr__. | |||||
| """ | |||||
| return cls.__setattr__ == _frozen_setattrs | |||||
| def _attrs_to_tuple(obj, attrs): | |||||
| """ | |||||
| Create a tuple of all values of *obj*'s *attrs*. | |||||
| """ | |||||
| return tuple(getattr(obj, a.name) for a in attrs) | |||||
| def _add_hash(cls, attrs=None): | |||||
| """ | |||||
| Add a hash method to *cls*. | |||||
| """ | |||||
| if attrs is None: | |||||
| attrs = [a | |||||
| for a in cls.__attrs_attrs__ | |||||
| if a.hash is True or (a.hash is None and a.cmp is True)] | |||||
| def hash_(self): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| return hash(_attrs_to_tuple(self, attrs)) | |||||
| cls.__hash__ = hash_ | |||||
| return cls | |||||
| def _add_cmp(cls, attrs=None): | |||||
| """ | |||||
| Add comparison methods to *cls*. | |||||
| """ | |||||
| if attrs is None: | |||||
| attrs = [a for a in cls.__attrs_attrs__ if a.cmp] | |||||
| def attrs_to_tuple(obj): | |||||
| """ | |||||
| Save us some typing. | |||||
| """ | |||||
| return _attrs_to_tuple(obj, attrs) | |||||
| def eq(self, other): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| if other.__class__ is self.__class__: | |||||
| return attrs_to_tuple(self) == attrs_to_tuple(other) | |||||
| else: | |||||
| return NotImplemented | |||||
| def ne(self, other): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| result = eq(self, other) | |||||
| if result is NotImplemented: | |||||
| return NotImplemented | |||||
| else: | |||||
| return not result | |||||
| def lt(self, other): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| if isinstance(other, self.__class__): | |||||
| return attrs_to_tuple(self) < attrs_to_tuple(other) | |||||
| else: | |||||
| return NotImplemented | |||||
| def le(self, other): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| if isinstance(other, self.__class__): | |||||
| return attrs_to_tuple(self) <= attrs_to_tuple(other) | |||||
| else: | |||||
| return NotImplemented | |||||
| def gt(self, other): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| if isinstance(other, self.__class__): | |||||
| return attrs_to_tuple(self) > attrs_to_tuple(other) | |||||
| else: | |||||
| return NotImplemented | |||||
| def ge(self, other): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| if isinstance(other, self.__class__): | |||||
| return attrs_to_tuple(self) >= attrs_to_tuple(other) | |||||
| else: | |||||
| return NotImplemented | |||||
| cls.__eq__ = eq | |||||
| cls.__ne__ = ne | |||||
| cls.__lt__ = lt | |||||
| cls.__le__ = le | |||||
| cls.__gt__ = gt | |||||
| cls.__ge__ = ge | |||||
| return cls | |||||
| def _add_repr(cls, ns=None, attrs=None): | |||||
| """ | |||||
| Add a repr method to *cls*. | |||||
| """ | |||||
| if attrs is None: | |||||
| attrs = [a for a in cls.__attrs_attrs__ if a.repr] | |||||
| def repr_(self): | |||||
| """ | |||||
| Automatically created by attrs. | |||||
| """ | |||||
| real_cls = self.__class__ | |||||
| if ns is None: | |||||
| qualname = getattr(real_cls, "__qualname__", None) | |||||
| if qualname is not None: | |||||
| class_name = qualname.rsplit(">.", 1)[-1] | |||||
| else: | |||||
| class_name = real_cls.__name__ | |||||
| else: | |||||
| class_name = ns + "." + real_cls.__name__ | |||||
| return "{0}({1})".format( | |||||
| class_name, | |||||
| ", ".join(a.name + "=" + repr(getattr(self, a.name)) | |||||
| for a in attrs) | |||||
| ) | |||||
| cls.__repr__ = repr_ | |||||
| return cls | |||||
| def _add_init(cls, frozen): | |||||
| """ | |||||
| Add a __init__ method to *cls*. If *frozen* is True, make it immutable. | |||||
| """ | |||||
| attrs = [a for a in cls.__attrs_attrs__ | |||||
| if a.init or a.default is not NOTHING] | |||||
| # We cache the generated init methods for the same kinds of attributes. | |||||
| sha1 = hashlib.sha1() | |||||
| sha1.update(repr(attrs).encode("utf-8")) | |||||
| unique_filename = "<attrs generated init {0}>".format( | |||||
| sha1.hexdigest() | |||||
| ) | |||||
| script, globs = _attrs_to_script( | |||||
| attrs, | |||||
| frozen, | |||||
| getattr(cls, "__attrs_post_init__", False), | |||||
| ) | |||||
| locs = {} | |||||
| bytecode = compile(script, unique_filename, "exec") | |||||
| attr_dict = dict((a.name, a) for a in attrs) | |||||
| globs.update({ | |||||
| "NOTHING": NOTHING, | |||||
| "attr_dict": attr_dict, | |||||
| }) | |||||
| if frozen is True: | |||||
| # Save the lookup overhead in __init__ if we need to circumvent | |||||
| # immutability. | |||||
| globs["_cached_setattr"] = _obj_setattr | |||||
| eval(bytecode, globs, locs) | |||||
| init = locs["__init__"] | |||||
| # In order of debuggers like PDB being able to step through the code, | |||||
| # we add a fake linecache entry. | |||||
| linecache.cache[unique_filename] = ( | |||||
| len(script), | |||||
| None, | |||||
| script.splitlines(True), | |||||
| unique_filename | |||||
| ) | |||||
| cls.__init__ = init | |||||
| return cls | |||||
| def _add_pickle(cls): | |||||
| """ | |||||
| Add pickle helpers, needed for frozen and slotted classes | |||||
| """ | |||||
| def _slots_getstate__(obj): | |||||
| """ | |||||
| Play nice with pickle. | |||||
| """ | |||||
| return tuple(getattr(obj, a.name) for a in fields(obj.__class__)) | |||||
| def _slots_setstate__(obj, state): | |||||
| """ | |||||
| Play nice with pickle. | |||||
| """ | |||||
| __bound_setattr = _obj_setattr.__get__(obj, Attribute) | |||||
| for a, value in zip(fields(obj.__class__), state): | |||||
| __bound_setattr(a.name, value) | |||||
| cls.__getstate__ = _slots_getstate__ | |||||
| cls.__setstate__ = _slots_setstate__ | |||||
| return cls | |||||
| def fields(cls): | |||||
| """ | |||||
| Returns the tuple of ``attrs`` attributes for a class. | |||||
| The tuple also allows accessing the fields by their names (see below for | |||||
| examples). | |||||
| :param type cls: Class to introspect. | |||||
| :raise TypeError: If *cls* is not a class. | |||||
| :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` | |||||
| class. | |||||
| :rtype: tuple (with name accesors) of :class:`attr.Attribute` | |||||
| .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields | |||||
| by name. | |||||
| """ | |||||
| if not isclass(cls): | |||||
| raise TypeError("Passed object must be a class.") | |||||
| attrs = getattr(cls, "__attrs_attrs__", None) | |||||
| if attrs is None: | |||||
| raise NotAnAttrsClassError( | |||||
| "{cls!r} is not an attrs-decorated class.".format(cls=cls) | |||||
| ) | |||||
| return attrs | |||||
| def validate(inst): | |||||
| """ | |||||
| Validate all attributes on *inst* that have a validator. | |||||
| Leaves all exceptions through. | |||||
| :param inst: Instance of a class with ``attrs`` attributes. | |||||
| """ | |||||
| if _config._run_validators is False: | |||||
| return | |||||
| for a in fields(inst.__class__): | |||||
| v = a.validator | |||||
| if v is not None: | |||||
| v(inst, a, getattr(inst, a.name)) | |||||
| def _attrs_to_script(attrs, frozen, post_init): | |||||
| """ | |||||
| Return a script of an initializer for *attrs* and a dict of globals. | |||||
| The globals are expected by the generated script. | |||||
| If *frozen* is True, we cannot set the attributes directly so we use | |||||
| a cached ``object.__setattr__``. | |||||
| """ | |||||
| lines = [] | |||||
| if frozen is True: | |||||
| lines.append( | |||||
| # Circumvent the __setattr__ descriptor to save one lookup per | |||||
| # assignment. | |||||
| "_setattr = _cached_setattr.__get__(self, self.__class__)" | |||||
| ) | |||||
| def fmt_setter(attr_name, value_var): | |||||
| return "_setattr('%(attr_name)s', %(value_var)s)" % { | |||||
| "attr_name": attr_name, | |||||
| "value_var": value_var, | |||||
| } | |||||
| def fmt_setter_with_converter(attr_name, value_var): | |||||
| conv_name = _init_convert_pat.format(attr_name) | |||||
| return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % { | |||||
| "attr_name": attr_name, | |||||
| "value_var": value_var, | |||||
| "conv": conv_name, | |||||
| } | |||||
| else: | |||||
| def fmt_setter(attr_name, value): | |||||
| return "self.%(attr_name)s = %(value)s" % { | |||||
| "attr_name": attr_name, | |||||
| "value": value, | |||||
| } | |||||
| def fmt_setter_with_converter(attr_name, value_var): | |||||
| conv_name = _init_convert_pat.format(attr_name) | |||||
| return "self.%(attr_name)s = %(conv)s(%(value_var)s)" % { | |||||
| "attr_name": attr_name, | |||||
| "value_var": value_var, | |||||
| "conv": conv_name, | |||||
| } | |||||
| args = [] | |||||
| attrs_to_validate = [] | |||||
| # This is a dictionary of names to validator and converter callables. | |||||
| # Injecting this into __init__ globals lets us avoid lookups. | |||||
| names_for_globals = {} | |||||
| for a in attrs: | |||||
| if a.validator: | |||||
| attrs_to_validate.append(a) | |||||
| attr_name = a.name | |||||
| arg_name = a.name.lstrip("_") | |||||
| has_factory = isinstance(a.default, Factory) | |||||
| if has_factory and a.default.takes_self: | |||||
| maybe_self = "self" | |||||
| else: | |||||
| maybe_self = "" | |||||
| if a.init is False: | |||||
| if has_factory: | |||||
| init_factory_name = _init_factory_pat.format(a.name) | |||||
| if a.convert is not None: | |||||
| lines.append(fmt_setter_with_converter( | |||||
| attr_name, | |||||
| init_factory_name + "({0})".format(maybe_self))) | |||||
| conv_name = _init_convert_pat.format(a.name) | |||||
| names_for_globals[conv_name] = a.convert | |||||
| else: | |||||
| lines.append(fmt_setter( | |||||
| attr_name, | |||||
| init_factory_name + "({0})".format(maybe_self) | |||||
| )) | |||||
| names_for_globals[init_factory_name] = a.default.factory | |||||
| else: | |||||
| if a.convert is not None: | |||||
| lines.append(fmt_setter_with_converter( | |||||
| attr_name, | |||||
| "attr_dict['{attr_name}'].default" | |||||
| .format(attr_name=attr_name) | |||||
| )) | |||||
| conv_name = _init_convert_pat.format(a.name) | |||||
| names_for_globals[conv_name] = a.convert | |||||
| else: | |||||
| lines.append(fmt_setter( | |||||
| attr_name, | |||||
| "attr_dict['{attr_name}'].default" | |||||
| .format(attr_name=attr_name) | |||||
| )) | |||||
| elif a.default is not NOTHING and not has_factory: | |||||
| args.append( | |||||
| "{arg_name}=attr_dict['{attr_name}'].default".format( | |||||
| arg_name=arg_name, | |||||
| attr_name=attr_name, | |||||
| ) | |||||
| ) | |||||
| if a.convert is not None: | |||||
| lines.append(fmt_setter_with_converter(attr_name, arg_name)) | |||||
| names_for_globals[_init_convert_pat.format(a.name)] = a.convert | |||||
| else: | |||||
| lines.append(fmt_setter(attr_name, arg_name)) | |||||
| elif has_factory: | |||||
| args.append("{arg_name}=NOTHING".format(arg_name=arg_name)) | |||||
| lines.append("if {arg_name} is not NOTHING:" | |||||
| .format(arg_name=arg_name)) | |||||
| init_factory_name = _init_factory_pat.format(a.name) | |||||
| if a.convert is not None: | |||||
| lines.append(" " + fmt_setter_with_converter(attr_name, | |||||
| arg_name)) | |||||
| lines.append("else:") | |||||
| lines.append(" " + fmt_setter_with_converter( | |||||
| attr_name, | |||||
| init_factory_name + "({0})".format(maybe_self) | |||||
| )) | |||||
| names_for_globals[_init_convert_pat.format(a.name)] = a.convert | |||||
| else: | |||||
| lines.append(" " + fmt_setter(attr_name, arg_name)) | |||||
| lines.append("else:") | |||||
| lines.append(" " + fmt_setter( | |||||
| attr_name, | |||||
| init_factory_name + "({0})".format(maybe_self) | |||||
| )) | |||||
| names_for_globals[init_factory_name] = a.default.factory | |||||
| else: | |||||
| args.append(arg_name) | |||||
| if a.convert is not None: | |||||
| lines.append(fmt_setter_with_converter(attr_name, arg_name)) | |||||
| names_for_globals[_init_convert_pat.format(a.name)] = a.convert | |||||
| else: | |||||
| lines.append(fmt_setter(attr_name, arg_name)) | |||||
| if attrs_to_validate: # we can skip this if there are no validators. | |||||
| names_for_globals["_config"] = _config | |||||
| lines.append("if _config._run_validators is True:") | |||||
| for a in attrs_to_validate: | |||||
| val_name = "__attr_validator_{}".format(a.name) | |||||
| attr_name = "__attr_{}".format(a.name) | |||||
| lines.append(" {}(self, {}, self.{})".format( | |||||
| val_name, attr_name, a.name)) | |||||
| names_for_globals[val_name] = a.validator | |||||
| names_for_globals[attr_name] = a | |||||
| if post_init: | |||||
| lines.append("self.__attrs_post_init__()") | |||||
| return """\ | |||||
| def __init__(self, {args}): | |||||
| {lines} | |||||
| """.format( | |||||
| args=", ".join(args), | |||||
| lines="\n ".join(lines) if lines else "pass", | |||||
| ), names_for_globals | |||||
| class Attribute(object): | |||||
| """ | |||||
| *Read-only* representation of an attribute. | |||||
| :attribute name: The name of the attribute. | |||||
| Plus *all* arguments of :func:`attr.ib`. | |||||
| """ | |||||
| __slots__ = ( | |||||
| "name", "default", "validator", "repr", "cmp", "hash", "init", | |||||
| "convert", "metadata", | |||||
| ) | |||||
| def __init__(self, name, default, validator, repr, cmp, hash, init, | |||||
| convert=None, metadata=None): | |||||
| # Cache this descriptor here to speed things up later. | |||||
| bound_setattr = _obj_setattr.__get__(self, Attribute) | |||||
| bound_setattr("name", name) | |||||
| bound_setattr("default", default) | |||||
| bound_setattr("validator", validator) | |||||
| bound_setattr("repr", repr) | |||||
| bound_setattr("cmp", cmp) | |||||
| bound_setattr("hash", hash) | |||||
| bound_setattr("init", init) | |||||
| bound_setattr("convert", convert) | |||||
| bound_setattr("metadata", (metadata_proxy(metadata) if metadata | |||||
| else _empty_metadata_singleton)) | |||||
| def __setattr__(self, name, value): | |||||
| raise FrozenInstanceError() | |||||
| @classmethod | |||||
| def from_counting_attr(cls, name, ca): | |||||
| inst_dict = { | |||||
| k: getattr(ca, k) | |||||
| for k | |||||
| in Attribute.__slots__ | |||||
| if k not in ( | |||||
| "name", "validator", "default", | |||||
| ) # exclude methods | |||||
| } | |||||
| return cls(name=name, validator=ca._validator, default=ca._default, | |||||
| **inst_dict) | |||||
| # Don't use _add_pickle since fields(Attribute) doesn't work | |||||
| def __getstate__(self): | |||||
| """ | |||||
| Play nice with pickle. | |||||
| """ | |||||
| return tuple(getattr(self, name) if name != "metadata" | |||||
| else dict(self.metadata) | |||||
| for name in self.__slots__) | |||||
| def __setstate__(self, state): | |||||
| """ | |||||
| Play nice with pickle. | |||||
| """ | |||||
| bound_setattr = _obj_setattr.__get__(self, Attribute) | |||||
| for name, value in zip(self.__slots__, state): | |||||
| if name != "metadata": | |||||
| bound_setattr(name, value) | |||||
| else: | |||||
| bound_setattr(name, metadata_proxy(value) if value else | |||||
| _empty_metadata_singleton) | |||||
| _a = [Attribute(name=name, default=NOTHING, validator=None, | |||||
| repr=True, cmp=True, hash=(name != "metadata"), init=True) | |||||
| for name in Attribute.__slots__] | |||||
| Attribute = _add_hash( | |||||
| _add_cmp(_add_repr(Attribute, attrs=_a), attrs=_a), | |||||
| attrs=[a for a in _a if a.hash] | |||||
| ) | |||||
| class _CountingAttr(object): | |||||
| """ | |||||
| Intermediate representation of attributes that uses a counter to preserve | |||||
| the order in which the attributes have been defined. | |||||
| *Internal* data structure of the attrs library. Running into is most | |||||
| likely the result of a bug like a forgotten `@attr.s` decorator. | |||||
| """ | |||||
| __slots__ = ("counter", "_default", "repr", "cmp", "hash", "init", | |||||
| "metadata", "_validator", "convert") | |||||
| __attrs_attrs__ = tuple( | |||||
| Attribute(name=name, default=NOTHING, validator=None, | |||||
| repr=True, cmp=True, hash=True, init=True) | |||||
| for name | |||||
| in ("counter", "_default", "repr", "cmp", "hash", "init",) | |||||
| ) + ( | |||||
| Attribute(name="metadata", default=None, validator=None, | |||||
| repr=True, cmp=True, hash=False, init=True), | |||||
| ) | |||||
| cls_counter = 0 | |||||
| def __init__(self, default, validator, repr, cmp, hash, init, convert, | |||||
| metadata): | |||||
| _CountingAttr.cls_counter += 1 | |||||
| self.counter = _CountingAttr.cls_counter | |||||
| self._default = default | |||||
| # If validator is a list/tuple, wrap it using helper validator. | |||||
| if validator and isinstance(validator, (list, tuple)): | |||||
| self._validator = and_(*validator) | |||||
| else: | |||||
| self._validator = validator | |||||
| self.repr = repr | |||||
| self.cmp = cmp | |||||
| self.hash = hash | |||||
| self.init = init | |||||
| self.convert = convert | |||||
| self.metadata = metadata | |||||
| def validator(self, meth): | |||||
| """ | |||||
| Decorator that adds *meth* to the list of validators. | |||||
| Returns *meth* unchanged. | |||||
| .. versionadded:: 17.1.0 | |||||
| """ | |||||
| if self._validator is None: | |||||
| self._validator = meth | |||||
| else: | |||||
| self._validator = and_(self._validator, meth) | |||||
| return meth | |||||
| def default(self, meth): | |||||
| """ | |||||
| Decorator that allows to set the default for an attribute. | |||||
| Returns *meth* unchanged. | |||||
| :raises DefaultAlreadySetError: If default has been set before. | |||||
| .. versionadded:: 17.1.0 | |||||
| """ | |||||
| if self._default is not NOTHING: | |||||
| raise DefaultAlreadySetError() | |||||
| self._default = Factory(meth, takes_self=True) | |||||
| return meth | |||||
| _CountingAttr = _add_cmp(_add_repr(_CountingAttr)) | |||||
| @attributes(slots=True, init=False) | |||||
| class Factory(object): | |||||
| """ | |||||
| Stores a factory callable. | |||||
| If passed as the default value to :func:`attr.ib`, the factory is used to | |||||
| generate a new value. | |||||
| :param callable factory: A callable that takes either none or exactly one | |||||
| mandatory positional argument depending on *takes_self*. | |||||
| :param bool takes_self: Pass the partially initialized instance that is | |||||
| being initialized as a positional argument. | |||||
| .. versionadded:: 17.1.0 *takes_self* | |||||
| """ | |||||
| factory = attr() | |||||
| takes_self = attr() | |||||
| def __init__(self, factory, takes_self=False): | |||||
| """ | |||||
| `Factory` is part of the default machinery so if we want a default | |||||
| value here, we have to implement it ourselves. | |||||
| """ | |||||
| self.factory = factory | |||||
| self.takes_self = takes_self | |||||
| def make_class(name, attrs, bases=(object,), **attributes_arguments): | |||||
| """ | |||||
| A quick way to create a new class called *name* with *attrs*. | |||||
| :param name: The name for the new class. | |||||
| :type name: str | |||||
| :param attrs: A list of names or a dictionary of mappings of names to | |||||
| attributes. | |||||
| :type attrs: :class:`list` or :class:`dict` | |||||
| :param tuple bases: Classes that the new class will subclass. | |||||
| :param attributes_arguments: Passed unmodified to :func:`attr.s`. | |||||
| :return: A new class with *attrs*. | |||||
| :rtype: type | |||||
| .. versionadded:: 17.1.0 *bases* | |||||
| """ | |||||
| if isinstance(attrs, dict): | |||||
| cls_dict = attrs | |||||
| elif isinstance(attrs, (list, tuple)): | |||||
| cls_dict = dict((a, attr()) for a in attrs) | |||||
| else: | |||||
| raise TypeError("attrs argument must be a dict or a list.") | |||||
| return attributes(**attributes_arguments)(type(name, bases, cls_dict)) | |||||
| # These are required by whithin this module so we define them here and merely | |||||
| # import into .validators. | |||||
| @attributes(slots=True, hash=True) | |||||
| class _AndValidator(object): | |||||
| """ | |||||
| Compose many validators to a single one. | |||||
| """ | |||||
| _validators = attr() | |||||
| def __call__(self, inst, attr, value): | |||||
| for v in self._validators: | |||||
| v(inst, attr, value) | |||||
| def and_(*validators): | |||||
| """ | |||||
| A validator that composes multiple validators into one. | |||||
| When called on a value, it runs all wrapped validators. | |||||
| :param validators: Arbitrary number of validators. | |||||
| :type validators: callables | |||||
| .. versionadded:: 17.1.0 | |||||
| """ | |||||
| vals = [] | |||||
| for validator in validators: | |||||
| vals.extend( | |||||
| validator._validators if isinstance(validator, _AndValidator) | |||||
| else [validator] | |||||
| ) | |||||
| return _AndValidator(tuple(vals)) | |||||
| """ | |||||
| Commonly useful converters. | |||||
| """ | |||||
| from __future__ import absolute_import, division, print_function | |||||
| def optional(converter): | |||||
| """ | |||||
| A converter that allows an attribute to be optional. An optional attribute | |||||
| is one which can be set to ``None``. | |||||
| :param callable converter: the converter that is used for non-``None`` | |||||
| values. | |||||
| .. versionadded:: 17.1.0 | |||||
| """ | |||||
| def optional_converter(val): | |||||
| if val is None: | |||||
| return None | |||||
| return converter(val) | |||||
| return optional_converter | |||||
| from __future__ import absolute_import, division, print_function | |||||
| class FrozenInstanceError(AttributeError): | |||||
| """ | |||||
| A frozen/immutable instance has been attempted to be modified. | |||||
| It mirrors the behavior of ``namedtuples`` by using the same error message | |||||
| and subclassing :exc:`AttributeError`. | |||||
| .. versionadded:: 16.1.0 | |||||
| """ | |||||
| msg = "can't set attribute" | |||||
| args = [msg] | |||||
| class AttrsAttributeNotFoundError(ValueError): | |||||
| """ | |||||
| An ``attrs`` function couldn't find an attribute that the user asked for. | |||||
| .. versionadded:: 16.2.0 | |||||
| """ | |||||
| class NotAnAttrsClassError(ValueError): | |||||
| """ | |||||
| A non-``attrs`` class has been passed into an ``attrs`` function. | |||||
| .. versionadded:: 16.2.0 | |||||
| """ | |||||
| class DefaultAlreadySetError(RuntimeError): | |||||
| """ | |||||
| A default has been set using ``attr.ib()`` and is attempted to be reset | |||||
| using the decorator. | |||||
| .. versionadded:: 17.1.0 | |||||
| """ | |||||
| """ | |||||
| Commonly useful filters for :func:`attr.asdict`. | |||||
| """ | |||||
| from __future__ import absolute_import, division, print_function | |||||
| from ._compat import isclass | |||||
| from ._make import Attribute | |||||
| def _split_what(what): | |||||
| """ | |||||
| Returns a tuple of `frozenset`s of classes and attributes. | |||||
| """ | |||||
| return ( | |||||
| frozenset(cls for cls in what if isclass(cls)), | |||||
| frozenset(cls for cls in what if isinstance(cls, Attribute)), | |||||
| ) | |||||
| def include(*what): | |||||
| """ | |||||
| Whitelist *what*. | |||||
| :param what: What to whitelist. | |||||
| :type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\ s | |||||
| :rtype: :class:`callable` | |||||
| """ | |||||
| cls, attrs = _split_what(what) | |||||
| def include_(attribute, value): | |||||
| return value.__class__ in cls or attribute in attrs | |||||
| return include_ | |||||
| def exclude(*what): | |||||
| """ | |||||
| Blacklist *what*. | |||||
| :param what: What to blacklist. | |||||
| :type what: :class:`list` of classes or :class:`attr.Attribute`\ s. | |||||
| :rtype: :class:`callable` | |||||
| """ | |||||
| cls, attrs = _split_what(what) | |||||
| def exclude_(attribute, value): | |||||
| return value.__class__ not in cls and attribute not in attrs | |||||
| return exclude_ | |||||
| """ | |||||
| Commonly useful validators. | |||||
| """ | |||||
| from __future__ import absolute_import, division, print_function | |||||
| from ._make import attr, attributes, and_, _AndValidator | |||||
| __all__ = [ | |||||
| "and_", | |||||
| "in_", | |||||
| "instance_of", | |||||
| "optional", | |||||
| "provides", | |||||
| ] | |||||
| @attributes(repr=False, slots=True, hash=True) | |||||
| class _InstanceOfValidator(object): | |||||
| type = attr() | |||||
| def __call__(self, inst, attr, value): | |||||
| """ | |||||
| We use a callable class to be able to change the ``__repr__``. | |||||
| """ | |||||
| if not isinstance(value, self.type): | |||||
| raise TypeError( | |||||
| "'{name}' must be {type!r} (got {value!r} that is a " | |||||
| "{actual!r})." | |||||
| .format(name=attr.name, type=self.type, | |||||
| actual=value.__class__, value=value), | |||||
| attr, self.type, value, | |||||
| ) | |||||
| def __repr__(self): | |||||
| return ( | |||||
| "<instance_of validator for type {type!r}>" | |||||
| .format(type=self.type) | |||||
| ) | |||||
| def instance_of(type): | |||||
| """ | |||||
| A validator that raises a :exc:`TypeError` if the initializer is called | |||||
| with a wrong type for this particular attribute (checks are perfomed using | |||||
| :func:`isinstance` therefore it's also valid to pass a tuple of types). | |||||
| :param type: The type to check for. | |||||
| :type type: type or tuple of types | |||||
| :raises TypeError: With a human readable error message, the attribute | |||||
| (of type :class:`attr.Attribute`), the expected type, and the value it | |||||
| got. | |||||
| """ | |||||
| return _InstanceOfValidator(type) | |||||
| @attributes(repr=False, slots=True, hash=True) | |||||
| class _ProvidesValidator(object): | |||||
| interface = attr() | |||||
| def __call__(self, inst, attr, value): | |||||
| """ | |||||
| We use a callable class to be able to change the ``__repr__``. | |||||
| """ | |||||
| if not self.interface.providedBy(value): | |||||
| raise TypeError( | |||||
| "'{name}' must provide {interface!r} which {value!r} " | |||||
| "doesn't." | |||||
| .format(name=attr.name, interface=self.interface, value=value), | |||||
| attr, self.interface, value, | |||||
| ) | |||||
| def __repr__(self): | |||||
| return ( | |||||
| "<provides validator for interface {interface!r}>" | |||||
| .format(interface=self.interface) | |||||
| ) | |||||
| def provides(interface): | |||||
| """ | |||||
| A validator that raises a :exc:`TypeError` if the initializer is called | |||||
| with an object that does not provide the requested *interface* (checks are | |||||
| performed using ``interface.providedBy(value)`` (see `zope.interface | |||||
| <https://zopeinterface.readthedocs.io/en/latest/>`_). | |||||
| :param zope.interface.Interface interface: The interface to check for. | |||||
| :raises TypeError: With a human readable error message, the attribute | |||||
| (of type :class:`attr.Attribute`), the expected interface, and the | |||||
| value it got. | |||||
| """ | |||||
| return _ProvidesValidator(interface) | |||||
| @attributes(repr=False, slots=True, hash=True) | |||||
| class _OptionalValidator(object): | |||||
| validator = attr() | |||||
| def __call__(self, inst, attr, value): | |||||
| if value is None: | |||||
| return | |||||
| self.validator(inst, attr, value) | |||||
| def __repr__(self): | |||||
| return ( | |||||
| "<optional validator for {what} or None>" | |||||
| .format(what=repr(self.validator)) | |||||
| ) | |||||
| def optional(validator): | |||||
| """ | |||||
| A validator that makes an attribute optional. An optional attribute is one | |||||
| which can be set to ``None`` in addition to satisfying the requirements of | |||||
| the sub-validator. | |||||
| :param validator: A validator (or a list of validators) that is used for | |||||
| non-``None`` values. | |||||
| :type validator: callable or :class:`list` of callables. | |||||
| .. versionadded:: 15.1.0 | |||||
| .. versionchanged:: 17.1.0 *validator* can be a list of validators. | |||||
| """ | |||||
| if isinstance(validator, list): | |||||
| return _OptionalValidator(_AndValidator(validator)) | |||||
| return _OptionalValidator(validator) | |||||
| @attributes(repr=False, slots=True, hash=True) | |||||
| class _InValidator(object): | |||||
| options = attr() | |||||
| def __call__(self, inst, attr, value): | |||||
| if value not in self.options: | |||||
| raise ValueError( | |||||
| "'{name}' must be in {options!r} (got {value!r})" | |||||
| .format(name=attr.name, options=self.options, value=value) | |||||
| ) | |||||
| def __repr__(self): | |||||
| return ( | |||||
| "<in_ validator with options {options!r}>" | |||||
| .format(options=self.options) | |||||
| ) | |||||
| def in_(options): | |||||
| """ | |||||
| A validator that raises a :exc:`ValueError` if the initializer is called | |||||
| with a value that does not belong in the options provided. The check is | |||||
| performed using ``value in options``. | |||||
| :param options: Allowed options. | |||||
| :type options: list, tuple, :class:`enum.Enum`, ... | |||||
| :raises ValueError: With a human readable error message, the attribute (of | |||||
| type :class:`attr.Attribute`), the expected options, and the value it | |||||
| got. | |||||
| .. versionadded:: 17.1.0 | |||||
| """ | |||||
| return _InValidator(options) | |||||
| } | } | ||||
| packages = ['mercurial', | packages = ['mercurial', | ||||
| 'mercurial.cext', | 'mercurial.cext', | ||||
| 'mercurial.cffi', | 'mercurial.cffi', | ||||
| 'mercurial.hgweb', | 'mercurial.hgweb', | ||||
| 'mercurial.httpclient', | 'mercurial.httpclient', | ||||
| 'mercurial.pure', | 'mercurial.pure', | ||||
| 'mercurial.thirdparty', | |||||
| 'mercurial.thirdparty.attr', | |||||
| 'hgext', 'hgext.convert', 'hgext.fsmonitor', | 'hgext', 'hgext.convert', 'hgext.fsmonitor', | ||||
| 'hgext.fsmonitor.pywatchman', 'hgext.highlight', | 'hgext.fsmonitor.pywatchman', 'hgext.highlight', | ||||
| 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd', | 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd', | ||||
| 'hgdemandimport'] | 'hgdemandimport'] | ||||
| common_depends = ['mercurial/bitmanipulation.h', | common_depends = ['mercurial/bitmanipulation.h', | ||||
| 'mercurial/compat.h', | 'mercurial/compat.h', | ||||
| 'mercurial/cext/util.h'] | 'mercurial/cext/util.h'] | ||||