##// END OF EJS Templates
attr: vendor 22.1.0...
Matt Harbison -
r50538:e1c586b9 default
parent child Browse files
Show More
@@ -0,0 +1,486 b''
1 import sys
2
3 from typing import (
4 Any,
5 Callable,
6 ClassVar,
7 Dict,
8 Generic,
9 List,
10 Mapping,
11 Optional,
12 Protocol,
13 Sequence,
14 Tuple,
15 Type,
16 TypeVar,
17 Union,
18 overload,
19 )
20
21 # `import X as X` is required to make these public
22 from . import converters as converters
23 from . import exceptions as exceptions
24 from . import filters as filters
25 from . import setters as setters
26 from . import validators as validators
27 from ._cmp import cmp_using as cmp_using
28 from ._version_info import VersionInfo
29
30 __version__: str
31 __version_info__: VersionInfo
32 __title__: str
33 __description__: str
34 __url__: str
35 __uri__: str
36 __author__: str
37 __email__: str
38 __license__: str
39 __copyright__: str
40
41 _T = TypeVar("_T")
42 _C = TypeVar("_C", bound=type)
43
44 _EqOrderType = Union[bool, Callable[[Any], Any]]
45 _ValidatorType = Callable[[Any, Attribute[_T], _T], Any]
46 _ConverterType = Callable[[Any], Any]
47 _FilterType = Callable[[Attribute[_T], _T], bool]
48 _ReprType = Callable[[Any], str]
49 _ReprArgType = Union[bool, _ReprType]
50 _OnSetAttrType = Callable[[Any, Attribute[Any], Any], Any]
51 _OnSetAttrArgType = Union[
52 _OnSetAttrType, List[_OnSetAttrType], setters._NoOpType
53 ]
54 _FieldTransformer = Callable[
55 [type, List[Attribute[Any]]], List[Attribute[Any]]
56 ]
57 # FIXME: in reality, if multiple validators are passed they must be in a list
58 # or tuple, but those are invariant and so would prevent subtypes of
59 # _ValidatorType from working when passed in a list or tuple.
60 _ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
61
62 # A protocol to be able to statically accept an attrs class.
63 class AttrsInstance(Protocol):
64 __attrs_attrs__: ClassVar[Any]
65
66 # _make --
67
68 NOTHING: object
69
70 # NOTE: Factory lies about its return type to make this possible:
71 # `x: List[int] # = Factory(list)`
72 # Work around mypy issue #4554 in the common case by using an overload.
73 if sys.version_info >= (3, 8):
74 from typing import Literal
75 @overload
76 def Factory(factory: Callable[[], _T]) -> _T: ...
77 @overload
78 def Factory(
79 factory: Callable[[Any], _T],
80 takes_self: Literal[True],
81 ) -> _T: ...
82 @overload
83 def Factory(
84 factory: Callable[[], _T],
85 takes_self: Literal[False],
86 ) -> _T: ...
87
88 else:
89 @overload
90 def Factory(factory: Callable[[], _T]) -> _T: ...
91 @overload
92 def Factory(
93 factory: Union[Callable[[Any], _T], Callable[[], _T]],
94 takes_self: bool = ...,
95 ) -> _T: ...
96
97 # Static type inference support via __dataclass_transform__ implemented as per:
98 # https://github.com/microsoft/pyright/blob/1.1.135/specs/dataclass_transforms.md
99 # This annotation must be applied to all overloads of "define" and "attrs"
100 #
101 # NOTE: This is a typing construct and does not exist at runtime. Extensions
102 # wrapping attrs decorators should declare a separate __dataclass_transform__
103 # signature in the extension module using the specification linked above to
104 # provide pyright support.
105 def __dataclass_transform__(
106 *,
107 eq_default: bool = True,
108 order_default: bool = False,
109 kw_only_default: bool = False,
110 field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()),
111 ) -> Callable[[_T], _T]: ...
112
113 class Attribute(Generic[_T]):
114 name: str
115 default: Optional[_T]
116 validator: Optional[_ValidatorType[_T]]
117 repr: _ReprArgType
118 cmp: _EqOrderType
119 eq: _EqOrderType
120 order: _EqOrderType
121 hash: Optional[bool]
122 init: bool
123 converter: Optional[_ConverterType]
124 metadata: Dict[Any, Any]
125 type: Optional[Type[_T]]
126 kw_only: bool
127 on_setattr: _OnSetAttrType
128 def evolve(self, **changes: Any) -> "Attribute[Any]": ...
129
130 # NOTE: We had several choices for the annotation to use for type arg:
131 # 1) Type[_T]
132 # - Pros: Handles simple cases correctly
133 # - Cons: Might produce less informative errors in the case of conflicting
134 # TypeVars e.g. `attr.ib(default='bad', type=int)`
135 # 2) Callable[..., _T]
136 # - Pros: Better error messages than #1 for conflicting TypeVars
137 # - Cons: Terrible error messages for validator checks.
138 # e.g. attr.ib(type=int, validator=validate_str)
139 # -> error: Cannot infer function type argument
140 # 3) type (and do all of the work in the mypy plugin)
141 # - Pros: Simple here, and we could customize the plugin with our own errors.
142 # - Cons: Would need to write mypy plugin code to handle all the cases.
143 # We chose option #1.
144
145 # `attr` lies about its return type to make the following possible:
146 # attr() -> Any
147 # attr(8) -> int
148 # attr(validator=<some callable>) -> Whatever the callable expects.
149 # This makes this type of assignments possible:
150 # x: int = attr(8)
151 #
152 # This form catches explicit None or no default but with no other arguments
153 # returns Any.
154 @overload
155 def attrib(
156 default: None = ...,
157 validator: None = ...,
158 repr: _ReprArgType = ...,
159 cmp: Optional[_EqOrderType] = ...,
160 hash: Optional[bool] = ...,
161 init: bool = ...,
162 metadata: Optional[Mapping[Any, Any]] = ...,
163 type: None = ...,
164 converter: None = ...,
165 factory: None = ...,
166 kw_only: bool = ...,
167 eq: Optional[_EqOrderType] = ...,
168 order: Optional[_EqOrderType] = ...,
169 on_setattr: Optional[_OnSetAttrArgType] = ...,
170 ) -> Any: ...
171
172 # This form catches an explicit None or no default and infers the type from the
173 # other arguments.
174 @overload
175 def attrib(
176 default: None = ...,
177 validator: Optional[_ValidatorArgType[_T]] = ...,
178 repr: _ReprArgType = ...,
179 cmp: Optional[_EqOrderType] = ...,
180 hash: Optional[bool] = ...,
181 init: bool = ...,
182 metadata: Optional[Mapping[Any, Any]] = ...,
183 type: Optional[Type[_T]] = ...,
184 converter: Optional[_ConverterType] = ...,
185 factory: Optional[Callable[[], _T]] = ...,
186 kw_only: bool = ...,
187 eq: Optional[_EqOrderType] = ...,
188 order: Optional[_EqOrderType] = ...,
189 on_setattr: Optional[_OnSetAttrArgType] = ...,
190 ) -> _T: ...
191
192 # This form catches an explicit default argument.
193 @overload
194 def attrib(
195 default: _T,
196 validator: Optional[_ValidatorArgType[_T]] = ...,
197 repr: _ReprArgType = ...,
198 cmp: Optional[_EqOrderType] = ...,
199 hash: Optional[bool] = ...,
200 init: bool = ...,
201 metadata: Optional[Mapping[Any, Any]] = ...,
202 type: Optional[Type[_T]] = ...,
203 converter: Optional[_ConverterType] = ...,
204 factory: Optional[Callable[[], _T]] = ...,
205 kw_only: bool = ...,
206 eq: Optional[_EqOrderType] = ...,
207 order: Optional[_EqOrderType] = ...,
208 on_setattr: Optional[_OnSetAttrArgType] = ...,
209 ) -> _T: ...
210
211 # This form covers type=non-Type: e.g. forward references (str), Any
212 @overload
213 def attrib(
214 default: Optional[_T] = ...,
215 validator: Optional[_ValidatorArgType[_T]] = ...,
216 repr: _ReprArgType = ...,
217 cmp: Optional[_EqOrderType] = ...,
218 hash: Optional[bool] = ...,
219 init: bool = ...,
220 metadata: Optional[Mapping[Any, Any]] = ...,
221 type: object = ...,
222 converter: Optional[_ConverterType] = ...,
223 factory: Optional[Callable[[], _T]] = ...,
224 kw_only: bool = ...,
225 eq: Optional[_EqOrderType] = ...,
226 order: Optional[_EqOrderType] = ...,
227 on_setattr: Optional[_OnSetAttrArgType] = ...,
228 ) -> Any: ...
229 @overload
230 def field(
231 *,
232 default: None = ...,
233 validator: None = ...,
234 repr: _ReprArgType = ...,
235 hash: Optional[bool] = ...,
236 init: bool = ...,
237 metadata: Optional[Mapping[Any, Any]] = ...,
238 converter: None = ...,
239 factory: None = ...,
240 kw_only: bool = ...,
241 eq: Optional[bool] = ...,
242 order: Optional[bool] = ...,
243 on_setattr: Optional[_OnSetAttrArgType] = ...,
244 ) -> Any: ...
245
246 # This form catches an explicit None or no default and infers the type from the
247 # other arguments.
248 @overload
249 def field(
250 *,
251 default: None = ...,
252 validator: Optional[_ValidatorArgType[_T]] = ...,
253 repr: _ReprArgType = ...,
254 hash: Optional[bool] = ...,
255 init: bool = ...,
256 metadata: Optional[Mapping[Any, Any]] = ...,
257 converter: Optional[_ConverterType] = ...,
258 factory: Optional[Callable[[], _T]] = ...,
259 kw_only: bool = ...,
260 eq: Optional[_EqOrderType] = ...,
261 order: Optional[_EqOrderType] = ...,
262 on_setattr: Optional[_OnSetAttrArgType] = ...,
263 ) -> _T: ...
264
265 # This form catches an explicit default argument.
266 @overload
267 def field(
268 *,
269 default: _T,
270 validator: Optional[_ValidatorArgType[_T]] = ...,
271 repr: _ReprArgType = ...,
272 hash: Optional[bool] = ...,
273 init: bool = ...,
274 metadata: Optional[Mapping[Any, Any]] = ...,
275 converter: Optional[_ConverterType] = ...,
276 factory: Optional[Callable[[], _T]] = ...,
277 kw_only: bool = ...,
278 eq: Optional[_EqOrderType] = ...,
279 order: Optional[_EqOrderType] = ...,
280 on_setattr: Optional[_OnSetAttrArgType] = ...,
281 ) -> _T: ...
282
283 # This form covers type=non-Type: e.g. forward references (str), Any
284 @overload
285 def field(
286 *,
287 default: Optional[_T] = ...,
288 validator: Optional[_ValidatorArgType[_T]] = ...,
289 repr: _ReprArgType = ...,
290 hash: Optional[bool] = ...,
291 init: bool = ...,
292 metadata: Optional[Mapping[Any, Any]] = ...,
293 converter: Optional[_ConverterType] = ...,
294 factory: Optional[Callable[[], _T]] = ...,
295 kw_only: bool = ...,
296 eq: Optional[_EqOrderType] = ...,
297 order: Optional[_EqOrderType] = ...,
298 on_setattr: Optional[_OnSetAttrArgType] = ...,
299 ) -> Any: ...
300 @overload
301 @__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
302 def attrs(
303 maybe_cls: _C,
304 these: Optional[Dict[str, Any]] = ...,
305 repr_ns: Optional[str] = ...,
306 repr: bool = ...,
307 cmp: Optional[_EqOrderType] = ...,
308 hash: Optional[bool] = ...,
309 init: bool = ...,
310 slots: bool = ...,
311 frozen: bool = ...,
312 weakref_slot: bool = ...,
313 str: bool = ...,
314 auto_attribs: bool = ...,
315 kw_only: bool = ...,
316 cache_hash: bool = ...,
317 auto_exc: bool = ...,
318 eq: Optional[_EqOrderType] = ...,
319 order: Optional[_EqOrderType] = ...,
320 auto_detect: bool = ...,
321 collect_by_mro: bool = ...,
322 getstate_setstate: Optional[bool] = ...,
323 on_setattr: Optional[_OnSetAttrArgType] = ...,
324 field_transformer: Optional[_FieldTransformer] = ...,
325 match_args: bool = ...,
326 ) -> _C: ...
327 @overload
328 @__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
329 def attrs(
330 maybe_cls: None = ...,
331 these: Optional[Dict[str, Any]] = ...,
332 repr_ns: Optional[str] = ...,
333 repr: bool = ...,
334 cmp: Optional[_EqOrderType] = ...,
335 hash: Optional[bool] = ...,
336 init: bool = ...,
337 slots: bool = ...,
338 frozen: bool = ...,
339 weakref_slot: bool = ...,
340 str: bool = ...,
341 auto_attribs: bool = ...,
342 kw_only: bool = ...,
343 cache_hash: bool = ...,
344 auto_exc: bool = ...,
345 eq: Optional[_EqOrderType] = ...,
346 order: Optional[_EqOrderType] = ...,
347 auto_detect: bool = ...,
348 collect_by_mro: bool = ...,
349 getstate_setstate: Optional[bool] = ...,
350 on_setattr: Optional[_OnSetAttrArgType] = ...,
351 field_transformer: Optional[_FieldTransformer] = ...,
352 match_args: bool = ...,
353 ) -> Callable[[_C], _C]: ...
354 @overload
355 @__dataclass_transform__(field_descriptors=(attrib, field))
356 def define(
357 maybe_cls: _C,
358 *,
359 these: Optional[Dict[str, Any]] = ...,
360 repr: bool = ...,
361 hash: Optional[bool] = ...,
362 init: bool = ...,
363 slots: bool = ...,
364 frozen: bool = ...,
365 weakref_slot: bool = ...,
366 str: bool = ...,
367 auto_attribs: bool = ...,
368 kw_only: bool = ...,
369 cache_hash: bool = ...,
370 auto_exc: bool = ...,
371 eq: Optional[bool] = ...,
372 order: Optional[bool] = ...,
373 auto_detect: bool = ...,
374 getstate_setstate: Optional[bool] = ...,
375 on_setattr: Optional[_OnSetAttrArgType] = ...,
376 field_transformer: Optional[_FieldTransformer] = ...,
377 match_args: bool = ...,
378 ) -> _C: ...
379 @overload
380 @__dataclass_transform__(field_descriptors=(attrib, field))
381 def define(
382 maybe_cls: None = ...,
383 *,
384 these: Optional[Dict[str, Any]] = ...,
385 repr: bool = ...,
386 hash: Optional[bool] = ...,
387 init: bool = ...,
388 slots: bool = ...,
389 frozen: bool = ...,
390 weakref_slot: bool = ...,
391 str: bool = ...,
392 auto_attribs: bool = ...,
393 kw_only: bool = ...,
394 cache_hash: bool = ...,
395 auto_exc: bool = ...,
396 eq: Optional[bool] = ...,
397 order: Optional[bool] = ...,
398 auto_detect: bool = ...,
399 getstate_setstate: Optional[bool] = ...,
400 on_setattr: Optional[_OnSetAttrArgType] = ...,
401 field_transformer: Optional[_FieldTransformer] = ...,
402 match_args: bool = ...,
403 ) -> Callable[[_C], _C]: ...
404
405 mutable = define
406 frozen = define # they differ only in their defaults
407
408 def fields(cls: Type[AttrsInstance]) -> Any: ...
409 def fields_dict(cls: Type[AttrsInstance]) -> Dict[str, Attribute[Any]]: ...
410 def validate(inst: AttrsInstance) -> None: ...
411 def resolve_types(
412 cls: _C,
413 globalns: Optional[Dict[str, Any]] = ...,
414 localns: Optional[Dict[str, Any]] = ...,
415 attribs: Optional[List[Attribute[Any]]] = ...,
416 ) -> _C: ...
417
418 # TODO: add support for returning a proper attrs class from the mypy plugin
419 # we use Any instead of _CountingAttr so that e.g. `make_class('Foo',
420 # [attr.ib()])` is valid
421 def make_class(
422 name: str,
423 attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
424 bases: Tuple[type, ...] = ...,
425 repr_ns: Optional[str] = ...,
426 repr: bool = ...,
427 cmp: Optional[_EqOrderType] = ...,
428 hash: Optional[bool] = ...,
429 init: bool = ...,
430 slots: bool = ...,
431 frozen: bool = ...,
432 weakref_slot: bool = ...,
433 str: bool = ...,
434 auto_attribs: bool = ...,
435 kw_only: bool = ...,
436 cache_hash: bool = ...,
437 auto_exc: bool = ...,
438 eq: Optional[_EqOrderType] = ...,
439 order: Optional[_EqOrderType] = ...,
440 collect_by_mro: bool = ...,
441 on_setattr: Optional[_OnSetAttrArgType] = ...,
442 field_transformer: Optional[_FieldTransformer] = ...,
443 ) -> type: ...
444
445 # _funcs --
446
447 # TODO: add support for returning TypedDict from the mypy plugin
448 # FIXME: asdict/astuple do not honor their factory args. Waiting on one of
449 # these:
450 # https://github.com/python/mypy/issues/4236
451 # https://github.com/python/typing/issues/253
452 # XXX: remember to fix attrs.asdict/astuple too!
453 def asdict(
454 inst: AttrsInstance,
455 recurse: bool = ...,
456 filter: Optional[_FilterType[Any]] = ...,
457 dict_factory: Type[Mapping[Any, Any]] = ...,
458 retain_collection_types: bool = ...,
459 value_serializer: Optional[
460 Callable[[type, Attribute[Any], Any], Any]
461 ] = ...,
462 tuple_keys: Optional[bool] = ...,
463 ) -> Dict[str, Any]: ...
464
465 # TODO: add support for returning NamedTuple from the mypy plugin
466 def astuple(
467 inst: AttrsInstance,
468 recurse: bool = ...,
469 filter: Optional[_FilterType[Any]] = ...,
470 tuple_factory: Type[Sequence[Any]] = ...,
471 retain_collection_types: bool = ...,
472 ) -> Tuple[Any, ...]: ...
473 def has(cls: type) -> bool: ...
474 def assoc(inst: _T, **changes: Any) -> _T: ...
475 def evolve(inst: _T, **changes: Any) -> _T: ...
476
477 # _config --
478
479 def set_run_validators(run: bool) -> None: ...
480 def get_run_validators() -> bool: ...
481
482 # aliases --
483
484 s = attributes = attrs
485 ib = attr = attrib
486 dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;)
@@ -0,0 +1,155 b''
1 # SPDX-License-Identifier: MIT
2
3
4 import functools
5 import types
6
7 from ._make import _make_ne
8
9
10 _operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="}
11
12
13 def cmp_using(
14 eq=None,
15 lt=None,
16 le=None,
17 gt=None,
18 ge=None,
19 require_same_type=True,
20 class_name="Comparable",
21 ):
22 """
23 Create a class that can be passed into `attr.ib`'s ``eq``, ``order``, and
24 ``cmp`` arguments to customize field comparison.
25
26 The resulting class will have a full set of ordering methods if
27 at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
28
29 :param Optional[callable] eq: `callable` used to evaluate equality
30 of two objects.
31 :param Optional[callable] lt: `callable` used to evaluate whether
32 one object is less than another object.
33 :param Optional[callable] le: `callable` used to evaluate whether
34 one object is less than or equal to another object.
35 :param Optional[callable] gt: `callable` used to evaluate whether
36 one object is greater than another object.
37 :param Optional[callable] ge: `callable` used to evaluate whether
38 one object is greater than or equal to another object.
39
40 :param bool require_same_type: When `True`, equality and ordering methods
41 will return `NotImplemented` if objects are not of the same type.
42
43 :param Optional[str] class_name: Name of class. Defaults to 'Comparable'.
44
45 See `comparison` for more details.
46
47 .. versionadded:: 21.1.0
48 """
49
50 body = {
51 "__slots__": ["value"],
52 "__init__": _make_init(),
53 "_requirements": [],
54 "_is_comparable_to": _is_comparable_to,
55 }
56
57 # Add operations.
58 num_order_functions = 0
59 has_eq_function = False
60
61 if eq is not None:
62 has_eq_function = True
63 body["__eq__"] = _make_operator("eq", eq)
64 body["__ne__"] = _make_ne()
65
66 if lt is not None:
67 num_order_functions += 1
68 body["__lt__"] = _make_operator("lt", lt)
69
70 if le is not None:
71 num_order_functions += 1
72 body["__le__"] = _make_operator("le", le)
73
74 if gt is not None:
75 num_order_functions += 1
76 body["__gt__"] = _make_operator("gt", gt)
77
78 if ge is not None:
79 num_order_functions += 1
80 body["__ge__"] = _make_operator("ge", ge)
81
82 type_ = types.new_class(
83 class_name, (object,), {}, lambda ns: ns.update(body)
84 )
85
86 # Add same type requirement.
87 if require_same_type:
88 type_._requirements.append(_check_same_type)
89
90 # Add total ordering if at least one operation was defined.
91 if 0 < num_order_functions < 4:
92 if not has_eq_function:
93 # functools.total_ordering requires __eq__ to be defined,
94 # so raise early error here to keep a nice stack.
95 raise ValueError(
96 "eq must be define is order to complete ordering from "
97 "lt, le, gt, ge."
98 )
99 type_ = functools.total_ordering(type_)
100
101 return type_
102
103
104 def _make_init():
105 """
106 Create __init__ method.
107 """
108
109 def __init__(self, value):
110 """
111 Initialize object with *value*.
112 """
113 self.value = value
114
115 return __init__
116
117
118 def _make_operator(name, func):
119 """
120 Create operator method.
121 """
122
123 def method(self, other):
124 if not self._is_comparable_to(other):
125 return NotImplemented
126
127 result = func(self.value, other.value)
128 if result is NotImplemented:
129 return NotImplemented
130
131 return result
132
133 method.__name__ = "__%s__" % (name,)
134 method.__doc__ = "Return a %s b. Computed by attrs." % (
135 _operation_names[name],
136 )
137
138 return method
139
140
141 def _is_comparable_to(self, other):
142 """
143 Check whether `other` is comparable to `self`.
144 """
145 for func in self._requirements:
146 if not func(self, other):
147 return False
148 return True
149
150
151 def _check_same_type(self, other):
152 """
153 Return True if *self* and *other* are of the same type, False otherwise.
154 """
155 return other.value.__class__ is self.value.__class__
@@ -0,0 +1,13 b''
1 from typing import Any, Callable, Optional, Type
2
3 _CompareWithType = Callable[[Any, Any], bool]
4
5 def cmp_using(
6 eq: Optional[_CompareWithType],
7 lt: Optional[_CompareWithType],
8 le: Optional[_CompareWithType],
9 gt: Optional[_CompareWithType],
10 ge: Optional[_CompareWithType],
11 require_same_type: bool,
12 class_name: str,
13 ) -> Type: ...
@@ -0,0 +1,220 b''
1 # SPDX-License-Identifier: MIT
2
3 """
4 These are Python 3.6+-only and keyword-only APIs that call `attr.s` and
5 `attr.ib` with different default values.
6 """
7
8
9 from functools import partial
10
11 from . import setters
12 from ._funcs import asdict as _asdict
13 from ._funcs import astuple as _astuple
14 from ._make import (
15 NOTHING,
16 _frozen_setattrs,
17 _ng_default_on_setattr,
18 attrib,
19 attrs,
20 )
21 from .exceptions import UnannotatedAttributeError
22
23
24 def define(
25 maybe_cls=None,
26 *,
27 these=None,
28 repr=None,
29 hash=None,
30 init=None,
31 slots=True,
32 frozen=False,
33 weakref_slot=True,
34 str=False,
35 auto_attribs=None,
36 kw_only=False,
37 cache_hash=False,
38 auto_exc=True,
39 eq=None,
40 order=False,
41 auto_detect=True,
42 getstate_setstate=None,
43 on_setattr=None,
44 field_transformer=None,
45 match_args=True,
46 ):
47 r"""
48 Define an ``attrs`` class.
49
50 Differences to the classic `attr.s` that it uses underneath:
51
52 - Automatically detect whether or not *auto_attribs* should be `True` (c.f.
53 *auto_attribs* parameter).
54 - If *frozen* is `False`, run converters and validators when setting an
55 attribute by default.
56 - *slots=True*
57
58 .. caution::
59
60 Usually this has only upsides and few visible effects in everyday
61 programming. But it *can* lead to some suprising behaviors, so please
62 make sure to read :term:`slotted classes`.
63 - *auto_exc=True*
64 - *auto_detect=True*
65 - *order=False*
66 - Some options that were only relevant on Python 2 or were kept around for
67 backwards-compatibility have been removed.
68
69 Please note that these are all defaults and you can change them as you
70 wish.
71
72 :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves
73 exactly like `attr.s`. If left `None`, `attr.s` will try to guess:
74
75 1. If any attributes are annotated and no unannotated `attrs.fields`\ s
76 are found, it assumes *auto_attribs=True*.
77 2. Otherwise it assumes *auto_attribs=False* and tries to collect
78 `attrs.fields`\ s.
79
80 For now, please refer to `attr.s` for the rest of the parameters.
81
82 .. versionadded:: 20.1.0
83 .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``.
84 """
85
86 def do_it(cls, auto_attribs):
87 return attrs(
88 maybe_cls=cls,
89 these=these,
90 repr=repr,
91 hash=hash,
92 init=init,
93 slots=slots,
94 frozen=frozen,
95 weakref_slot=weakref_slot,
96 str=str,
97 auto_attribs=auto_attribs,
98 kw_only=kw_only,
99 cache_hash=cache_hash,
100 auto_exc=auto_exc,
101 eq=eq,
102 order=order,
103 auto_detect=auto_detect,
104 collect_by_mro=True,
105 getstate_setstate=getstate_setstate,
106 on_setattr=on_setattr,
107 field_transformer=field_transformer,
108 match_args=match_args,
109 )
110
111 def wrap(cls):
112 """
113 Making this a wrapper ensures this code runs during class creation.
114
115 We also ensure that frozen-ness of classes is inherited.
116 """
117 nonlocal frozen, on_setattr
118
119 had_on_setattr = on_setattr not in (None, setters.NO_OP)
120
121 # By default, mutable classes convert & validate on setattr.
122 if frozen is False and on_setattr is None:
123 on_setattr = _ng_default_on_setattr
124
125 # However, if we subclass a frozen class, we inherit the immutability
126 # and disable on_setattr.
127 for base_cls in cls.__bases__:
128 if base_cls.__setattr__ is _frozen_setattrs:
129 if had_on_setattr:
130 raise ValueError(
131 "Frozen classes can't use on_setattr "
132 "(frozen-ness was inherited)."
133 )
134
135 on_setattr = setters.NO_OP
136 break
137
138 if auto_attribs is not None:
139 return do_it(cls, auto_attribs)
140
141 try:
142 return do_it(cls, True)
143 except UnannotatedAttributeError:
144 return do_it(cls, False)
145
146 # maybe_cls's type depends on the usage of the decorator. It's a class
147 # if it's used as `@attrs` but ``None`` if used as `@attrs()`.
148 if maybe_cls is None:
149 return wrap
150 else:
151 return wrap(maybe_cls)
152
153
154 mutable = define
155 frozen = partial(define, frozen=True, on_setattr=None)
156
157
158 def field(
159 *,
160 default=NOTHING,
161 validator=None,
162 repr=True,
163 hash=None,
164 init=True,
165 metadata=None,
166 converter=None,
167 factory=None,
168 kw_only=False,
169 eq=None,
170 order=None,
171 on_setattr=None,
172 ):
173 """
174 Identical to `attr.ib`, except keyword-only and with some arguments
175 removed.
176
177 .. versionadded:: 20.1.0
178 """
179 return attrib(
180 default=default,
181 validator=validator,
182 repr=repr,
183 hash=hash,
184 init=init,
185 metadata=metadata,
186 converter=converter,
187 factory=factory,
188 kw_only=kw_only,
189 eq=eq,
190 order=order,
191 on_setattr=on_setattr,
192 )
193
194
195 def asdict(inst, *, recurse=True, filter=None, value_serializer=None):
196 """
197 Same as `attr.asdict`, except that collections types are always retained
198 and dict is always used as *dict_factory*.
199
200 .. versionadded:: 21.3.0
201 """
202 return _asdict(
203 inst=inst,
204 recurse=recurse,
205 filter=filter,
206 value_serializer=value_serializer,
207 retain_collection_types=True,
208 )
209
210
211 def astuple(inst, *, recurse=True, filter=None):
212 """
213 Same as `attr.astuple`, except that collections types are always retained
214 and `tuple` is always used as the *tuple_factory*.
215
216 .. versionadded:: 21.3.0
217 """
218 return _astuple(
219 inst=inst, recurse=recurse, filter=filter, retain_collection_types=True
220 )
@@ -0,0 +1,86 b''
1 # SPDX-License-Identifier: MIT
2
3
4 from functools import total_ordering
5
6 from ._funcs import astuple
7 from ._make import attrib, attrs
8
9
10 @total_ordering
11 @attrs(eq=False, order=False, slots=True, frozen=True)
12 class VersionInfo:
13 """
14 A version object that can be compared to tuple of length 1--4:
15
16 >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2)
17 True
18 >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1)
19 True
20 >>> vi = attr.VersionInfo(19, 2, 0, "final")
21 >>> vi < (19, 1, 1)
22 False
23 >>> vi < (19,)
24 False
25 >>> vi == (19, 2,)
26 True
27 >>> vi == (19, 2, 1)
28 False
29
30 .. versionadded:: 19.2
31 """
32
33 year = attrib(type=int)
34 minor = attrib(type=int)
35 micro = attrib(type=int)
36 releaselevel = attrib(type=str)
37
38 @classmethod
39 def _from_version_string(cls, s):
40 """
41 Parse *s* and return a _VersionInfo.
42 """
43 v = s.split(".")
44 if len(v) == 3:
45 v.append("final")
46
47 return cls(
48 year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3]
49 )
50
51 def _ensure_tuple(self, other):
52 """
53 Ensure *other* is a tuple of a valid length.
54
55 Returns a possibly transformed *other* and ourselves as a tuple of
56 the same length as *other*.
57 """
58
59 if self.__class__ is other.__class__:
60 other = astuple(other)
61
62 if not isinstance(other, tuple):
63 raise NotImplementedError
64
65 if not (1 <= len(other) <= 4):
66 raise NotImplementedError
67
68 return astuple(self)[: len(other)], other
69
70 def __eq__(self, other):
71 try:
72 us, them = self._ensure_tuple(other)
73 except NotImplementedError:
74 return NotImplemented
75
76 return us == them
77
78 def __lt__(self, other):
79 try:
80 us, them = self._ensure_tuple(other)
81 except NotImplementedError:
82 return NotImplemented
83
84 # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't
85 # have to do anything special with releaselevel for now.
86 return us < them
@@ -0,0 +1,9 b''
1 class VersionInfo:
2 @property
3 def year(self) -> int: ...
4 @property
5 def minor(self) -> int: ...
6 @property
7 def micro(self) -> int: ...
8 @property
9 def releaselevel(self) -> str: ...
@@ -0,0 +1,13 b''
1 from typing import Callable, Optional, TypeVar, overload
2
3 from . import _ConverterType
4
5 _T = TypeVar("_T")
6
7 def pipe(*validators: _ConverterType) -> _ConverterType: ...
8 def optional(converter: _ConverterType) -> _ConverterType: ...
9 @overload
10 def default_if_none(default: _T) -> _ConverterType: ...
11 @overload
12 def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType: ...
13 def to_bool(val: str) -> bool: ...
@@ -0,0 +1,17 b''
1 from typing import Any
2
3 class FrozenError(AttributeError):
4 msg: str = ...
5
6 class FrozenInstanceError(FrozenError): ...
7 class FrozenAttributeError(FrozenError): ...
8 class AttrsAttributeNotFoundError(ValueError): ...
9 class NotAnAttrsClassError(ValueError): ...
10 class DefaultAlreadySetError(RuntimeError): ...
11 class UnannotatedAttributeError(RuntimeError): ...
12 class PythonTooOldError(RuntimeError): ...
13
14 class NotCallableError(TypeError):
15 msg: str = ...
16 value: Any = ...
17 def __init__(self, msg: str, value: Any) -> None: ...
@@ -0,0 +1,6 b''
1 from typing import Any, Union
2
3 from . import Attribute, _FilterType
4
5 def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
6 def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,73 b''
1 # SPDX-License-Identifier: MIT
2
3 """
4 Commonly used hooks for on_setattr.
5 """
6
7
8 from . import _config
9 from .exceptions import FrozenAttributeError
10
11
12 def pipe(*setters):
13 """
14 Run all *setters* and return the return value of the last one.
15
16 .. versionadded:: 20.1.0
17 """
18
19 def wrapped_pipe(instance, attrib, new_value):
20 rv = new_value
21
22 for setter in setters:
23 rv = setter(instance, attrib, rv)
24
25 return rv
26
27 return wrapped_pipe
28
29
30 def frozen(_, __, ___):
31 """
32 Prevent an attribute to be modified.
33
34 .. versionadded:: 20.1.0
35 """
36 raise FrozenAttributeError()
37
38
39 def validate(instance, attrib, new_value):
40 """
41 Run *attrib*'s validator on *new_value* if it has one.
42
43 .. versionadded:: 20.1.0
44 """
45 if _config._run_validators is False:
46 return new_value
47
48 v = attrib.validator
49 if not v:
50 return new_value
51
52 v(instance, attrib, new_value)
53
54 return new_value
55
56
57 def convert(instance, attrib, new_value):
58 """
59 Run *attrib*'s converter -- if it has one -- on *new_value* and return the
60 result.
61
62 .. versionadded:: 20.1.0
63 """
64 c = attrib.converter
65 if c:
66 return c(new_value)
67
68 return new_value
69
70
71 # Sentinel for disabling class-wide *on_setattr* hooks for certain attributes.
72 # autodata stopped working, so the docstring is inlined in the API docs.
73 NO_OP = object()
@@ -0,0 +1,19 b''
1 from typing import Any, NewType, NoReturn, TypeVar, cast
2
3 from . import Attribute, _OnSetAttrType
4
5 _T = TypeVar("_T")
6
7 def frozen(
8 instance: Any, attribute: Attribute[Any], new_value: Any
9 ) -> NoReturn: ...
10 def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ...
11 def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ...
12
13 # convert is allowed to return Any, because they can be chained using pipe.
14 def convert(
15 instance: Any, attribute: Attribute[Any], new_value: Any
16 ) -> Any: ...
17
18 _NoOpType = NewType("_NoOpType", object)
19 NO_OP: _NoOpType
@@ -0,0 +1,80 b''
1 from typing import (
2 Any,
3 AnyStr,
4 Callable,
5 Container,
6 ContextManager,
7 Iterable,
8 List,
9 Mapping,
10 Match,
11 Optional,
12 Pattern,
13 Tuple,
14 Type,
15 TypeVar,
16 Union,
17 overload,
18 )
19
20 from . import _ValidatorType
21 from . import _ValidatorArgType
22
23 _T = TypeVar("_T")
24 _T1 = TypeVar("_T1")
25 _T2 = TypeVar("_T2")
26 _T3 = TypeVar("_T3")
27 _I = TypeVar("_I", bound=Iterable)
28 _K = TypeVar("_K")
29 _V = TypeVar("_V")
30 _M = TypeVar("_M", bound=Mapping)
31
32 def set_disabled(run: bool) -> None: ...
33 def get_disabled() -> bool: ...
34 def disabled() -> ContextManager[None]: ...
35
36 # To be more precise on instance_of use some overloads.
37 # If there are more than 3 items in the tuple then we fall back to Any
38 @overload
39 def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ...
40 @overload
41 def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ...
42 @overload
43 def instance_of(
44 type: Tuple[Type[_T1], Type[_T2]]
45 ) -> _ValidatorType[Union[_T1, _T2]]: ...
46 @overload
47 def instance_of(
48 type: Tuple[Type[_T1], Type[_T2], Type[_T3]]
49 ) -> _ValidatorType[Union[_T1, _T2, _T3]]: ...
50 @overload
51 def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ...
52 def provides(interface: Any) -> _ValidatorType[Any]: ...
53 def optional(
54 validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
55 ) -> _ValidatorType[Optional[_T]]: ...
56 def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
57 def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
58 def matches_re(
59 regex: Union[Pattern[AnyStr], AnyStr],
60 flags: int = ...,
61 func: Optional[
62 Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]]
63 ] = ...,
64 ) -> _ValidatorType[AnyStr]: ...
65 def deep_iterable(
66 member_validator: _ValidatorArgType[_T],
67 iterable_validator: Optional[_ValidatorType[_I]] = ...,
68 ) -> _ValidatorType[_I]: ...
69 def deep_mapping(
70 key_validator: _ValidatorType[_K],
71 value_validator: _ValidatorType[_V],
72 mapping_validator: Optional[_ValidatorType[_M]] = ...,
73 ) -> _ValidatorType[_M]: ...
74 def is_callable() -> _ValidatorType[_T]: ...
75 def lt(val: _T) -> _ValidatorType[_T]: ...
76 def le(val: _T) -> _ValidatorType[_T]: ...
77 def ge(val: _T) -> _ValidatorType[_T]: ...
78 def gt(val: _T) -> _ValidatorType[_T]: ...
79 def max_len(length: int) -> _ValidatorType[_T]: ...
80 def min_len(length: int) -> _ValidatorType[_T]: ...
@@ -1,6 +1,6 b''
1 The MIT License (MIT)
1 The MIT License (MIT)
2
2
3 Copyright (c) 2015 Hynek Schlawack
3 Copyright (c) 2015 Hynek Schlawack and the attrs contributors
4
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
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
6 of this software and associated documentation files (the "Software"), to deal
@@ -1,37 +1,35 b''
1 from __future__ import absolute_import, division, print_function
1 # SPDX-License-Identifier: MIT
2
3
4 import sys
5
6 from functools import partial
2
7
3 from ._funcs import (
8 from . import converters, exceptions, filters, setters, validators
4 asdict,
9 from ._cmp import cmp_using
5 assoc,
10 from ._config import get_run_validators, set_run_validators
6 astuple,
11 from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types
7 evolve,
8 has,
9 )
10 from ._make import (
12 from ._make import (
13 NOTHING,
11 Attribute,
14 Attribute,
12 Factory,
15 Factory,
13 NOTHING,
16 attrib,
14 attr,
17 attrs,
15 attributes,
16 fields,
18 fields,
19 fields_dict,
17 make_class,
20 make_class,
18 validate,
21 validate,
19 )
22 )
20 from ._config import (
23 from ._version_info import VersionInfo
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
24
29
25
30 __version__ = "17.2.0"
26 __version__ = "22.1.0"
27 __version_info__ = VersionInfo._from_version_string(__version__)
31
28
32 __title__ = "attrs"
29 __title__ = "attrs"
33 __description__ = "Classes Without Boilerplate"
30 __description__ = "Classes Without Boilerplate"
34 __uri__ = "http://www.attrs.org/"
31 __url__ = "https://www.attrs.org/"
32 __uri__ = __url__
35 __doc__ = __description__ + " <" + __uri__ + ">"
33 __doc__ = __description__ + " <" + __uri__ + ">"
36
34
37 __author__ = "Hynek Schlawack"
35 __author__ = "Hynek Schlawack"
@@ -41,8 +39,9 b' from . import validators'
41 __copyright__ = "Copyright (c) 2015 Hynek Schlawack"
39 __copyright__ = "Copyright (c) 2015 Hynek Schlawack"
42
40
43
41
44 s = attrs = attributes
42 s = attributes = attrs
45 ib = attrib = attr
43 ib = attr = attrib
44 dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
46
45
47 __all__ = [
46 __all__ = [
48 "Attribute",
47 "Attribute",
@@ -55,17 +54,26 b' ib = attrib = attr'
55 "attrib",
54 "attrib",
56 "attributes",
55 "attributes",
57 "attrs",
56 "attrs",
57 "cmp_using",
58 "converters",
58 "converters",
59 "evolve",
59 "evolve",
60 "exceptions",
60 "exceptions",
61 "fields",
61 "fields",
62 "fields_dict",
62 "filters",
63 "filters",
63 "get_run_validators",
64 "get_run_validators",
64 "has",
65 "has",
65 "ib",
66 "ib",
66 "make_class",
67 "make_class",
68 "resolve_types",
67 "s",
69 "s",
68 "set_run_validators",
70 "set_run_validators",
71 "setters",
69 "validate",
72 "validate",
70 "validators",
73 "validators",
71 ]
74 ]
75
76 if sys.version_info[:2] >= (3, 6):
77 from ._next_gen import define, field, frozen, mutable # noqa: F401
78
79 __all__.extend(("define", "field", "frozen", "mutable"))
@@ -1,90 +1,185 b''
1 from __future__ import absolute_import, division, print_function
1 # SPDX-License-Identifier: MIT
2
3
4 import inspect
5 import platform
6 import sys
7 import threading
8 import types
9 import warnings
10
11 from collections.abc import Mapping, Sequence # noqa
12
13
14 PYPY = platform.python_implementation() == "PyPy"
15 PY36 = sys.version_info[:2] >= (3, 6)
16 HAS_F_STRINGS = PY36
17 PY310 = sys.version_info[:2] >= (3, 10)
2
18
3 import sys
19
4 import types
20 if PYPY or PY36:
21 ordered_dict = dict
22 else:
23 from collections import OrderedDict
24
25 ordered_dict = OrderedDict
26
27
28 def just_warn(*args, **kw):
29 warnings.warn(
30 "Running interpreter doesn't sufficiently support code object "
31 "introspection. Some features like bare super() or accessing "
32 "__class__ will not work with slotted classes.",
33 RuntimeWarning,
34 stacklevel=2,
35 )
5
36
6
37
7 PY2 = sys.version_info[0] == 2
38 class _AnnotationExtractor:
39 """
40 Extract type annotations from a callable, returning None whenever there
41 is none.
42 """
43
44 __slots__ = ["sig"]
45
46 def __init__(self, callable):
47 try:
48 self.sig = inspect.signature(callable)
49 except (ValueError, TypeError): # inspect failed
50 self.sig = None
51
52 def get_first_param_type(self):
53 """
54 Return the type annotation of the first argument if it's not empty.
55 """
56 if not self.sig:
57 return None
58
59 params = list(self.sig.parameters.values())
60 if params and params[0].annotation is not inspect.Parameter.empty:
61 return params[0].annotation
62
63 return None
64
65 def get_return_type(self):
66 """
67 Return the return type if it's not empty.
68 """
69 if (
70 self.sig
71 and self.sig.return_annotation is not inspect.Signature.empty
72 ):
73 return self.sig.return_annotation
74
75 return None
8
76
9
77
10 if PY2:
78 def make_set_closure_cell():
11 from UserDict import IterableUserDict
79 """Return a function of two arguments (cell, value) which sets
12
80 the value stored in the closure cell `cell` to `value`.
13 # We 'bundle' isclass instead of using inspect as importing inspect is
81 """
14 # fairly expensive (order of 10-15 ms for a modern machine in 2016)
82 # pypy makes this easy. (It also supports the logic below, but
15 def isclass(klass):
83 # why not do the easy/fast thing?)
16 return isinstance(klass, (type, types.ClassType))
84 if PYPY:
17
85
18 # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
86 def set_closure_cell(cell, value):
19 TYPE = "type"
87 cell.__setstate__((value,))
88
89 return set_closure_cell
20
90
21 def iteritems(d):
91 # Otherwise gotta do it the hard way.
22 return d.iteritems()
23
92
24 def iterkeys(d):
93 # Create a function that will set its first cellvar to `value`.
25 return d.iterkeys()
94 def set_first_cellvar_to(value):
95 x = value
96 return
26
97
27 # Python 2 is bereft of a read-only dict proxy, so we make one!
98 # This function will be eliminated as dead code, but
28 class ReadOnlyDict(IterableUserDict):
99 # not before its reference to `x` forces `x` to be
29 """
100 # represented as a closure cell rather than a local.
30 Best-effort read-only dict wrapper.
101 def force_x_to_be_a_cell(): # pragma: no cover
31 """
102 return x
32
103
33 def __setitem__(self, key, val):
104 try:
34 # We gently pretend we're a Python 3 mappingproxy.
105 # Extract the code object and make sure our assumptions about
35 raise TypeError("'mappingproxy' object does not support item "
106 # the closure behavior are correct.
36 "assignment")
107 co = set_first_cellvar_to.__code__
108 if co.co_cellvars != ("x",) or co.co_freevars != ():
109 raise AssertionError # pragma: no cover
37
110
38 def update(self, _):
111 # Convert this code object to a code object that sets the
39 # We gently pretend we're a Python 3 mappingproxy.
112 # function's first _freevar_ (not cellvar) to the argument.
40 raise AttributeError("'mappingproxy' object has no attribute "
113 if sys.version_info >= (3, 8):
41 "'update'")
42
114
43 def __delitem__(self, _):
115 def set_closure_cell(cell, value):
44 # We gently pretend we're a Python 3 mappingproxy.
116 cell.cell_contents = value
45 raise TypeError("'mappingproxy' object does not support item "
46 "deletion")
47
117
48 def clear(self):
118 else:
49 # We gently pretend we're a Python 3 mappingproxy.
119 args = [co.co_argcount]
50 raise AttributeError("'mappingproxy' object has no attribute "
120 args.append(co.co_kwonlyargcount)
51 "'clear'")
121 args.extend(
52
122 [
53 def pop(self, key, default=None):
123 co.co_nlocals,
54 # We gently pretend we're a Python 3 mappingproxy.
124 co.co_stacksize,
55 raise AttributeError("'mappingproxy' object has no attribute "
125 co.co_flags,
56 "'pop'")
126 co.co_code,
127 co.co_consts,
128 co.co_names,
129 co.co_varnames,
130 co.co_filename,
131 co.co_name,
132 co.co_firstlineno,
133 co.co_lnotab,
134 # These two arguments are reversed:
135 co.co_cellvars,
136 co.co_freevars,
137 ]
138 )
139 set_first_freevar_code = types.CodeType(*args)
57
140
58 def popitem(self):
141 def set_closure_cell(cell, value):
59 # We gently pretend we're a Python 3 mappingproxy.
142 # Create a function using the set_first_freevar_code,
60 raise AttributeError("'mappingproxy' object has no attribute "
143 # whose first closure cell is `cell`. Calling it will
61 "'popitem'")
144 # change the value of that cell.
62
145 setter = types.FunctionType(
63 def setdefault(self, key, default=None):
146 set_first_freevar_code, {}, "setter", (), (cell,)
64 # We gently pretend we're a Python 3 mappingproxy.
147 )
65 raise AttributeError("'mappingproxy' object has no attribute "
148 # And call it to set the cell.
66 "'setdefault'")
149 setter(value)
67
150
68 def __repr__(self):
151 # Make sure it works on this interpreter:
69 # Override to be identical to the Python 3 version.
152 def make_func_with_cell():
70 return "mappingproxy(" + repr(self.data) + ")"
153 x = None
154
155 def func():
156 return x # pragma: no cover
71
157
72 def metadata_proxy(d):
158 return func
73 res = ReadOnlyDict()
159
74 res.data.update(d) # We blocked update, so we have to do it like this.
160 cell = make_func_with_cell().__closure__[0]
75 return res
161 set_closure_cell(cell, 100)
162 if cell.cell_contents != 100:
163 raise AssertionError # pragma: no cover
76
164
77 else:
165 except Exception:
78 def isclass(klass):
166 return just_warn
79 return isinstance(klass, type)
167 else:
168 return set_closure_cell
80
169
81 TYPE = "class"
170
171 set_closure_cell = make_set_closure_cell()
82
172
83 def iteritems(d):
173 # Thread-local global to track attrs instances which are already being repr'd.
84 return d.items()
174 # This is needed because there is no other (thread-safe) way to pass info
85
175 # about the instances that are already being repr'd through the call stack
86 def iterkeys(d):
176 # in order to ensure we don't perform infinite recursion.
87 return d.keys()
177 #
88
178 # For instance, if an instance contains a dict which contains that instance,
89 def metadata_proxy(d):
179 # we need to know that we're already repr'ing the outside instance from within
90 return types.MappingProxyType(dict(d))
180 # the dict's repr() call.
181 #
182 # This lives here rather than in _make.py so that the functions in _make.py
183 # don't have a direct reference to the thread-local in their globals dict.
184 # If they have such a reference, it breaks cloudpickle.
185 repr_context = threading.local()
@@ -1,4 +1,4 b''
1 from __future__ import absolute_import, division, print_function
1 # SPDX-License-Identifier: MIT
2
2
3
3
4 __all__ = ["set_run_validators", "get_run_validators"]
4 __all__ = ["set_run_validators", "get_run_validators"]
@@ -9,6 +9,10 b' from __future__ import absolute_import, '
9 def set_run_validators(run):
9 def set_run_validators(run):
10 """
10 """
11 Set whether or not validators are run. By default, they are run.
11 Set whether or not validators are run. By default, they are run.
12
13 .. deprecated:: 21.3.0 It will not be removed, but it also will not be
14 moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()`
15 instead.
12 """
16 """
13 if not isinstance(run, bool):
17 if not isinstance(run, bool):
14 raise TypeError("'run' must be bool.")
18 raise TypeError("'run' must be bool.")
@@ -19,5 +23,9 b' def set_run_validators(run):'
19 def get_run_validators():
23 def get_run_validators():
20 """
24 """
21 Return whether or not validators are run.
25 Return whether or not validators are run.
26
27 .. deprecated:: 21.3.0 It will not be removed, but it also will not be
28 moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()`
29 instead.
22 """
30 """
23 return _run_validators
31 return _run_validators
@@ -1,14 +1,20 b''
1 from __future__ import absolute_import, division, print_function
1 # SPDX-License-Identifier: MIT
2
2
3
3 import copy
4 import copy
4
5
5 from ._compat import iteritems
6 from ._make import NOTHING, _obj_setattr, fields
6 from ._make import NOTHING, fields, _obj_setattr
7 from .exceptions import AttrsAttributeNotFoundError
7 from .exceptions import AttrsAttributeNotFoundError
8
8
9
9
10 def asdict(inst, recurse=True, filter=None, dict_factory=dict,
10 def asdict(
11 retain_collection_types=False):
11 inst,
12 recurse=True,
13 filter=None,
14 dict_factory=dict,
15 retain_collection_types=False,
16 value_serializer=None,
17 ):
12 """
18 """
13 Return the ``attrs`` attribute values of *inst* as a dict.
19 Return the ``attrs`` attribute values of *inst* as a dict.
14
20
@@ -17,9 +23,9 b' def asdict(inst, recurse=True, filter=No'
17 :param inst: Instance of an ``attrs``-decorated class.
23 :param inst: Instance of an ``attrs``-decorated class.
18 :param bool recurse: Recurse into classes that are also
24 :param bool recurse: Recurse into classes that are also
19 ``attrs``-decorated.
25 ``attrs``-decorated.
20 :param callable filter: A callable whose return code deteremines whether an
26 :param callable filter: A callable whose return code determines whether an
21 attribute or element is included (``True``) or dropped (``False``). Is
27 attribute or element is included (``True``) or dropped (``False``). Is
22 called with the :class:`attr.Attribute` as the first argument and the
28 called with the `attrs.Attribute` as the first argument and the
23 value as the second argument.
29 value as the second argument.
24 :param callable dict_factory: A callable to produce dictionaries from. For
30 :param callable dict_factory: A callable to produce dictionaries from. For
25 example, to produce ordered dictionaries instead of normal Python
31 example, to produce ordered dictionaries instead of normal Python
@@ -27,6 +33,10 b' def asdict(inst, recurse=True, filter=No'
27 :param bool retain_collection_types: Do not convert to ``list`` when
33 :param bool retain_collection_types: Do not convert to ``list`` when
28 encountering an attribute whose type is ``tuple`` or ``set``. Only
34 encountering an attribute whose type is ``tuple`` or ``set``. Only
29 meaningful if ``recurse`` is ``True``.
35 meaningful if ``recurse`` is ``True``.
36 :param Optional[callable] value_serializer: A hook that is called for every
37 attribute or dict key/value. It receives the current instance, field
38 and value and must return the (updated) value. The hook is run *after*
39 the optional *filter* has been applied.
30
40
31 :rtype: return type of *dict_factory*
41 :rtype: return type of *dict_factory*
32
42
@@ -35,6 +45,9 b' def asdict(inst, recurse=True, filter=No'
35
45
36 .. versionadded:: 16.0.0 *dict_factory*
46 .. versionadded:: 16.0.0 *dict_factory*
37 .. versionadded:: 16.1.0 *retain_collection_types*
47 .. versionadded:: 16.1.0 *retain_collection_types*
48 .. versionadded:: 20.3.0 *value_serializer*
49 .. versionadded:: 21.3.0 If a dict has a collection for a key, it is
50 serialized as a tuple.
38 """
51 """
39 attrs = fields(inst.__class__)
52 attrs = fields(inst.__class__)
40 rv = dict_factory()
53 rv = dict_factory()
@@ -42,24 +55,58 b' def asdict(inst, recurse=True, filter=No'
42 v = getattr(inst, a.name)
55 v = getattr(inst, a.name)
43 if filter is not None and not filter(a, v):
56 if filter is not None and not filter(a, v):
44 continue
57 continue
58
59 if value_serializer is not None:
60 v = value_serializer(inst, a, v)
61
45 if recurse is True:
62 if recurse is True:
46 if has(v.__class__):
63 if has(v.__class__):
47 rv[a.name] = asdict(v, recurse=True, filter=filter,
64 rv[a.name] = asdict(
48 dict_factory=dict_factory)
65 v,
49 elif isinstance(v, (tuple, list, set)):
66 recurse=True,
67 filter=filter,
68 dict_factory=dict_factory,
69 retain_collection_types=retain_collection_types,
70 value_serializer=value_serializer,
71 )
72 elif isinstance(v, (tuple, list, set, frozenset)):
50 cf = v.__class__ if retain_collection_types is True else list
73 cf = v.__class__ if retain_collection_types is True else list
51 rv[a.name] = cf([
74 rv[a.name] = cf(
52 asdict(i, recurse=True, filter=filter,
75 [
53 dict_factory=dict_factory)
76 _asdict_anything(
54 if has(i.__class__) else i
77 i,
55 for i in v
78 is_key=False,
56 ])
79 filter=filter,
80 dict_factory=dict_factory,
81 retain_collection_types=retain_collection_types,
82 value_serializer=value_serializer,
83 )
84 for i in v
85 ]
86 )
57 elif isinstance(v, dict):
87 elif isinstance(v, dict):
58 df = dict_factory
88 df = dict_factory
59 rv[a.name] = df((
89 rv[a.name] = df(
60 asdict(kk, dict_factory=df) if has(kk.__class__) else kk,
90 (
61 asdict(vv, dict_factory=df) if has(vv.__class__) else vv)
91 _asdict_anything(
62 for kk, vv in iteritems(v))
92 kk,
93 is_key=True,
94 filter=filter,
95 dict_factory=df,
96 retain_collection_types=retain_collection_types,
97 value_serializer=value_serializer,
98 ),
99 _asdict_anything(
100 vv,
101 is_key=False,
102 filter=filter,
103 dict_factory=df,
104 retain_collection_types=retain_collection_types,
105 value_serializer=value_serializer,
106 ),
107 )
108 for kk, vv in v.items()
109 )
63 else:
110 else:
64 rv[a.name] = v
111 rv[a.name] = v
65 else:
112 else:
@@ -67,8 +114,86 b' def asdict(inst, recurse=True, filter=No'
67 return rv
114 return rv
68
115
69
116
70 def astuple(inst, recurse=True, filter=None, tuple_factory=tuple,
117 def _asdict_anything(
71 retain_collection_types=False):
118 val,
119 is_key,
120 filter,
121 dict_factory,
122 retain_collection_types,
123 value_serializer,
124 ):
125 """
126 ``asdict`` only works on attrs instances, this works on anything.
127 """
128 if getattr(val.__class__, "__attrs_attrs__", None) is not None:
129 # Attrs class.
130 rv = asdict(
131 val,
132 recurse=True,
133 filter=filter,
134 dict_factory=dict_factory,
135 retain_collection_types=retain_collection_types,
136 value_serializer=value_serializer,
137 )
138 elif isinstance(val, (tuple, list, set, frozenset)):
139 if retain_collection_types is True:
140 cf = val.__class__
141 elif is_key:
142 cf = tuple
143 else:
144 cf = list
145
146 rv = cf(
147 [
148 _asdict_anything(
149 i,
150 is_key=False,
151 filter=filter,
152 dict_factory=dict_factory,
153 retain_collection_types=retain_collection_types,
154 value_serializer=value_serializer,
155 )
156 for i in val
157 ]
158 )
159 elif isinstance(val, dict):
160 df = dict_factory
161 rv = df(
162 (
163 _asdict_anything(
164 kk,
165 is_key=True,
166 filter=filter,
167 dict_factory=df,
168 retain_collection_types=retain_collection_types,
169 value_serializer=value_serializer,
170 ),
171 _asdict_anything(
172 vv,
173 is_key=False,
174 filter=filter,
175 dict_factory=df,
176 retain_collection_types=retain_collection_types,
177 value_serializer=value_serializer,
178 ),
179 )
180 for kk, vv in val.items()
181 )
182 else:
183 rv = val
184 if value_serializer is not None:
185 rv = value_serializer(None, None, rv)
186
187 return rv
188
189
190 def astuple(
191 inst,
192 recurse=True,
193 filter=None,
194 tuple_factory=tuple,
195 retain_collection_types=False,
196 ):
72 """
197 """
73 Return the ``attrs`` attribute values of *inst* as a tuple.
198 Return the ``attrs`` attribute values of *inst* as a tuple.
74
199
@@ -79,7 +204,7 b' def astuple(inst, recurse=True, filter=N'
79 ``attrs``-decorated.
204 ``attrs``-decorated.
80 :param callable filter: A callable whose return code determines whether an
205 :param callable filter: A callable whose return code determines whether an
81 attribute or element is included (``True``) or dropped (``False``). Is
206 attribute or element is included (``True``) or dropped (``False``). Is
82 called with the :class:`attr.Attribute` as the first argument and the
207 called with the `attrs.Attribute` as the first argument and the
83 value as the second argument.
208 value as the second argument.
84 :param callable tuple_factory: A callable to produce tuples from. For
209 :param callable tuple_factory: A callable to produce tuples from. For
85 example, to produce lists instead of tuples.
210 example, to produce lists instead of tuples.
@@ -104,38 +229,61 b' def astuple(inst, recurse=True, filter=N'
104 continue
229 continue
105 if recurse is True:
230 if recurse is True:
106 if has(v.__class__):
231 if has(v.__class__):
107 rv.append(astuple(v, recurse=True, filter=filter,
232 rv.append(
108 tuple_factory=tuple_factory,
233 astuple(
109 retain_collection_types=retain))
234 v,
110 elif isinstance(v, (tuple, list, set)):
235 recurse=True,
236 filter=filter,
237 tuple_factory=tuple_factory,
238 retain_collection_types=retain,
239 )
240 )
241 elif isinstance(v, (tuple, list, set, frozenset)):
111 cf = v.__class__ if retain is True else list
242 cf = v.__class__ if retain is True else list
112 rv.append(cf([
243 rv.append(
113 astuple(j, recurse=True, filter=filter,
244 cf(
114 tuple_factory=tuple_factory,
245 [
115 retain_collection_types=retain)
246 astuple(
116 if has(j.__class__) else j
247 j,
117 for j in v
248 recurse=True,
118 ]))
249 filter=filter,
250 tuple_factory=tuple_factory,
251 retain_collection_types=retain,
252 )
253 if has(j.__class__)
254 else j
255 for j in v
256 ]
257 )
258 )
119 elif isinstance(v, dict):
259 elif isinstance(v, dict):
120 df = v.__class__ if retain is True else dict
260 df = v.__class__ if retain is True else dict
121 rv.append(df(
261 rv.append(
262 df(
122 (
263 (
123 astuple(
264 astuple(
124 kk,
265 kk,
125 tuple_factory=tuple_factory,
266 tuple_factory=tuple_factory,
126 retain_collection_types=retain
267 retain_collection_types=retain,
127 ) if has(kk.__class__) else kk,
268 )
269 if has(kk.__class__)
270 else kk,
128 astuple(
271 astuple(
129 vv,
272 vv,
130 tuple_factory=tuple_factory,
273 tuple_factory=tuple_factory,
131 retain_collection_types=retain
274 retain_collection_types=retain,
132 ) if has(vv.__class__) else vv
275 )
276 if has(vv.__class__)
277 else vv,
133 )
278 )
134 for kk, vv in iteritems(v)))
279 for kk, vv in v.items()
280 )
281 )
135 else:
282 else:
136 rv.append(v)
283 rv.append(v)
137 else:
284 else:
138 rv.append(v)
285 rv.append(v)
286
139 return rv if tuple_factory is list else tuple_factory(rv)
287 return rv if tuple_factory is list else tuple_factory(rv)
140
288
141
289
@@ -146,7 +294,7 b' def has(cls):'
146 :param type cls: Class to introspect.
294 :param type cls: Class to introspect.
147 :raise TypeError: If *cls* is not a class.
295 :raise TypeError: If *cls* is not a class.
148
296
149 :rtype: :class:`bool`
297 :rtype: bool
150 """
298 """
151 return getattr(cls, "__attrs_attrs__", None) is not None
299 return getattr(cls, "__attrs_attrs__", None) is not None
152
300
@@ -166,19 +314,26 b' def assoc(inst, **changes):'
166 class.
314 class.
167
315
168 .. deprecated:: 17.1.0
316 .. deprecated:: 17.1.0
169 Use :func:`evolve` instead.
317 Use `attrs.evolve` instead if you can.
318 This function will not be removed du to the slightly different approach
319 compared to `attrs.evolve`.
170 """
320 """
171 import warnings
321 import warnings
172 warnings.warn("assoc is deprecated and will be removed after 2018/01.",
322
173 DeprecationWarning)
323 warnings.warn(
324 "assoc is deprecated and will be removed after 2018/01.",
325 DeprecationWarning,
326 stacklevel=2,
327 )
174 new = copy.copy(inst)
328 new = copy.copy(inst)
175 attrs = fields(inst.__class__)
329 attrs = fields(inst.__class__)
176 for k, v in iteritems(changes):
330 for k, v in changes.items():
177 a = getattr(attrs, k, NOTHING)
331 a = getattr(attrs, k, NOTHING)
178 if a is NOTHING:
332 if a is NOTHING:
179 raise AttrsAttributeNotFoundError(
333 raise AttrsAttributeNotFoundError(
180 "{k} is not an attrs attribute on {cl}."
334 "{k} is not an attrs attribute on {cl}.".format(
181 .format(k=k, cl=new.__class__)
335 k=k, cl=new.__class__
336 )
182 )
337 )
183 _obj_setattr(new, k, v)
338 _obj_setattr(new, k, v)
184 return new
339 return new
@@ -209,4 +364,57 b' def evolve(inst, **changes):'
209 init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
364 init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
210 if init_name not in changes:
365 if init_name not in changes:
211 changes[init_name] = getattr(inst, attr_name)
366 changes[init_name] = getattr(inst, attr_name)
367
212 return cls(**changes)
368 return cls(**changes)
369
370
371 def resolve_types(cls, globalns=None, localns=None, attribs=None):
372 """
373 Resolve any strings and forward annotations in type annotations.
374
375 This is only required if you need concrete types in `Attribute`'s *type*
376 field. In other words, you don't need to resolve your types if you only
377 use them for static type checking.
378
379 With no arguments, names will be looked up in the module in which the class
380 was created. If this is not what you want, e.g. if the name only exists
381 inside a method, you may pass *globalns* or *localns* to specify other
382 dictionaries in which to look up these names. See the docs of
383 `typing.get_type_hints` for more details.
384
385 :param type cls: Class to resolve.
386 :param Optional[dict] globalns: Dictionary containing global variables.
387 :param Optional[dict] localns: Dictionary containing local variables.
388 :param Optional[list] attribs: List of attribs for the given class.
389 This is necessary when calling from inside a ``field_transformer``
390 since *cls* is not an ``attrs`` class yet.
391
392 :raise TypeError: If *cls* is not a class.
393 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
394 class and you didn't pass any attribs.
395 :raise NameError: If types cannot be resolved because of missing variables.
396
397 :returns: *cls* so you can use this function also as a class decorator.
398 Please note that you have to apply it **after** `attrs.define`. That
399 means the decorator has to come in the line **before** `attrs.define`.
400
401 .. versionadded:: 20.1.0
402 .. versionadded:: 21.1.0 *attribs*
403
404 """
405 # Since calling get_type_hints is expensive we cache whether we've
406 # done it already.
407 if getattr(cls, "__attrs_types_resolved__", None) != cls:
408 import typing
409
410 hints = typing.get_type_hints(cls, globalns=globalns, localns=localns)
411 for field in fields(cls) if attribs is None else attribs:
412 if field.name in hints:
413 # Since fields have been frozen we must work around it.
414 _obj_setattr(field, "type", hints[field.name])
415 # We store the class we resolved so that subclasses know they haven't
416 # been resolved.
417 cls.__attrs_types_resolved__ = cls
418
419 # Return the class so you can use it as a decorator too.
420 return cls
This diff has been collapsed as it changes many lines, (3118 lines changed) Show them Hide them
@@ -1,50 +1,79 b''
1 from __future__ import absolute_import, division, print_function
1 # SPDX-License-Identifier: MIT
2
2
3 import hashlib
3 import copy
4 import linecache
4 import linecache
5 import sys
6 import types
7 import typing
5
8
6 from operator import itemgetter
9 from operator import itemgetter
7
10
8 from . import _config
11 # We need to import _compat itself in addition to the _compat members to avoid
9 from ._compat import PY2, iteritems, isclass, iterkeys, metadata_proxy
12 # having the thread-local in the globals here.
13 from . import _compat, _config, setters
14 from ._compat import (
15 HAS_F_STRINGS,
16 PY310,
17 PYPY,
18 _AnnotationExtractor,
19 ordered_dict,
20 set_closure_cell,
21 )
10 from .exceptions import (
22 from .exceptions import (
11 DefaultAlreadySetError,
23 DefaultAlreadySetError,
12 FrozenInstanceError,
24 FrozenInstanceError,
13 NotAnAttrsClassError,
25 NotAnAttrsClassError,
26 UnannotatedAttributeError,
14 )
27 )
15
28
16
29
17 # This is used at least twice, so cache it here.
30 # This is used at least twice, so cache it here.
18 _obj_setattr = object.__setattr__
31 _obj_setattr = object.__setattr__
19 _init_convert_pat = "__attr_convert_{}"
32 _init_converter_pat = "__attr_converter_%s"
20 _init_factory_pat = "__attr_factory_{}"
33 _init_factory_pat = "__attr_factory_{}"
21 _tuple_property_pat = " {attr_name} = property(itemgetter({index}))"
34 _tuple_property_pat = (
22 _empty_metadata_singleton = metadata_proxy({})
35 " {attr_name} = _attrs_property(_attrs_itemgetter({index}))"
23
36 )
24
37 _classvar_prefixes = (
25 class _Nothing(object):
38 "typing.ClassVar",
39 "t.ClassVar",
40 "ClassVar",
41 "typing_extensions.ClassVar",
42 )
43 # we don't use a double-underscore prefix because that triggers
44 # name mangling when trying to create a slot for the field
45 # (when slots=True)
46 _hash_cache_field = "_attrs_cached_hash"
47
48 _empty_metadata_singleton = types.MappingProxyType({})
49
50 # Unique object for unequivocal getattr() defaults.
51 _sentinel = object()
52
53 _ng_default_on_setattr = setters.pipe(setters.convert, setters.validate)
54
55
56 class _Nothing:
26 """
57 """
27 Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
58 Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
28
59
29 All instances of `_Nothing` are equal.
60 ``_Nothing`` is a singleton. There is only ever one of it.
61
62 .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False.
30 """
63 """
31 def __copy__(self):
64
32 return self
65 _singleton = None
33
66
34 def __deepcopy__(self, _):
67 def __new__(cls):
35 return self
68 if _Nothing._singleton is None:
36
69 _Nothing._singleton = super().__new__(cls)
37 def __eq__(self, other):
70 return _Nothing._singleton
38 return other.__class__ == _Nothing
39
40 def __ne__(self, other):
41 return not self == other
42
71
43 def __repr__(self):
72 def __repr__(self):
44 return "NOTHING"
73 return "NOTHING"
45
74
46 def __hash__(self):
75 def __bool__(self):
47 return 0xdeadbeef
76 return False
48
77
49
78
50 NOTHING = _Nothing()
79 NOTHING = _Nothing()
@@ -53,92 +82,255 b' Sentinel to indicate the lack of a value'
53 """
82 """
54
83
55
84
56 def attr(default=NOTHING, validator=None,
85 class _CacheHashWrapper(int):
57 repr=True, cmp=True, hash=None, init=True,
86 """
58 convert=None, metadata={}):
87 An integer subclass that pickles / copies as None
59 r"""
88
89 This is used for non-slots classes with ``cache_hash=True``, to avoid
90 serializing a potentially (even likely) invalid hash value. Since ``None``
91 is the default value for uncalculated hashes, whenever this is copied,
92 the copy's value for the hash should automatically reset.
93
94 See GH #613 for more details.
95 """
96
97 def __reduce__(self, _none_constructor=type(None), _args=()):
98 return _none_constructor, _args
99
100
101 def attrib(
102 default=NOTHING,
103 validator=None,
104 repr=True,
105 cmp=None,
106 hash=None,
107 init=True,
108 metadata=None,
109 type=None,
110 converter=None,
111 factory=None,
112 kw_only=False,
113 eq=None,
114 order=None,
115 on_setattr=None,
116 ):
117 """
60 Create a new attribute on a class.
118 Create a new attribute on a class.
61
119
62 .. warning::
120 .. warning::
63
121
64 Does *not* do anything unless the class is also decorated with
122 Does *not* do anything unless the class is also decorated with
65 :func:`attr.s`!
123 `attr.s`!
66
124
67 :param default: A value that is used if an ``attrs``-generated ``__init__``
125 :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
126 is used and no value is passed while instantiating or the attribute is
69 excluded using ``init=False``.
127 excluded using ``init=False``.
70
128
71 If the value is an instance of :class:`Factory`, its callable will be
129 If the value is an instance of `attrs.Factory`, its callable will be
72 used to construct a new value (useful for mutable datatypes like lists
130 used to construct a new value (useful for mutable data types like lists
73 or dicts).
131 or dicts).
74
132
75 If a default is not set (or set manually to ``attr.NOTHING``), a value
133 If a default is not set (or set manually to `attrs.NOTHING`), a value
76 *must* be supplied when instantiating; otherwise a :exc:`TypeError`
134 *must* be supplied when instantiating; otherwise a `TypeError`
77 will be raised.
135 will be raised.
78
136
79 The default can also be set using decorator notation as shown below.
137 The default can also be set using decorator notation as shown below.
80
138
81 :type default: Any value.
139 :type default: Any value
82
140
83 :param validator: :func:`callable` that is called by ``attrs``-generated
141 :param callable factory: Syntactic sugar for
142 ``default=attr.Factory(factory)``.
143
144 :param validator: `callable` that is called by ``attrs``-generated
84 ``__init__`` methods after the instance has been initialized. They
145 ``__init__`` methods after the instance has been initialized. They
85 receive the initialized instance, the :class:`Attribute`, and the
146 receive the initialized instance, the :func:`~attrs.Attribute`, and the
86 passed value.
147 passed value.
87
148
88 The return value is *not* inspected so the validator has to throw an
149 The return value is *not* inspected so the validator has to throw an
89 exception itself.
150 exception itself.
90
151
91 If a ``list`` is passed, its items are treated as validators and must
152 If a `list` is passed, its items are treated as validators and must
92 all pass.
153 all pass.
93
154
94 Validators can be globally disabled and re-enabled using
155 Validators can be globally disabled and re-enabled using
95 :func:`get_run_validators`.
156 `get_run_validators`.
96
157
97 The validator can also be set using decorator notation as shown below.
158 The validator can also be set using decorator notation as shown below.
98
159
99 :type validator: ``callable`` or a ``list`` of ``callable``\ s.
160 :type validator: `callable` or a `list` of `callable`\\ s.
100
161
101 :param bool repr: Include this attribute in the generated ``__repr__``
162 :param repr: Include this attribute in the generated ``__repr__``
102 method.
163 method. If ``True``, include the attribute; if ``False``, omit it. By
103 :param bool cmp: Include this attribute in the generated comparison methods
164 default, the built-in ``repr()`` function is used. To override how the
104 (``__eq__`` et al).
165 attribute value is formatted, pass a ``callable`` that takes a single
105 :param hash: Include this attribute in the generated ``__hash__``
166 value and returns a string. Note that the resulting string is used
106 method. If ``None`` (default), mirror *cmp*'s value. This is the
167 as-is, i.e. it will be used directly *instead* of calling ``repr()``
107 correct behavior according the Python spec. Setting this value to
168 (the default).
108 anything else than ``None`` is *discouraged*.
169 :type repr: a `bool` or a `callable` to use a custom function.
109 :type hash: ``bool`` or ``None``
170
171 :param eq: If ``True`` (default), include this attribute in the
172 generated ``__eq__`` and ``__ne__`` methods that check two instances
173 for equality. To override how the attribute value is compared,
174 pass a ``callable`` that takes a single value and returns the value
175 to be compared.
176 :type eq: a `bool` or a `callable`.
177
178 :param order: If ``True`` (default), include this attributes in the
179 generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods.
180 To override how the attribute value is ordered,
181 pass a ``callable`` that takes a single value and returns the value
182 to be ordered.
183 :type order: a `bool` or a `callable`.
184
185 :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the
186 same value. Must not be mixed with *eq* or *order*.
187 :type cmp: a `bool` or a `callable`.
188
189 :param Optional[bool] hash: Include this attribute in the generated
190 ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This
191 is the correct behavior according the Python spec. Setting this value
192 to anything else than ``None`` is *discouraged*.
110 :param bool init: Include this attribute in the generated ``__init__``
193 :param bool init: Include this attribute in the generated ``__init__``
111 method. It is possible to set this to ``False`` and set a default
194 method. It is possible to set this to ``False`` and set a default
112 value. In that case this attributed is unconditionally initialized
195 value. In that case this attributed is unconditionally initialized
113 with the specified default value or factory.
196 with the specified default value or factory.
114 :param callable convert: :func:`callable` that is called by
197 :param callable converter: `callable` that is called by
115 ``attrs``-generated ``__init__`` methods to convert attribute's value
198 ``attrs``-generated ``__init__`` methods to convert attribute's value
116 to the desired format. It is given the passed-in value, and the
199 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
200 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.
201 value is converted before being passed to the validator, if any.
119 :param metadata: An arbitrary mapping, to be used by third-party
202 :param metadata: An arbitrary mapping, to be used by third-party
120 components. See :ref:`extending_metadata`.
203 components. See `extending_metadata`.
121
204 :param type: The type of the attribute. In Python 3.6 or greater, the
122 .. versionchanged:: 17.1.0 *validator* can be a ``list`` now.
205 preferred method to specify the type is using a variable annotation
123 .. versionchanged:: 17.1.0
206 (see :pep:`526`).
124 *hash* is ``None`` and therefore mirrors *cmp* by default .
207 This argument is provided for backward compatibility.
208 Regardless of the approach used, the type will be stored on
209 ``Attribute.type``.
210
211 Please note that ``attrs`` doesn't do anything with this metadata by
212 itself. You can use it as part of your own code or for
213 `static type checking <types>`.
214 :param kw_only: Make this attribute keyword-only (Python 3+)
215 in the generated ``__init__`` (if ``init`` is ``False``, this
216 parameter is ignored).
217 :param on_setattr: Allows to overwrite the *on_setattr* setting from
218 `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used.
219 Set to `attrs.setters.NO_OP` to run **no** `setattr` hooks for this
220 attribute -- regardless of the setting in `attr.s`.
221 :type on_setattr: `callable`, or a list of callables, or `None`, or
222 `attrs.setters.NO_OP`
223
224 .. versionadded:: 15.2.0 *convert*
225 .. versionadded:: 16.3.0 *metadata*
226 .. versionchanged:: 17.1.0 *validator* can be a ``list`` now.
227 .. versionchanged:: 17.1.0
228 *hash* is ``None`` and therefore mirrors *eq* by default.
229 .. versionadded:: 17.3.0 *type*
230 .. deprecated:: 17.4.0 *convert*
231 .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated
232 *convert* to achieve consistency with other noun-based arguments.
233 .. versionadded:: 18.1.0
234 ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``.
235 .. versionadded:: 18.2.0 *kw_only*
236 .. versionchanged:: 19.2.0 *convert* keyword argument removed.
237 .. versionchanged:: 19.2.0 *repr* also accepts a custom callable.
238 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
239 .. versionadded:: 19.2.0 *eq* and *order*
240 .. versionadded:: 20.1.0 *on_setattr*
241 .. versionchanged:: 20.3.0 *kw_only* backported to Python 2
242 .. versionchanged:: 21.1.0
243 *eq*, *order*, and *cmp* also accept a custom callable
244 .. versionchanged:: 21.1.0 *cmp* undeprecated
125 """
245 """
246 eq, eq_key, order, order_key = _determine_attrib_eq_order(
247 cmp, eq, order, True
248 )
249
126 if hash is not None and hash is not True and hash is not False:
250 if hash is not None and hash is not True and hash is not False:
127 raise TypeError(
251 raise TypeError(
128 "Invalid value for hash. Must be True, False, or None."
252 "Invalid value for hash. Must be True, False, or None."
129 )
253 )
254
255 if factory is not None:
256 if default is not NOTHING:
257 raise ValueError(
258 "The `default` and `factory` arguments are mutually "
259 "exclusive."
260 )
261 if not callable(factory):
262 raise ValueError("The `factory` argument must be a callable.")
263 default = Factory(factory)
264
265 if metadata is None:
266 metadata = {}
267
268 # Apply syntactic sugar by auto-wrapping.
269 if isinstance(on_setattr, (list, tuple)):
270 on_setattr = setters.pipe(*on_setattr)
271
272 if validator and isinstance(validator, (list, tuple)):
273 validator = and_(*validator)
274
275 if converter and isinstance(converter, (list, tuple)):
276 converter = pipe(*converter)
277
130 return _CountingAttr(
278 return _CountingAttr(
131 default=default,
279 default=default,
132 validator=validator,
280 validator=validator,
133 repr=repr,
281 repr=repr,
134 cmp=cmp,
282 cmp=None,
135 hash=hash,
283 hash=hash,
136 init=init,
284 init=init,
137 convert=convert,
285 converter=converter,
138 metadata=metadata,
286 metadata=metadata,
287 type=type,
288 kw_only=kw_only,
289 eq=eq,
290 eq_key=eq_key,
291 order=order,
292 order_key=order_key,
293 on_setattr=on_setattr,
139 )
294 )
140
295
141
296
297 def _compile_and_eval(script, globs, locs=None, filename=""):
298 """
299 "Exec" the script with the given global (globs) and local (locs) variables.
300 """
301 bytecode = compile(script, filename, "exec")
302 eval(bytecode, globs, locs)
303
304
305 def _make_method(name, script, filename, globs):
306 """
307 Create the method with the script given and return the method object.
308 """
309 locs = {}
310
311 # In order of debuggers like PDB being able to step through the code,
312 # we add a fake linecache entry.
313 count = 1
314 base_filename = filename
315 while True:
316 linecache_tuple = (
317 len(script),
318 None,
319 script.splitlines(True),
320 filename,
321 )
322 old_val = linecache.cache.setdefault(filename, linecache_tuple)
323 if old_val == linecache_tuple:
324 break
325 else:
326 filename = "{}-{}>".format(base_filename[:-1], count)
327 count += 1
328
329 _compile_and_eval(script, globs, locs, filename)
330
331 return locs[name]
332
333
142 def _make_attr_tuple_class(cls_name, attr_names):
334 def _make_attr_tuple_class(cls_name, attr_names):
143 """
335 """
144 Create a tuple subclass to hold `Attribute`s for an `attrs` class.
336 Create a tuple subclass to hold `Attribute`s for an `attrs` class.
@@ -156,75 +348,273 b' def _make_attr_tuple_class(cls_name, att'
156 ]
348 ]
157 if attr_names:
349 if attr_names:
158 for i, attr_name in enumerate(attr_names):
350 for i, attr_name in enumerate(attr_names):
159 attr_class_template.append(_tuple_property_pat.format(
351 attr_class_template.append(
160 index=i,
352 _tuple_property_pat.format(index=i, attr_name=attr_name)
161 attr_name=attr_name,
353 )
162 ))
163 else:
354 else:
164 attr_class_template.append(" pass")
355 attr_class_template.append(" pass")
165 globs = {"itemgetter": itemgetter}
356 globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property}
166 eval(compile("\n".join(attr_class_template), "", "exec"), globs)
357 _compile_and_eval("\n".join(attr_class_template), globs)
167 return globs[attr_class_name]
358 return globs[attr_class_name]
168
359
169
360
170 def _transform_attrs(cls, these):
361 # Tuple class for extracted attributes from a class definition.
362 # `base_attrs` is a subset of `attrs`.
363 _Attributes = _make_attr_tuple_class(
364 "_Attributes",
365 [
366 # all attributes to build dunder methods for
367 "attrs",
368 # attributes that have been inherited
369 "base_attrs",
370 # map inherited attributes to their originating classes
371 "base_attrs_map",
372 ],
373 )
374
375
376 def _is_class_var(annot):
377 """
378 Check whether *annot* is a typing.ClassVar.
379
380 The string comparison hack is used to avoid evaluating all string
381 annotations which would put attrs-based classes at a performance
382 disadvantage compared to plain old classes.
383 """
384 annot = str(annot)
385
386 # Annotation can be quoted.
387 if annot.startswith(("'", '"')) and annot.endswith(("'", '"')):
388 annot = annot[1:-1]
389
390 return annot.startswith(_classvar_prefixes)
391
392
393 def _has_own_attribute(cls, attrib_name):
394 """
395 Check whether *cls* defines *attrib_name* (and doesn't just inherit it).
396
397 Requires Python 3.
398 """
399 attr = getattr(cls, attrib_name, _sentinel)
400 if attr is _sentinel:
401 return False
402
403 for base_cls in cls.__mro__[1:]:
404 a = getattr(base_cls, attrib_name, None)
405 if attr is a:
406 return False
407
408 return True
409
410
411 def _get_annotations(cls):
412 """
413 Get annotations for *cls*.
414 """
415 if _has_own_attribute(cls, "__annotations__"):
416 return cls.__annotations__
417
418 return {}
419
420
421 def _counter_getter(e):
422 """
423 Key function for sorting to avoid re-creating a lambda for every class.
171 """
424 """
172 Transforms all `_CountingAttr`s on a class into `Attribute`s and saves the
425 return e[1].counter
173 list in `__attrs_attrs__`.
426
427
428 def _collect_base_attrs(cls, taken_attr_names):
429 """
430 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.
431 """
432 base_attrs = []
433 base_attr_map = {} # A dictionary of base attrs to their classes.
434
435 # Traverse the MRO and collect attributes.
436 for base_cls in reversed(cls.__mro__[1:-1]):
437 for a in getattr(base_cls, "__attrs_attrs__", []):
438 if a.inherited or a.name in taken_attr_names:
439 continue
440
441 a = a.evolve(inherited=True)
442 base_attrs.append(a)
443 base_attr_map[a.name] = base_cls
444
445 # For each name, only keep the freshest definition i.e. the furthest at the
446 # back. base_attr_map is fine because it gets overwritten with every new
447 # instance.
448 filtered = []
449 seen = set()
450 for a in reversed(base_attrs):
451 if a.name in seen:
452 continue
453 filtered.insert(0, a)
454 seen.add(a.name)
455
456 return filtered, base_attr_map
457
458
459 def _collect_base_attrs_broken(cls, taken_attr_names):
460 """
461 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.
462
463 N.B. *taken_attr_names* will be mutated.
464
465 Adhere to the old incorrect behavior.
466
467 Notably it collects from the front and considers inherited attributes which
468 leads to the buggy behavior reported in #428.
469 """
470 base_attrs = []
471 base_attr_map = {} # A dictionary of base attrs to their classes.
472
473 # Traverse the MRO and collect attributes.
474 for base_cls in cls.__mro__[1:-1]:
475 for a in getattr(base_cls, "__attrs_attrs__", []):
476 if a.name in taken_attr_names:
477 continue
478
479 a = a.evolve(inherited=True)
480 taken_attr_names.add(a.name)
481 base_attrs.append(a)
482 base_attr_map[a.name] = base_cls
483
484 return base_attrs, base_attr_map
485
486
487 def _transform_attrs(
488 cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer
489 ):
490 """
491 Transform all `_CountingAttr`s on a class into `Attribute`s.
174
492
175 If *these* is passed, use that and don't look for them on the class.
493 If *these* is passed, use that and don't look for them on the class.
494
495 *collect_by_mro* is True, collect them in the correct MRO order, otherwise
496 use the old -- incorrect -- order. See #428.
497
498 Return an `_Attributes`.
176 """
499 """
177 super_cls = []
500 cd = cls.__dict__
178 for c in reversed(cls.__mro__[1:-1]):
501 anns = _get_annotations(cls)
179 sub_attrs = getattr(c, "__attrs_attrs__", None)
502
180 if sub_attrs is not None:
503 if these is not None:
181 super_cls.extend(a for a in sub_attrs if a not in super_cls)
504 ca_list = [(name, ca) for name, ca in these.items()]
182 if these is None:
505
183 ca_list = [(name, attr)
506 if not isinstance(these, ordered_dict):
184 for name, attr
507 ca_list.sort(key=_counter_getter)
185 in cls.__dict__.items()
508 elif auto_attribs is True:
186 if isinstance(attr, _CountingAttr)]
509 ca_names = {
510 name
511 for name, attr in cd.items()
512 if isinstance(attr, _CountingAttr)
513 }
514 ca_list = []
515 annot_names = set()
516 for attr_name, type in anns.items():
517 if _is_class_var(type):
518 continue
519 annot_names.add(attr_name)
520 a = cd.get(attr_name, NOTHING)
521
522 if not isinstance(a, _CountingAttr):
523 if a is NOTHING:
524 a = attrib()
525 else:
526 a = attrib(default=a)
527 ca_list.append((attr_name, a))
528
529 unannotated = ca_names - annot_names
530 if len(unannotated) > 0:
531 raise UnannotatedAttributeError(
532 "The following `attr.ib`s lack a type annotation: "
533 + ", ".join(
534 sorted(unannotated, key=lambda n: cd.get(n).counter)
535 )
536 + "."
537 )
187 else:
538 else:
188 ca_list = [(name, ca)
539 ca_list = sorted(
189 for name, ca
540 (
190 in iteritems(these)]
541 (name, attr)
191
542 for name, attr in cd.items()
192 non_super_attrs = [
543 if isinstance(attr, _CountingAttr)
193 Attribute.from_counting_attr(name=attr_name, ca=ca)
544 ),
194 for attr_name, ca
545 key=lambda e: e[1].counter,
195 in sorted(ca_list, key=lambda e: e[1].counter)
546 )
547
548 own_attrs = [
549 Attribute.from_counting_attr(
550 name=attr_name, ca=ca, type=anns.get(attr_name)
551 )
552 for attr_name, ca in ca_list
196 ]
553 ]
197 attr_names = [a.name for a in super_cls + non_super_attrs]
554
198
555 if collect_by_mro:
199 AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
556 base_attrs, base_attr_map = _collect_base_attrs(
200
557 cls, {a.name for a in own_attrs}
201 cls.__attrs_attrs__ = AttrsClass(super_cls + [
558 )
202 Attribute.from_counting_attr(name=attr_name, ca=ca)
559 else:
203 for attr_name, ca
560 base_attrs, base_attr_map = _collect_base_attrs_broken(
204 in sorted(ca_list, key=lambda e: e[1].counter)
561 cls, {a.name for a in own_attrs}
205 ])
562 )
206
563
564 if kw_only:
565 own_attrs = [a.evolve(kw_only=True) for a in own_attrs]
566 base_attrs = [a.evolve(kw_only=True) for a in base_attrs]
567
568 attrs = base_attrs + own_attrs
569
570 # Mandatory vs non-mandatory attr order only matters when they are part of
571 # the __init__ signature and when they aren't kw_only (which are moved to
572 # the end and can be mandatory or non-mandatory in any order, as they will
573 # be specified as keyword args anyway). Check the order of those attrs:
207 had_default = False
574 had_default = False
208 for a in cls.__attrs_attrs__:
575 for a in (a for a in attrs if a.init is not False and a.kw_only is False):
209 if these is None and a not in super_cls:
576 if had_default is True and a.default is NOTHING:
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(
577 raise ValueError(
213 "No mandatory attributes allowed after an attribute with a "
578 "No mandatory attributes allowed after an attribute with a "
214 "default value or factory. Attribute in question: {a!r}"
579 "default value or factory. Attribute in question: %r" % (a,)
215 .format(a=a)
216 )
580 )
217 elif had_default is False and \
581
218 a.default is not NOTHING and \
582 if had_default is False and a.default is not NOTHING:
219 a.init is not False:
220 had_default = True
583 had_default = True
221
584
222
585 if field_transformer is not None:
223 def _frozen_setattrs(self, name, value):
586 attrs = field_transformer(cls, attrs)
224 """
587
225 Attached to frozen classes as __setattr__.
588 # Create AttrsClass *after* applying the field_transformer since it may
226 """
589 # add or remove attributes!
227 raise FrozenInstanceError()
590 attr_names = [a.name for a in attrs]
591 AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
592
593 return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map))
594
595
596 if PYPY:
597
598 def _frozen_setattrs(self, name, value):
599 """
600 Attached to frozen classes as __setattr__.
601 """
602 if isinstance(self, BaseException) and name in (
603 "__cause__",
604 "__context__",
605 ):
606 BaseException.__setattr__(self, name, value)
607 return
608
609 raise FrozenInstanceError()
610
611 else:
612
613 def _frozen_setattrs(self, name, value):
614 """
615 Attached to frozen classes as __setattr__.
616 """
617 raise FrozenInstanceError()
228
618
229
619
230 def _frozen_delattrs(self, name):
620 def _frozen_delattrs(self, name):
@@ -234,44 +624,661 b' def _frozen_delattrs(self, name):'
234 raise FrozenInstanceError()
624 raise FrozenInstanceError()
235
625
236
626
237 def attributes(maybe_cls=None, these=None, repr_ns=None,
627 class _ClassBuilder:
238 repr=True, cmp=True, hash=None, init=True,
628 """
239 slots=False, frozen=False, str=False):
629 Iteratively build *one* class.
630 """
631
632 __slots__ = (
633 "_attr_names",
634 "_attrs",
635 "_base_attr_map",
636 "_base_names",
637 "_cache_hash",
638 "_cls",
639 "_cls_dict",
640 "_delete_attribs",
641 "_frozen",
642 "_has_pre_init",
643 "_has_post_init",
644 "_is_exc",
645 "_on_setattr",
646 "_slots",
647 "_weakref_slot",
648 "_wrote_own_setattr",
649 "_has_custom_setattr",
650 )
651
652 def __init__(
653 self,
654 cls,
655 these,
656 slots,
657 frozen,
658 weakref_slot,
659 getstate_setstate,
660 auto_attribs,
661 kw_only,
662 cache_hash,
663 is_exc,
664 collect_by_mro,
665 on_setattr,
666 has_custom_setattr,
667 field_transformer,
668 ):
669 attrs, base_attrs, base_map = _transform_attrs(
670 cls,
671 these,
672 auto_attribs,
673 kw_only,
674 collect_by_mro,
675 field_transformer,
676 )
677
678 self._cls = cls
679 self._cls_dict = dict(cls.__dict__) if slots else {}
680 self._attrs = attrs
681 self._base_names = {a.name for a in base_attrs}
682 self._base_attr_map = base_map
683 self._attr_names = tuple(a.name for a in attrs)
684 self._slots = slots
685 self._frozen = frozen
686 self._weakref_slot = weakref_slot
687 self._cache_hash = cache_hash
688 self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False))
689 self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))
690 self._delete_attribs = not bool(these)
691 self._is_exc = is_exc
692 self._on_setattr = on_setattr
693
694 self._has_custom_setattr = has_custom_setattr
695 self._wrote_own_setattr = False
696
697 self._cls_dict["__attrs_attrs__"] = self._attrs
698
699 if frozen:
700 self._cls_dict["__setattr__"] = _frozen_setattrs
701 self._cls_dict["__delattr__"] = _frozen_delattrs
702
703 self._wrote_own_setattr = True
704 elif on_setattr in (
705 _ng_default_on_setattr,
706 setters.validate,
707 setters.convert,
708 ):
709 has_validator = has_converter = False
710 for a in attrs:
711 if a.validator is not None:
712 has_validator = True
713 if a.converter is not None:
714 has_converter = True
715
716 if has_validator and has_converter:
717 break
718 if (
719 (
720 on_setattr == _ng_default_on_setattr
721 and not (has_validator or has_converter)
722 )
723 or (on_setattr == setters.validate and not has_validator)
724 or (on_setattr == setters.convert and not has_converter)
725 ):
726 # If class-level on_setattr is set to convert + validate, but
727 # there's no field to convert or validate, pretend like there's
728 # no on_setattr.
729 self._on_setattr = None
730
731 if getstate_setstate:
732 (
733 self._cls_dict["__getstate__"],
734 self._cls_dict["__setstate__"],
735 ) = self._make_getstate_setstate()
736
737 def __repr__(self):
738 return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__)
739
740 def build_class(self):
741 """
742 Finalize class based on the accumulated configuration.
743
744 Builder cannot be used after calling this method.
745 """
746 if self._slots is True:
747 return self._create_slots_class()
748 else:
749 return self._patch_original_class()
750
751 def _patch_original_class(self):
752 """
753 Apply accumulated methods and return the class.
754 """
755 cls = self._cls
756 base_names = self._base_names
757
758 # Clean class of attribute definitions (`attr.ib()`s).
759 if self._delete_attribs:
760 for name in self._attr_names:
761 if (
762 name not in base_names
763 and getattr(cls, name, _sentinel) is not _sentinel
764 ):
765 try:
766 delattr(cls, name)
767 except AttributeError:
768 # This can happen if a base class defines a class
769 # variable and we want to set an attribute with the
770 # same name by using only a type annotation.
771 pass
772
773 # Attach our dunder methods.
774 for name, value in self._cls_dict.items():
775 setattr(cls, name, value)
776
777 # If we've inherited an attrs __setattr__ and don't write our own,
778 # reset it to object's.
779 if not self._wrote_own_setattr and getattr(
780 cls, "__attrs_own_setattr__", False
781 ):
782 cls.__attrs_own_setattr__ = False
783
784 if not self._has_custom_setattr:
785 cls.__setattr__ = _obj_setattr
786
787 return cls
788
789 def _create_slots_class(self):
790 """
791 Build and return a new class with a `__slots__` attribute.
792 """
793 cd = {
794 k: v
795 for k, v in self._cls_dict.items()
796 if k not in tuple(self._attr_names) + ("__dict__", "__weakref__")
797 }
798
799 # If our class doesn't have its own implementation of __setattr__
800 # (either from the user or by us), check the bases, if one of them has
801 # an attrs-made __setattr__, that needs to be reset. We don't walk the
802 # MRO because we only care about our immediate base classes.
803 # XXX: This can be confused by subclassing a slotted attrs class with
804 # XXX: a non-attrs class and subclass the resulting class with an attrs
805 # XXX: class. See `test_slotted_confused` for details. For now that's
806 # XXX: OK with us.
807 if not self._wrote_own_setattr:
808 cd["__attrs_own_setattr__"] = False
809
810 if not self._has_custom_setattr:
811 for base_cls in self._cls.__bases__:
812 if base_cls.__dict__.get("__attrs_own_setattr__", False):
813 cd["__setattr__"] = _obj_setattr
814 break
815
816 # Traverse the MRO to collect existing slots
817 # and check for an existing __weakref__.
818 existing_slots = dict()
819 weakref_inherited = False
820 for base_cls in self._cls.__mro__[1:-1]:
821 if base_cls.__dict__.get("__weakref__", None) is not None:
822 weakref_inherited = True
823 existing_slots.update(
824 {
825 name: getattr(base_cls, name)
826 for name in getattr(base_cls, "__slots__", [])
827 }
828 )
829
830 base_names = set(self._base_names)
831
832 names = self._attr_names
833 if (
834 self._weakref_slot
835 and "__weakref__" not in getattr(self._cls, "__slots__", ())
836 and "__weakref__" not in names
837 and not weakref_inherited
838 ):
839 names += ("__weakref__",)
840
841 # We only add the names of attributes that aren't inherited.
842 # Setting __slots__ to inherited attributes wastes memory.
843 slot_names = [name for name in names if name not in base_names]
844 # There are slots for attributes from current class
845 # that are defined in parent classes.
846 # As their descriptors may be overridden by a child class,
847 # we collect them here and update the class dict
848 reused_slots = {
849 slot: slot_descriptor
850 for slot, slot_descriptor in existing_slots.items()
851 if slot in slot_names
852 }
853 slot_names = [name for name in slot_names if name not in reused_slots]
854 cd.update(reused_slots)
855 if self._cache_hash:
856 slot_names.append(_hash_cache_field)
857 cd["__slots__"] = tuple(slot_names)
858
859 cd["__qualname__"] = self._cls.__qualname__
860
861 # Create new class based on old class and our methods.
862 cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd)
863
864 # The following is a fix for
865 # <https://github.com/python-attrs/attrs/issues/102>. On Python 3,
866 # if a method mentions `__class__` or uses the no-arg super(), the
867 # compiler will bake a reference to the class in the method itself
868 # as `method.__closure__`. Since we replace the class with a
869 # clone, we rewrite these references so it keeps working.
870 for item in cls.__dict__.values():
871 if isinstance(item, (classmethod, staticmethod)):
872 # Class- and staticmethods hide their functions inside.
873 # These might need to be rewritten as well.
874 closure_cells = getattr(item.__func__, "__closure__", None)
875 elif isinstance(item, property):
876 # Workaround for property `super()` shortcut (PY3-only).
877 # There is no universal way for other descriptors.
878 closure_cells = getattr(item.fget, "__closure__", None)
879 else:
880 closure_cells = getattr(item, "__closure__", None)
881
882 if not closure_cells: # Catch None or the empty list.
883 continue
884 for cell in closure_cells:
885 try:
886 match = cell.cell_contents is self._cls
887 except ValueError: # ValueError: Cell is empty
888 pass
889 else:
890 if match:
891 set_closure_cell(cell, cls)
892
893 return cls
894
895 def add_repr(self, ns):
896 self._cls_dict["__repr__"] = self._add_method_dunders(
897 _make_repr(self._attrs, ns, self._cls)
898 )
899 return self
900
901 def add_str(self):
902 repr = self._cls_dict.get("__repr__")
903 if repr is None:
904 raise ValueError(
905 "__str__ can only be generated if a __repr__ exists."
906 )
907
908 def __str__(self):
909 return self.__repr__()
910
911 self._cls_dict["__str__"] = self._add_method_dunders(__str__)
912 return self
913
914 def _make_getstate_setstate(self):
915 """
916 Create custom __setstate__ and __getstate__ methods.
917 """
918 # __weakref__ is not writable.
919 state_attr_names = tuple(
920 an for an in self._attr_names if an != "__weakref__"
921 )
922
923 def slots_getstate(self):
924 """
925 Automatically created by attrs.
926 """
927 return tuple(getattr(self, name) for name in state_attr_names)
928
929 hash_caching_enabled = self._cache_hash
930
931 def slots_setstate(self, state):
932 """
933 Automatically created by attrs.
934 """
935 __bound_setattr = _obj_setattr.__get__(self, Attribute)
936 for name, value in zip(state_attr_names, state):
937 __bound_setattr(name, value)
938
939 # The hash code cache is not included when the object is
940 # serialized, but it still needs to be initialized to None to
941 # indicate that the first call to __hash__ should be a cache
942 # miss.
943 if hash_caching_enabled:
944 __bound_setattr(_hash_cache_field, None)
945
946 return slots_getstate, slots_setstate
947
948 def make_unhashable(self):
949 self._cls_dict["__hash__"] = None
950 return self
951
952 def add_hash(self):
953 self._cls_dict["__hash__"] = self._add_method_dunders(
954 _make_hash(
955 self._cls,
956 self._attrs,
957 frozen=self._frozen,
958 cache_hash=self._cache_hash,
959 )
960 )
961
962 return self
963
964 def add_init(self):
965 self._cls_dict["__init__"] = self._add_method_dunders(
966 _make_init(
967 self._cls,
968 self._attrs,
969 self._has_pre_init,
970 self._has_post_init,
971 self._frozen,
972 self._slots,
973 self._cache_hash,
974 self._base_attr_map,
975 self._is_exc,
976 self._on_setattr,
977 attrs_init=False,
978 )
979 )
980
981 return self
982
983 def add_match_args(self):
984 self._cls_dict["__match_args__"] = tuple(
985 field.name
986 for field in self._attrs
987 if field.init and not field.kw_only
988 )
989
990 def add_attrs_init(self):
991 self._cls_dict["__attrs_init__"] = self._add_method_dunders(
992 _make_init(
993 self._cls,
994 self._attrs,
995 self._has_pre_init,
996 self._has_post_init,
997 self._frozen,
998 self._slots,
999 self._cache_hash,
1000 self._base_attr_map,
1001 self._is_exc,
1002 self._on_setattr,
1003 attrs_init=True,
1004 )
1005 )
1006
1007 return self
1008
1009 def add_eq(self):
1010 cd = self._cls_dict
1011
1012 cd["__eq__"] = self._add_method_dunders(
1013 _make_eq(self._cls, self._attrs)
1014 )
1015 cd["__ne__"] = self._add_method_dunders(_make_ne())
1016
1017 return self
1018
1019 def add_order(self):
1020 cd = self._cls_dict
1021
1022 cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = (
1023 self._add_method_dunders(meth)
1024 for meth in _make_order(self._cls, self._attrs)
1025 )
1026
1027 return self
1028
1029 def add_setattr(self):
1030 if self._frozen:
1031 return self
1032
1033 sa_attrs = {}
1034 for a in self._attrs:
1035 on_setattr = a.on_setattr or self._on_setattr
1036 if on_setattr and on_setattr is not setters.NO_OP:
1037 sa_attrs[a.name] = a, on_setattr
1038
1039 if not sa_attrs:
1040 return self
1041
1042 if self._has_custom_setattr:
1043 # We need to write a __setattr__ but there already is one!
1044 raise ValueError(
1045 "Can't combine custom __setattr__ with on_setattr hooks."
1046 )
1047
1048 # docstring comes from _add_method_dunders
1049 def __setattr__(self, name, val):
1050 try:
1051 a, hook = sa_attrs[name]
1052 except KeyError:
1053 nval = val
1054 else:
1055 nval = hook(self, a, val)
1056
1057 _obj_setattr(self, name, nval)
1058
1059 self._cls_dict["__attrs_own_setattr__"] = True
1060 self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__)
1061 self._wrote_own_setattr = True
1062
1063 return self
1064
1065 def _add_method_dunders(self, method):
1066 """
1067 Add __module__ and __qualname__ to a *method* if possible.
1068 """
1069 try:
1070 method.__module__ = self._cls.__module__
1071 except AttributeError:
1072 pass
1073
1074 try:
1075 method.__qualname__ = ".".join(
1076 (self._cls.__qualname__, method.__name__)
1077 )
1078 except AttributeError:
1079 pass
1080
1081 try:
1082 method.__doc__ = "Method generated by attrs for class %s." % (
1083 self._cls.__qualname__,
1084 )
1085 except AttributeError:
1086 pass
1087
1088 return method
1089
1090
1091 def _determine_attrs_eq_order(cmp, eq, order, default_eq):
1092 """
1093 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
1094 values of eq and order. If *eq* is None, set it to *default_eq*.
1095 """
1096 if cmp is not None and any((eq is not None, order is not None)):
1097 raise ValueError("Don't mix `cmp` with `eq' and `order`.")
1098
1099 # cmp takes precedence due to bw-compatibility.
1100 if cmp is not None:
1101 return cmp, cmp
1102
1103 # If left None, equality is set to the specified default and ordering
1104 # mirrors equality.
1105 if eq is None:
1106 eq = default_eq
1107
1108 if order is None:
1109 order = eq
1110
1111 if eq is False and order is True:
1112 raise ValueError("`order` can only be True if `eq` is True too.")
1113
1114 return eq, order
1115
1116
1117 def _determine_attrib_eq_order(cmp, eq, order, default_eq):
1118 """
1119 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
1120 values of eq and order. If *eq* is None, set it to *default_eq*.
1121 """
1122 if cmp is not None and any((eq is not None, order is not None)):
1123 raise ValueError("Don't mix `cmp` with `eq' and `order`.")
1124
1125 def decide_callable_or_boolean(value):
1126 """
1127 Decide whether a key function is used.
1128 """
1129 if callable(value):
1130 value, key = True, value
1131 else:
1132 key = None
1133 return value, key
1134
1135 # cmp takes precedence due to bw-compatibility.
1136 if cmp is not None:
1137 cmp, cmp_key = decide_callable_or_boolean(cmp)
1138 return cmp, cmp_key, cmp, cmp_key
1139
1140 # If left None, equality is set to the specified default and ordering
1141 # mirrors equality.
1142 if eq is None:
1143 eq, eq_key = default_eq, None
1144 else:
1145 eq, eq_key = decide_callable_or_boolean(eq)
1146
1147 if order is None:
1148 order, order_key = eq, eq_key
1149 else:
1150 order, order_key = decide_callable_or_boolean(order)
1151
1152 if eq is False and order is True:
1153 raise ValueError("`order` can only be True if `eq` is True too.")
1154
1155 return eq, eq_key, order, order_key
1156
1157
1158 def _determine_whether_to_implement(
1159 cls, flag, auto_detect, dunders, default=True
1160 ):
1161 """
1162 Check whether we should implement a set of methods for *cls*.
1163
1164 *flag* is the argument passed into @attr.s like 'init', *auto_detect* the
1165 same as passed into @attr.s and *dunders* is a tuple of attribute names
1166 whose presence signal that the user has implemented it themselves.
1167
1168 Return *default* if no reason for either for or against is found.
1169 """
1170 if flag is True or flag is False:
1171 return flag
1172
1173 if flag is None and auto_detect is False:
1174 return default
1175
1176 # Logically, flag is None and auto_detect is True here.
1177 for dunder in dunders:
1178 if _has_own_attribute(cls, dunder):
1179 return False
1180
1181 return default
1182
1183
1184 def attrs(
1185 maybe_cls=None,
1186 these=None,
1187 repr_ns=None,
1188 repr=None,
1189 cmp=None,
1190 hash=None,
1191 init=None,
1192 slots=False,
1193 frozen=False,
1194 weakref_slot=True,
1195 str=False,
1196 auto_attribs=False,
1197 kw_only=False,
1198 cache_hash=False,
1199 auto_exc=False,
1200 eq=None,
1201 order=None,
1202 auto_detect=False,
1203 collect_by_mro=False,
1204 getstate_setstate=None,
1205 on_setattr=None,
1206 field_transformer=None,
1207 match_args=True,
1208 ):
240 r"""
1209 r"""
241 A class decorator that adds `dunder
1210 A class decorator that adds `dunder
242 <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the
1211 <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the
243 specified attributes using :func:`attr.ib` or the *these* argument.
1212 specified attributes using `attr.ib` or the *these* argument.
244
1213
245 :param these: A dictionary of name to :func:`attr.ib` mappings. This is
1214 :param these: A dictionary of name to `attr.ib` mappings. This is
246 useful to avoid the definition of your attributes within the class body
1215 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
1216 because you can't (e.g. if you want to add ``__repr__`` methods to
248 Django models) or don't want to.
1217 Django models) or don't want to.
249
1218
250 If *these* is not ``None``, ``attrs`` will *not* search the class body
1219 If *these* is not ``None``, ``attrs`` will *not* search the class body
251 for attributes.
1220 for attributes and will *not* remove any attributes from it.
252
1221
253 :type these: :class:`dict` of :class:`str` to :func:`attr.ib`
1222 If *these* is an ordered dict (`dict` on Python 3.6+,
1223 `collections.OrderedDict` otherwise), the order is deduced from
1224 the order of the attributes inside *these*. Otherwise the order
1225 of the definition of the attributes is used.
1226
1227 :type these: `dict` of `str` to `attr.ib`
254
1228
255 :param str repr_ns: When using nested classes, there's no way in Python 2
1229 :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
1230 to automatically detect that. Therefore it's possible to set the
257 namespace explicitly for a more meaningful ``repr`` output.
1231 namespace explicitly for a more meaningful ``repr`` output.
1232 :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*,
1233 *order*, and *hash* arguments explicitly, assume they are set to
1234 ``True`` **unless any** of the involved methods for one of the
1235 arguments is implemented in the *current* class (i.e. it is *not*
1236 inherited from some base class).
1237
1238 So for example by implementing ``__eq__`` on a class yourself,
1239 ``attrs`` will deduce ``eq=False`` and will create *neither*
1240 ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible
1241 ``__ne__`` by default, so it *should* be enough to only implement
1242 ``__eq__`` in most cases).
1243
1244 .. warning::
1245
1246 If you prevent ``attrs`` from creating the ordering methods for you
1247 (``order=False``, e.g. by implementing ``__le__``), it becomes
1248 *your* responsibility to make sure its ordering is sound. The best
1249 way is to use the `functools.total_ordering` decorator.
1250
1251
1252 Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*,
1253 *cmp*, or *hash* overrides whatever *auto_detect* would determine.
1254
1255 *auto_detect* requires Python 3. Setting it ``True`` on Python 2 raises
1256 an `attrs.exceptions.PythonTooOldError`.
1257
258 :param bool repr: Create a ``__repr__`` method with a human readable
1258 :param bool repr: Create a ``__repr__`` method with a human readable
259 represantation of ``attrs`` attributes..
1259 representation of ``attrs`` attributes..
260 :param bool str: Create a ``__str__`` method that is identical to
1260 :param bool str: Create a ``__str__`` method that is identical to
261 ``__repr__``. This is usually not necessary except for
1261 ``__repr__``. This is usually not necessary except for
262 :class:`Exception`\ s.
1262 `Exception`\ s.
263 :param bool cmp: Create ``__eq__``, ``__ne__``, ``__lt__``, ``__le__``,
1263 :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__``
264 ``__gt__``, and ``__ge__`` methods that compare the class as if it were
1264 and ``__ne__`` methods that check two instances for equality.
265 a tuple of its ``attrs`` attributes. But the attributes are *only*
1265
266 compared, if the type of both classes is *identical*!
1266 They compare the instances as if they were tuples of their ``attrs``
267 :param hash: If ``None`` (default), the ``__hash__`` method is generated
1267 attributes if and only if the types of both classes are *identical*!
268 according how *cmp* and *frozen* are set.
1268 :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``,
1269 ``__gt__``, and ``__ge__`` methods that behave like *eq* above and
1270 allow instances to be ordered. If ``None`` (default) mirror value of
1271 *eq*.
1272 :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq*
1273 and *order* to the same value. Must not be mixed with *eq* or *order*.
1274 :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method
1275 is generated according how *eq* and *frozen* are set.
269
1276
270 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you.
1277 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
1278 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to
272 None, marking it unhashable (which it is).
1279 None, marking it unhashable (which it is).
273 3. If *cmp* is False, ``__hash__`` will be left untouched meaning the
1280 3. If *eq* is False, ``__hash__`` will be left untouched meaning the
274 ``__hash__`` method of the superclass will be used (if superclass is
1281 ``__hash__`` method of the base class will be used (if base class is
275 ``object``, this means it will fall back to id-based hashing.).
1282 ``object``, this means it will fall back to id-based hashing.).
276
1283
277 Although not recommended, you can decide for yourself and force
1284 Although not recommended, you can decide for yourself and force
@@ -279,29 +1286,37 b' def attributes(maybe_cls=None, these=Non'
279 didn't freeze it programmatically) by passing ``True`` or not. Both of
1286 didn't freeze it programmatically) by passing ``True`` or not. Both of
280 these cases are rather special and should be used carefully.
1287 these cases are rather special and should be used carefully.
281
1288
282 See the `Python documentation \
1289 See our documentation on `hashing`, Python's documentation on
283 <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`_
1290 `object.__hash__`, and the `GitHub issue that led to the default \
284 and the `GitHub issue that led to the default behavior \
1291 behavior <https://github.com/python-attrs/attrs/issues/136>`_ for more
285 <https://github.com/python-attrs/attrs/issues/136>`_ for more details.
1292 details.
286 :type hash: ``bool`` or ``None``
1293 :param bool init: Create a ``__init__`` method that initializes the
287 :param bool init: Create a ``__init__`` method that initialiazes the
1294 ``attrs`` attributes. Leading underscores are stripped for the argument
288 ``attrs`` attributes. Leading underscores are stripped for the
1295 name. If a ``__attrs_pre_init__`` method exists on the class, it will
289 argument name. If a ``__attrs_post_init__`` method exists on the
1296 be called before the class is initialized. If a ``__attrs_post_init__``
290 class, it will be called after the class is fully initialized.
1297 method exists on the class, it will be called after the class is fully
291 :param bool slots: Create a slots_-style class that's more
1298 initialized.
292 memory-efficient. See :ref:`slots` for further ramifications.
1299
1300 If ``init`` is ``False``, an ``__attrs_init__`` method will be
1301 injected instead. This allows you to define a custom ``__init__``
1302 method that can do pre-init work such as ``super().__init__()``,
1303 and then call ``__attrs_init__()`` and ``__attrs_post_init__()``.
1304 :param bool slots: Create a `slotted class <slotted classes>` that's more
1305 memory-efficient. Slotted classes are generally superior to the default
1306 dict classes, but have some gotchas you should know about, so we
1307 encourage you to read the `glossary entry <slotted classes>`.
293 :param bool frozen: Make instances immutable after initialization. If
1308 :param bool frozen: Make instances immutable after initialization. If
294 someone attempts to modify a frozen instance,
1309 someone attempts to modify a frozen instance,
295 :exc:`attr.exceptions.FrozenInstanceError` is raised.
1310 `attr.exceptions.FrozenInstanceError` is raised.
296
1311
297 Please note:
1312 .. note::
298
1313
299 1. This is achieved by installing a custom ``__setattr__`` method
1314 1. This is achieved by installing a custom ``__setattr__`` method
300 on your class so you can't implement an own one.
1315 on your class, so you can't implement your own.
301
1316
302 2. True immutability is impossible in Python.
1317 2. True immutability is impossible in Python.
303
1318
304 3. This *does* have a minor a runtime performance :ref:`impact
1319 3. This *does* have a minor a runtime performance `impact
305 <how-frozen>` when initializing new instances. In other words:
1320 <how-frozen>` when initializing new instances. In other words:
306 ``__init__`` is slightly slower with ``frozen=True``.
1321 ``__init__`` is slightly slower with ``frozen=True``.
307
1322
@@ -310,316 +1325,651 b' def attributes(maybe_cls=None, these=Non'
310 circumvent that limitation by using
1325 circumvent that limitation by using
311 ``object.__setattr__(self, "attribute_name", value)``.
1326 ``object.__setattr__(self, "attribute_name", value)``.
312
1327
313 .. _slots: https://docs.python.org/3.5/reference/datamodel.html#slots
1328 5. Subclasses of a frozen class are frozen too.
314
1329
315 .. versionadded:: 16.0.0 *slots*
1330 :param bool weakref_slot: Make instances weak-referenceable. This has no
316 .. versionadded:: 16.1.0 *frozen*
1331 effect unless ``slots`` is also enabled.
317 .. versionadded:: 16.3.0 *str*, and support for ``__attrs_post_init__``.
1332 :param bool auto_attribs: If ``True``, collect :pep:`526`-annotated
318 .. versionchanged::
1333 attributes (Python 3.6 and later only) from the class body.
319 17.1.0 *hash* supports ``None`` as value which is also the default
1334
320 now.
1335 In this case, you **must** annotate every field. If ``attrs``
1336 encounters a field that is set to an `attr.ib` but lacks a type
1337 annotation, an `attr.exceptions.UnannotatedAttributeError` is
1338 raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't
1339 want to set a type.
1340
1341 If you assign a value to those attributes (e.g. ``x: int = 42``), that
1342 value becomes the default value like if it were passed using
1343 ``attr.ib(default=42)``. Passing an instance of `attrs.Factory` also
1344 works as expected in most cases (see warning below).
1345
1346 Attributes annotated as `typing.ClassVar`, and attributes that are
1347 neither annotated nor set to an `attr.ib` are **ignored**.
1348
1349 .. warning::
1350 For features that use the attribute name to create decorators (e.g.
1351 `validators <validators>`), you still *must* assign `attr.ib` to
1352 them. Otherwise Python will either not find the name or try to use
1353 the default value to call e.g. ``validator`` on it.
1354
1355 These errors can be quite confusing and probably the most common bug
1356 report on our bug tracker.
1357
1358 :param bool kw_only: Make all attributes keyword-only (Python 3+)
1359 in the generated ``__init__`` (if ``init`` is ``False``, this
1360 parameter is ignored).
1361 :param bool cache_hash: Ensure that the object's hash code is computed
1362 only once and stored on the object. If this is set to ``True``,
1363 hashing must be either explicitly or implicitly enabled for this
1364 class. If the hash code is cached, avoid any reassignments of
1365 fields involved in hash code computation or mutations of the objects
1366 those fields point to after object creation. If such changes occur,
1367 the behavior of the object's hash code is undefined.
1368 :param bool auto_exc: If the class subclasses `BaseException`
1369 (which implicitly includes any subclass of any exception), the
1370 following happens to behave like a well-behaved Python exceptions
1371 class:
1372
1373 - the values for *eq*, *order*, and *hash* are ignored and the
1374 instances compare and hash by the instance's ids (N.B. ``attrs`` will
1375 *not* remove existing implementations of ``__hash__`` or the equality
1376 methods. It just won't add own ones.),
1377 - all attributes that are either passed into ``__init__`` or have a
1378 default value are additionally available as a tuple in the ``args``
1379 attribute,
1380 - the value of *str* is ignored leaving ``__str__`` to base classes.
1381 :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs``
1382 collects attributes from base classes. The default behavior is
1383 incorrect in certain cases of multiple inheritance. It should be on by
1384 default but is kept off for backward-compatibility.
1385
1386 See issue `#428 <https://github.com/python-attrs/attrs/issues/428>`_ for
1387 more details.
1388
1389 :param Optional[bool] getstate_setstate:
1390 .. note::
1391 This is usually only interesting for slotted classes and you should
1392 probably just set *auto_detect* to `True`.
1393
1394 If `True`, ``__getstate__`` and
1395 ``__setstate__`` are generated and attached to the class. This is
1396 necessary for slotted classes to be pickleable. If left `None`, it's
1397 `True` by default for slotted classes and ``False`` for dict classes.
1398
1399 If *auto_detect* is `True`, and *getstate_setstate* is left `None`,
1400 and **either** ``__getstate__`` or ``__setstate__`` is detected directly
1401 on the class (i.e. not inherited), it is set to `False` (this is usually
1402 what you want).
1403
1404 :param on_setattr: A callable that is run whenever the user attempts to set
1405 an attribute (either by assignment like ``i.x = 42`` or by using
1406 `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments
1407 as validators: the instance, the attribute that is being modified, and
1408 the new value.
1409
1410 If no exception is raised, the attribute is set to the return value of
1411 the callable.
1412
1413 If a list of callables is passed, they're automatically wrapped in an
1414 `attrs.setters.pipe`.
1415 :type on_setattr: `callable`, or a list of callables, or `None`, or
1416 `attrs.setters.NO_OP`
1417
1418 :param Optional[callable] field_transformer:
1419 A function that is called with the original class object and all
1420 fields right before ``attrs`` finalizes the class. You can use
1421 this, e.g., to automatically add converters or validators to
1422 fields based on their types. See `transform-fields` for more details.
1423
1424 :param bool match_args:
1425 If `True` (default), set ``__match_args__`` on the class to support
1426 :pep:`634` (Structural Pattern Matching). It is a tuple of all
1427 non-keyword-only ``__init__`` parameter names on Python 3.10 and later.
1428 Ignored on older Python versions.
1429
1430 .. versionadded:: 16.0.0 *slots*
1431 .. versionadded:: 16.1.0 *frozen*
1432 .. versionadded:: 16.3.0 *str*
1433 .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``.
1434 .. versionchanged:: 17.1.0
1435 *hash* supports ``None`` as value which is also the default now.
1436 .. versionadded:: 17.3.0 *auto_attribs*
1437 .. versionchanged:: 18.1.0
1438 If *these* is passed, no attributes are deleted from the class body.
1439 .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained.
1440 .. versionadded:: 18.2.0 *weakref_slot*
1441 .. deprecated:: 18.2.0
1442 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a
1443 `DeprecationWarning` if the classes compared are subclasses of
1444 each other. ``__eq`` and ``__ne__`` never tried to compared subclasses
1445 to each other.
1446 .. versionchanged:: 19.2.0
1447 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider
1448 subclasses comparable anymore.
1449 .. versionadded:: 18.2.0 *kw_only*
1450 .. versionadded:: 18.2.0 *cache_hash*
1451 .. versionadded:: 19.1.0 *auto_exc*
1452 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
1453 .. versionadded:: 19.2.0 *eq* and *order*
1454 .. versionadded:: 20.1.0 *auto_detect*
1455 .. versionadded:: 20.1.0 *collect_by_mro*
1456 .. versionadded:: 20.1.0 *getstate_setstate*
1457 .. versionadded:: 20.1.0 *on_setattr*
1458 .. versionadded:: 20.3.0 *field_transformer*
1459 .. versionchanged:: 21.1.0
1460 ``init=False`` injects ``__attrs_init__``
1461 .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__``
1462 .. versionchanged:: 21.1.0 *cmp* undeprecated
1463 .. versionadded:: 21.3.0 *match_args*
321 """
1464 """
1465 eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None)
1466 hash_ = hash # work around the lack of nonlocal
1467
1468 if isinstance(on_setattr, (list, tuple)):
1469 on_setattr = setters.pipe(*on_setattr)
1470
322 def wrap(cls):
1471 def wrap(cls):
323 if getattr(cls, "__class__", None) is None:
1472 is_frozen = frozen or _has_frozen_base_class(cls)
324 raise TypeError("attrs only works with new-style classes.")
1473 is_exc = auto_exc is True and issubclass(cls, BaseException)
325
1474 has_own_setattr = auto_detect and _has_own_attribute(
326 if repr is False and str is True:
1475 cls, "__setattr__"
327 raise ValueError(
1476 )
328 "__str__ can only be generated if a __repr__ exists."
1477
329 )
1478 if has_own_setattr and is_frozen:
330
1479 raise ValueError("Can't freeze a class with a custom __setattr__.")
331 if slots:
1480
332 # Only need this later if we're using slots.
1481 builder = _ClassBuilder(
333 if these is None:
1482 cls,
334 ca_list = [name
1483 these,
335 for name, attr
1484 slots,
336 in cls.__dict__.items()
1485 is_frozen,
337 if isinstance(attr, _CountingAttr)]
1486 weakref_slot,
338 else:
1487 _determine_whether_to_implement(
339 ca_list = list(iterkeys(these))
1488 cls,
340 _transform_attrs(cls, these)
1489 getstate_setstate,
341
1490 auto_detect,
342 # Can't just re-use frozen name because Python's scoping. :(
1491 ("__getstate__", "__setstate__"),
343 # Can't compare function objects because Python 2 is terrible. :(
1492 default=slots,
344 effectively_frozen = _has_frozen_superclass(cls) or frozen
1493 ),
345 if repr is True:
1494 auto_attribs,
346 cls = _add_repr(cls, ns=repr_ns)
1495 kw_only,
1496 cache_hash,
1497 is_exc,
1498 collect_by_mro,
1499 on_setattr,
1500 has_own_setattr,
1501 field_transformer,
1502 )
1503 if _determine_whether_to_implement(
1504 cls, repr, auto_detect, ("__repr__",)
1505 ):
1506 builder.add_repr(repr_ns)
347 if str is True:
1507 if str is True:
348 cls.__str__ = cls.__repr__
1508 builder.add_str()
349 if cmp is True:
1509
350 cls = _add_cmp(cls)
1510 eq = _determine_whether_to_implement(
351
1511 cls, eq_, auto_detect, ("__eq__", "__ne__")
1512 )
1513 if not is_exc and eq is True:
1514 builder.add_eq()
1515 if not is_exc and _determine_whether_to_implement(
1516 cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__")
1517 ):
1518 builder.add_order()
1519
1520 builder.add_setattr()
1521
1522 if (
1523 hash_ is None
1524 and auto_detect is True
1525 and _has_own_attribute(cls, "__hash__")
1526 ):
1527 hash = False
1528 else:
1529 hash = hash_
352 if hash is not True and hash is not False and hash is not None:
1530 if hash is not True and hash is not False and hash is not None:
1531 # Can't use `hash in` because 1 == True for example.
353 raise TypeError(
1532 raise TypeError(
354 "Invalid value for hash. Must be True, False, or None."
1533 "Invalid value for hash. Must be True, False, or None."
355 )
1534 )
356 elif hash is False or (hash is None and cmp is False):
1535 elif hash is False or (hash is None and eq is False) or is_exc:
357 pass
1536 # Don't do anything. Should fall back to __object__'s __hash__
358 elif hash is True or (hash is None and cmp is True and frozen is True):
1537 # which is by id.
359 cls = _add_hash(cls)
1538 if cache_hash:
1539 raise TypeError(
1540 "Invalid value for cache_hash. To use hash caching,"
1541 " hashing must be either explicitly or implicitly "
1542 "enabled."
1543 )
1544 elif hash is True or (
1545 hash is None and eq is True and is_frozen is True
1546 ):
1547 # Build a __hash__ if told so, or if it's safe.
1548 builder.add_hash()
360 else:
1549 else:
361 cls.__hash__ = None
1550 # Raise TypeError on attempts to hash.
362
1551 if cache_hash:
363 if init is True:
1552 raise TypeError(
364 cls = _add_init(cls, effectively_frozen)
1553 "Invalid value for cache_hash. To use hash caching,"
365 if effectively_frozen is True:
1554 " hashing must be either explicitly or implicitly "
366 cls.__setattr__ = _frozen_setattrs
1555 "enabled."
367 cls.__delattr__ = _frozen_delattrs
1556 )
368 if slots is True:
1557 builder.make_unhashable()
369 # slots and frozen require __getstate__/__setstate__ to work
1558
370 cls = _add_pickle(cls)
1559 if _determine_whether_to_implement(
371 if slots is True:
1560 cls, init, auto_detect, ("__init__",)
372 cls_dict = dict(cls.__dict__)
1561 ):
373 cls_dict["__slots__"] = tuple(ca_list)
1562 builder.add_init()
374 for ca_name in ca_list:
1563 else:
375 # It might not actually be in there, e.g. if using 'these'.
1564 builder.add_attrs_init()
376 cls_dict.pop(ca_name, None)
1565 if cache_hash:
377 cls_dict.pop("__dict__", None)
1566 raise TypeError(
378
1567 "Invalid value for cache_hash. To use hash caching,"
379 qualname = getattr(cls, "__qualname__", None)
1568 " init must be True."
380 cls = type(cls)(cls.__name__, cls.__bases__, cls_dict)
1569 )
381 if qualname is not None:
1570
382 cls.__qualname__ = qualname
1571 if (
383
1572 PY310
384 return cls
1573 and match_args
385
1574 and not _has_own_attribute(cls, "__match_args__")
386 # attrs_or class type depends on the usage of the decorator. It's a class
1575 ):
387 # if it's used as `@attributes` but ``None`` if used # as `@attributes()`.
1576 builder.add_match_args()
1577
1578 return builder.build_class()
1579
1580 # maybe_cls's type depends on the usage of the decorator. It's a class
1581 # if it's used as `@attrs` but ``None`` if used as `@attrs()`.
388 if maybe_cls is None:
1582 if maybe_cls is None:
389 return wrap
1583 return wrap
390 else:
1584 else:
391 return wrap(maybe_cls)
1585 return wrap(maybe_cls)
392
1586
393
1587
394 if PY2:
1588 _attrs = attrs
395 def _has_frozen_superclass(cls):
1589 """
396 """
1590 Internal alias so we can use it in functions that take an argument called
397 Check whether *cls* has a frozen ancestor by looking at its
1591 *attrs*.
398 __setattr__.
1592 """
399 """
1593
400 return (
1594
401 getattr(
1595 def _has_frozen_base_class(cls):
402 cls.__setattr__, "__module__", None
1596 """
403 ) == _frozen_setattrs.__module__ and
1597 Check whether *cls* has a frozen ancestor by looking at its
404 cls.__setattr__.__name__ == _frozen_setattrs.__name__
1598 __setattr__.
1599 """
1600 return cls.__setattr__ is _frozen_setattrs
1601
1602
1603 def _generate_unique_filename(cls, func_name):
1604 """
1605 Create a "filename" suitable for a function being generated.
1606 """
1607 unique_filename = "<attrs generated {} {}.{}>".format(
1608 func_name,
1609 cls.__module__,
1610 getattr(cls, "__qualname__", cls.__name__),
1611 )
1612 return unique_filename
1613
1614
1615 def _make_hash(cls, attrs, frozen, cache_hash):
1616 attrs = tuple(
1617 a for a in attrs if a.hash is True or (a.hash is None and a.eq is True)
1618 )
1619
1620 tab = " "
1621
1622 unique_filename = _generate_unique_filename(cls, "hash")
1623 type_hash = hash(unique_filename)
1624 # If eq is custom generated, we need to include the functions in globs
1625 globs = {}
1626
1627 hash_def = "def __hash__(self"
1628 hash_func = "hash(("
1629 closing_braces = "))"
1630 if not cache_hash:
1631 hash_def += "):"
1632 else:
1633 hash_def += ", *"
1634
1635 hash_def += (
1636 ", _cache_wrapper="
1637 + "__import__('attr._make')._make._CacheHashWrapper):"
405 )
1638 )
406 else:
1639 hash_func = "_cache_wrapper(" + hash_func
407 def _has_frozen_superclass(cls):
1640 closing_braces += ")"
1641
1642 method_lines = [hash_def]
1643
1644 def append_hash_computation_lines(prefix, indent):
408 """
1645 """
409 Check whether *cls* has a frozen ancestor by looking at its
1646 Generate the code for actually computing the hash code.
410 __setattr__.
1647 Below this will either be returned directly or used to compute
1648 a value which is then cached, depending on the value of cache_hash
411 """
1649 """
412 return cls.__setattr__ == _frozen_setattrs
1650
413
1651 method_lines.extend(
414
1652 [
415 def _attrs_to_tuple(obj, attrs):
1653 indent + prefix + hash_func,
416 """
1654 indent + " %d," % (type_hash,),
417 Create a tuple of all values of *obj*'s *attrs*.
1655 ]
418 """
1656 )
419 return tuple(getattr(obj, a.name) for a in attrs)
1657
420
1658 for a in attrs:
421
1659 if a.eq_key:
422 def _add_hash(cls, attrs=None):
1660 cmp_name = "_%s_key" % (a.name,)
1661 globs[cmp_name] = a.eq_key
1662 method_lines.append(
1663 indent + " %s(self.%s)," % (cmp_name, a.name)
1664 )
1665 else:
1666 method_lines.append(indent + " self.%s," % a.name)
1667
1668 method_lines.append(indent + " " + closing_braces)
1669
1670 if cache_hash:
1671 method_lines.append(tab + "if self.%s is None:" % _hash_cache_field)
1672 if frozen:
1673 append_hash_computation_lines(
1674 "object.__setattr__(self, '%s', " % _hash_cache_field, tab * 2
1675 )
1676 method_lines.append(tab * 2 + ")") # close __setattr__
1677 else:
1678 append_hash_computation_lines(
1679 "self.%s = " % _hash_cache_field, tab * 2
1680 )
1681 method_lines.append(tab + "return self.%s" % _hash_cache_field)
1682 else:
1683 append_hash_computation_lines("return ", tab)
1684
1685 script = "\n".join(method_lines)
1686 return _make_method("__hash__", script, unique_filename, globs)
1687
1688
1689 def _add_hash(cls, attrs):
423 """
1690 """
424 Add a hash method to *cls*.
1691 Add a hash method to *cls*.
425 """
1692 """
426 if attrs is None:
1693 cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False)
427 attrs = [a
1694 return cls
428 for a in cls.__attrs_attrs__
1695
429 if a.hash is True or (a.hash is None and a.cmp is True)]
1696
430
1697 def _make_ne():
431 def hash_(self):
1698 """
1699 Create __ne__ method.
1700 """
1701
1702 def __ne__(self, other):
432 """
1703 """
433 Automatically created by attrs.
1704 Check equality and either forward a NotImplemented or
1705 return the result negated.
434 """
1706 """
435 return hash(_attrs_to_tuple(self, attrs))
1707 result = self.__eq__(other)
436
1708 if result is NotImplemented:
437 cls.__hash__ = hash_
1709 return NotImplemented
438 return cls
1710
439
1711 return not result
440
1712
441 def _add_cmp(cls, attrs=None):
1713 return __ne__
1714
1715
1716 def _make_eq(cls, attrs):
1717 """
1718 Create __eq__ method for *cls* with *attrs*.
442 """
1719 """
443 Add comparison methods to *cls*.
1720 attrs = [a for a in attrs if a.eq]
1721
1722 unique_filename = _generate_unique_filename(cls, "eq")
1723 lines = [
1724 "def __eq__(self, other):",
1725 " if other.__class__ is not self.__class__:",
1726 " return NotImplemented",
1727 ]
1728
1729 # We can't just do a big self.x = other.x and... clause due to
1730 # irregularities like nan == nan is false but (nan,) == (nan,) is true.
1731 globs = {}
1732 if attrs:
1733 lines.append(" return (")
1734 others = [" ) == ("]
1735 for a in attrs:
1736 if a.eq_key:
1737 cmp_name = "_%s_key" % (a.name,)
1738 # Add the key function to the global namespace
1739 # of the evaluated function.
1740 globs[cmp_name] = a.eq_key
1741 lines.append(
1742 " %s(self.%s),"
1743 % (
1744 cmp_name,
1745 a.name,
1746 )
1747 )
1748 others.append(
1749 " %s(other.%s),"
1750 % (
1751 cmp_name,
1752 a.name,
1753 )
1754 )
1755 else:
1756 lines.append(" self.%s," % (a.name,))
1757 others.append(" other.%s," % (a.name,))
1758
1759 lines += others + [" )"]
1760 else:
1761 lines.append(" return True")
1762
1763 script = "\n".join(lines)
1764
1765 return _make_method("__eq__", script, unique_filename, globs)
1766
1767
1768 def _make_order(cls, attrs):
444 """
1769 """
445 if attrs is None:
1770 Create ordering methods for *cls* with *attrs*.
446 attrs = [a for a in cls.__attrs_attrs__ if a.cmp]
1771 """
1772 attrs = [a for a in attrs if a.order]
447
1773
448 def attrs_to_tuple(obj):
1774 def attrs_to_tuple(obj):
449 """
1775 """
450 Save us some typing.
1776 Save us some typing.
451 """
1777 """
452 return _attrs_to_tuple(obj, attrs)
1778 return tuple(
453
1779 key(value) if key else value
454 def eq(self, other):
1780 for value, key in (
1781 (getattr(obj, a.name), a.order_key) for a in attrs
1782 )
1783 )
1784
1785 def __lt__(self, other):
1786 """
1787 Automatically created by attrs.
1788 """
1789 if other.__class__ is self.__class__:
1790 return attrs_to_tuple(self) < attrs_to_tuple(other)
1791
1792 return NotImplemented
1793
1794 def __le__(self, other):
1795 """
1796 Automatically created by attrs.
1797 """
1798 if other.__class__ is self.__class__:
1799 return attrs_to_tuple(self) <= attrs_to_tuple(other)
1800
1801 return NotImplemented
1802
1803 def __gt__(self, other):
1804 """
1805 Automatically created by attrs.
1806 """
1807 if other.__class__ is self.__class__:
1808 return attrs_to_tuple(self) > attrs_to_tuple(other)
1809
1810 return NotImplemented
1811
1812 def __ge__(self, other):
455 """
1813 """
456 Automatically created by attrs.
1814 Automatically created by attrs.
457 """
1815 """
458 if other.__class__ is self.__class__:
1816 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)
1817 return attrs_to_tuple(self) >= attrs_to_tuple(other)
506 else:
1818
507 return NotImplemented
1819 return NotImplemented
508
1820
509 cls.__eq__ = eq
1821 return __lt__, __le__, __gt__, __ge__
510 cls.__ne__ = ne
1822
511 cls.__lt__ = lt
1823
512 cls.__le__ = le
1824 def _add_eq(cls, attrs=None):
513 cls.__gt__ = gt
1825 """
514 cls.__ge__ = ge
1826 Add equality methods to *cls* with *attrs*.
1827 """
1828 if attrs is None:
1829 attrs = cls.__attrs_attrs__
1830
1831 cls.__eq__ = _make_eq(cls, attrs)
1832 cls.__ne__ = _make_ne()
515
1833
516 return cls
1834 return cls
517
1835
518
1836
1837 if HAS_F_STRINGS:
1838
1839 def _make_repr(attrs, ns, cls):
1840 unique_filename = _generate_unique_filename(cls, "repr")
1841 # Figure out which attributes to include, and which function to use to
1842 # format them. The a.repr value can be either bool or a custom
1843 # callable.
1844 attr_names_with_reprs = tuple(
1845 (a.name, (repr if a.repr is True else a.repr), a.init)
1846 for a in attrs
1847 if a.repr is not False
1848 )
1849 globs = {
1850 name + "_repr": r
1851 for name, r, _ in attr_names_with_reprs
1852 if r != repr
1853 }
1854 globs["_compat"] = _compat
1855 globs["AttributeError"] = AttributeError
1856 globs["NOTHING"] = NOTHING
1857 attribute_fragments = []
1858 for name, r, i in attr_names_with_reprs:
1859 accessor = (
1860 "self." + name
1861 if i
1862 else 'getattr(self, "' + name + '", NOTHING)'
1863 )
1864 fragment = (
1865 "%s={%s!r}" % (name, accessor)
1866 if r == repr
1867 else "%s={%s_repr(%s)}" % (name, name, accessor)
1868 )
1869 attribute_fragments.append(fragment)
1870 repr_fragment = ", ".join(attribute_fragments)
1871
1872 if ns is None:
1873 cls_name_fragment = (
1874 '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}'
1875 )
1876 else:
1877 cls_name_fragment = ns + ".{self.__class__.__name__}"
1878
1879 lines = [
1880 "def __repr__(self):",
1881 " try:",
1882 " already_repring = _compat.repr_context.already_repring",
1883 " except AttributeError:",
1884 " already_repring = {id(self),}",
1885 " _compat.repr_context.already_repring = already_repring",
1886 " else:",
1887 " if id(self) in already_repring:",
1888 " return '...'",
1889 " else:",
1890 " already_repring.add(id(self))",
1891 " try:",
1892 " return f'%s(%s)'" % (cls_name_fragment, repr_fragment),
1893 " finally:",
1894 " already_repring.remove(id(self))",
1895 ]
1896
1897 return _make_method(
1898 "__repr__", "\n".join(lines), unique_filename, globs=globs
1899 )
1900
1901 else:
1902
1903 def _make_repr(attrs, ns, _):
1904 """
1905 Make a repr method that includes relevant *attrs*, adding *ns* to the
1906 full name.
1907 """
1908
1909 # Figure out which attributes to include, and which function to use to
1910 # format them. The a.repr value can be either bool or a custom
1911 # callable.
1912 attr_names_with_reprs = tuple(
1913 (a.name, repr if a.repr is True else a.repr)
1914 for a in attrs
1915 if a.repr is not False
1916 )
1917
1918 def __repr__(self):
1919 """
1920 Automatically created by attrs.
1921 """
1922 try:
1923 already_repring = _compat.repr_context.already_repring
1924 except AttributeError:
1925 already_repring = set()
1926 _compat.repr_context.already_repring = already_repring
1927
1928 if id(self) in already_repring:
1929 return "..."
1930 real_cls = self.__class__
1931 if ns is None:
1932 class_name = real_cls.__qualname__.rsplit(">.", 1)[-1]
1933 else:
1934 class_name = ns + "." + real_cls.__name__
1935
1936 # Since 'self' remains on the stack (i.e.: strongly referenced)
1937 # for the duration of this call, it's safe to depend on id(...)
1938 # stability, and not need to track the instance and therefore
1939 # worry about properties like weakref- or hash-ability.
1940 already_repring.add(id(self))
1941 try:
1942 result = [class_name, "("]
1943 first = True
1944 for name, attr_repr in attr_names_with_reprs:
1945 if first:
1946 first = False
1947 else:
1948 result.append(", ")
1949 result.extend(
1950 (name, "=", attr_repr(getattr(self, name, NOTHING)))
1951 )
1952 return "".join(result) + ")"
1953 finally:
1954 already_repring.remove(id(self))
1955
1956 return __repr__
1957
1958
519 def _add_repr(cls, ns=None, attrs=None):
1959 def _add_repr(cls, ns=None, attrs=None):
520 """
1960 """
521 Add a repr method to *cls*.
1961 Add a repr method to *cls*.
522 """
1962 """
523 if attrs is None:
1963 if attrs is None:
524 attrs = [a for a in cls.__attrs_attrs__ if a.repr]
1964 attrs = cls.__attrs_attrs__
525
1965
526 def repr_(self):
1966 cls.__repr__ = _make_repr(attrs, ns, cls)
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 r = repr(attrs)
559 if not isinstance(r, bytes):
560 r = r.encode('utf-8')
561 sha1.update(r)
562 unique_filename = "<attrs generated init {0}>".format(
563 sha1.hexdigest()
564 )
565
566 script, globs = _attrs_to_script(
567 attrs,
568 frozen,
569 getattr(cls, "__attrs_post_init__", False),
570 )
571 locs = {}
572 bytecode = compile(script, unique_filename, "exec")
573 attr_dict = dict((a.name, a) for a in attrs)
574 globs.update({
575 "NOTHING": NOTHING,
576 "attr_dict": attr_dict,
577 })
578 if frozen is True:
579 # Save the lookup overhead in __init__ if we need to circumvent
580 # immutability.
581 globs["_cached_setattr"] = _obj_setattr
582 eval(bytecode, globs, locs)
583 init = locs["__init__"]
584
585 # In order of debuggers like PDB being able to step through the code,
586 # we add a fake linecache entry.
587 linecache.cache[unique_filename] = (
588 len(script),
589 None,
590 script.splitlines(True),
591 unique_filename
592 )
593 cls.__init__ = init
594 return cls
595
596
597 def _add_pickle(cls):
598 """
599 Add pickle helpers, needed for frozen and slotted classes
600 """
601 def _slots_getstate__(obj):
602 """
603 Play nice with pickle.
604 """
605 return tuple(getattr(obj, a.name) for a in fields(obj.__class__))
606
607 def _slots_setstate__(obj, state):
608 """
609 Play nice with pickle.
610 """
611 __bound_setattr = _obj_setattr.__get__(obj, Attribute)
612 for a, value in zip(fields(obj.__class__), state):
613 __bound_setattr(a.name, value)
614
615 cls.__getstate__ = _slots_getstate__
616 cls.__setstate__ = _slots_setstate__
617 return cls
1967 return cls
618
1968
619
1969
620 def fields(cls):
1970 def fields(cls):
621 """
1971 """
622 Returns the tuple of ``attrs`` attributes for a class.
1972 Return the tuple of ``attrs`` attributes for a class.
623
1973
624 The tuple also allows accessing the fields by their names (see below for
1974 The tuple also allows accessing the fields by their names (see below for
625 examples).
1975 examples).
@@ -630,12 +1980,12 b' def fields(cls):'
630 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
1980 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
631 class.
1981 class.
632
1982
633 :rtype: tuple (with name accesors) of :class:`attr.Attribute`
1983 :rtype: tuple (with name accessors) of `attrs.Attribute`
634
1984
635 .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
1985 .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
636 by name.
1986 by name.
637 """
1987 """
638 if not isclass(cls):
1988 if not isinstance(cls, type):
639 raise TypeError("Passed object must be a class.")
1989 raise TypeError("Passed object must be a class.")
640 attrs = getattr(cls, "__attrs_attrs__", None)
1990 attrs = getattr(cls, "__attrs_attrs__", None)
641 if attrs is None:
1991 if attrs is None:
@@ -645,6 +1995,34 b' def fields(cls):'
645 return attrs
1995 return attrs
646
1996
647
1997
1998 def fields_dict(cls):
1999 """
2000 Return an ordered dictionary of ``attrs`` attributes for a class, whose
2001 keys are the attribute names.
2002
2003 :param type cls: Class to introspect.
2004
2005 :raise TypeError: If *cls* is not a class.
2006 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
2007 class.
2008
2009 :rtype: an ordered dict where keys are attribute names and values are
2010 `attrs.Attribute`\\ s. This will be a `dict` if it's
2011 naturally ordered like on Python 3.6+ or an
2012 :class:`~collections.OrderedDict` otherwise.
2013
2014 .. versionadded:: 18.1.0
2015 """
2016 if not isinstance(cls, type):
2017 raise TypeError("Passed object must be a class.")
2018 attrs = getattr(cls, "__attrs_attrs__", None)
2019 if attrs is None:
2020 raise NotAnAttrsClassError(
2021 "{cls!r} is not an attrs-decorated class.".format(cls=cls)
2022 )
2023 return ordered_dict((a.name, a) for a in attrs)
2024
2025
648 def validate(inst):
2026 def validate(inst):
649 """
2027 """
650 Validate all attributes on *inst* that have a validator.
2028 Validate all attributes on *inst* that have a validator.
@@ -662,240 +2040,623 b' def validate(inst):'
662 v(inst, a, getattr(inst, a.name))
2040 v(inst, a, getattr(inst, a.name))
663
2041
664
2042
665 def _attrs_to_script(attrs, frozen, post_init):
2043 def _is_slot_cls(cls):
2044 return "__slots__" in cls.__dict__
2045
2046
2047 def _is_slot_attr(a_name, base_attr_map):
2048 """
2049 Check if the attribute name comes from a slot class.
2050 """
2051 return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name])
2052
2053
2054 def _make_init(
2055 cls,
2056 attrs,
2057 pre_init,
2058 post_init,
2059 frozen,
2060 slots,
2061 cache_hash,
2062 base_attr_map,
2063 is_exc,
2064 cls_on_setattr,
2065 attrs_init,
2066 ):
2067 has_cls_on_setattr = (
2068 cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP
2069 )
2070
2071 if frozen and has_cls_on_setattr:
2072 raise ValueError("Frozen classes can't use on_setattr.")
2073
2074 needs_cached_setattr = cache_hash or frozen
2075 filtered_attrs = []
2076 attr_dict = {}
2077 for a in attrs:
2078 if not a.init and a.default is NOTHING:
2079 continue
2080
2081 filtered_attrs.append(a)
2082 attr_dict[a.name] = a
2083
2084 if a.on_setattr is not None:
2085 if frozen is True:
2086 raise ValueError("Frozen classes can't use on_setattr.")
2087
2088 needs_cached_setattr = True
2089 elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP:
2090 needs_cached_setattr = True
2091
2092 unique_filename = _generate_unique_filename(cls, "init")
2093
2094 script, globs, annotations = _attrs_to_init_script(
2095 filtered_attrs,
2096 frozen,
2097 slots,
2098 pre_init,
2099 post_init,
2100 cache_hash,
2101 base_attr_map,
2102 is_exc,
2103 has_cls_on_setattr,
2104 attrs_init,
2105 )
2106 if cls.__module__ in sys.modules:
2107 # This makes typing.get_type_hints(CLS.__init__) resolve string types.
2108 globs.update(sys.modules[cls.__module__].__dict__)
2109
2110 globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict})
2111
2112 if needs_cached_setattr:
2113 # Save the lookup overhead in __init__ if we need to circumvent
2114 # setattr hooks.
2115 globs["_setattr"] = _obj_setattr
2116
2117 init = _make_method(
2118 "__attrs_init__" if attrs_init else "__init__",
2119 script,
2120 unique_filename,
2121 globs,
2122 )
2123 init.__annotations__ = annotations
2124
2125 return init
2126
2127
2128 def _setattr(attr_name, value_var, has_on_setattr):
2129 """
2130 Use the cached object.setattr to set *attr_name* to *value_var*.
2131 """
2132 return "_setattr(self, '%s', %s)" % (attr_name, value_var)
2133
2134
2135 def _setattr_with_converter(attr_name, value_var, has_on_setattr):
2136 """
2137 Use the cached object.setattr to set *attr_name* to *value_var*, but run
2138 its converter first.
2139 """
2140 return "_setattr(self, '%s', %s(%s))" % (
2141 attr_name,
2142 _init_converter_pat % (attr_name,),
2143 value_var,
2144 )
2145
2146
2147 def _assign(attr_name, value, has_on_setattr):
2148 """
2149 Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise
2150 relegate to _setattr.
2151 """
2152 if has_on_setattr:
2153 return _setattr(attr_name, value, True)
2154
2155 return "self.%s = %s" % (attr_name, value)
2156
2157
2158 def _assign_with_converter(attr_name, value_var, has_on_setattr):
2159 """
2160 Unless *attr_name* has an on_setattr hook, use normal assignment after
2161 conversion. Otherwise relegate to _setattr_with_converter.
2162 """
2163 if has_on_setattr:
2164 return _setattr_with_converter(attr_name, value_var, True)
2165
2166 return "self.%s = %s(%s)" % (
2167 attr_name,
2168 _init_converter_pat % (attr_name,),
2169 value_var,
2170 )
2171
2172
2173 def _attrs_to_init_script(
2174 attrs,
2175 frozen,
2176 slots,
2177 pre_init,
2178 post_init,
2179 cache_hash,
2180 base_attr_map,
2181 is_exc,
2182 has_cls_on_setattr,
2183 attrs_init,
2184 ):
666 """
2185 """
667 Return a script of an initializer for *attrs* and a dict of globals.
2186 Return a script of an initializer for *attrs* and a dict of globals.
668
2187
669 The globals are expected by the generated script.
2188 The globals are expected by the generated script.
670
2189
671 If *frozen* is True, we cannot set the attributes directly so we use
2190 If *frozen* is True, we cannot set the attributes directly so we use
672 a cached ``object.__setattr__``.
2191 a cached ``object.__setattr__``.
673 """
2192 """
674 lines = []
2193 lines = []
2194 if pre_init:
2195 lines.append("self.__attrs_pre_init__()")
2196
675 if frozen is True:
2197 if frozen is True:
676 lines.append(
2198 if slots is True:
677 # Circumvent the __setattr__ descriptor to save one lookup per
2199 fmt_setter = _setattr
678 # assignment.
2200 fmt_setter_with_converter = _setattr_with_converter
679 "_setattr = _cached_setattr.__get__(self, self.__class__)"
2201 else:
680 )
2202 # Dict frozen classes assign directly to __dict__.
681
2203 # But only if the attribute doesn't come from an ancestor slot
682 def fmt_setter(attr_name, value_var):
2204 # class.
683 return "_setattr('%(attr_name)s', %(value_var)s)" % {
2205 # Note _inst_dict will be used again below if cache_hash is True
684 "attr_name": attr_name,
2206 lines.append("_inst_dict = self.__dict__")
685 "value_var": value_var,
2207
686 }
2208 def fmt_setter(attr_name, value_var, has_on_setattr):
687
2209 if _is_slot_attr(attr_name, base_attr_map):
688 def fmt_setter_with_converter(attr_name, value_var):
2210 return _setattr(attr_name, value_var, has_on_setattr)
689 conv_name = _init_convert_pat.format(attr_name)
2211
690 return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % {
2212 return "_inst_dict['%s'] = %s" % (attr_name, value_var)
691 "attr_name": attr_name,
2213
692 "value_var": value_var,
2214 def fmt_setter_with_converter(
693 "conv": conv_name,
2215 attr_name, value_var, has_on_setattr
694 }
2216 ):
2217 if has_on_setattr or _is_slot_attr(attr_name, base_attr_map):
2218 return _setattr_with_converter(
2219 attr_name, value_var, has_on_setattr
2220 )
2221
2222 return "_inst_dict['%s'] = %s(%s)" % (
2223 attr_name,
2224 _init_converter_pat % (attr_name,),
2225 value_var,
2226 )
2227
695 else:
2228 else:
696 def fmt_setter(attr_name, value):
2229 # Not frozen.
697 return "self.%(attr_name)s = %(value)s" % {
2230 fmt_setter = _assign
698 "attr_name": attr_name,
2231 fmt_setter_with_converter = _assign_with_converter
699 "value": value,
700 }
701
702 def fmt_setter_with_converter(attr_name, value_var):
703 conv_name = _init_convert_pat.format(attr_name)
704 return "self.%(attr_name)s = %(conv)s(%(value_var)s)" % {
705 "attr_name": attr_name,
706 "value_var": value_var,
707 "conv": conv_name,
708 }
709
2232
710 args = []
2233 args = []
2234 kw_only_args = []
711 attrs_to_validate = []
2235 attrs_to_validate = []
712
2236
713 # This is a dictionary of names to validator and converter callables.
2237 # This is a dictionary of names to validator and converter callables.
714 # Injecting this into __init__ globals lets us avoid lookups.
2238 # Injecting this into __init__ globals lets us avoid lookups.
715 names_for_globals = {}
2239 names_for_globals = {}
2240 annotations = {"return": None}
716
2241
717 for a in attrs:
2242 for a in attrs:
718 if a.validator:
2243 if a.validator:
719 attrs_to_validate.append(a)
2244 attrs_to_validate.append(a)
2245
720 attr_name = a.name
2246 attr_name = a.name
2247 has_on_setattr = a.on_setattr is not None or (
2248 a.on_setattr is not setters.NO_OP and has_cls_on_setattr
2249 )
721 arg_name = a.name.lstrip("_")
2250 arg_name = a.name.lstrip("_")
2251
722 has_factory = isinstance(a.default, Factory)
2252 has_factory = isinstance(a.default, Factory)
723 if has_factory and a.default.takes_self:
2253 if has_factory and a.default.takes_self:
724 maybe_self = "self"
2254 maybe_self = "self"
725 else:
2255 else:
726 maybe_self = ""
2256 maybe_self = ""
2257
727 if a.init is False:
2258 if a.init is False:
728 if has_factory:
2259 if has_factory:
729 init_factory_name = _init_factory_pat.format(a.name)
2260 init_factory_name = _init_factory_pat.format(a.name)
730 if a.convert is not None:
2261 if a.converter is not None:
731 lines.append(fmt_setter_with_converter(
2262 lines.append(
732 attr_name,
2263 fmt_setter_with_converter(
733 init_factory_name + "({0})".format(maybe_self)))
2264 attr_name,
734 conv_name = _init_convert_pat.format(a.name)
2265 init_factory_name + "(%s)" % (maybe_self,),
735 names_for_globals[conv_name] = a.convert
2266 has_on_setattr,
2267 )
2268 )
2269 conv_name = _init_converter_pat % (a.name,)
2270 names_for_globals[conv_name] = a.converter
736 else:
2271 else:
737 lines.append(fmt_setter(
2272 lines.append(
738 attr_name,
2273 fmt_setter(
739 init_factory_name + "({0})".format(maybe_self)
2274 attr_name,
740 ))
2275 init_factory_name + "(%s)" % (maybe_self,),
2276 has_on_setattr,
2277 )
2278 )
741 names_for_globals[init_factory_name] = a.default.factory
2279 names_for_globals[init_factory_name] = a.default.factory
742 else:
2280 else:
743 if a.convert is not None:
2281 if a.converter is not None:
744 lines.append(fmt_setter_with_converter(
2282 lines.append(
745 attr_name,
2283 fmt_setter_with_converter(
746 "attr_dict['{attr_name}'].default"
2284 attr_name,
747 .format(attr_name=attr_name)
2285 "attr_dict['%s'].default" % (attr_name,),
748 ))
2286 has_on_setattr,
749 conv_name = _init_convert_pat.format(a.name)
2287 )
750 names_for_globals[conv_name] = a.convert
2288 )
2289 conv_name = _init_converter_pat % (a.name,)
2290 names_for_globals[conv_name] = a.converter
751 else:
2291 else:
752 lines.append(fmt_setter(
2292 lines.append(
753 attr_name,
2293 fmt_setter(
754 "attr_dict['{attr_name}'].default"
2294 attr_name,
755 .format(attr_name=attr_name)
2295 "attr_dict['%s'].default" % (attr_name,),
756 ))
2296 has_on_setattr,
2297 )
2298 )
757 elif a.default is not NOTHING and not has_factory:
2299 elif a.default is not NOTHING and not has_factory:
758 args.append(
2300 arg = "%s=attr_dict['%s'].default" % (arg_name, attr_name)
759 "{arg_name}=attr_dict['{attr_name}'].default".format(
2301 if a.kw_only:
760 arg_name=arg_name,
2302 kw_only_args.append(arg)
761 attr_name=attr_name,
2303 else:
2304 args.append(arg)
2305
2306 if a.converter is not None:
2307 lines.append(
2308 fmt_setter_with_converter(
2309 attr_name, arg_name, has_on_setattr
2310 )
762 )
2311 )
763 )
2312 names_for_globals[
764 if a.convert is not None:
2313 _init_converter_pat % (a.name,)
765 lines.append(fmt_setter_with_converter(attr_name, arg_name))
2314 ] = a.converter
766 names_for_globals[_init_convert_pat.format(a.name)] = a.convert
767 else:
2315 else:
768 lines.append(fmt_setter(attr_name, arg_name))
2316 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))
2317
769 elif has_factory:
2318 elif has_factory:
770 args.append("{arg_name}=NOTHING".format(arg_name=arg_name))
2319 arg = "%s=NOTHING" % (arg_name,)
771 lines.append("if {arg_name} is not NOTHING:"
2320 if a.kw_only:
772 .format(arg_name=arg_name))
2321 kw_only_args.append(arg)
2322 else:
2323 args.append(arg)
2324 lines.append("if %s is not NOTHING:" % (arg_name,))
2325
773 init_factory_name = _init_factory_pat.format(a.name)
2326 init_factory_name = _init_factory_pat.format(a.name)
774 if a.convert is not None:
2327 if a.converter is not None:
775 lines.append(" " + fmt_setter_with_converter(attr_name,
2328 lines.append(
776 arg_name))
2329 " "
2330 + fmt_setter_with_converter(
2331 attr_name, arg_name, has_on_setattr
2332 )
2333 )
777 lines.append("else:")
2334 lines.append("else:")
778 lines.append(" " + fmt_setter_with_converter(
2335 lines.append(
779 attr_name,
2336 " "
780 init_factory_name + "({0})".format(maybe_self)
2337 + fmt_setter_with_converter(
781 ))
2338 attr_name,
782 names_for_globals[_init_convert_pat.format(a.name)] = a.convert
2339 init_factory_name + "(" + maybe_self + ")",
2340 has_on_setattr,
2341 )
2342 )
2343 names_for_globals[
2344 _init_converter_pat % (a.name,)
2345 ] = a.converter
783 else:
2346 else:
784 lines.append(" " + fmt_setter(attr_name, arg_name))
2347 lines.append(
2348 " " + fmt_setter(attr_name, arg_name, has_on_setattr)
2349 )
785 lines.append("else:")
2350 lines.append("else:")
786 lines.append(" " + fmt_setter(
2351 lines.append(
787 attr_name,
2352 " "
788 init_factory_name + "({0})".format(maybe_self)
2353 + fmt_setter(
789 ))
2354 attr_name,
2355 init_factory_name + "(" + maybe_self + ")",
2356 has_on_setattr,
2357 )
2358 )
790 names_for_globals[init_factory_name] = a.default.factory
2359 names_for_globals[init_factory_name] = a.default.factory
791 else:
2360 else:
792 args.append(arg_name)
2361 if a.kw_only:
793 if a.convert is not None:
2362 kw_only_args.append(arg_name)
794 lines.append(fmt_setter_with_converter(attr_name, arg_name))
795 names_for_globals[_init_convert_pat.format(a.name)] = a.convert
796 else:
2363 else:
797 lines.append(fmt_setter(attr_name, arg_name))
2364 args.append(arg_name)
2365
2366 if a.converter is not None:
2367 lines.append(
2368 fmt_setter_with_converter(
2369 attr_name, arg_name, has_on_setattr
2370 )
2371 )
2372 names_for_globals[
2373 _init_converter_pat % (a.name,)
2374 ] = a.converter
2375 else:
2376 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))
2377
2378 if a.init is True:
2379 if a.type is not None and a.converter is None:
2380 annotations[arg_name] = a.type
2381 elif a.converter is not None:
2382 # Try to get the type from the converter.
2383 t = _AnnotationExtractor(a.converter).get_first_param_type()
2384 if t:
2385 annotations[arg_name] = t
798
2386
799 if attrs_to_validate: # we can skip this if there are no validators.
2387 if attrs_to_validate: # we can skip this if there are no validators.
800 names_for_globals["_config"] = _config
2388 names_for_globals["_config"] = _config
801 lines.append("if _config._run_validators is True:")
2389 lines.append("if _config._run_validators is True:")
802 for a in attrs_to_validate:
2390 for a in attrs_to_validate:
803 val_name = "__attr_validator_{}".format(a.name)
2391 val_name = "__attr_validator_" + a.name
804 attr_name = "__attr_{}".format(a.name)
2392 attr_name = "__attr_" + a.name
805 lines.append(" {}(self, {}, self.{})".format(
2393 lines.append(
806 val_name, attr_name, a.name))
2394 " %s(self, %s, self.%s)" % (val_name, attr_name, a.name)
2395 )
807 names_for_globals[val_name] = a.validator
2396 names_for_globals[val_name] = a.validator
808 names_for_globals[attr_name] = a
2397 names_for_globals[attr_name] = a
2398
809 if post_init:
2399 if post_init:
810 lines.append("self.__attrs_post_init__()")
2400 lines.append("self.__attrs_post_init__()")
811
2401
812 return """\
2402 # because this is set only after __attrs_post_init__ is called, a crash
813 def __init__(self, {args}):
2403 # will result if post-init tries to access the hash code. This seemed
2404 # preferable to setting this beforehand, in which case alteration to
2405 # field values during post-init combined with post-init accessing the
2406 # hash code would result in silent bugs.
2407 if cache_hash:
2408 if frozen:
2409 if slots:
2410 # if frozen and slots, then _setattr defined above
2411 init_hash_cache = "_setattr(self, '%s', %s)"
2412 else:
2413 # if frozen and not slots, then _inst_dict defined above
2414 init_hash_cache = "_inst_dict['%s'] = %s"
2415 else:
2416 init_hash_cache = "self.%s = %s"
2417 lines.append(init_hash_cache % (_hash_cache_field, "None"))
2418
2419 # For exceptions we rely on BaseException.__init__ for proper
2420 # initialization.
2421 if is_exc:
2422 vals = ",".join("self." + a.name for a in attrs if a.init)
2423
2424 lines.append("BaseException.__init__(self, %s)" % (vals,))
2425
2426 args = ", ".join(args)
2427 if kw_only_args:
2428 args += "%s*, %s" % (
2429 ", " if args else "", # leading comma
2430 ", ".join(kw_only_args), # kw_only args
2431 )
2432 return (
2433 """\
2434 def {init_name}(self, {args}):
814 {lines}
2435 {lines}
815 """.format(
2436 """.format(
816 args=", ".join(args),
2437 init_name=("__attrs_init__" if attrs_init else "__init__"),
817 lines="\n ".join(lines) if lines else "pass",
2438 args=args,
818 ), names_for_globals
2439 lines="\n ".join(lines) if lines else "pass",
819
2440 ),
820
2441 names_for_globals,
821 class Attribute(object):
2442 annotations,
2443 )
2444
2445
2446 class Attribute:
822 """
2447 """
823 *Read-only* representation of an attribute.
2448 *Read-only* representation of an attribute.
824
2449
825 :attribute name: The name of the attribute.
2450 The class has *all* arguments of `attr.ib` (except for ``factory``
826
2451 which is only syntactic sugar for ``default=Factory(...)`` plus the
827 Plus *all* arguments of :func:`attr.ib`.
2452 following:
2453
2454 - ``name`` (`str`): The name of the attribute.
2455 - ``inherited`` (`bool`): Whether or not that attribute has been inherited
2456 from a base class.
2457 - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The callables
2458 that are used for comparing and ordering objects by this attribute,
2459 respectively. These are set by passing a callable to `attr.ib`'s ``eq``,
2460 ``order``, or ``cmp`` arguments. See also :ref:`comparison customization
2461 <custom-comparison>`.
2462
2463 Instances of this class are frequently used for introspection purposes
2464 like:
2465
2466 - `fields` returns a tuple of them.
2467 - Validators get them passed as the first argument.
2468 - The :ref:`field transformer <transform-fields>` hook receives a list of
2469 them.
2470
2471 .. versionadded:: 20.1.0 *inherited*
2472 .. versionadded:: 20.1.0 *on_setattr*
2473 .. versionchanged:: 20.2.0 *inherited* is not taken into account for
2474 equality checks and hashing anymore.
2475 .. versionadded:: 21.1.0 *eq_key* and *order_key*
2476
2477 For the full version history of the fields, see `attr.ib`.
828 """
2478 """
2479
829 __slots__ = (
2480 __slots__ = (
830 "name", "default", "validator", "repr", "cmp", "hash", "init",
2481 "name",
831 "convert", "metadata",
2482 "default",
2483 "validator",
2484 "repr",
2485 "eq",
2486 "eq_key",
2487 "order",
2488 "order_key",
2489 "hash",
2490 "init",
2491 "metadata",
2492 "type",
2493 "converter",
2494 "kw_only",
2495 "inherited",
2496 "on_setattr",
832 )
2497 )
833
2498
834 def __init__(self, name, default, validator, repr, cmp, hash, init,
2499 def __init__(
835 convert=None, metadata=None):
2500 self,
2501 name,
2502 default,
2503 validator,
2504 repr,
2505 cmp, # XXX: unused, remove along with other cmp code.
2506 hash,
2507 init,
2508 inherited,
2509 metadata=None,
2510 type=None,
2511 converter=None,
2512 kw_only=False,
2513 eq=None,
2514 eq_key=None,
2515 order=None,
2516 order_key=None,
2517 on_setattr=None,
2518 ):
2519 eq, eq_key, order, order_key = _determine_attrib_eq_order(
2520 cmp, eq_key or eq, order_key or order, True
2521 )
2522
836 # Cache this descriptor here to speed things up later.
2523 # Cache this descriptor here to speed things up later.
837 bound_setattr = _obj_setattr.__get__(self, Attribute)
2524 bound_setattr = _obj_setattr.__get__(self, Attribute)
838
2525
2526 # Despite the big red warning, people *do* instantiate `Attribute`
2527 # themselves.
839 bound_setattr("name", name)
2528 bound_setattr("name", name)
840 bound_setattr("default", default)
2529 bound_setattr("default", default)
841 bound_setattr("validator", validator)
2530 bound_setattr("validator", validator)
842 bound_setattr("repr", repr)
2531 bound_setattr("repr", repr)
843 bound_setattr("cmp", cmp)
2532 bound_setattr("eq", eq)
2533 bound_setattr("eq_key", eq_key)
2534 bound_setattr("order", order)
2535 bound_setattr("order_key", order_key)
844 bound_setattr("hash", hash)
2536 bound_setattr("hash", hash)
845 bound_setattr("init", init)
2537 bound_setattr("init", init)
846 bound_setattr("convert", convert)
2538 bound_setattr("converter", converter)
847 bound_setattr("metadata", (metadata_proxy(metadata) if metadata
2539 bound_setattr(
848 else _empty_metadata_singleton))
2540 "metadata",
2541 (
2542 types.MappingProxyType(dict(metadata)) # Shallow copy
2543 if metadata
2544 else _empty_metadata_singleton
2545 ),
2546 )
2547 bound_setattr("type", type)
2548 bound_setattr("kw_only", kw_only)
2549 bound_setattr("inherited", inherited)
2550 bound_setattr("on_setattr", on_setattr)
849
2551
850 def __setattr__(self, name, value):
2552 def __setattr__(self, name, value):
851 raise FrozenInstanceError()
2553 raise FrozenInstanceError()
852
2554
853 @classmethod
2555 @classmethod
854 def from_counting_attr(cls, name, ca):
2556 def from_counting_attr(cls, name, ca, type=None):
2557 # type holds the annotated value. deal with conflicts:
2558 if type is None:
2559 type = ca.type
2560 elif ca.type is not None:
2561 raise ValueError(
2562 "Type annotation and type argument cannot both be present"
2563 )
855 inst_dict = {
2564 inst_dict = {
856 k: getattr(ca, k)
2565 k: getattr(ca, k)
857 for k
2566 for k in Attribute.__slots__
858 in Attribute.__slots__
2567 if k
859 if k not in (
2568 not in (
860 "name", "validator", "default",
2569 "name",
861 ) # exclude methods
2570 "validator",
2571 "default",
2572 "type",
2573 "inherited",
2574 ) # exclude methods and deprecated alias
862 }
2575 }
863 return cls(name=name, validator=ca._validator, default=ca._default,
2576 return cls(
864 **inst_dict)
2577 name=name,
2578 validator=ca._validator,
2579 default=ca._default,
2580 type=type,
2581 cmp=None,
2582 inherited=False,
2583 **inst_dict
2584 )
2585
2586 # Don't use attr.evolve since fields(Attribute) doesn't work
2587 def evolve(self, **changes):
2588 """
2589 Copy *self* and apply *changes*.
2590
2591 This works similarly to `attr.evolve` but that function does not work
2592 with ``Attribute``.
2593
2594 It is mainly meant to be used for `transform-fields`.
2595
2596 .. versionadded:: 20.3.0
2597 """
2598 new = copy.copy(self)
2599
2600 new._setattrs(changes.items())
2601
2602 return new
865
2603
866 # Don't use _add_pickle since fields(Attribute) doesn't work
2604 # Don't use _add_pickle since fields(Attribute) doesn't work
867 def __getstate__(self):
2605 def __getstate__(self):
868 """
2606 """
869 Play nice with pickle.
2607 Play nice with pickle.
870 """
2608 """
871 return tuple(getattr(self, name) if name != "metadata"
2609 return tuple(
872 else dict(self.metadata)
2610 getattr(self, name) if name != "metadata" else dict(self.metadata)
873 for name in self.__slots__)
2611 for name in self.__slots__
2612 )
874
2613
875 def __setstate__(self, state):
2614 def __setstate__(self, state):
876 """
2615 """
877 Play nice with pickle.
2616 Play nice with pickle.
878 """
2617 """
2618 self._setattrs(zip(self.__slots__, state))
2619
2620 def _setattrs(self, name_values_pairs):
879 bound_setattr = _obj_setattr.__get__(self, Attribute)
2621 bound_setattr = _obj_setattr.__get__(self, Attribute)
880 for name, value in zip(self.__slots__, state):
2622 for name, value in name_values_pairs:
881 if name != "metadata":
2623 if name != "metadata":
882 bound_setattr(name, value)
2624 bound_setattr(name, value)
883 else:
2625 else:
884 bound_setattr(name, metadata_proxy(value) if value else
2626 bound_setattr(
885 _empty_metadata_singleton)
2627 name,
886
2628 types.MappingProxyType(dict(value))
887
2629 if value
888 _a = [Attribute(name=name, default=NOTHING, validator=None,
2630 else _empty_metadata_singleton,
889 repr=True, cmp=True, hash=(name != "metadata"), init=True)
2631 )
890 for name in Attribute.__slots__]
2632
2633
2634 _a = [
2635 Attribute(
2636 name=name,
2637 default=NOTHING,
2638 validator=None,
2639 repr=True,
2640 cmp=None,
2641 eq=True,
2642 order=False,
2643 hash=(name != "metadata"),
2644 init=True,
2645 inherited=False,
2646 )
2647 for name in Attribute.__slots__
2648 ]
891
2649
892 Attribute = _add_hash(
2650 Attribute = _add_hash(
893 _add_cmp(_add_repr(Attribute, attrs=_a), attrs=_a),
2651 _add_eq(
894 attrs=[a for a in _a if a.hash]
2652 _add_repr(Attribute, attrs=_a),
2653 attrs=[a for a in _a if a.name != "inherited"],
2654 ),
2655 attrs=[a for a in _a if a.hash and a.name != "inherited"],
895 )
2656 )
896
2657
897
2658
898 class _CountingAttr(object):
2659 class _CountingAttr:
899 """
2660 """
900 Intermediate representation of attributes that uses a counter to preserve
2661 Intermediate representation of attributes that uses a counter to preserve
901 the order in which the attributes have been defined.
2662 the order in which the attributes have been defined.
@@ -903,35 +2664,105 b' class _CountingAttr(object):'
903 *Internal* data structure of the attrs library. Running into is most
2664 *Internal* data structure of the attrs library. Running into is most
904 likely the result of a bug like a forgotten `@attr.s` decorator.
2665 likely the result of a bug like a forgotten `@attr.s` decorator.
905 """
2666 """
906 __slots__ = ("counter", "_default", "repr", "cmp", "hash", "init",
2667
907 "metadata", "_validator", "convert")
2668 __slots__ = (
2669 "counter",
2670 "_default",
2671 "repr",
2672 "eq",
2673 "eq_key",
2674 "order",
2675 "order_key",
2676 "hash",
2677 "init",
2678 "metadata",
2679 "_validator",
2680 "converter",
2681 "type",
2682 "kw_only",
2683 "on_setattr",
2684 )
908 __attrs_attrs__ = tuple(
2685 __attrs_attrs__ = tuple(
909 Attribute(name=name, default=NOTHING, validator=None,
2686 Attribute(
910 repr=True, cmp=True, hash=True, init=True)
2687 name=name,
911 for name
2688 default=NOTHING,
912 in ("counter", "_default", "repr", "cmp", "hash", "init",)
2689 validator=None,
2690 repr=True,
2691 cmp=None,
2692 hash=True,
2693 init=True,
2694 kw_only=False,
2695 eq=True,
2696 eq_key=None,
2697 order=False,
2698 order_key=None,
2699 inherited=False,
2700 on_setattr=None,
2701 )
2702 for name in (
2703 "counter",
2704 "_default",
2705 "repr",
2706 "eq",
2707 "order",
2708 "hash",
2709 "init",
2710 "on_setattr",
2711 )
913 ) + (
2712 ) + (
914 Attribute(name="metadata", default=None, validator=None,
2713 Attribute(
915 repr=True, cmp=True, hash=False, init=True),
2714 name="metadata",
2715 default=None,
2716 validator=None,
2717 repr=True,
2718 cmp=None,
2719 hash=False,
2720 init=True,
2721 kw_only=False,
2722 eq=True,
2723 eq_key=None,
2724 order=False,
2725 order_key=None,
2726 inherited=False,
2727 on_setattr=None,
2728 ),
916 )
2729 )
917 cls_counter = 0
2730 cls_counter = 0
918
2731
919 def __init__(self, default, validator, repr, cmp, hash, init, convert,
2732 def __init__(
920 metadata):
2733 self,
2734 default,
2735 validator,
2736 repr,
2737 cmp,
2738 hash,
2739 init,
2740 converter,
2741 metadata,
2742 type,
2743 kw_only,
2744 eq,
2745 eq_key,
2746 order,
2747 order_key,
2748 on_setattr,
2749 ):
921 _CountingAttr.cls_counter += 1
2750 _CountingAttr.cls_counter += 1
922 self.counter = _CountingAttr.cls_counter
2751 self.counter = _CountingAttr.cls_counter
923 self._default = default
2752 self._default = default
924 # If validator is a list/tuple, wrap it using helper validator.
2753 self._validator = validator
925 if validator and isinstance(validator, (list, tuple)):
2754 self.converter = converter
926 self._validator = and_(*validator)
927 else:
928 self._validator = validator
929 self.repr = repr
2755 self.repr = repr
930 self.cmp = cmp
2756 self.eq = eq
2757 self.eq_key = eq_key
2758 self.order = order
2759 self.order_key = order_key
931 self.hash = hash
2760 self.hash = hash
932 self.init = init
2761 self.init = init
933 self.convert = convert
934 self.metadata = metadata
2762 self.metadata = metadata
2763 self.type = type
2764 self.kw_only = kw_only
2765 self.on_setattr = on_setattr
935
2766
936 def validator(self, meth):
2767 def validator(self, meth):
937 """
2768 """
@@ -965,15 +2796,14 b' class _CountingAttr(object):'
965 return meth
2796 return meth
966
2797
967
2798
968 _CountingAttr = _add_cmp(_add_repr(_CountingAttr))
2799 _CountingAttr = _add_eq(_add_repr(_CountingAttr))
969
2800
970
2801
971 @attributes(slots=True, init=False)
2802 class Factory:
972 class Factory(object):
973 """
2803 """
974 Stores a factory callable.
2804 Stores a factory callable.
975
2805
976 If passed as the default value to :func:`attr.ib`, the factory is used to
2806 If passed as the default value to `attrs.field`, the factory is used to
977 generate a new value.
2807 generate a new value.
978
2808
979 :param callable factory: A callable that takes either none or exactly one
2809 :param callable factory: A callable that takes either none or exactly one
@@ -983,8 +2813,8 b' class Factory(object):'
983
2813
984 .. versionadded:: 17.1.0 *takes_self*
2814 .. versionadded:: 17.1.0 *takes_self*
985 """
2815 """
986 factory = attr()
2816
987 takes_self = attr()
2817 __slots__ = ("factory", "takes_self")
988
2818
989 def __init__(self, factory, takes_self=False):
2819 def __init__(self, factory, takes_self=False):
990 """
2820 """
@@ -994,47 +2824,122 b' class Factory(object):'
994 self.factory = factory
2824 self.factory = factory
995 self.takes_self = takes_self
2825 self.takes_self = takes_self
996
2826
2827 def __getstate__(self):
2828 """
2829 Play nice with pickle.
2830 """
2831 return tuple(getattr(self, name) for name in self.__slots__)
2832
2833 def __setstate__(self, state):
2834 """
2835 Play nice with pickle.
2836 """
2837 for name, value in zip(self.__slots__, state):
2838 setattr(self, name, value)
2839
2840
2841 _f = [
2842 Attribute(
2843 name=name,
2844 default=NOTHING,
2845 validator=None,
2846 repr=True,
2847 cmp=None,
2848 eq=True,
2849 order=False,
2850 hash=True,
2851 init=True,
2852 inherited=False,
2853 )
2854 for name in Factory.__slots__
2855 ]
2856
2857 Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)
2858
997
2859
998 def make_class(name, attrs, bases=(object,), **attributes_arguments):
2860 def make_class(name, attrs, bases=(object,), **attributes_arguments):
999 """
2861 """
1000 A quick way to create a new class called *name* with *attrs*.
2862 A quick way to create a new class called *name* with *attrs*.
1001
2863
1002 :param name: The name for the new class.
2864 :param str name: The name for the new class.
1003 :type name: str
1004
2865
1005 :param attrs: A list of names or a dictionary of mappings of names to
2866 :param attrs: A list of names or a dictionary of mappings of names to
1006 attributes.
2867 attributes.
1007 :type attrs: :class:`list` or :class:`dict`
2868
2869 If *attrs* is a list or an ordered dict (`dict` on Python 3.6+,
2870 `collections.OrderedDict` otherwise), the order is deduced from
2871 the order of the names or attributes inside *attrs*. Otherwise the
2872 order of the definition of the attributes is used.
2873 :type attrs: `list` or `dict`
1008
2874
1009 :param tuple bases: Classes that the new class will subclass.
2875 :param tuple bases: Classes that the new class will subclass.
1010
2876
1011 :param attributes_arguments: Passed unmodified to :func:`attr.s`.
2877 :param attributes_arguments: Passed unmodified to `attr.s`.
1012
2878
1013 :return: A new class with *attrs*.
2879 :return: A new class with *attrs*.
1014 :rtype: type
2880 :rtype: type
1015
2881
1016 .. versionadded:: 17.1.0 *bases*
2882 .. versionadded:: 17.1.0 *bases*
2883 .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
1017 """
2884 """
1018 if isinstance(attrs, dict):
2885 if isinstance(attrs, dict):
1019 cls_dict = attrs
2886 cls_dict = attrs
1020 elif isinstance(attrs, (list, tuple)):
2887 elif isinstance(attrs, (list, tuple)):
1021 cls_dict = dict((a, attr()) for a in attrs)
2888 cls_dict = {a: attrib() for a in attrs}
1022 else:
2889 else:
1023 raise TypeError("attrs argument must be a dict or a list.")
2890 raise TypeError("attrs argument must be a dict or a list.")
1024
2891
1025 return attributes(**attributes_arguments)(type(name, bases, cls_dict))
2892 pre_init = cls_dict.pop("__attrs_pre_init__", None)
1026
2893 post_init = cls_dict.pop("__attrs_post_init__", None)
1027
2894 user_init = cls_dict.pop("__init__", None)
1028 # These are required by whithin this module so we define them here and merely
2895
1029 # import into .validators.
2896 body = {}
1030
2897 if pre_init is not None:
1031
2898 body["__attrs_pre_init__"] = pre_init
1032 @attributes(slots=True, hash=True)
2899 if post_init is not None:
1033 class _AndValidator(object):
2900 body["__attrs_post_init__"] = post_init
2901 if user_init is not None:
2902 body["__init__"] = user_init
2903
2904 type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body))
2905
2906 # For pickling to work, the __module__ variable needs to be set to the
2907 # frame where the class is created. Bypass this step in environments where
2908 # sys._getframe is not defined (Jython for example) or sys._getframe is not
2909 # defined for arguments greater than 0 (IronPython).
2910 try:
2911 type_.__module__ = sys._getframe(1).f_globals.get(
2912 "__name__", "__main__"
2913 )
2914 except (AttributeError, ValueError):
2915 pass
2916
2917 # We do it here for proper warnings with meaningful stacklevel.
2918 cmp = attributes_arguments.pop("cmp", None)
2919 (
2920 attributes_arguments["eq"],
2921 attributes_arguments["order"],
2922 ) = _determine_attrs_eq_order(
2923 cmp,
2924 attributes_arguments.get("eq"),
2925 attributes_arguments.get("order"),
2926 True,
2927 )
2928
2929 return _attrs(these=cls_dict, **attributes_arguments)(type_)
2930
2931
2932 # These are required by within this module so we define them here and merely
2933 # import into .validators / .converters.
2934
2935
2936 @attrs(slots=True, hash=True)
2937 class _AndValidator:
1034 """
2938 """
1035 Compose many validators to a single one.
2939 Compose many validators to a single one.
1036 """
2940 """
1037 _validators = attr()
2941
2942 _validators = attrib()
1038
2943
1039 def __call__(self, inst, attr, value):
2944 def __call__(self, inst, attr, value):
1040 for v in self._validators:
2945 for v in self._validators:
@@ -1047,16 +2952,55 b' def and_(*validators):'
1047
2952
1048 When called on a value, it runs all wrapped validators.
2953 When called on a value, it runs all wrapped validators.
1049
2954
1050 :param validators: Arbitrary number of validators.
2955 :param callables validators: Arbitrary number of validators.
1051 :type validators: callables
1052
2956
1053 .. versionadded:: 17.1.0
2957 .. versionadded:: 17.1.0
1054 """
2958 """
1055 vals = []
2959 vals = []
1056 for validator in validators:
2960 for validator in validators:
1057 vals.extend(
2961 vals.extend(
1058 validator._validators if isinstance(validator, _AndValidator)
2962 validator._validators
2963 if isinstance(validator, _AndValidator)
1059 else [validator]
2964 else [validator]
1060 )
2965 )
1061
2966
1062 return _AndValidator(tuple(vals))
2967 return _AndValidator(tuple(vals))
2968
2969
2970 def pipe(*converters):
2971 """
2972 A converter that composes multiple converters into one.
2973
2974 When called on a value, it runs all wrapped converters, returning the
2975 *last* value.
2976
2977 Type annotations will be inferred from the wrapped converters', if
2978 they have any.
2979
2980 :param callables converters: Arbitrary number of converters.
2981
2982 .. versionadded:: 20.1.0
2983 """
2984
2985 def pipe_converter(val):
2986 for converter in converters:
2987 val = converter(val)
2988
2989 return val
2990
2991 if not converters:
2992 # If the converter list is empty, pipe_converter is the identity.
2993 A = typing.TypeVar("A")
2994 pipe_converter.__annotations__ = {"val": A, "return": A}
2995 else:
2996 # Get parameter type from first converter.
2997 t = _AnnotationExtractor(converters[0]).get_first_param_type()
2998 if t:
2999 pipe_converter.__annotations__["val"] = t
3000
3001 # Get return type from last converter.
3002 rt = _AnnotationExtractor(converters[-1]).get_return_type()
3003 if rt:
3004 pipe_converter.__annotations__["return"] = rt
3005
3006 return pipe_converter
@@ -1,8 +1,22 b''
1 # SPDX-License-Identifier: MIT
2
1 """
3 """
2 Commonly useful converters.
4 Commonly useful converters.
3 """
5 """
4
6
5 from __future__ import absolute_import, division, print_function
7
8 import typing
9
10 from ._compat import _AnnotationExtractor
11 from ._make import NOTHING, Factory, pipe
12
13
14 __all__ = [
15 "default_if_none",
16 "optional",
17 "pipe",
18 "to_bool",
19 ]
6
20
7
21
8 def optional(converter):
22 def optional(converter):
@@ -10,10 +24,13 b' def optional(converter):'
10 A converter that allows an attribute to be optional. An optional attribute
24 A converter that allows an attribute to be optional. An optional attribute
11 is one which can be set to ``None``.
25 is one which can be set to ``None``.
12
26
27 Type annotations will be inferred from the wrapped converter's, if it
28 has any.
29
13 :param callable converter: the converter that is used for non-``None``
30 :param callable converter: the converter that is used for non-``None``
14 values.
31 values.
15
32
16 .. versionadded:: 17.1.0
33 .. versionadded:: 17.1.0
17 """
34 """
18
35
19 def optional_converter(val):
36 def optional_converter(val):
@@ -21,4 +38,107 b' def optional(converter):'
21 return None
38 return None
22 return converter(val)
39 return converter(val)
23
40
41 xtr = _AnnotationExtractor(converter)
42
43 t = xtr.get_first_param_type()
44 if t:
45 optional_converter.__annotations__["val"] = typing.Optional[t]
46
47 rt = xtr.get_return_type()
48 if rt:
49 optional_converter.__annotations__["return"] = typing.Optional[rt]
50
24 return optional_converter
51 return optional_converter
52
53
54 def default_if_none(default=NOTHING, factory=None):
55 """
56 A converter that allows to replace ``None`` values by *default* or the
57 result of *factory*.
58
59 :param default: Value to be used if ``None`` is passed. Passing an instance
60 of `attrs.Factory` is supported, however the ``takes_self`` option
61 is *not*.
62 :param callable factory: A callable that takes no parameters whose result
63 is used if ``None`` is passed.
64
65 :raises TypeError: If **neither** *default* or *factory* is passed.
66 :raises TypeError: If **both** *default* and *factory* are passed.
67 :raises ValueError: If an instance of `attrs.Factory` is passed with
68 ``takes_self=True``.
69
70 .. versionadded:: 18.2.0
71 """
72 if default is NOTHING and factory is None:
73 raise TypeError("Must pass either `default` or `factory`.")
74
75 if default is not NOTHING and factory is not None:
76 raise TypeError(
77 "Must pass either `default` or `factory` but not both."
78 )
79
80 if factory is not None:
81 default = Factory(factory)
82
83 if isinstance(default, Factory):
84 if default.takes_self:
85 raise ValueError(
86 "`takes_self` is not supported by default_if_none."
87 )
88
89 def default_if_none_converter(val):
90 if val is not None:
91 return val
92
93 return default.factory()
94
95 else:
96
97 def default_if_none_converter(val):
98 if val is not None:
99 return val
100
101 return default
102
103 return default_if_none_converter
104
105
106 def to_bool(val):
107 """
108 Convert "boolean" strings (e.g., from env. vars.) to real booleans.
109
110 Values mapping to :code:`True`:
111
112 - :code:`True`
113 - :code:`"true"` / :code:`"t"`
114 - :code:`"yes"` / :code:`"y"`
115 - :code:`"on"`
116 - :code:`"1"`
117 - :code:`1`
118
119 Values mapping to :code:`False`:
120
121 - :code:`False`
122 - :code:`"false"` / :code:`"f"`
123 - :code:`"no"` / :code:`"n"`
124 - :code:`"off"`
125 - :code:`"0"`
126 - :code:`0`
127
128 :raises ValueError: for any other value.
129
130 .. versionadded:: 21.3.0
131 """
132 if isinstance(val, str):
133 val = val.lower()
134 truthy = {True, "true", "t", "yes", "y", "on", "1", 1}
135 falsy = {False, "false", "f", "no", "n", "off", "0", 0}
136 try:
137 if val in truthy:
138 return True
139 if val in falsy:
140 return False
141 except TypeError:
142 # Raised when "val" is not hashable (e.g., lists)
143 pass
144 raise ValueError("Cannot convert value to bool: {}".format(val))
@@ -1,17 +1,35 b''
1 from __future__ import absolute_import, division, print_function
1 # SPDX-License-Identifier: MIT
2
2
3
3
4 class FrozenInstanceError(AttributeError):
4 class FrozenError(AttributeError):
5 """
5 """
6 A frozen/immutable instance has been attempted to be modified.
6 A frozen/immutable instance or attribute have been attempted to be
7 modified.
7
8
8 It mirrors the behavior of ``namedtuples`` by using the same error message
9 It mirrors the behavior of ``namedtuples`` by using the same error message
9 and subclassing :exc:`AttributeError`.
10 and subclassing `AttributeError`.
11
12 .. versionadded:: 20.1.0
13 """
14
15 msg = "can't set attribute"
16 args = [msg]
17
18
19 class FrozenInstanceError(FrozenError):
20 """
21 A frozen instance has been attempted to be modified.
10
22
11 .. versionadded:: 16.1.0
23 .. versionadded:: 16.1.0
12 """
24 """
13 msg = "can't set attribute"
25
14 args = [msg]
26
27 class FrozenAttributeError(FrozenError):
28 """
29 A frozen attribute has been attempted to be modified.
30
31 .. versionadded:: 20.1.0
32 """
15
33
16
34
17 class AttrsAttributeNotFoundError(ValueError):
35 class AttrsAttributeNotFoundError(ValueError):
@@ -37,3 +55,38 b' class DefaultAlreadySetError(RuntimeErro'
37
55
38 .. versionadded:: 17.1.0
56 .. versionadded:: 17.1.0
39 """
57 """
58
59
60 class UnannotatedAttributeError(RuntimeError):
61 """
62 A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type
63 annotation.
64
65 .. versionadded:: 17.3.0
66 """
67
68
69 class PythonTooOldError(RuntimeError):
70 """
71 It was attempted to use an ``attrs`` feature that requires a newer Python
72 version.
73
74 .. versionadded:: 18.2.0
75 """
76
77
78 class NotCallableError(TypeError):
79 """
80 A ``attr.ib()`` requiring a callable has been set with a value
81 that is not callable.
82
83 .. versionadded:: 19.2.0
84 """
85
86 def __init__(self, msg, value):
87 super(TypeError, self).__init__(msg, value)
88 self.msg = msg
89 self.value = value
90
91 def __str__(self):
92 return str(self.msg)
@@ -1,10 +1,9 b''
1 # SPDX-License-Identifier: MIT
2
1 """
3 """
2 Commonly useful filters for :func:`attr.asdict`.
4 Commonly useful filters for `attr.asdict`.
3 """
5 """
4
6
5 from __future__ import absolute_import, division, print_function
6
7 from ._compat import isclass
8 from ._make import Attribute
7 from ._make import Attribute
9
8
10
9
@@ -13,19 +12,19 b' def _split_what(what):'
13 Returns a tuple of `frozenset`s of classes and attributes.
12 Returns a tuple of `frozenset`s of classes and attributes.
14 """
13 """
15 return (
14 return (
16 frozenset(cls for cls in what if isclass(cls)),
15 frozenset(cls for cls in what if isinstance(cls, type)),
17 frozenset(cls for cls in what if isinstance(cls, Attribute)),
16 frozenset(cls for cls in what if isinstance(cls, Attribute)),
18 )
17 )
19
18
20
19
21 def include(*what):
20 def include(*what):
22 r"""
21 """
23 Whitelist *what*.
22 Include *what*.
24
23
25 :param what: What to whitelist.
24 :param what: What to include.
26 :type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\ s
25 :type what: `list` of `type` or `attrs.Attribute`\\ s
27
26
28 :rtype: :class:`callable`
27 :rtype: `callable`
29 """
28 """
30 cls, attrs = _split_what(what)
29 cls, attrs = _split_what(what)
31
30
@@ -36,13 +35,13 b' def include(*what):'
36
35
37
36
38 def exclude(*what):
37 def exclude(*what):
39 r"""
38 """
40 Blacklist *what*.
39 Exclude *what*.
41
40
42 :param what: What to blacklist.
41 :param what: What to exclude.
43 :type what: :class:`list` of classes or :class:`attr.Attribute`\ s.
42 :type what: `list` of classes or `attrs.Attribute`\\ s.
44
43
45 :rtype: :class:`callable`
44 :rtype: `callable`
46 """
45 """
47 cls, attrs = _split_what(what)
46 cls, attrs = _split_what(what)
48
47
This diff has been collapsed as it changes many lines, (522 lines changed) Show them Hide them
@@ -1,24 +1,99 b''
1 # SPDX-License-Identifier: MIT
2
1 """
3 """
2 Commonly useful validators.
4 Commonly useful validators.
3 """
5 """
4
6
5 from __future__ import absolute_import, division, print_function
7
8 import operator
9 import re
10
11 from contextlib import contextmanager
6
12
7 from ._make import attr, attributes, and_, _AndValidator
13 from ._config import get_run_validators, set_run_validators
14 from ._make import _AndValidator, and_, attrib, attrs
15 from .exceptions import NotCallableError
16
17
18 try:
19 Pattern = re.Pattern
20 except AttributeError: # Python <3.7 lacks a Pattern type.
21 Pattern = type(re.compile(""))
8
22
9
23
10 __all__ = [
24 __all__ = [
11 "and_",
25 "and_",
26 "deep_iterable",
27 "deep_mapping",
28 "disabled",
29 "ge",
30 "get_disabled",
31 "gt",
12 "in_",
32 "in_",
13 "instance_of",
33 "instance_of",
34 "is_callable",
35 "le",
36 "lt",
37 "matches_re",
38 "max_len",
39 "min_len",
14 "optional",
40 "optional",
15 "provides",
41 "provides",
42 "set_disabled",
16 ]
43 ]
17
44
18
45
19 @attributes(repr=False, slots=True, hash=True)
46 def set_disabled(disabled):
20 class _InstanceOfValidator(object):
47 """
21 type = attr()
48 Globally disable or enable running validators.
49
50 By default, they are run.
51
52 :param disabled: If ``True``, disable running all validators.
53 :type disabled: bool
54
55 .. warning::
56
57 This function is not thread-safe!
58
59 .. versionadded:: 21.3.0
60 """
61 set_run_validators(not disabled)
62
63
64 def get_disabled():
65 """
66 Return a bool indicating whether validators are currently disabled or not.
67
68 :return: ``True`` if validators are currently disabled.
69 :rtype: bool
70
71 .. versionadded:: 21.3.0
72 """
73 return not get_run_validators()
74
75
76 @contextmanager
77 def disabled():
78 """
79 Context manager that disables running validators within its context.
80
81 .. warning::
82
83 This context manager is not thread-safe!
84
85 .. versionadded:: 21.3.0
86 """
87 set_run_validators(False)
88 try:
89 yield
90 finally:
91 set_run_validators(True)
92
93
94 @attrs(repr=False, slots=True, hash=True)
95 class _InstanceOfValidator:
96 type = attrib()
22
97
23 def __call__(self, inst, attr, value):
98 def __call__(self, inst, attr, value):
24 """
99 """
@@ -27,38 +102,116 b' class _InstanceOfValidator(object):'
27 if not isinstance(value, self.type):
102 if not isinstance(value, self.type):
28 raise TypeError(
103 raise TypeError(
29 "'{name}' must be {type!r} (got {value!r} that is a "
104 "'{name}' must be {type!r} (got {value!r} that is a "
30 "{actual!r})."
105 "{actual!r}).".format(
31 .format(name=attr.name, type=self.type,
106 name=attr.name,
32 actual=value.__class__, value=value),
107 type=self.type,
33 attr, self.type, value,
108 actual=value.__class__,
109 value=value,
110 ),
111 attr,
112 self.type,
113 value,
34 )
114 )
35
115
36 def __repr__(self):
116 def __repr__(self):
37 return (
117 return "<instance_of validator for type {type!r}>".format(
38 "<instance_of validator for type {type!r}>"
118 type=self.type
39 .format(type=self.type)
40 )
119 )
41
120
42
121
43 def instance_of(type):
122 def instance_of(type):
44 """
123 """
45 A validator that raises a :exc:`TypeError` if the initializer is called
124 A validator that raises a `TypeError` if the initializer is called
46 with a wrong type for this particular attribute (checks are perfomed using
125 with a wrong type for this particular attribute (checks are performed using
47 :func:`isinstance` therefore it's also valid to pass a tuple of types).
126 `isinstance` therefore it's also valid to pass a tuple of types).
48
127
49 :param type: The type to check for.
128 :param type: The type to check for.
50 :type type: type or tuple of types
129 :type type: type or tuple of types
51
130
52 :raises TypeError: With a human readable error message, the attribute
131 :raises TypeError: With a human readable error message, the attribute
53 (of type :class:`attr.Attribute`), the expected type, and the value it
132 (of type `attrs.Attribute`), the expected type, and the value it
54 got.
133 got.
55 """
134 """
56 return _InstanceOfValidator(type)
135 return _InstanceOfValidator(type)
57
136
58
137
59 @attributes(repr=False, slots=True, hash=True)
138 @attrs(repr=False, frozen=True, slots=True)
60 class _ProvidesValidator(object):
139 class _MatchesReValidator:
61 interface = attr()
140 pattern = attrib()
141 match_func = attrib()
142
143 def __call__(self, inst, attr, value):
144 """
145 We use a callable class to be able to change the ``__repr__``.
146 """
147 if not self.match_func(value):
148 raise ValueError(
149 "'{name}' must match regex {pattern!r}"
150 " ({value!r} doesn't)".format(
151 name=attr.name, pattern=self.pattern.pattern, value=value
152 ),
153 attr,
154 self.pattern,
155 value,
156 )
157
158 def __repr__(self):
159 return "<matches_re validator for pattern {pattern!r}>".format(
160 pattern=self.pattern
161 )
162
163
164 def matches_re(regex, flags=0, func=None):
165 r"""
166 A validator that raises `ValueError` if the initializer is called
167 with a string that doesn't match *regex*.
168
169 :param regex: a regex string or precompiled pattern to match against
170 :param int flags: flags that will be passed to the underlying re function
171 (default 0)
172 :param callable func: which underlying `re` function to call. Valid options
173 are `re.fullmatch`, `re.search`, and `re.match`; the default ``None``
174 means `re.fullmatch`. For performance reasons, the pattern is always
175 precompiled using `re.compile`.
176
177 .. versionadded:: 19.2.0
178 .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern.
179 """
180 valid_funcs = (re.fullmatch, None, re.search, re.match)
181 if func not in valid_funcs:
182 raise ValueError(
183 "'func' must be one of {}.".format(
184 ", ".join(
185 sorted(
186 e and e.__name__ or "None" for e in set(valid_funcs)
187 )
188 )
189 )
190 )
191
192 if isinstance(regex, Pattern):
193 if flags:
194 raise TypeError(
195 "'flags' can only be used with a string pattern; "
196 "pass flags to re.compile() instead"
197 )
198 pattern = regex
199 else:
200 pattern = re.compile(regex, flags)
201
202 if func is re.match:
203 match_func = pattern.match
204 elif func is re.search:
205 match_func = pattern.search
206 else:
207 match_func = pattern.fullmatch
208
209 return _MatchesReValidator(pattern, match_func)
210
211
212 @attrs(repr=False, slots=True, hash=True)
213 class _ProvidesValidator:
214 interface = attrib()
62
215
63 def __call__(self, inst, attr, value):
216 def __call__(self, inst, attr, value):
64 """
217 """
@@ -67,37 +220,40 b' class _ProvidesValidator(object):'
67 if not self.interface.providedBy(value):
220 if not self.interface.providedBy(value):
68 raise TypeError(
221 raise TypeError(
69 "'{name}' must provide {interface!r} which {value!r} "
222 "'{name}' must provide {interface!r} which {value!r} "
70 "doesn't."
223 "doesn't.".format(
71 .format(name=attr.name, interface=self.interface, value=value),
224 name=attr.name, interface=self.interface, value=value
72 attr, self.interface, value,
225 ),
226 attr,
227 self.interface,
228 value,
73 )
229 )
74
230
75 def __repr__(self):
231 def __repr__(self):
76 return (
232 return "<provides validator for interface {interface!r}>".format(
77 "<provides validator for interface {interface!r}>"
233 interface=self.interface
78 .format(interface=self.interface)
79 )
234 )
80
235
81
236
82 def provides(interface):
237 def provides(interface):
83 """
238 """
84 A validator that raises a :exc:`TypeError` if the initializer is called
239 A validator that raises a `TypeError` if the initializer is called
85 with an object that does not provide the requested *interface* (checks are
240 with an object that does not provide the requested *interface* (checks are
86 performed using ``interface.providedBy(value)`` (see `zope.interface
241 performed using ``interface.providedBy(value)`` (see `zope.interface
87 <https://zopeinterface.readthedocs.io/en/latest/>`_).
242 <https://zopeinterface.readthedocs.io/en/latest/>`_).
88
243
89 :param zope.interface.Interface interface: The interface to check for.
244 :param interface: The interface to check for.
245 :type interface: ``zope.interface.Interface``
90
246
91 :raises TypeError: With a human readable error message, the attribute
247 :raises TypeError: With a human readable error message, the attribute
92 (of type :class:`attr.Attribute`), the expected interface, and the
248 (of type `attrs.Attribute`), the expected interface, and the
93 value it got.
249 value it got.
94 """
250 """
95 return _ProvidesValidator(interface)
251 return _ProvidesValidator(interface)
96
252
97
253
98 @attributes(repr=False, slots=True, hash=True)
254 @attrs(repr=False, slots=True, hash=True)
99 class _OptionalValidator(object):
255 class _OptionalValidator:
100 validator = attr()
256 validator = attrib()
101
257
102 def __call__(self, inst, attr, value):
258 def __call__(self, inst, attr, value):
103 if value is None:
259 if value is None:
@@ -106,9 +262,8 b' class _OptionalValidator(object):'
106 self.validator(inst, attr, value)
262 self.validator(inst, attr, value)
107
263
108 def __repr__(self):
264 def __repr__(self):
109 return (
265 return "<optional validator for {what} or None>".format(
110 "<optional validator for {what} or None>"
266 what=repr(self.validator)
111 .format(what=repr(self.validator))
112 )
267 )
113
268
114
269
@@ -120,7 +275,7 b' def optional(validator):'
120
275
121 :param validator: A validator (or a list of validators) that is used for
276 :param validator: A validator (or a list of validators) that is used for
122 non-``None`` values.
277 non-``None`` values.
123 :type validator: callable or :class:`list` of callables.
278 :type validator: callable or `list` of callables.
124
279
125 .. versionadded:: 15.1.0
280 .. versionadded:: 15.1.0
126 .. versionchanged:: 17.1.0 *validator* can be a list of validators.
281 .. versionchanged:: 17.1.0 *validator* can be a list of validators.
@@ -130,37 +285,310 b' def optional(validator):'
130 return _OptionalValidator(validator)
285 return _OptionalValidator(validator)
131
286
132
287
133 @attributes(repr=False, slots=True, hash=True)
288 @attrs(repr=False, slots=True, hash=True)
134 class _InValidator(object):
289 class _InValidator:
135 options = attr()
290 options = attrib()
136
291
137 def __call__(self, inst, attr, value):
292 def __call__(self, inst, attr, value):
138 if value not in self.options:
293 try:
294 in_options = value in self.options
295 except TypeError: # e.g. `1 in "abc"`
296 in_options = False
297
298 if not in_options:
139 raise ValueError(
299 raise ValueError(
140 "'{name}' must be in {options!r} (got {value!r})"
300 "'{name}' must be in {options!r} (got {value!r})".format(
141 .format(name=attr.name, options=self.options, value=value)
301 name=attr.name, options=self.options, value=value
302 ),
303 attr,
304 self.options,
305 value,
142 )
306 )
143
307
144 def __repr__(self):
308 def __repr__(self):
145 return (
309 return "<in_ validator with options {options!r}>".format(
146 "<in_ validator with options {options!r}>"
310 options=self.options
147 .format(options=self.options)
148 )
311 )
149
312
150
313
151 def in_(options):
314 def in_(options):
152 """
315 """
153 A validator that raises a :exc:`ValueError` if the initializer is called
316 A validator that raises a `ValueError` if the initializer is called
154 with a value that does not belong in the options provided. The check is
317 with a value that does not belong in the options provided. The check is
155 performed using ``value in options``.
318 performed using ``value in options``.
156
319
157 :param options: Allowed options.
320 :param options: Allowed options.
158 :type options: list, tuple, :class:`enum.Enum`, ...
321 :type options: list, tuple, `enum.Enum`, ...
159
322
160 :raises ValueError: With a human readable error message, the attribute (of
323 :raises ValueError: With a human readable error message, the attribute (of
161 type :class:`attr.Attribute`), the expected options, and the value it
324 type `attrs.Attribute`), the expected options, and the value it
162 got.
325 got.
163
326
164 .. versionadded:: 17.1.0
327 .. versionadded:: 17.1.0
328 .. versionchanged:: 22.1.0
329 The ValueError was incomplete until now and only contained the human
330 readable error message. Now it contains all the information that has
331 been promised since 17.1.0.
165 """
332 """
166 return _InValidator(options)
333 return _InValidator(options)
334
335
336 @attrs(repr=False, slots=False, hash=True)
337 class _IsCallableValidator:
338 def __call__(self, inst, attr, value):
339 """
340 We use a callable class to be able to change the ``__repr__``.
341 """
342 if not callable(value):
343 message = (
344 "'{name}' must be callable "
345 "(got {value!r} that is a {actual!r})."
346 )
347 raise NotCallableError(
348 msg=message.format(
349 name=attr.name, value=value, actual=value.__class__
350 ),
351 value=value,
352 )
353
354 def __repr__(self):
355 return "<is_callable validator>"
356
357
358 def is_callable():
359 """
360 A validator that raises a `attr.exceptions.NotCallableError` if the
361 initializer is called with a value for this particular attribute
362 that is not callable.
363
364 .. versionadded:: 19.1.0
365
366 :raises `attr.exceptions.NotCallableError`: With a human readable error
367 message containing the attribute (`attrs.Attribute`) name,
368 and the value it got.
369 """
370 return _IsCallableValidator()
371
372
373 @attrs(repr=False, slots=True, hash=True)
374 class _DeepIterable:
375 member_validator = attrib(validator=is_callable())
376 iterable_validator = attrib(
377 default=None, validator=optional(is_callable())
378 )
379
380 def __call__(self, inst, attr, value):
381 """
382 We use a callable class to be able to change the ``__repr__``.
383 """
384 if self.iterable_validator is not None:
385 self.iterable_validator(inst, attr, value)
386
387 for member in value:
388 self.member_validator(inst, attr, member)
389
390 def __repr__(self):
391 iterable_identifier = (
392 ""
393 if self.iterable_validator is None
394 else " {iterable!r}".format(iterable=self.iterable_validator)
395 )
396 return (
397 "<deep_iterable validator for{iterable_identifier}"
398 " iterables of {member!r}>"
399 ).format(
400 iterable_identifier=iterable_identifier,
401 member=self.member_validator,
402 )
403
404
405 def deep_iterable(member_validator, iterable_validator=None):
406 """
407 A validator that performs deep validation of an iterable.
408
409 :param member_validator: Validator(s) to apply to iterable members
410 :param iterable_validator: Validator to apply to iterable itself
411 (optional)
412
413 .. versionadded:: 19.1.0
414
415 :raises TypeError: if any sub-validators fail
416 """
417 if isinstance(member_validator, (list, tuple)):
418 member_validator = and_(*member_validator)
419 return _DeepIterable(member_validator, iterable_validator)
420
421
422 @attrs(repr=False, slots=True, hash=True)
423 class _DeepMapping:
424 key_validator = attrib(validator=is_callable())
425 value_validator = attrib(validator=is_callable())
426 mapping_validator = attrib(default=None, validator=optional(is_callable()))
427
428 def __call__(self, inst, attr, value):
429 """
430 We use a callable class to be able to change the ``__repr__``.
431 """
432 if self.mapping_validator is not None:
433 self.mapping_validator(inst, attr, value)
434
435 for key in value:
436 self.key_validator(inst, attr, key)
437 self.value_validator(inst, attr, value[key])
438
439 def __repr__(self):
440 return (
441 "<deep_mapping validator for objects mapping {key!r} to {value!r}>"
442 ).format(key=self.key_validator, value=self.value_validator)
443
444
445 def deep_mapping(key_validator, value_validator, mapping_validator=None):
446 """
447 A validator that performs deep validation of a dictionary.
448
449 :param key_validator: Validator to apply to dictionary keys
450 :param value_validator: Validator to apply to dictionary values
451 :param mapping_validator: Validator to apply to top-level mapping
452 attribute (optional)
453
454 .. versionadded:: 19.1.0
455
456 :raises TypeError: if any sub-validators fail
457 """
458 return _DeepMapping(key_validator, value_validator, mapping_validator)
459
460
461 @attrs(repr=False, frozen=True, slots=True)
462 class _NumberValidator:
463 bound = attrib()
464 compare_op = attrib()
465 compare_func = attrib()
466
467 def __call__(self, inst, attr, value):
468 """
469 We use a callable class to be able to change the ``__repr__``.
470 """
471 if not self.compare_func(value, self.bound):
472 raise ValueError(
473 "'{name}' must be {op} {bound}: {value}".format(
474 name=attr.name,
475 op=self.compare_op,
476 bound=self.bound,
477 value=value,
478 )
479 )
480
481 def __repr__(self):
482 return "<Validator for x {op} {bound}>".format(
483 op=self.compare_op, bound=self.bound
484 )
485
486
487 def lt(val):
488 """
489 A validator that raises `ValueError` if the initializer is called
490 with a number larger or equal to *val*.
491
492 :param val: Exclusive upper bound for values
493
494 .. versionadded:: 21.3.0
495 """
496 return _NumberValidator(val, "<", operator.lt)
497
498
499 def le(val):
500 """
501 A validator that raises `ValueError` if the initializer is called
502 with a number greater than *val*.
503
504 :param val: Inclusive upper bound for values
505
506 .. versionadded:: 21.3.0
507 """
508 return _NumberValidator(val, "<=", operator.le)
509
510
511 def ge(val):
512 """
513 A validator that raises `ValueError` if the initializer is called
514 with a number smaller than *val*.
515
516 :param val: Inclusive lower bound for values
517
518 .. versionadded:: 21.3.0
519 """
520 return _NumberValidator(val, ">=", operator.ge)
521
522
523 def gt(val):
524 """
525 A validator that raises `ValueError` if the initializer is called
526 with a number smaller or equal to *val*.
527
528 :param val: Exclusive lower bound for values
529
530 .. versionadded:: 21.3.0
531 """
532 return _NumberValidator(val, ">", operator.gt)
533
534
535 @attrs(repr=False, frozen=True, slots=True)
536 class _MaxLengthValidator:
537 max_length = attrib()
538
539 def __call__(self, inst, attr, value):
540 """
541 We use a callable class to be able to change the ``__repr__``.
542 """
543 if len(value) > self.max_length:
544 raise ValueError(
545 "Length of '{name}' must be <= {max}: {len}".format(
546 name=attr.name, max=self.max_length, len=len(value)
547 )
548 )
549
550 def __repr__(self):
551 return "<max_len validator for {max}>".format(max=self.max_length)
552
553
554 def max_len(length):
555 """
556 A validator that raises `ValueError` if the initializer is called
557 with a string or iterable that is longer than *length*.
558
559 :param int length: Maximum length of the string or iterable
560
561 .. versionadded:: 21.3.0
562 """
563 return _MaxLengthValidator(length)
564
565
566 @attrs(repr=False, frozen=True, slots=True)
567 class _MinLengthValidator:
568 min_length = attrib()
569
570 def __call__(self, inst, attr, value):
571 """
572 We use a callable class to be able to change the ``__repr__``.
573 """
574 if len(value) < self.min_length:
575 raise ValueError(
576 "Length of '{name}' must be => {min}: {len}".format(
577 name=attr.name, min=self.min_length, len=len(value)
578 )
579 )
580
581 def __repr__(self):
582 return "<min_len validator for {min}>".format(min=self.min_length)
583
584
585 def min_len(length):
586 """
587 A validator that raises `ValueError` if the initializer is called
588 with a string or iterable that is shorter than *length*.
589
590 :param int length: Minimum length of the string or iterable
591
592 .. versionadded:: 22.1.0
593 """
594 return _MinLengthValidator(length)
General Comments 0
You need to be logged in to leave comments. Login now