mercurial/thirdparty/attr/_make.py
changeset 34397 765eb17a7eb8
child 41564 a5493a251ad3
equal deleted inserted replaced
34396:9fb9f8440b71 34397:765eb17a7eb8
       
     1 from __future__ import absolute_import, division, print_function
       
     2 
       
     3 import hashlib
       
     4 import linecache
       
     5 
       
     6 from operator import itemgetter
       
     7 
       
     8 from . import _config
       
     9 from ._compat import PY2, iteritems, isclass, iterkeys, metadata_proxy
       
    10 from .exceptions import (
       
    11     DefaultAlreadySetError,
       
    12     FrozenInstanceError,
       
    13     NotAnAttrsClassError,
       
    14 )
       
    15 
       
    16 
       
    17 # This is used at least twice, so cache it here.
       
    18 _obj_setattr = object.__setattr__
       
    19 _init_convert_pat = "__attr_convert_{}"
       
    20 _init_factory_pat = "__attr_factory_{}"
       
    21 _tuple_property_pat = "    {attr_name} = property(itemgetter({index}))"
       
    22 _empty_metadata_singleton = metadata_proxy({})
       
    23 
       
    24 
       
    25 class _Nothing(object):
       
    26     """
       
    27     Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
       
    28 
       
    29     All instances of `_Nothing` are equal.
       
    30     """
       
    31     def __copy__(self):
       
    32         return self
       
    33 
       
    34     def __deepcopy__(self, _):
       
    35         return self
       
    36 
       
    37     def __eq__(self, other):
       
    38         return other.__class__ == _Nothing
       
    39 
       
    40     def __ne__(self, other):
       
    41         return not self == other
       
    42 
       
    43     def __repr__(self):
       
    44         return "NOTHING"
       
    45 
       
    46     def __hash__(self):
       
    47         return 0xdeadbeef
       
    48 
       
    49 
       
    50 NOTHING = _Nothing()
       
    51 """
       
    52 Sentinel to indicate the lack of a value when ``None`` is ambiguous.
       
    53 """
       
    54 
       
    55 
       
    56 def attr(default=NOTHING, validator=None,
       
    57          repr=True, cmp=True, hash=None, init=True,
       
    58          convert=None, metadata={}):
       
    59     """
       
    60     Create a new attribute on a class.
       
    61 
       
    62     ..  warning::
       
    63 
       
    64         Does *not* do anything unless the class is also decorated with
       
    65         :func:`attr.s`!
       
    66 
       
    67     :param default: A value that is used if an ``attrs``-generated ``__init__``
       
    68         is used and no value is passed while instantiating or the attribute is
       
    69         excluded using ``init=False``.
       
    70 
       
    71         If the value is an instance of :class:`Factory`, its callable will be
       
    72         used to construct a new value (useful for mutable datatypes like lists
       
    73         or dicts).
       
    74 
       
    75         If a default is not set (or set manually to ``attr.NOTHING``), a value
       
    76         *must* be supplied when instantiating; otherwise a :exc:`TypeError`
       
    77         will be raised.
       
    78 
       
    79         The default can also be set using decorator notation as shown below.
       
    80 
       
    81     :type default: Any value.
       
    82 
       
    83     :param validator: :func:`callable` that is called by ``attrs``-generated
       
    84         ``__init__`` methods after the instance has been initialized.  They
       
    85         receive the initialized instance, the :class:`Attribute`, and the
       
    86         passed value.
       
    87 
       
    88         The return value is *not* inspected so the validator has to throw an
       
    89         exception itself.
       
    90 
       
    91         If a ``list`` is passed, its items are treated as validators and must
       
    92         all pass.
       
    93 
       
    94         Validators can be globally disabled and re-enabled using
       
    95         :func:`get_run_validators`.
       
    96 
       
    97         The validator can also be set using decorator notation as shown below.
       
    98 
       
    99     :type validator: ``callable`` or a ``list`` of ``callable``\ s.
       
   100 
       
   101     :param bool repr: Include this attribute in the generated ``__repr__``
       
   102         method.
       
   103     :param bool cmp: Include this attribute in the generated comparison methods
       
   104         (``__eq__`` et al).
       
   105     :param hash: Include this attribute in the generated ``__hash__``
       
   106         method.  If ``None`` (default), mirror *cmp*'s value.  This is the
       
   107         correct behavior according the Python spec.  Setting this value to
       
   108         anything else than ``None`` is *discouraged*.
       
   109     :type hash: ``bool`` or ``None``
       
   110     :param bool init: Include this attribute in the generated ``__init__``
       
   111         method.  It is possible to set this to ``False`` and set a default
       
   112         value.  In that case this attributed is unconditionally initialized
       
   113         with the specified default value or factory.
       
   114     :param callable convert: :func:`callable` that is called by
       
   115         ``attrs``-generated ``__init__`` methods to convert attribute's value
       
   116         to the desired format.  It is given the passed-in value, and the
       
   117         returned value will be used as the new value of the attribute.  The
       
   118         value is converted before being passed to the validator, if any.
       
   119     :param metadata: An arbitrary mapping, to be used by third-party
       
   120         components.  See :ref:`extending_metadata`.
       
   121 
       
   122     ..  versionchanged:: 17.1.0 *validator* can be a ``list`` now.
       
   123     ..  versionchanged:: 17.1.0
       
   124         *hash* is ``None`` and therefore mirrors *cmp* by default .
       
   125     """
       
   126     if hash is not None and hash is not True and hash is not False:
       
   127         raise TypeError(
       
   128             "Invalid value for hash.  Must be True, False, or None."
       
   129         )
       
   130     return _CountingAttr(
       
   131         default=default,
       
   132         validator=validator,
       
   133         repr=repr,
       
   134         cmp=cmp,
       
   135         hash=hash,
       
   136         init=init,
       
   137         convert=convert,
       
   138         metadata=metadata,
       
   139     )
       
   140 
       
   141 
       
   142 def _make_attr_tuple_class(cls_name, attr_names):
       
   143     """
       
   144     Create a tuple subclass to hold `Attribute`s for an `attrs` class.
       
   145 
       
   146     The subclass is a bare tuple with properties for names.
       
   147 
       
   148     class MyClassAttributes(tuple):
       
   149         __slots__ = ()
       
   150         x = property(itemgetter(0))
       
   151     """
       
   152     attr_class_name = "{}Attributes".format(cls_name)
       
   153     attr_class_template = [
       
   154         "class {}(tuple):".format(attr_class_name),
       
   155         "    __slots__ = ()",
       
   156     ]
       
   157     if attr_names:
       
   158         for i, attr_name in enumerate(attr_names):
       
   159             attr_class_template.append(_tuple_property_pat.format(
       
   160                 index=i,
       
   161                 attr_name=attr_name,
       
   162             ))
       
   163     else:
       
   164         attr_class_template.append("    pass")
       
   165     globs = {"itemgetter": itemgetter}
       
   166     eval(compile("\n".join(attr_class_template), "", "exec"), globs)
       
   167     return globs[attr_class_name]
       
   168 
       
   169 
       
   170 def _transform_attrs(cls, these):
       
   171     """
       
   172     Transforms all `_CountingAttr`s on a class into `Attribute`s and saves the
       
   173     list in `__attrs_attrs__`.
       
   174 
       
   175     If *these* is passed, use that and don't look for them on the class.
       
   176     """
       
   177     super_cls = []
       
   178     for c in reversed(cls.__mro__[1:-1]):
       
   179         sub_attrs = getattr(c, "__attrs_attrs__", None)
       
   180         if sub_attrs is not None:
       
   181             super_cls.extend(a for a in sub_attrs if a not in super_cls)
       
   182     if these is None:
       
   183         ca_list = [(name, attr)
       
   184                    for name, attr
       
   185                    in cls.__dict__.items()
       
   186                    if isinstance(attr, _CountingAttr)]
       
   187     else:
       
   188         ca_list = [(name, ca)
       
   189                    for name, ca
       
   190                    in iteritems(these)]
       
   191 
       
   192     non_super_attrs = [
       
   193         Attribute.from_counting_attr(name=attr_name, ca=ca)
       
   194         for attr_name, ca
       
   195         in sorted(ca_list, key=lambda e: e[1].counter)
       
   196     ]
       
   197     attr_names = [a.name for a in super_cls + non_super_attrs]
       
   198 
       
   199     AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
       
   200 
       
   201     cls.__attrs_attrs__ = AttrsClass(super_cls + [
       
   202         Attribute.from_counting_attr(name=attr_name, ca=ca)
       
   203         for attr_name, ca
       
   204         in sorted(ca_list, key=lambda e: e[1].counter)
       
   205     ])
       
   206 
       
   207     had_default = False
       
   208     for a in cls.__attrs_attrs__:
       
   209         if these is None and a not in super_cls:
       
   210             setattr(cls, a.name, a)
       
   211         if had_default is True and a.default is NOTHING and a.init is True:
       
   212             raise ValueError(
       
   213                 "No mandatory attributes allowed after an attribute with a "
       
   214                 "default value or factory.  Attribute in question: {a!r}"
       
   215                 .format(a=a)
       
   216             )
       
   217         elif had_default is False and \
       
   218                 a.default is not NOTHING and \
       
   219                 a.init is not False:
       
   220             had_default = True
       
   221 
       
   222 
       
   223 def _frozen_setattrs(self, name, value):
       
   224     """
       
   225     Attached to frozen classes as __setattr__.
       
   226     """
       
   227     raise FrozenInstanceError()
       
   228 
       
   229 
       
   230 def _frozen_delattrs(self, name):
       
   231     """
       
   232     Attached to frozen classes as __delattr__.
       
   233     """
       
   234     raise FrozenInstanceError()
       
   235 
       
   236 
       
   237 def attributes(maybe_cls=None, these=None, repr_ns=None,
       
   238                repr=True, cmp=True, hash=None, init=True,
       
   239                slots=False, frozen=False, str=False):
       
   240     r"""
       
   241     A class decorator that adds `dunder
       
   242     <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the
       
   243     specified attributes using :func:`attr.ib` or the *these* argument.
       
   244 
       
   245     :param these: A dictionary of name to :func:`attr.ib` mappings.  This is
       
   246         useful to avoid the definition of your attributes within the class body
       
   247         because you can't (e.g. if you want to add ``__repr__`` methods to
       
   248         Django models) or don't want to.
       
   249 
       
   250         If *these* is not ``None``, ``attrs`` will *not* search the class body
       
   251         for attributes.
       
   252 
       
   253     :type these: :class:`dict` of :class:`str` to :func:`attr.ib`
       
   254 
       
   255     :param str repr_ns: When using nested classes, there's no way in Python 2
       
   256         to automatically detect that.  Therefore it's possible to set the
       
   257         namespace explicitly for a more meaningful ``repr`` output.
       
   258     :param bool repr: Create a ``__repr__`` method with a human readable
       
   259         represantation of ``attrs`` attributes..
       
   260     :param bool str: Create a ``__str__`` method that is identical to
       
   261         ``__repr__``.  This is usually not necessary except for
       
   262         :class:`Exception`\ s.
       
   263     :param bool cmp: Create ``__eq__``, ``__ne__``, ``__lt__``, ``__le__``,
       
   264         ``__gt__``, and ``__ge__`` methods that compare the class as if it were
       
   265         a tuple of its ``attrs`` attributes.  But the attributes are *only*
       
   266         compared, if the type of both classes is *identical*!
       
   267     :param hash: If ``None`` (default), the ``__hash__`` method is generated
       
   268         according how *cmp* and *frozen* are set.
       
   269 
       
   270         1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you.
       
   271         2. If *cmp* is True and *frozen* is False, ``__hash__`` will be set to
       
   272            None, marking it unhashable (which it is).
       
   273         3. If *cmp* is False, ``__hash__`` will be left untouched meaning the
       
   274            ``__hash__`` method of the superclass will be used (if superclass is
       
   275            ``object``, this means it will fall back to id-based hashing.).
       
   276 
       
   277         Although not recommended, you can decide for yourself and force
       
   278         ``attrs`` to create one (e.g. if the class is immutable even though you
       
   279         didn't freeze it programmatically) by passing ``True`` or not.  Both of
       
   280         these cases are rather special and should be used carefully.
       
   281 
       
   282         See the `Python documentation \
       
   283         <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`_
       
   284         and the `GitHub issue that led to the default behavior \
       
   285         <https://github.com/python-attrs/attrs/issues/136>`_ for more details.
       
   286     :type hash: ``bool`` or ``None``
       
   287     :param bool init: Create a ``__init__`` method that initialiazes the
       
   288         ``attrs`` attributes.  Leading underscores are stripped for the
       
   289         argument name.  If a ``__attrs_post_init__`` method exists on the
       
   290         class, it will be called after the class is fully initialized.
       
   291     :param bool slots: Create a slots_-style class that's more
       
   292         memory-efficient.  See :ref:`slots` for further ramifications.
       
   293     :param bool frozen: Make instances immutable after initialization.  If
       
   294         someone attempts to modify a frozen instance,
       
   295         :exc:`attr.exceptions.FrozenInstanceError` is raised.
       
   296 
       
   297         Please note:
       
   298 
       
   299             1. This is achieved by installing a custom ``__setattr__`` method
       
   300                on your class so you can't implement an own one.
       
   301 
       
   302             2. True immutability is impossible in Python.
       
   303 
       
   304             3. This *does* have a minor a runtime performance :ref:`impact
       
   305                <how-frozen>` when initializing new instances.  In other words:
       
   306                ``__init__`` is slightly slower with ``frozen=True``.
       
   307 
       
   308             4. If a class is frozen, you cannot modify ``self`` in
       
   309                ``__attrs_post_init__`` or a self-written ``__init__``. You can
       
   310                circumvent that limitation by using
       
   311                ``object.__setattr__(self, "attribute_name", value)``.
       
   312 
       
   313         ..  _slots: https://docs.python.org/3.5/reference/datamodel.html#slots
       
   314 
       
   315     ..  versionadded:: 16.0.0 *slots*
       
   316     ..  versionadded:: 16.1.0 *frozen*
       
   317     ..  versionadded:: 16.3.0 *str*, and support for ``__attrs_post_init__``.
       
   318     ..  versionchanged::
       
   319             17.1.0 *hash* supports ``None`` as value which is also the default
       
   320             now.
       
   321     """
       
   322     def wrap(cls):
       
   323         if getattr(cls, "__class__", None) is None:
       
   324             raise TypeError("attrs only works with new-style classes.")
       
   325 
       
   326         if repr is False and str is True:
       
   327             raise ValueError(
       
   328                 "__str__ can only be generated if a __repr__ exists."
       
   329             )
       
   330 
       
   331         if slots:
       
   332             # Only need this later if we're using slots.
       
   333             if these is None:
       
   334                 ca_list = [name
       
   335                            for name, attr
       
   336                            in cls.__dict__.items()
       
   337                            if isinstance(attr, _CountingAttr)]
       
   338             else:
       
   339                 ca_list = list(iterkeys(these))
       
   340         _transform_attrs(cls, these)
       
   341 
       
   342         # Can't just re-use frozen name because Python's scoping. :(
       
   343         # Can't compare function objects because Python 2 is terrible. :(
       
   344         effectively_frozen = _has_frozen_superclass(cls) or frozen
       
   345         if repr is True:
       
   346             cls = _add_repr(cls, ns=repr_ns)
       
   347         if str is True:
       
   348             cls.__str__ = cls.__repr__
       
   349         if cmp is True:
       
   350             cls = _add_cmp(cls)
       
   351 
       
   352         if hash is not True and hash is not False and hash is not None:
       
   353             raise TypeError(
       
   354                 "Invalid value for hash.  Must be True, False, or None."
       
   355             )
       
   356         elif hash is False or (hash is None and cmp is False):
       
   357             pass
       
   358         elif hash is True or (hash is None and cmp is True and frozen is True):
       
   359             cls = _add_hash(cls)
       
   360         else:
       
   361             cls.__hash__ = None
       
   362 
       
   363         if init is True:
       
   364             cls = _add_init(cls, effectively_frozen)
       
   365         if effectively_frozen is True:
       
   366             cls.__setattr__ = _frozen_setattrs
       
   367             cls.__delattr__ = _frozen_delattrs
       
   368             if slots is True:
       
   369                 # slots and frozen require __getstate__/__setstate__ to work
       
   370                 cls = _add_pickle(cls)
       
   371         if slots is True:
       
   372             cls_dict = dict(cls.__dict__)
       
   373             cls_dict["__slots__"] = tuple(ca_list)
       
   374             for ca_name in ca_list:
       
   375                 # It might not actually be in there, e.g. if using 'these'.
       
   376                 cls_dict.pop(ca_name, None)
       
   377             cls_dict.pop("__dict__", None)
       
   378 
       
   379             qualname = getattr(cls, "__qualname__", None)
       
   380             cls = type(cls)(cls.__name__, cls.__bases__, cls_dict)
       
   381             if qualname is not None:
       
   382                 cls.__qualname__ = qualname
       
   383 
       
   384         return cls
       
   385 
       
   386     # attrs_or class type depends on the usage of the decorator.  It's a class
       
   387     # if it's used as `@attributes` but ``None`` if used # as `@attributes()`.
       
   388     if maybe_cls is None:
       
   389         return wrap
       
   390     else:
       
   391         return wrap(maybe_cls)
       
   392 
       
   393 
       
   394 if PY2:
       
   395     def _has_frozen_superclass(cls):
       
   396         """
       
   397         Check whether *cls* has a frozen ancestor by looking at its
       
   398         __setattr__.
       
   399         """
       
   400         return (
       
   401             getattr(
       
   402                 cls.__setattr__, "__module__", None
       
   403             ) == _frozen_setattrs.__module__ and
       
   404             cls.__setattr__.__name__ == _frozen_setattrs.__name__
       
   405         )
       
   406 else:
       
   407     def _has_frozen_superclass(cls):
       
   408         """
       
   409         Check whether *cls* has a frozen ancestor by looking at its
       
   410         __setattr__.
       
   411         """
       
   412         return cls.__setattr__ == _frozen_setattrs
       
   413 
       
   414 
       
   415 def _attrs_to_tuple(obj, attrs):
       
   416     """
       
   417     Create a tuple of all values of *obj*'s *attrs*.
       
   418     """
       
   419     return tuple(getattr(obj, a.name) for a in attrs)
       
   420 
       
   421 
       
   422 def _add_hash(cls, attrs=None):
       
   423     """
       
   424     Add a hash method to *cls*.
       
   425     """
       
   426     if attrs is None:
       
   427         attrs = [a
       
   428                  for a in cls.__attrs_attrs__
       
   429                  if a.hash is True or (a.hash is None and a.cmp is True)]
       
   430 
       
   431     def hash_(self):
       
   432         """
       
   433         Automatically created by attrs.
       
   434         """
       
   435         return hash(_attrs_to_tuple(self, attrs))
       
   436 
       
   437     cls.__hash__ = hash_
       
   438     return cls
       
   439 
       
   440 
       
   441 def _add_cmp(cls, attrs=None):
       
   442     """
       
   443     Add comparison methods to *cls*.
       
   444     """
       
   445     if attrs is None:
       
   446         attrs = [a for a in cls.__attrs_attrs__ if a.cmp]
       
   447 
       
   448     def attrs_to_tuple(obj):
       
   449         """
       
   450         Save us some typing.
       
   451         """
       
   452         return _attrs_to_tuple(obj, attrs)
       
   453 
       
   454     def eq(self, other):
       
   455         """
       
   456         Automatically created by attrs.
       
   457         """
       
   458         if other.__class__ is self.__class__:
       
   459             return attrs_to_tuple(self) == attrs_to_tuple(other)
       
   460         else:
       
   461             return NotImplemented
       
   462 
       
   463     def ne(self, other):
       
   464         """
       
   465         Automatically created by attrs.
       
   466         """
       
   467         result = eq(self, other)
       
   468         if result is NotImplemented:
       
   469             return NotImplemented
       
   470         else:
       
   471             return not result
       
   472 
       
   473     def lt(self, other):
       
   474         """
       
   475         Automatically created by attrs.
       
   476         """
       
   477         if isinstance(other, self.__class__):
       
   478             return attrs_to_tuple(self) < attrs_to_tuple(other)
       
   479         else:
       
   480             return NotImplemented
       
   481 
       
   482     def le(self, other):
       
   483         """
       
   484         Automatically created by attrs.
       
   485         """
       
   486         if isinstance(other, self.__class__):
       
   487             return attrs_to_tuple(self) <= attrs_to_tuple(other)
       
   488         else:
       
   489             return NotImplemented
       
   490 
       
   491     def gt(self, other):
       
   492         """
       
   493         Automatically created by attrs.
       
   494         """
       
   495         if isinstance(other, self.__class__):
       
   496             return attrs_to_tuple(self) > attrs_to_tuple(other)
       
   497         else:
       
   498             return NotImplemented
       
   499 
       
   500     def ge(self, other):
       
   501         """
       
   502         Automatically created by attrs.
       
   503         """
       
   504         if isinstance(other, self.__class__):
       
   505             return attrs_to_tuple(self) >= attrs_to_tuple(other)
       
   506         else:
       
   507             return NotImplemented
       
   508 
       
   509     cls.__eq__ = eq
       
   510     cls.__ne__ = ne
       
   511     cls.__lt__ = lt
       
   512     cls.__le__ = le
       
   513     cls.__gt__ = gt
       
   514     cls.__ge__ = ge
       
   515 
       
   516     return cls
       
   517 
       
   518 
       
   519 def _add_repr(cls, ns=None, attrs=None):
       
   520     """
       
   521     Add a repr method to *cls*.
       
   522     """
       
   523     if attrs is None:
       
   524         attrs = [a for a in cls.__attrs_attrs__ if a.repr]
       
   525 
       
   526     def repr_(self):
       
   527         """
       
   528         Automatically created by attrs.
       
   529         """
       
   530         real_cls = self.__class__
       
   531         if ns is None:
       
   532             qualname = getattr(real_cls, "__qualname__", None)
       
   533             if qualname is not None:
       
   534                 class_name = qualname.rsplit(">.", 1)[-1]
       
   535             else:
       
   536                 class_name = real_cls.__name__
       
   537         else:
       
   538             class_name = ns + "." + real_cls.__name__
       
   539 
       
   540         return "{0}({1})".format(
       
   541             class_name,
       
   542             ", ".join(a.name + "=" + repr(getattr(self, a.name))
       
   543                       for a in attrs)
       
   544         )
       
   545     cls.__repr__ = repr_
       
   546     return cls
       
   547 
       
   548 
       
   549 def _add_init(cls, frozen):
       
   550     """
       
   551     Add a __init__ method to *cls*.  If *frozen* is True, make it immutable.
       
   552     """
       
   553     attrs = [a for a in cls.__attrs_attrs__
       
   554              if a.init or a.default is not NOTHING]
       
   555 
       
   556     # We cache the generated init methods for the same kinds of attributes.
       
   557     sha1 = hashlib.sha1()
       
   558     sha1.update(repr(attrs).encode("utf-8"))
       
   559     unique_filename = "<attrs generated init {0}>".format(
       
   560         sha1.hexdigest()
       
   561     )
       
   562 
       
   563     script, globs = _attrs_to_script(
       
   564         attrs,
       
   565         frozen,
       
   566         getattr(cls, "__attrs_post_init__", False),
       
   567     )
       
   568     locs = {}
       
   569     bytecode = compile(script, unique_filename, "exec")
       
   570     attr_dict = dict((a.name, a) for a in attrs)
       
   571     globs.update({
       
   572         "NOTHING": NOTHING,
       
   573         "attr_dict": attr_dict,
       
   574     })
       
   575     if frozen is True:
       
   576         # Save the lookup overhead in __init__ if we need to circumvent
       
   577         # immutability.
       
   578         globs["_cached_setattr"] = _obj_setattr
       
   579     eval(bytecode, globs, locs)
       
   580     init = locs["__init__"]
       
   581 
       
   582     # In order of debuggers like PDB being able to step through the code,
       
   583     # we add a fake linecache entry.
       
   584     linecache.cache[unique_filename] = (
       
   585         len(script),
       
   586         None,
       
   587         script.splitlines(True),
       
   588         unique_filename
       
   589     )
       
   590     cls.__init__ = init
       
   591     return cls
       
   592 
       
   593 
       
   594 def _add_pickle(cls):
       
   595     """
       
   596     Add pickle helpers, needed for frozen and slotted classes
       
   597     """
       
   598     def _slots_getstate__(obj):
       
   599         """
       
   600         Play nice with pickle.
       
   601         """
       
   602         return tuple(getattr(obj, a.name) for a in fields(obj.__class__))
       
   603 
       
   604     def _slots_setstate__(obj, state):
       
   605         """
       
   606         Play nice with pickle.
       
   607         """
       
   608         __bound_setattr = _obj_setattr.__get__(obj, Attribute)
       
   609         for a, value in zip(fields(obj.__class__), state):
       
   610             __bound_setattr(a.name, value)
       
   611 
       
   612     cls.__getstate__ = _slots_getstate__
       
   613     cls.__setstate__ = _slots_setstate__
       
   614     return cls
       
   615 
       
   616 
       
   617 def fields(cls):
       
   618     """
       
   619     Returns the tuple of ``attrs`` attributes for a class.
       
   620 
       
   621     The tuple also allows accessing the fields by their names (see below for
       
   622     examples).
       
   623 
       
   624     :param type cls: Class to introspect.
       
   625 
       
   626     :raise TypeError: If *cls* is not a class.
       
   627     :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
       
   628         class.
       
   629 
       
   630     :rtype: tuple (with name accesors) of :class:`attr.Attribute`
       
   631 
       
   632     ..  versionchanged:: 16.2.0 Returned tuple allows accessing the fields
       
   633         by name.
       
   634     """
       
   635     if not isclass(cls):
       
   636         raise TypeError("Passed object must be a class.")
       
   637     attrs = getattr(cls, "__attrs_attrs__", None)
       
   638     if attrs is None:
       
   639         raise NotAnAttrsClassError(
       
   640             "{cls!r} is not an attrs-decorated class.".format(cls=cls)
       
   641         )
       
   642     return attrs
       
   643 
       
   644 
       
   645 def validate(inst):
       
   646     """
       
   647     Validate all attributes on *inst* that have a validator.
       
   648 
       
   649     Leaves all exceptions through.
       
   650 
       
   651     :param inst: Instance of a class with ``attrs`` attributes.
       
   652     """
       
   653     if _config._run_validators is False:
       
   654         return
       
   655 
       
   656     for a in fields(inst.__class__):
       
   657         v = a.validator
       
   658         if v is not None:
       
   659             v(inst, a, getattr(inst, a.name))
       
   660 
       
   661 
       
   662 def _attrs_to_script(attrs, frozen, post_init):
       
   663     """
       
   664     Return a script of an initializer for *attrs* and a dict of globals.
       
   665 
       
   666     The globals are expected by the generated script.
       
   667 
       
   668      If *frozen* is True, we cannot set the attributes directly so we use
       
   669     a cached ``object.__setattr__``.
       
   670     """
       
   671     lines = []
       
   672     if frozen is True:
       
   673         lines.append(
       
   674             # Circumvent the __setattr__ descriptor to save one lookup per
       
   675             # assignment.
       
   676             "_setattr = _cached_setattr.__get__(self, self.__class__)"
       
   677         )
       
   678 
       
   679         def fmt_setter(attr_name, value_var):
       
   680             return "_setattr('%(attr_name)s', %(value_var)s)" % {
       
   681                 "attr_name": attr_name,
       
   682                 "value_var": value_var,
       
   683             }
       
   684 
       
   685         def fmt_setter_with_converter(attr_name, value_var):
       
   686             conv_name = _init_convert_pat.format(attr_name)
       
   687             return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % {
       
   688                 "attr_name": attr_name,
       
   689                 "value_var": value_var,
       
   690                 "conv": conv_name,
       
   691             }
       
   692     else:
       
   693         def fmt_setter(attr_name, value):
       
   694             return "self.%(attr_name)s = %(value)s" % {
       
   695                 "attr_name": attr_name,
       
   696                 "value": value,
       
   697             }
       
   698 
       
   699         def fmt_setter_with_converter(attr_name, value_var):
       
   700             conv_name = _init_convert_pat.format(attr_name)
       
   701             return "self.%(attr_name)s = %(conv)s(%(value_var)s)" % {
       
   702                 "attr_name": attr_name,
       
   703                 "value_var": value_var,
       
   704                 "conv": conv_name,
       
   705             }
       
   706 
       
   707     args = []
       
   708     attrs_to_validate = []
       
   709 
       
   710     # This is a dictionary of names to validator and converter callables.
       
   711     # Injecting this into __init__ globals lets us avoid lookups.
       
   712     names_for_globals = {}
       
   713 
       
   714     for a in attrs:
       
   715         if a.validator:
       
   716             attrs_to_validate.append(a)
       
   717         attr_name = a.name
       
   718         arg_name = a.name.lstrip("_")
       
   719         has_factory = isinstance(a.default, Factory)
       
   720         if has_factory and a.default.takes_self:
       
   721             maybe_self = "self"
       
   722         else:
       
   723             maybe_self = ""
       
   724         if a.init is False:
       
   725             if has_factory:
       
   726                 init_factory_name = _init_factory_pat.format(a.name)
       
   727                 if a.convert is not None:
       
   728                     lines.append(fmt_setter_with_converter(
       
   729                         attr_name,
       
   730                         init_factory_name + "({0})".format(maybe_self)))
       
   731                     conv_name = _init_convert_pat.format(a.name)
       
   732                     names_for_globals[conv_name] = a.convert
       
   733                 else:
       
   734                     lines.append(fmt_setter(
       
   735                         attr_name,
       
   736                         init_factory_name + "({0})".format(maybe_self)
       
   737                     ))
       
   738                 names_for_globals[init_factory_name] = a.default.factory
       
   739             else:
       
   740                 if a.convert is not None:
       
   741                     lines.append(fmt_setter_with_converter(
       
   742                         attr_name,
       
   743                         "attr_dict['{attr_name}'].default"
       
   744                         .format(attr_name=attr_name)
       
   745                     ))
       
   746                     conv_name = _init_convert_pat.format(a.name)
       
   747                     names_for_globals[conv_name] = a.convert
       
   748                 else:
       
   749                     lines.append(fmt_setter(
       
   750                         attr_name,
       
   751                         "attr_dict['{attr_name}'].default"
       
   752                         .format(attr_name=attr_name)
       
   753                     ))
       
   754         elif a.default is not NOTHING and not has_factory:
       
   755             args.append(
       
   756                 "{arg_name}=attr_dict['{attr_name}'].default".format(
       
   757                     arg_name=arg_name,
       
   758                     attr_name=attr_name,
       
   759                 )
       
   760             )
       
   761             if a.convert is not None:
       
   762                 lines.append(fmt_setter_with_converter(attr_name, arg_name))
       
   763                 names_for_globals[_init_convert_pat.format(a.name)] = a.convert
       
   764             else:
       
   765                 lines.append(fmt_setter(attr_name, arg_name))
       
   766         elif has_factory:
       
   767             args.append("{arg_name}=NOTHING".format(arg_name=arg_name))
       
   768             lines.append("if {arg_name} is not NOTHING:"
       
   769                          .format(arg_name=arg_name))
       
   770             init_factory_name = _init_factory_pat.format(a.name)
       
   771             if a.convert is not None:
       
   772                 lines.append("    " + fmt_setter_with_converter(attr_name,
       
   773                                                                 arg_name))
       
   774                 lines.append("else:")
       
   775                 lines.append("    " + fmt_setter_with_converter(
       
   776                     attr_name,
       
   777                     init_factory_name + "({0})".format(maybe_self)
       
   778                 ))
       
   779                 names_for_globals[_init_convert_pat.format(a.name)] = a.convert
       
   780             else:
       
   781                 lines.append("    " + fmt_setter(attr_name, arg_name))
       
   782                 lines.append("else:")
       
   783                 lines.append("    " + fmt_setter(
       
   784                     attr_name,
       
   785                     init_factory_name + "({0})".format(maybe_self)
       
   786                 ))
       
   787             names_for_globals[init_factory_name] = a.default.factory
       
   788         else:
       
   789             args.append(arg_name)
       
   790             if a.convert is not None:
       
   791                 lines.append(fmt_setter_with_converter(attr_name, arg_name))
       
   792                 names_for_globals[_init_convert_pat.format(a.name)] = a.convert
       
   793             else:
       
   794                 lines.append(fmt_setter(attr_name, arg_name))
       
   795 
       
   796     if attrs_to_validate:  # we can skip this if there are no validators.
       
   797         names_for_globals["_config"] = _config
       
   798         lines.append("if _config._run_validators is True:")
       
   799         for a in attrs_to_validate:
       
   800             val_name = "__attr_validator_{}".format(a.name)
       
   801             attr_name = "__attr_{}".format(a.name)
       
   802             lines.append("    {}(self, {}, self.{})".format(
       
   803                 val_name, attr_name, a.name))
       
   804             names_for_globals[val_name] = a.validator
       
   805             names_for_globals[attr_name] = a
       
   806     if post_init:
       
   807         lines.append("self.__attrs_post_init__()")
       
   808 
       
   809     return """\
       
   810 def __init__(self, {args}):
       
   811     {lines}
       
   812 """.format(
       
   813         args=", ".join(args),
       
   814         lines="\n    ".join(lines) if lines else "pass",
       
   815     ), names_for_globals
       
   816 
       
   817 
       
   818 class Attribute(object):
       
   819     """
       
   820     *Read-only* representation of an attribute.
       
   821 
       
   822     :attribute name: The name of the attribute.
       
   823 
       
   824     Plus *all* arguments of :func:`attr.ib`.
       
   825     """
       
   826     __slots__ = (
       
   827         "name", "default", "validator", "repr", "cmp", "hash", "init",
       
   828         "convert", "metadata",
       
   829     )
       
   830 
       
   831     def __init__(self, name, default, validator, repr, cmp, hash, init,
       
   832                  convert=None, metadata=None):
       
   833         # Cache this descriptor here to speed things up later.
       
   834         bound_setattr = _obj_setattr.__get__(self, Attribute)
       
   835 
       
   836         bound_setattr("name", name)
       
   837         bound_setattr("default", default)
       
   838         bound_setattr("validator", validator)
       
   839         bound_setattr("repr", repr)
       
   840         bound_setattr("cmp", cmp)
       
   841         bound_setattr("hash", hash)
       
   842         bound_setattr("init", init)
       
   843         bound_setattr("convert", convert)
       
   844         bound_setattr("metadata", (metadata_proxy(metadata) if metadata
       
   845                                    else _empty_metadata_singleton))
       
   846 
       
   847     def __setattr__(self, name, value):
       
   848         raise FrozenInstanceError()
       
   849 
       
   850     @classmethod
       
   851     def from_counting_attr(cls, name, ca):
       
   852         inst_dict = {
       
   853             k: getattr(ca, k)
       
   854             for k
       
   855             in Attribute.__slots__
       
   856             if k not in (
       
   857                 "name", "validator", "default",
       
   858             )  # exclude methods
       
   859         }
       
   860         return cls(name=name, validator=ca._validator, default=ca._default,
       
   861                    **inst_dict)
       
   862 
       
   863     # Don't use _add_pickle since fields(Attribute) doesn't work
       
   864     def __getstate__(self):
       
   865         """
       
   866         Play nice with pickle.
       
   867         """
       
   868         return tuple(getattr(self, name) if name != "metadata"
       
   869                      else dict(self.metadata)
       
   870                      for name in self.__slots__)
       
   871 
       
   872     def __setstate__(self, state):
       
   873         """
       
   874         Play nice with pickle.
       
   875         """
       
   876         bound_setattr = _obj_setattr.__get__(self, Attribute)
       
   877         for name, value in zip(self.__slots__, state):
       
   878             if name != "metadata":
       
   879                 bound_setattr(name, value)
       
   880             else:
       
   881                 bound_setattr(name, metadata_proxy(value) if value else
       
   882                               _empty_metadata_singleton)
       
   883 
       
   884 
       
   885 _a = [Attribute(name=name, default=NOTHING, validator=None,
       
   886                 repr=True, cmp=True, hash=(name != "metadata"), init=True)
       
   887       for name in Attribute.__slots__]
       
   888 
       
   889 Attribute = _add_hash(
       
   890     _add_cmp(_add_repr(Attribute, attrs=_a), attrs=_a),
       
   891     attrs=[a for a in _a if a.hash]
       
   892 )
       
   893 
       
   894 
       
   895 class _CountingAttr(object):
       
   896     """
       
   897     Intermediate representation of attributes that uses a counter to preserve
       
   898     the order in which the attributes have been defined.
       
   899 
       
   900     *Internal* data structure of the attrs library.  Running into is most
       
   901     likely the result of a bug like a forgotten `@attr.s` decorator.
       
   902     """
       
   903     __slots__ = ("counter", "_default", "repr", "cmp", "hash", "init",
       
   904                  "metadata", "_validator", "convert")
       
   905     __attrs_attrs__ = tuple(
       
   906         Attribute(name=name, default=NOTHING, validator=None,
       
   907                   repr=True, cmp=True, hash=True, init=True)
       
   908         for name
       
   909         in ("counter", "_default", "repr", "cmp", "hash", "init",)
       
   910     ) + (
       
   911         Attribute(name="metadata", default=None, validator=None,
       
   912                   repr=True, cmp=True, hash=False, init=True),
       
   913     )
       
   914     cls_counter = 0
       
   915 
       
   916     def __init__(self, default, validator, repr, cmp, hash, init, convert,
       
   917                  metadata):
       
   918         _CountingAttr.cls_counter += 1
       
   919         self.counter = _CountingAttr.cls_counter
       
   920         self._default = default
       
   921         # If validator is a list/tuple, wrap it using helper validator.
       
   922         if validator and isinstance(validator, (list, tuple)):
       
   923             self._validator = and_(*validator)
       
   924         else:
       
   925             self._validator = validator
       
   926         self.repr = repr
       
   927         self.cmp = cmp
       
   928         self.hash = hash
       
   929         self.init = init
       
   930         self.convert = convert
       
   931         self.metadata = metadata
       
   932 
       
   933     def validator(self, meth):
       
   934         """
       
   935         Decorator that adds *meth* to the list of validators.
       
   936 
       
   937         Returns *meth* unchanged.
       
   938 
       
   939         .. versionadded:: 17.1.0
       
   940         """
       
   941         if self._validator is None:
       
   942             self._validator = meth
       
   943         else:
       
   944             self._validator = and_(self._validator, meth)
       
   945         return meth
       
   946 
       
   947     def default(self, meth):
       
   948         """
       
   949         Decorator that allows to set the default for an attribute.
       
   950 
       
   951         Returns *meth* unchanged.
       
   952 
       
   953         :raises DefaultAlreadySetError: If default has been set before.
       
   954 
       
   955         .. versionadded:: 17.1.0
       
   956         """
       
   957         if self._default is not NOTHING:
       
   958             raise DefaultAlreadySetError()
       
   959 
       
   960         self._default = Factory(meth, takes_self=True)
       
   961 
       
   962         return meth
       
   963 
       
   964 
       
   965 _CountingAttr = _add_cmp(_add_repr(_CountingAttr))
       
   966 
       
   967 
       
   968 @attributes(slots=True, init=False)
       
   969 class Factory(object):
       
   970     """
       
   971     Stores a factory callable.
       
   972 
       
   973     If passed as the default value to :func:`attr.ib`, the factory is used to
       
   974     generate a new value.
       
   975 
       
   976     :param callable factory: A callable that takes either none or exactly one
       
   977         mandatory positional argument depending on *takes_self*.
       
   978     :param bool takes_self: Pass the partially initialized instance that is
       
   979         being initialized as a positional argument.
       
   980 
       
   981     .. versionadded:: 17.1.0  *takes_self*
       
   982     """
       
   983     factory = attr()
       
   984     takes_self = attr()
       
   985 
       
   986     def __init__(self, factory, takes_self=False):
       
   987         """
       
   988         `Factory` is part of the default machinery so if we want a default
       
   989         value here, we have to implement it ourselves.
       
   990         """
       
   991         self.factory = factory
       
   992         self.takes_self = takes_self
       
   993 
       
   994 
       
   995 def make_class(name, attrs, bases=(object,), **attributes_arguments):
       
   996     """
       
   997     A quick way to create a new class called *name* with *attrs*.
       
   998 
       
   999     :param name: The name for the new class.
       
  1000     :type name: str
       
  1001 
       
  1002     :param attrs: A list of names or a dictionary of mappings of names to
       
  1003         attributes.
       
  1004     :type attrs: :class:`list` or :class:`dict`
       
  1005 
       
  1006     :param tuple bases: Classes that the new class will subclass.
       
  1007 
       
  1008     :param attributes_arguments: Passed unmodified to :func:`attr.s`.
       
  1009 
       
  1010     :return: A new class with *attrs*.
       
  1011     :rtype: type
       
  1012 
       
  1013     ..  versionadded:: 17.1.0 *bases*
       
  1014     """
       
  1015     if isinstance(attrs, dict):
       
  1016         cls_dict = attrs
       
  1017     elif isinstance(attrs, (list, tuple)):
       
  1018         cls_dict = dict((a, attr()) for a in attrs)
       
  1019     else:
       
  1020         raise TypeError("attrs argument must be a dict or a list.")
       
  1021 
       
  1022     return attributes(**attributes_arguments)(type(name, bases, cls_dict))
       
  1023 
       
  1024 
       
  1025 # These are required by whithin this module so we define them here and merely
       
  1026 # import into .validators.
       
  1027 
       
  1028 
       
  1029 @attributes(slots=True, hash=True)
       
  1030 class _AndValidator(object):
       
  1031     """
       
  1032     Compose many validators to a single one.
       
  1033     """
       
  1034     _validators = attr()
       
  1035 
       
  1036     def __call__(self, inst, attr, value):
       
  1037         for v in self._validators:
       
  1038             v(inst, attr, value)
       
  1039 
       
  1040 
       
  1041 def and_(*validators):
       
  1042     """
       
  1043     A validator that composes multiple validators into one.
       
  1044 
       
  1045     When called on a value, it runs all wrapped validators.
       
  1046 
       
  1047     :param validators: Arbitrary number of validators.
       
  1048     :type validators: callables
       
  1049 
       
  1050     .. versionadded:: 17.1.0
       
  1051     """
       
  1052     vals = []
       
  1053     for validator in validators:
       
  1054         vals.extend(
       
  1055             validator._validators if isinstance(validator, _AndValidator)
       
  1056             else [validator]
       
  1057         )
       
  1058 
       
  1059     return _AndValidator(tuple(vals))