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