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