##// END OF EJS Templates
thirdparty: vendor attrs...
Siddharth Agarwal -
r34398:765eb17a default
parent child Browse files
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,21
1 The MIT License (MIT)
2
3 Copyright (c) 2015 Hynek Schlawack
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in all
13 copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 SOFTWARE.
@@ -0,0 +1,71
1 from __future__ import absolute_import, division, print_function
2
3 from ._funcs import (
4 asdict,
5 assoc,
6 astuple,
7 evolve,
8 has,
9 )
10 from ._make import (
11 Attribute,
12 Factory,
13 NOTHING,
14 attr,
15 attributes,
16 fields,
17 make_class,
18 validate,
19 )
20 from ._config import (
21 get_run_validators,
22 set_run_validators,
23 )
24 from . import exceptions
25 from . import filters
26 from . import converters
27 from . import validators
28
29
30 __version__ = "17.2.0"
31
32 __title__ = "attrs"
33 __description__ = "Classes Without Boilerplate"
34 __uri__ = "http://www.attrs.org/"
35 __doc__ = __description__ + " <" + __uri__ + ">"
36
37 __author__ = "Hynek Schlawack"
38 __email__ = "hs@ox.cx"
39
40 __license__ = "MIT"
41 __copyright__ = "Copyright (c) 2015 Hynek Schlawack"
42
43
44 s = attrs = attributes
45 ib = attrib = attr
46
47 __all__ = [
48 "Attribute",
49 "Factory",
50 "NOTHING",
51 "asdict",
52 "assoc",
53 "astuple",
54 "attr",
55 "attrib",
56 "attributes",
57 "attrs",
58 "converters",
59 "evolve",
60 "exceptions",
61 "fields",
62 "filters",
63 "get_run_validators",
64 "has",
65 "ib",
66 "make_class",
67 "s",
68 "set_run_validators",
69 "validate",
70 "validators",
71 ]
@@ -0,0 +1,90
1 from __future__ import absolute_import, division, print_function
2
3 import sys
4 import types
5
6
7 PY2 = sys.version_info[0] == 2
8
9
10 if PY2:
11 from UserDict import IterableUserDict
12
13 # We 'bundle' isclass instead of using inspect as importing inspect is
14 # fairly expensive (order of 10-15 ms for a modern machine in 2016)
15 def isclass(klass):
16 return isinstance(klass, (type, types.ClassType))
17
18 # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
19 TYPE = "type"
20
21 def iteritems(d):
22 return d.iteritems()
23
24 def iterkeys(d):
25 return d.iterkeys()
26
27 # Python 2 is bereft of a read-only dict proxy, so we make one!
28 class ReadOnlyDict(IterableUserDict):
29 """
30 Best-effort read-only dict wrapper.
31 """
32
33 def __setitem__(self, key, val):
34 # We gently pretend we're a Python 3 mappingproxy.
35 raise TypeError("'mappingproxy' object does not support item "
36 "assignment")
37
38 def update(self, _):
39 # We gently pretend we're a Python 3 mappingproxy.
40 raise AttributeError("'mappingproxy' object has no attribute "
41 "'update'")
42
43 def __delitem__(self, _):
44 # We gently pretend we're a Python 3 mappingproxy.
45 raise TypeError("'mappingproxy' object does not support item "
46 "deletion")
47
48 def clear(self):
49 # We gently pretend we're a Python 3 mappingproxy.
50 raise AttributeError("'mappingproxy' object has no attribute "
51 "'clear'")
52
53 def pop(self, key, default=None):
54 # We gently pretend we're a Python 3 mappingproxy.
55 raise AttributeError("'mappingproxy' object has no attribute "
56 "'pop'")
57
58 def popitem(self):
59 # We gently pretend we're a Python 3 mappingproxy.
60 raise AttributeError("'mappingproxy' object has no attribute "
61 "'popitem'")
62
63 def setdefault(self, key, default=None):
64 # We gently pretend we're a Python 3 mappingproxy.
65 raise AttributeError("'mappingproxy' object has no attribute "
66 "'setdefault'")
67
68 def __repr__(self):
69 # Override to be identical to the Python 3 version.
70 return "mappingproxy(" + repr(self.data) + ")"
71
72 def metadata_proxy(d):
73 res = ReadOnlyDict()
74 res.data.update(d) # We blocked update, so we have to do it like this.
75 return res
76
77 else:
78 def isclass(klass):
79 return isinstance(klass, type)
80
81 TYPE = "class"
82
83 def iteritems(d):
84 return d.items()
85
86 def iterkeys(d):
87 return d.keys()
88
89 def metadata_proxy(d):
90 return types.MappingProxyType(dict(d))
@@ -0,0 +1,23
1 from __future__ import absolute_import, division, print_function
2
3
4 __all__ = ["set_run_validators", "get_run_validators"]
5
6 _run_validators = True
7
8
9 def set_run_validators(run):
10 """
11 Set whether or not validators are run. By default, they are run.
12 """
13 if not isinstance(run, bool):
14 raise TypeError("'run' must be bool.")
15 global _run_validators
16 _run_validators = run
17
18
19 def get_run_validators():
20 """
21 Return whether or not validators are run.
22 """
23 return _run_validators
@@ -0,0 +1,212
1 from __future__ import absolute_import, division, print_function
2
3 import copy
4
5 from ._compat import iteritems
6 from ._make import NOTHING, fields, _obj_setattr
7 from .exceptions import AttrsAttributeNotFoundError
8
9
10 def asdict(inst, recurse=True, filter=None, dict_factory=dict,
11 retain_collection_types=False):
12 """
13 Return the ``attrs`` attribute values of *inst* as a dict.
14
15 Optionally recurse into other ``attrs``-decorated classes.
16
17 :param inst: Instance of an ``attrs``-decorated class.
18 :param bool recurse: Recurse into classes that are also
19 ``attrs``-decorated.
20 :param callable filter: A callable whose return code deteremines whether an
21 attribute or element is included (``True``) or dropped (``False``). Is
22 called with the :class:`attr.Attribute` as the first argument and the
23 value as the second argument.
24 :param callable dict_factory: A callable to produce dictionaries from. For
25 example, to produce ordered dictionaries instead of normal Python
26 dictionaries, pass in ``collections.OrderedDict``.
27 :param bool retain_collection_types: Do not convert to ``list`` when
28 encountering an attribute whose type is ``tuple`` or ``set``. Only
29 meaningful if ``recurse`` is ``True``.
30
31 :rtype: return type of *dict_factory*
32
33 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
34 class.
35
36 .. versionadded:: 16.0.0 *dict_factory*
37 .. versionadded:: 16.1.0 *retain_collection_types*
38 """
39 attrs = fields(inst.__class__)
40 rv = dict_factory()
41 for a in attrs:
42 v = getattr(inst, a.name)
43 if filter is not None and not filter(a, v):
44 continue
45 if recurse is True:
46 if has(v.__class__):
47 rv[a.name] = asdict(v, recurse=True, filter=filter,
48 dict_factory=dict_factory)
49 elif isinstance(v, (tuple, list, set)):
50 cf = v.__class__ if retain_collection_types is True else list
51 rv[a.name] = cf([
52 asdict(i, recurse=True, filter=filter,
53 dict_factory=dict_factory)
54 if has(i.__class__) else i
55 for i in v
56 ])
57 elif isinstance(v, dict):
58 df = dict_factory
59 rv[a.name] = df((
60 asdict(kk, dict_factory=df) if has(kk.__class__) else kk,
61 asdict(vv, dict_factory=df) if has(vv.__class__) else vv)
62 for kk, vv in iteritems(v))
63 else:
64 rv[a.name] = v
65 else:
66 rv[a.name] = v
67 return rv
68
69
70 def astuple(inst, recurse=True, filter=None, tuple_factory=tuple,
71 retain_collection_types=False):
72 """
73 Return the ``attrs`` attribute values of *inst* as a tuple.
74
75 Optionally recurse into other ``attrs``-decorated classes.
76
77 :param inst: Instance of an ``attrs``-decorated class.
78 :param bool recurse: Recurse into classes that are also
79 ``attrs``-decorated.
80 :param callable filter: A callable whose return code determines whether an
81 attribute or element is included (``True``) or dropped (``False``). Is
82 called with the :class:`attr.Attribute` as the first argument and the
83 value as the second argument.
84 :param callable tuple_factory: A callable to produce tuples from. For
85 example, to produce lists instead of tuples.
86 :param bool retain_collection_types: Do not convert to ``list``
87 or ``dict`` when encountering an attribute which type is
88 ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is
89 ``True``.
90
91 :rtype: return type of *tuple_factory*
92
93 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
94 class.
95
96 .. versionadded:: 16.2.0
97 """
98 attrs = fields(inst.__class__)
99 rv = []
100 retain = retain_collection_types # Very long. :/
101 for a in attrs:
102 v = getattr(inst, a.name)
103 if filter is not None and not filter(a, v):
104 continue
105 if recurse is True:
106 if has(v.__class__):
107 rv.append(astuple(v, recurse=True, filter=filter,
108 tuple_factory=tuple_factory,
109 retain_collection_types=retain))
110 elif isinstance(v, (tuple, list, set)):
111 cf = v.__class__ if retain is True else list
112 rv.append(cf([
113 astuple(j, recurse=True, filter=filter,
114 tuple_factory=tuple_factory,
115 retain_collection_types=retain)
116 if has(j.__class__) else j
117 for j in v
118 ]))
119 elif isinstance(v, dict):
120 df = v.__class__ if retain is True else dict
121 rv.append(df(
122 (
123 astuple(
124 kk,
125 tuple_factory=tuple_factory,
126 retain_collection_types=retain
127 ) if has(kk.__class__) else kk,
128 astuple(
129 vv,
130 tuple_factory=tuple_factory,
131 retain_collection_types=retain
132 ) if has(vv.__class__) else vv
133 )
134 for kk, vv in iteritems(v)))
135 else:
136 rv.append(v)
137 else:
138 rv.append(v)
139 return rv if tuple_factory is list else tuple_factory(rv)
140
141
142 def has(cls):
143 """
144 Check whether *cls* is a class with ``attrs`` attributes.
145
146 :param type cls: Class to introspect.
147 :raise TypeError: If *cls* is not a class.
148
149 :rtype: :class:`bool`
150 """
151 return getattr(cls, "__attrs_attrs__", None) is not None
152
153
154 def assoc(inst, **changes):
155 """
156 Copy *inst* and apply *changes*.
157
158 :param inst: Instance of a class with ``attrs`` attributes.
159 :param changes: Keyword changes in the new copy.
160
161 :return: A copy of inst with *changes* incorporated.
162
163 :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
164 be found on *cls*.
165 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
166 class.
167
168 .. deprecated:: 17.1.0
169 Use :func:`evolve` instead.
170 """
171 import warnings
172 warnings.warn("assoc is deprecated and will be removed after 2018/01.",
173 DeprecationWarning)
174 new = copy.copy(inst)
175 attrs = fields(inst.__class__)
176 for k, v in iteritems(changes):
177 a = getattr(attrs, k, NOTHING)
178 if a is NOTHING:
179 raise AttrsAttributeNotFoundError(
180 "{k} is not an attrs attribute on {cl}."
181 .format(k=k, cl=new.__class__)
182 )
183 _obj_setattr(new, k, v)
184 return new
185
186
187 def evolve(inst, **changes):
188 """
189 Create a new instance, based on *inst* with *changes* applied.
190
191 :param inst: Instance of a class with ``attrs`` attributes.
192 :param changes: Keyword changes in the new copy.
193
194 :return: A copy of inst with *changes* incorporated.
195
196 :raise TypeError: If *attr_name* couldn't be found in the class
197 ``__init__``.
198 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
199 class.
200
201 .. versionadded:: 17.1.0
202 """
203 cls = inst.__class__
204 attrs = fields(cls)
205 for a in attrs:
206 if not a.init:
207 continue
208 attr_name = a.name # To deal with private attributes.
209 init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
210 if init_name not in changes:
211 changes[init_name] = getattr(inst, attr_name)
212 return cls(**changes)
This diff has been collapsed as it changes many lines, (1059 lines changed) Show them Hide them
@@ -0,0 +1,1059
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))
@@ -0,0 +1,24
1 """
2 Commonly useful converters.
3 """
4
5 from __future__ import absolute_import, division, print_function
6
7
8 def optional(converter):
9 """
10 A converter that allows an attribute to be optional. An optional attribute
11 is one which can be set to ``None``.
12
13 :param callable converter: the converter that is used for non-``None``
14 values.
15
16 .. versionadded:: 17.1.0
17 """
18
19 def optional_converter(val):
20 if val is None:
21 return None
22 return converter(val)
23
24 return optional_converter
@@ -0,0 +1,39
1 from __future__ import absolute_import, division, print_function
2
3
4 class FrozenInstanceError(AttributeError):
5 """
6 A frozen/immutable instance has been attempted to be modified.
7
8 It mirrors the behavior of ``namedtuples`` by using the same error message
9 and subclassing :exc:`AttributeError`.
10
11 .. versionadded:: 16.1.0
12 """
13 msg = "can't set attribute"
14 args = [msg]
15
16
17 class AttrsAttributeNotFoundError(ValueError):
18 """
19 An ``attrs`` function couldn't find an attribute that the user asked for.
20
21 .. versionadded:: 16.2.0
22 """
23
24
25 class NotAnAttrsClassError(ValueError):
26 """
27 A non-``attrs`` class has been passed into an ``attrs`` function.
28
29 .. versionadded:: 16.2.0
30 """
31
32
33 class DefaultAlreadySetError(RuntimeError):
34 """
35 A default has been set using ``attr.ib()`` and is attempted to be reset
36 using the decorator.
37
38 .. versionadded:: 17.1.0
39 """
@@ -0,0 +1,52
1 """
2 Commonly useful filters for :func:`attr.asdict`.
3 """
4
5 from __future__ import absolute_import, division, print_function
6
7 from ._compat import isclass
8 from ._make import Attribute
9
10
11 def _split_what(what):
12 """
13 Returns a tuple of `frozenset`s of classes and attributes.
14 """
15 return (
16 frozenset(cls for cls in what if isclass(cls)),
17 frozenset(cls for cls in what if isinstance(cls, Attribute)),
18 )
19
20
21 def include(*what):
22 """
23 Whitelist *what*.
24
25 :param what: What to whitelist.
26 :type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\ s
27
28 :rtype: :class:`callable`
29 """
30 cls, attrs = _split_what(what)
31
32 def include_(attribute, value):
33 return value.__class__ in cls or attribute in attrs
34
35 return include_
36
37
38 def exclude(*what):
39 """
40 Blacklist *what*.
41
42 :param what: What to blacklist.
43 :type what: :class:`list` of classes or :class:`attr.Attribute`\ s.
44
45 :rtype: :class:`callable`
46 """
47 cls, attrs = _split_what(what)
48
49 def exclude_(attribute, value):
50 return value.__class__ not in cls and attribute not in attrs
51
52 return exclude_
@@ -0,0 +1,166
1 """
2 Commonly useful validators.
3 """
4
5 from __future__ import absolute_import, division, print_function
6
7 from ._make import attr, attributes, and_, _AndValidator
8
9
10 __all__ = [
11 "and_",
12 "in_",
13 "instance_of",
14 "optional",
15 "provides",
16 ]
17
18
19 @attributes(repr=False, slots=True, hash=True)
20 class _InstanceOfValidator(object):
21 type = attr()
22
23 def __call__(self, inst, attr, value):
24 """
25 We use a callable class to be able to change the ``__repr__``.
26 """
27 if not isinstance(value, self.type):
28 raise TypeError(
29 "'{name}' must be {type!r} (got {value!r} that is a "
30 "{actual!r})."
31 .format(name=attr.name, type=self.type,
32 actual=value.__class__, value=value),
33 attr, self.type, value,
34 )
35
36 def __repr__(self):
37 return (
38 "<instance_of validator for type {type!r}>"
39 .format(type=self.type)
40 )
41
42
43 def instance_of(type):
44 """
45 A validator that raises a :exc:`TypeError` if the initializer is called
46 with a wrong type for this particular attribute (checks are perfomed using
47 :func:`isinstance` therefore it's also valid to pass a tuple of types).
48
49 :param type: The type to check for.
50 :type type: type or tuple of types
51
52 :raises TypeError: With a human readable error message, the attribute
53 (of type :class:`attr.Attribute`), the expected type, and the value it
54 got.
55 """
56 return _InstanceOfValidator(type)
57
58
59 @attributes(repr=False, slots=True, hash=True)
60 class _ProvidesValidator(object):
61 interface = attr()
62
63 def __call__(self, inst, attr, value):
64 """
65 We use a callable class to be able to change the ``__repr__``.
66 """
67 if not self.interface.providedBy(value):
68 raise TypeError(
69 "'{name}' must provide {interface!r} which {value!r} "
70 "doesn't."
71 .format(name=attr.name, interface=self.interface, value=value),
72 attr, self.interface, value,
73 )
74
75 def __repr__(self):
76 return (
77 "<provides validator for interface {interface!r}>"
78 .format(interface=self.interface)
79 )
80
81
82 def provides(interface):
83 """
84 A validator that raises a :exc:`TypeError` if the initializer is called
85 with an object that does not provide the requested *interface* (checks are
86 performed using ``interface.providedBy(value)`` (see `zope.interface
87 <https://zopeinterface.readthedocs.io/en/latest/>`_).
88
89 :param zope.interface.Interface interface: The interface to check for.
90
91 :raises TypeError: With a human readable error message, the attribute
92 (of type :class:`attr.Attribute`), the expected interface, and the
93 value it got.
94 """
95 return _ProvidesValidator(interface)
96
97
98 @attributes(repr=False, slots=True, hash=True)
99 class _OptionalValidator(object):
100 validator = attr()
101
102 def __call__(self, inst, attr, value):
103 if value is None:
104 return
105
106 self.validator(inst, attr, value)
107
108 def __repr__(self):
109 return (
110 "<optional validator for {what} or None>"
111 .format(what=repr(self.validator))
112 )
113
114
115 def optional(validator):
116 """
117 A validator that makes an attribute optional. An optional attribute is one
118 which can be set to ``None`` in addition to satisfying the requirements of
119 the sub-validator.
120
121 :param validator: A validator (or a list of validators) that is used for
122 non-``None`` values.
123 :type validator: callable or :class:`list` of callables.
124
125 .. versionadded:: 15.1.0
126 .. versionchanged:: 17.1.0 *validator* can be a list of validators.
127 """
128 if isinstance(validator, list):
129 return _OptionalValidator(_AndValidator(validator))
130 return _OptionalValidator(validator)
131
132
133 @attributes(repr=False, slots=True, hash=True)
134 class _InValidator(object):
135 options = attr()
136
137 def __call__(self, inst, attr, value):
138 if value not in self.options:
139 raise ValueError(
140 "'{name}' must be in {options!r} (got {value!r})"
141 .format(name=attr.name, options=self.options, value=value)
142 )
143
144 def __repr__(self):
145 return (
146 "<in_ validator with options {options!r}>"
147 .format(options=self.options)
148 )
149
150
151 def in_(options):
152 """
153 A validator that raises a :exc:`ValueError` if the initializer is called
154 with a value that does not belong in the options provided. The check is
155 performed using ``value in options``.
156
157 :param options: Allowed options.
158 :type options: list, tuple, :class:`enum.Enum`, ...
159
160 :raises ValueError: With a human readable error message, the attribute (of
161 type :class:`attr.Attribute`), the expected options, and the value it
162 got.
163
164 .. versionadded:: 17.1.0
165 """
166 return _InValidator(options)
@@ -709,6 +709,8 packages = ['mercurial',
709 'mercurial.hgweb',
709 'mercurial.hgweb',
710 'mercurial.httpclient',
710 'mercurial.httpclient',
711 'mercurial.pure',
711 'mercurial.pure',
712 'mercurial.thirdparty',
713 'mercurial.thirdparty.attr',
712 'hgext', 'hgext.convert', 'hgext.fsmonitor',
714 'hgext', 'hgext.convert', 'hgext.fsmonitor',
713 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
715 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
714 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
716 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
General Comments 0
You need to be logged in to leave comments. Login now