1# SPDX-License-Identifier: MIT

2

3 import copy

4 import enum

5 import linecache

6 import sys

7 import types

8 import typing

9

10 from operator import itemgetter

11

12# We need to import _compat itself in addition to the _compat members to avoid

13# having the thread-local in the globals here.

14 from . import _compat, _config, setters

15 from ._compat import PY310, PYPY, _AnnotationExtractor, set_closure_cell

16 from .exceptions import (

17 DefaultAlreadySetError,

18 FrozenInstanceError,

19 NotAnAttrsClassError,

20 UnannotatedAttributeError,

21)

22

23

24# This is used at least twice, so cache it here.

25_obj_setattr = object.__setattr__

26_init_converter_pat = "__attr_converter_%s"

27_init_factory_pat = "__attr_factory_%s"

28_classvar_prefixes = (

29 "typing.ClassVar",

30 "t.ClassVar",

31 "ClassVar",

32 "typing_extensions.ClassVar",

33)

34# we don't use a double-underscore prefix because that triggers

35# name mangling when trying to create a slot for the field

36# (when slots=True)

37_hash_cache_field = "_attrs_cached_hash"

38

39_empty_metadata_singleton = types.MappingProxyType({})

40

41# Unique object for unequivocal getattr() defaults.

42_sentinel = object()

43

44_ng_default_on_setattr = setters.pipe(setters.convert, setters.validate)

45

46

47 class _Nothing(enum.Enum):

48 """

49 Sentinel to indicate the lack of a value when ``None`` is ambiguous.

50

51 If extending attrs, you can use ``typing.Literal[NOTHING]`` to show

52 that a value may be ``NOTHING``.

53

54 .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False.

55 .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant.

56 """

57

58 NOTHING = enum.auto()

59

60 def __repr__(self):

61 return "NOTHING"

62

63 def __bool__(self):

64 return False

65

66

67 NOTHING = _Nothing.NOTHING

68"""

69 Sentinel to indicate the lack of a value when ``None`` is ambiguous.

70"""

71

72

73 class _CacheHashWrapper(int):

74 """

75 An integer subclass that pickles / copies as None

76

77 This is used for non-slots classes with ``cache_hash=True``, to avoid

78 serializing a potentially (even likely) invalid hash value. Since ``None``

79 is the default value for uncalculated hashes, whenever this is copied,

80 the copy's value for the hash should automatically reset.

81

82 See GH #613 for more details.

83 """

84

85 def __reduce__(self, _none_constructor=type(None), _args=()):

86 return _none_constructor, _args

87

88

89 def attrib(

90 default=NOTHING,

91 validator=None,

92 repr=True,

93 cmp=None,

94 hash=None,

95 init=True,

96 metadata=None,

97 type=None,

98 converter=None,

99 factory=None,

100 kw_only=False,

101 eq=None,

102 order=None,

103 on_setattr=None,

104 alias=None,

105):

106 """

107 Create a new attribute on a class.

108

109 .. warning::

110

111 Does *not* do anything unless the class is also decorated with

112 `attr.s`!

113

114 :param default: A value that is used if an ``attrs``-generated ``__init__``

115 is used and no value is passed while instantiating or the attribute is

116 excluded using ``init=False``.

117

118 If the value is an instance of `attrs.Factory`, its callable will be

119 used to construct a new value (useful for mutable data types like lists

120 or dicts).

121

122 If a default is not set (or set manually to `attrs.NOTHING`), a value

123 *must* be supplied when instantiating; otherwise a `TypeError`

124 will be raised.

125

126 The default can also be set using decorator notation as shown below.

127

128 :type default: Any value

129

130 :param callable factory: Syntactic sugar for

131 ``default=attr.Factory(factory)``.

132

133 :param validator: `callable` that is called by ``attrs``-generated

134 ``__init__`` methods after the instance has been initialized. They

135 receive the initialized instance, the :func:`~attrs.Attribute`, and the

136 passed value.

137

138 The return value is *not* inspected so the validator has to throw an

139 exception itself.

140

141 If a `list` is passed, its items are treated as validators and must

142 all pass.

143

144 Validators can be globally disabled and re-enabled using

145 `get_run_validators`.

146

147 The validator can also be set using decorator notation as shown below.

148

149 :type validator: `callable` or a `list` of `callable`\\ s.

150

151 :param repr: Include this attribute in the generated ``__repr__``

152 method. If ``True``, include the attribute; if ``False``, omit it. By

153 default, the built-in ``repr()`` function is used. To override how the

154 attribute value is formatted, pass a ``callable`` that takes a single

155 value and returns a string. Note that the resulting string is used

156 as-is, i.e. it will be used directly *instead* of calling ``repr()``

157 (the default).

158 :type repr: a `bool` or a `callable` to use a custom function.

159

160 :param eq: If ``True`` (default), include this attribute in the

161 generated ``__eq__`` and ``__ne__`` methods that check two instances

162 for equality. To override how the attribute value is compared,

163 pass a ``callable`` that takes a single value and returns the value

164 to be compared.

165 :type eq: a `bool` or a `callable`.

166

167 :param order: If ``True`` (default), include this attributes in the

168 generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods.

169 To override how the attribute value is ordered,

170 pass a ``callable`` that takes a single value and returns the value

171 to be ordered.

172 :type order: a `bool` or a `callable`.

173

174 :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the

175 same value. Must not be mixed with *eq* or *order*.

176 :type cmp: a `bool` or a `callable`.

177

178 :param Optional[bool] hash: Include this attribute in the generated

179 ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This

180 is the correct behavior according the Python spec. Setting this value

181 to anything else than ``None`` is *discouraged*.

182 :param bool init: Include this attribute in the generated ``__init__``

183 method. It is possible to set this to ``False`` and set a default

184 value. In that case this attributed is unconditionally initialized

185 with the specified default value or factory.

186 :param callable converter: `callable` that is called by

187 ``attrs``-generated ``__init__`` methods to convert attribute's value

188 to the desired format. It is given the passed-in value, and the

189 returned value will be used as the new value of the attribute. The

190 value is converted before being passed to the validator, if any.

191 :param metadata: An arbitrary mapping, to be used by third-party

192 components. See `extending-metadata`.

193

194 :param type: The type of the attribute. Nowadays, the preferred method to

195 specify the type is using a variable annotation (see :pep:`526`).

196 This argument is provided for backward compatibility.

197 Regardless of the approach used, the type will be stored on

198 ``Attribute.type``.

199

200 Please note that ``attrs`` doesn't do anything with this metadata by

201 itself. You can use it as part of your own code or for

202 `static type checking <types>`.

203 :param kw_only: Make this attribute keyword-only in the generated

204 ``__init__`` (if ``init`` is ``False``, this parameter is ignored).

205 :param on_setattr: Allows to overwrite the *on_setattr* setting from

206 `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used.

207 Set to `attrs.setters.NO_OP` to run **no** `setattr` hooks for this

208 attribute -- regardless of the setting in `attr.s`.

209 :type on_setattr: `callable`, or a list of callables, or `None`, or

210 `attrs.setters.NO_OP`

211 :param Optional[str] alias: Override this attribute's parameter name in the

212 generated ``__init__`` method. If left `None`, default to ``name``

213 stripped of leading underscores. See `private-attributes`.

214

215 .. versionadded:: 15.2.0 *convert*

216 .. versionadded:: 16.3.0 *metadata*

217 .. versionchanged:: 17.1.0 *validator* can be a ``list`` now.

218 .. versionchanged:: 17.1.0

219 *hash* is ``None`` and therefore mirrors *eq* by default.

220 .. versionadded:: 17.3.0 *type*

221 .. deprecated:: 17.4.0 *convert*

222 .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated

223 *convert* to achieve consistency with other noun-based arguments.

224 .. versionadded:: 18.1.0

225 ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``.

226 .. versionadded:: 18.2.0 *kw_only*

227 .. versionchanged:: 19.2.0 *convert* keyword argument removed.

228 .. versionchanged:: 19.2.0 *repr* also accepts a custom callable.

229 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.

230 .. versionadded:: 19.2.0 *eq* and *order*

231 .. versionadded:: 20.1.0 *on_setattr*

232 .. versionchanged:: 20.3.0 *kw_only* backported to Python 2

233 .. versionchanged:: 21.1.0

234 *eq*, *order*, and *cmp* also accept a custom callable

235 .. versionchanged:: 21.1.0 *cmp* undeprecated

236 .. versionadded:: 22.2.0 *alias*

237 """

238 eq, eq_key, order, order_key = _determine_attrib_eq_order(

239 cmp, eq, order, True

240 )

241

242 if hash is not None and hash is not True and hash is not False:

243 raise TypeError(

244 "Invalid value for hash. Must be True, False, or None."

245 )

246

247 if factory is not None:

248 if default is not NOTHING:

249 raise ValueError(

250 "The `default` and `factory` arguments are mutually "

251 "exclusive."

252 )

253 if not callable(factory):

254 raise ValueError("The `factory` argument must be a callable.")

255 default = Factory(factory)

256

257 if metadata is None:

258 metadata = {}

259

260 # Apply syntactic sugar by auto-wrapping.

261 if isinstance(on_setattr, (list, tuple)):

262 on_setattr = setters.pipe(*on_setattr)

263

264 if validator and isinstance(validator, (list, tuple)):

265 validator = and_(*validator)

266

267 if converter and isinstance(converter, (list, tuple)):

268 converter = pipe(*converter)

269

270 return _CountingAttr(

271 default=default,

272 validator=validator,

273 repr=repr,

274 cmp=None,

275 hash=hash,

276 init=init,

277 converter=converter,

278 metadata=metadata,

279 type=type,

280 kw_only=kw_only,

281 eq=eq,

282 eq_key=eq_key,

283 order=order,

284 order_key=order_key,

285 on_setattr=on_setattr,

286 alias=alias,

287 )

288

289

290 def _compile_and_eval(script, globs, locs=None, filename=""):

291 """

292 "Exec" the script with the given global (globs) and local (locs) variables.

293 """

294 bytecode = compile(script, filename, "exec")

295 eval(bytecode, globs, locs)

296

297

298 def _make_method(name, script, filename, globs):

299 """

300 Create the method with the script given and return the method object.

301 """

302 locs = {}

303

304 # In order of debuggers like PDB being able to step through the code,

305 # we add a fake linecache entry.

306 count = 1

307 base_filename = filename

308 while True:

309 linecache_tuple = (

310 len(script),

311 None,

312 script.splitlines(True),

313 filename,

314 )

315 old_val = linecache.cache.setdefault(filename, linecache_tuple)

316 if old_val == linecache_tuple:

317 break

318 else:

319 filename = f"{base_filename[:-1]}-{count}>"

320 count += 1

321

322 _compile_and_eval(script, globs, locs, filename)

323

324 return locs[name]

325

326

327 def _make_attr_tuple_class(cls_name, attr_names):

328 """

329 Create a tuple subclass to hold `Attribute`s for an `attrs` class.

330

331 The subclass is a bare tuple with properties for names.

332

333 class MyClassAttributes(tuple):

334 __slots__ = ()

335 x = property(itemgetter(0))

336 """

337 attr_class_name = f"{cls_name}Attributes"

338 attr_class_template = [

339 f"class {attr_class_name}(tuple):",

340 " __slots__ = ()",

341 ]

342 if attr_names:

343 for i, attr_name in enumerate(attr_names):

344 attr_class_template.append(

345 f" {attr_name} = _attrs_property(_attrs_itemgetter({i}))"

346 )

347 else:

348 attr_class_template.append(" pass")

349 globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property}

350 _compile_and_eval("\n".join(attr_class_template), globs)

351 return globs[attr_class_name]

352

353

354# Tuple class for extracted attributes from a class definition.

355# `base_attrs` is a subset of `attrs`.

356_Attributes = _make_attr_tuple_class(

357 "_Attributes",

358 [

359 # all attributes to build dunder methods for

360 "attrs",

361 # attributes that have been inherited

362 "base_attrs",

363 # map inherited attributes to their originating classes

364 "base_attrs_map",

365 ],

366)

367

368

369 def _is_class_var(annot):

370 """

371 Check whether *annot* is a typing.ClassVar.

372

373 The string comparison hack is used to avoid evaluating all string

374 annotations which would put attrs-based classes at a performance

375 disadvantage compared to plain old classes.

376 """

377 annot = str(annot)

378

379 # Annotation can be quoted.

380 if annot.startswith(("'", '"')) and annot.endswith(("'", '"')):

381 annot = annot[1:-1]

382

383 return annot.startswith(_classvar_prefixes)

384

385

386 def _has_own_attribute(cls, attrib_name):

387 """

388 Check whether *cls* defines *attrib_name* (and doesn't just inherit it).

389 """

390 attr = getattr(cls, attrib_name, _sentinel)

391 if attr is _sentinel:

392 return False

393

394 for base_cls in cls.__mro__[1:]:

395 a = getattr(base_cls, attrib_name, None)

396 if attr is a:

397 return False

398

399 return True

400

401

402 def _get_annotations(cls):

403 """

404 Get annotations for *cls*.

405 """

406 if _has_own_attribute(cls, "__annotations__"):

407 return cls.__annotations__

408

409 return {}

410

411

412 def _collect_base_attrs(cls, taken_attr_names):

413 """

414 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.

415 """

416 base_attrs = []

417 base_attr_map = {} # A dictionary of base attrs to their classes.

418

419 # Traverse the MRO and collect attributes.

420 for base_cls in reversed(cls.__mro__[1:-1]):

421 for a in getattr(base_cls, "__attrs_attrs__", []):

422 if a.inherited or a.name in taken_attr_names:

423 continue

424

425 a = a.evolve(inherited=True)

426 base_attrs.append(a)

427 base_attr_map[a.name] = base_cls

428

429 # For each name, only keep the freshest definition i.e. the furthest at the

430 # back. base_attr_map is fine because it gets overwritten with every new

431 # instance.

432 filtered = []

433 seen = set()

434 for a in reversed(base_attrs):

435 if a.name in seen:

436 continue

437 filtered.insert(0, a)

438 seen.add(a.name)

439

440 return filtered, base_attr_map

441

442

443 def _collect_base_attrs_broken(cls, taken_attr_names):

444 """

445 Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.

446

447 N.B. *taken_attr_names* will be mutated.

448

449 Adhere to the old incorrect behavior.

450

451 Notably it collects from the front and considers inherited attributes which

452 leads to the buggy behavior reported in #428.

453 """

454 base_attrs = []

455 base_attr_map = {} # A dictionary of base attrs to their classes.

456

457 # Traverse the MRO and collect attributes.

458 for base_cls in cls.__mro__[1:-1]:

459 for a in getattr(base_cls, "__attrs_attrs__", []):

460 if a.name in taken_attr_names:

461 continue

462

463 a = a.evolve(inherited=True)

464 taken_attr_names.add(a.name)

465 base_attrs.append(a)

466 base_attr_map[a.name] = base_cls

467

468 return base_attrs, base_attr_map

469

470

471 def _transform_attrs(

472 cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer

473):

474 """

475 Transform all `_CountingAttr`s on a class into `Attribute`s.

476

477 If *these* is passed, use that and don't look for them on the class.

478

479 *collect_by_mro* is True, collect them in the correct MRO order, otherwise

480 use the old -- incorrect -- order. See #428.

481

482 Return an `_Attributes`.

483 """

484 cd = cls.__dict__

485 anns = _get_annotations(cls)

486

487 if these is not None:

488 ca_list = [(name, ca) for name, ca in these.items()]

489 elif auto_attribs is True:

490 ca_names = {

491 name

492 for name, attr in cd.items()

493 if isinstance(attr, _CountingAttr)

494 }

495 ca_list = []

496 annot_names = set()

497 for attr_name, type in anns.items():

498 if _is_class_var(type):

499 continue

500 annot_names.add(attr_name)

501 a = cd.get(attr_name, NOTHING)

502

503 if not isinstance(a, _CountingAttr):

504 if a is NOTHING:

505 a = attrib()

506 else:

507 a = attrib(default=a)

508 ca_list.append((attr_name, a))

509

510 unannotated = ca_names - annot_names

511 if len(unannotated) > 0:

512 raise UnannotatedAttributeError(

513 "The following `attr.ib`s lack a type annotation: "

514 + ", ".join(

515 sorted(unannotated, key=lambda n: cd.get(n).counter)

516 )

517 + "."

518 )

519 else:

520 ca_list = sorted(

521 (

522 (name, attr)

523 for name, attr in cd.items()

524 if isinstance(attr, _CountingAttr)

525 ),

526 key=lambda e: e[1].counter,

527 )

528

529 own_attrs = [

530 Attribute.from_counting_attr(

531 name=attr_name, ca=ca, type=anns.get(attr_name)

532 )

533 for attr_name, ca in ca_list

534 ]

535

536 if collect_by_mro:

537 base_attrs, base_attr_map = _collect_base_attrs(

538 cls, {a.name for a in own_attrs}

539 )

540 else:

541 base_attrs, base_attr_map = _collect_base_attrs_broken(

542 cls, {a.name for a in own_attrs}

543 )

544

545 if kw_only:

546 own_attrs = [a.evolve(kw_only=True) for a in own_attrs]

547 base_attrs = [a.evolve(kw_only=True) for a in base_attrs]

548

549 attrs = base_attrs + own_attrs

550

551 # Mandatory vs non-mandatory attr order only matters when they are part of

552 # the __init__ signature and when they aren't kw_only (which are moved to

553 # the end and can be mandatory or non-mandatory in any order, as they will

554 # be specified as keyword args anyway). Check the order of those attrs:

555 had_default = False

556 for a in (a for a in attrs if a.init is not False and a.kw_only is False):

557 if had_default is True and a.default is NOTHING:

558 raise ValueError(

559 "No mandatory attributes allowed after an attribute with a "

560 f"default value or factory. Attribute in question: {a!r}"

561 )

562

563 if had_default is False and a.default is not NOTHING:

564 had_default = True

565

566 if field_transformer is not None:

567 attrs = field_transformer(cls, attrs)

568

569 # Resolve default field alias after executing field_transformer.

570 # This allows field_transformer to differentiate between explicit vs

571 # default aliases and supply their own defaults.

572 attrs = [

573 a.evolve(alias=_default_init_alias_for(a.name)) if not a.alias else a

574 for a in attrs

575 ]

576

577 # Create AttrsClass *after* applying the field_transformer since it may

578 # add or remove attributes!

579 attr_names = [a.name for a in attrs]

580 AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)

581

582 return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map))

583

584

585 if PYPY:

586

587 def _frozen_setattrs(self, name, value):

588 """

589 Attached to frozen classes as __setattr__.

590 """

591 if isinstance(self, BaseException) and name in (

592 "__cause__",

593 "__context__",

594 ):

595 BaseException.__setattr__(self, name, value)

596 return

597

598 raise FrozenInstanceError()

599

600 else:

601

602 def _frozen_setattrs(self, name, value):

603 """

604 Attached to frozen classes as __setattr__.

605 """

606 raise FrozenInstanceError()

607

608

609 def _frozen_delattrs(self, name):

610 """

611 Attached to frozen classes as __delattr__.

612 """

613 raise FrozenInstanceError()

614

615

616 class _ClassBuilder:

617 """

618 Iteratively build *one* class.

619 """

620

621 __slots__ = (

622 "_attr_names",

623 "_attrs",

624 "_base_attr_map",

625 "_base_names",

626 "_cache_hash",

627 "_cls",

628 "_cls_dict",

629 "_delete_attribs",

630 "_frozen",

631 "_has_pre_init",

632 "_has_post_init",

633 "_is_exc",

634 "_on_setattr",

635 "_slots",

636 "_weakref_slot",

637 "_wrote_own_setattr",

638 "_has_custom_setattr",

639 )

640

641 def __init__(

642 self,

643 cls,

644 these,

645 slots,

646 frozen,

647 weakref_slot,

648 getstate_setstate,

649 auto_attribs,

650 kw_only,

651 cache_hash,

652 is_exc,

653 collect_by_mro,

654 on_setattr,

655 has_custom_setattr,

656 field_transformer,

657 ):

658 attrs, base_attrs, base_map = _transform_attrs(

659 cls,

660 these,

661 auto_attribs,

662 kw_only,

663 collect_by_mro,

664 field_transformer,

665 )

666

667 self._cls = cls

668 self._cls_dict = dict(cls.__dict__) if slots else {}

669 self._attrs = attrs

670 self._base_names = {a.name for a in base_attrs}

671 self._base_attr_map = base_map

672 self._attr_names = tuple(a.name for a in attrs)

673 self._slots = slots

674 self._frozen = frozen

675 self._weakref_slot = weakref_slot

676 self._cache_hash = cache_hash

677 self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False))

678 self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))

679 self._delete_attribs = not bool(these)

680 self._is_exc = is_exc

681 self._on_setattr = on_setattr

682

683 self._has_custom_setattr = has_custom_setattr

684 self._wrote_own_setattr = False

685

686 self._cls_dict["__attrs_attrs__"] = self._attrs

687

688 if frozen:

689 self._cls_dict["__setattr__"] = _frozen_setattrs

690 self._cls_dict["__delattr__"] = _frozen_delattrs

691

692 self._wrote_own_setattr = True

693 elif on_setattr in (

694 _ng_default_on_setattr,

695 setters.validate,

696 setters.convert,

697 ):

698 has_validator = has_converter = False

699 for a in attrs:

700 if a.validator is not None:

701 has_validator = True

702 if a.converter is not None:

703 has_converter = True

704

705 if has_validator and has_converter:

706 break

707 if (

708 (

709 on_setattr == _ng_default_on_setattr

710 and not (has_validator or has_converter)

711 )

712 or (on_setattr == setters.validate and not has_validator)

713 or (on_setattr == setters.convert and not has_converter)

714 ):

715 # If class-level on_setattr is set to convert + validate, but

716 # there's no field to convert or validate, pretend like there's

717 # no on_setattr.

718 self._on_setattr = None

719

720 if getstate_setstate:

721 (

722 self._cls_dict["__getstate__"],

723 self._cls_dict["__setstate__"],

724 ) = self._make_getstate_setstate()

725

726 def __repr__(self):

727 return f"<_ClassBuilder(cls={self._cls.__name__})>"

728

729 if PY310:

730 import abc

731

732 def build_class(self):

733 """

734 Finalize class based on the accumulated configuration.

735

736 Builder cannot be used after calling this method.

737 """

738 if self._slots is True:

739 return self._create_slots_class()

740

741 return self.abc.update_abstractmethods(

742 self._patch_original_class()

743 )

744

745 else:

746

747 def build_class(self):

748 """

749 Finalize class based on the accumulated configuration.

750

751 Builder cannot be used after calling this method.

752 """

753 if self._slots is True:

754 return self._create_slots_class()

755

756 return self._patch_original_class()

757

758 def _patch_original_class(self):

759 """

760 Apply accumulated methods and return the class.

761 """

762 cls = self._cls

763 base_names = self._base_names

764

765 # Clean class of attribute definitions (`attr.ib()`s).

766 if self._delete_attribs:

767 for name in self._attr_names:

768 if (

769 name not in base_names

770 and getattr(cls, name, _sentinel) is not _sentinel

771 ):

772 try:

773 delattr(cls, name)

774 except AttributeError:

775 # This can happen if a base class defines a class

776 # variable and we want to set an attribute with the

777 # same name by using only a type annotation.

778 pass

779

780 # Attach our dunder methods.

781 for name, value in self._cls_dict.items():

782 setattr(cls, name, value)

783

784 # If we've inherited an attrs __setattr__ and don't write our own,

785 # reset it to object's.

786 if not self._wrote_own_setattr and getattr(

787 cls, "__attrs_own_setattr__", False

788 ):

789 cls.__attrs_own_setattr__ = False

790

791 if not self._has_custom_setattr:

792 cls.__setattr__ = _obj_setattr

793

794 return cls

795

796 def _create_slots_class(self):

797 """

798 Build and return a new class with a `__slots__` attribute.

799 """

800 cd = {

801 k: v

802 for k, v in self._cls_dict.items()

803 if k not in tuple(self._attr_names) + ("__dict__", "__weakref__")

804 }

805

806 # If our class doesn't have its own implementation of __setattr__

807 # (either from the user or by us), check the bases, if one of them has

808 # an attrs-made __setattr__, that needs to be reset. We don't walk the

809 # MRO because we only care about our immediate base classes.

810 # XXX: This can be confused by subclassing a slotted attrs class with

811 # XXX: a non-attrs class and subclass the resulting class with an attrs

812 # XXX: class. See `test_slotted_confused` for details. For now that's

813 # XXX: OK with us.

814 if not self._wrote_own_setattr:

815 cd["__attrs_own_setattr__"] = False

816

817 if not self._has_custom_setattr:

818 for base_cls in self._cls.__bases__:

819 if base_cls.__dict__.get("__attrs_own_setattr__", False):

820 cd["__setattr__"] = _obj_setattr

821 break

822

823 # Traverse the MRO to collect existing slots

824 # and check for an existing __weakref__.

825 existing_slots = dict()

826 weakref_inherited = False

827 for base_cls in self._cls.__mro__[1:-1]:

828 if base_cls.__dict__.get("__weakref__", None) is not None:

829 weakref_inherited = True

830 existing_slots.update(

831 {

832 name: getattr(base_cls, name)

833 for name in getattr(base_cls, "__slots__", [])

834 }

835 )

836

837 base_names = set(self._base_names)

838

839 names = self._attr_names

840 if (

841 self._weakref_slot

842 and "__weakref__" not in getattr(self._cls, "__slots__", ())

843 and "__weakref__" not in names

844 and not weakref_inherited

845 ):

846 names += ("__weakref__",)

847

848 # We only add the names of attributes that aren't inherited.

849 # Setting __slots__ to inherited attributes wastes memory.

850 slot_names = [name for name in names if name not in base_names]

851 # There are slots for attributes from current class

852 # that are defined in parent classes.

853 # As their descriptors may be overridden by a child class,

854 # we collect them here and update the class dict

855 reused_slots = {

856 slot: slot_descriptor

857 for slot, slot_descriptor in existing_slots.items()

858 if slot in slot_names

859 }

860 slot_names = [name for name in slot_names if name not in reused_slots]

861 cd.update(reused_slots)

862 if self._cache_hash:

863 slot_names.append(_hash_cache_field)

864 cd["__slots__"] = tuple(slot_names)

865

866 cd["__qualname__"] = self._cls.__qualname__

867

868 # Create new class based on old class and our methods.

869 cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd)

870

871 # The following is a fix for

872 # <https://github.com/python-attrs/attrs/issues/102>.

873 # If a method mentions `__class__` or uses the no-arg super(), the

874 # compiler will bake a reference to the class in the method itself

875 # as `method.__closure__`. Since we replace the class with a

876 # clone, we rewrite these references so it keeps working.

877 for item in cls.__dict__.values():

878 if isinstance(item, (classmethod, staticmethod)):

879 # Class- and staticmethods hide their functions inside.

880 # These might need to be rewritten as well.

881 closure_cells = getattr(item.__func__, "__closure__", None)

882 elif isinstance(item, property):

883 # Workaround for property `super()` shortcut (PY3-only).

884 # There is no universal way for other descriptors.

885 closure_cells = getattr(item.fget, "__closure__", None)

886 else:

887 closure_cells = getattr(item, "__closure__", None)

888

889 if not closure_cells: # Catch None or the empty list.

890 continue

891 for cell in closure_cells:

892 try:

893 match = cell.cell_contents is self._cls

894 except ValueError: # ValueError: Cell is empty

895 pass

896 else:

897 if match:

898 set_closure_cell(cell, cls)

899

900 return cls

901

902 def add_repr(self, ns):

903 self._cls_dict["__repr__"] = self._add_method_dunders(

904 _make_repr(self._attrs, ns, self._cls)

905 )

906 return self

907

908 def add_str(self):

909 repr = self._cls_dict.get("__repr__")

910 if repr is None:

911 raise ValueError(

912 "__str__ can only be generated if a __repr__ exists."

913 )

914

915 def __str__(self):

916 return self.__repr__()

917

918 self._cls_dict["__str__"] = self._add_method_dunders(__str__)

919 return self

920

921 def _make_getstate_setstate(self):

922 """

923 Create custom __setstate__ and __getstate__ methods.

924 """

925 # __weakref__ is not writable.

926 state_attr_names = tuple(

927 an for an in self._attr_names if an != "__weakref__"

928 )

929

930 def slots_getstate(self):

931 """

932 Automatically created by attrs.

933 """

934 return {name: getattr(self, name) for name in state_attr_names}

935

936 hash_caching_enabled = self._cache_hash

937

938 def slots_setstate(self, state):

939 """

940 Automatically created by attrs.

941 """

942 __bound_setattr = _obj_setattr.__get__(self)

943 for name in state_attr_names:

944 if name in state:

945 __bound_setattr(name, state[name])

946

947 # The hash code cache is not included when the object is

948 # serialized, but it still needs to be initialized to None to

949 # indicate that the first call to __hash__ should be a cache

950 # miss.

951 if hash_caching_enabled:

952 __bound_setattr(_hash_cache_field, None)

953

954 return slots_getstate, slots_setstate

955

956 def make_unhashable(self):

957 self._cls_dict["__hash__"] = None

958 return self

959

960 def add_hash(self):

961 self._cls_dict["__hash__"] = self._add_method_dunders(

962 _make_hash(

963 self._cls,

964 self._attrs,

965 frozen=self._frozen,

966 cache_hash=self._cache_hash,

967 )

968 )

969

970 return self

971

972 def add_init(self):

973 self._cls_dict["__init__"] = self._add_method_dunders(

974 _make_init(

975 self._cls,

976 self._attrs,

977 self._has_pre_init,

978 self._has_post_init,

979 self._frozen,

980 self._slots,

981 self._cache_hash,

982 self._base_attr_map,

983 self._is_exc,

984 self._on_setattr,

985 attrs_init=False,

986 )

987 )

988

989 return self

990

991 def add_match_args(self):

992 self._cls_dict["__match_args__"] = tuple(

993 field.name

994 for field in self._attrs

995 if field.init and not field.kw_only

996 )

997

998 def add_attrs_init(self):

999 self._cls_dict["__attrs_init__"] = self._add_method_dunders(

1000 _make_init(

1001 self._cls,

1002 self._attrs,

1003 self._has_pre_init,

1004 self._has_post_init,

1005 self._frozen,

1006 self._slots,

1007 self._cache_hash,

1008 self._base_attr_map,

1009 self._is_exc,

1010 self._on_setattr,

1011 attrs_init=True,

1012 )

1013 )

1014

1015 return self

1016

1017 def add_eq(self):

1018 cd = self._cls_dict

1019

1020 cd["__eq__"] = self._add_method_dunders(

1021 _make_eq(self._cls, self._attrs)

1022 )

1023 cd["__ne__"] = self._add_method_dunders(_make_ne())

1024

1025 return self

1026

1027 def add_order(self):

1028 cd = self._cls_dict

1029

1030 cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = (

1031 self._add_method_dunders(meth)

1032 for meth in _make_order(self._cls, self._attrs)

1033 )

1034

1035 return self

1036

1037 def add_setattr(self):

1038 if self._frozen:

1039 return self

1040

1041 sa_attrs = {}

1042 for a in self._attrs:

1043 on_setattr = a.on_setattr or self._on_setattr

1044 if on_setattr and on_setattr is not setters.NO_OP:

1045 sa_attrs[a.name] = a, on_setattr

1046

1047 if not sa_attrs:

1048 return self

1049

1050 if self._has_custom_setattr:

1051 # We need to write a __setattr__ but there already is one!

1052 raise ValueError(

1053 "Can't combine custom __setattr__ with on_setattr hooks."

1054 )

1055

1056 # docstring comes from _add_method_dunders

1057 def __setattr__(self, name, val):

1058 try:

1059 a, hook = sa_attrs[name]

1060 except KeyError:

1061 nval = val

1062 else:

1063 nval = hook(self, a, val)

1064

1065 _obj_setattr(self, name, nval)

1066

1067 self._cls_dict["__attrs_own_setattr__"] = True

1068 self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__)

1069 self._wrote_own_setattr = True

1070

1071 return self

1072

1073 def _add_method_dunders(self, method):

1074 """

1075 Add __module__ and __qualname__ to a *method* if possible.

1076 """

1077 try:

1078 method.__module__ = self._cls.__module__

1079 except AttributeError:

1080 pass

1081

1082 try:

1083 method.__qualname__ = ".".join(

1084 (self._cls.__qualname__, method.__name__)

1085 )

1086 except AttributeError:

1087 pass

1088

1089 try:

1090 method.__doc__ = (

1091 "Method generated by attrs for class "

1092 f"{self._cls.__qualname__}."

1093 )

1094 except AttributeError:

1095 pass

1096

1097 return method

1098

1099

1100 def _determine_attrs_eq_order(cmp, eq, order, default_eq):

1101 """

1102 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective

1103 values of eq and order. If *eq* is None, set it to *default_eq*.

1104 """

1105 if cmp is not None and any((eq is not None, order is not None)):

1106 raise ValueError("Don't mix `cmp` with `eq' and `order`.")

1107

1108 # cmp takes precedence due to bw-compatibility.

1109 if cmp is not None:

1110 return cmp, cmp

1111

1112 # If left None, equality is set to the specified default and ordering

1113 # mirrors equality.

1114 if eq is None:

1115 eq = default_eq

1116

1117 if order is None:

1118 order = eq

1119

1120 if eq is False and order is True:

1121 raise ValueError("`order` can only be True if `eq` is True too.")

1122

1123 return eq, order

1124

1125

1126 def _determine_attrib_eq_order(cmp, eq, order, default_eq):

1127 """

1128 Validate the combination of *cmp*, *eq*, and *order*. Derive the effective

1129 values of eq and order. If *eq* is None, set it to *default_eq*.

1130 """

1131 if cmp is not None and any((eq is not None, order is not None)):

1132 raise ValueError("Don't mix `cmp` with `eq' and `order`.")

1133

1134 def decide_callable_or_boolean(value):

1135 """

1136 Decide whether a key function is used.

1137 """

1138 if callable(value):

1139 value, key = True, value

1140 else:

1141 key = None

1142 return value, key

1143

1144 # cmp takes precedence due to bw-compatibility.

1145 if cmp is not None:

1146 cmp, cmp_key = decide_callable_or_boolean(cmp)

1147 return cmp, cmp_key, cmp, cmp_key

1148

1149 # If left None, equality is set to the specified default and ordering

1150 # mirrors equality.

1151 if eq is None:

1152 eq, eq_key = default_eq, None

1153 else:

1154 eq, eq_key = decide_callable_or_boolean(eq)

1155

1156 if order is None:

1157 order, order_key = eq, eq_key

1158 else:

1159 order, order_key = decide_callable_or_boolean(order)

1160

1161 if eq is False and order is True:

1162 raise ValueError("`order` can only be True if `eq` is True too.")

1163

1164 return eq, eq_key, order, order_key

1165

1166

1167 def _determine_whether_to_implement(

1168 cls, flag, auto_detect, dunders, default=True

1169):

1170 """

1171 Check whether we should implement a set of methods for *cls*.

1172

1173 *flag* is the argument passed into @attr.s like 'init', *auto_detect* the

1174 same as passed into @attr.s and *dunders* is a tuple of attribute names

1175 whose presence signal that the user has implemented it themselves.

1176

1177 Return *default* if no reason for either for or against is found.

1178 """

1179 if flag is True or flag is False:

1180 return flag

1181

1182 if flag is None and auto_detect is False:

1183 return default

1184

1185 # Logically, flag is None and auto_detect is True here.

1186 for dunder in dunders:

1187 if _has_own_attribute(cls, dunder):

1188 return False

1189

1190 return default

1191

1192

1193 def attrs(

1194 maybe_cls=None,

1195 these=None,

1196 repr_ns=None,

1197 repr=None,

1198 cmp=None,

1199 hash=None,

1200 init=None,

1201 slots=False,

1202 frozen=False,

1203 weakref_slot=True,

1204 str=False,

1205 auto_attribs=False,

1206 kw_only=False,

1207 cache_hash=False,

1208 auto_exc=False,

1209 eq=None,

1210 order=None,

1211 auto_detect=False,

1212 collect_by_mro=False,

1213 getstate_setstate=None,

1214 on_setattr=None,

1215 field_transformer=None,

1216 match_args=True,

1217 unsafe_hash=None,

1218):

1219 r"""

1220 A class decorator that adds :term:`dunder methods` according to the

1221 specified attributes using `attr.ib` or the *these* argument.

1222

1223 :param these: A dictionary of name to `attr.ib` mappings. This is

1224 useful to avoid the definition of your attributes within the class body

1225 because you can't (e.g. if you want to add ``__repr__`` methods to

1226 Django models) or don't want to.

1227

1228 If *these* is not ``None``, ``attrs`` will *not* search the class body

1229 for attributes and will *not* remove any attributes from it.

1230

1231 The order is deduced from the order of the attributes inside *these*.

1232

1233 :type these: `dict` of `str` to `attr.ib`

1234

1235 :param str repr_ns: When using nested classes, there's no way in Python 2

1236 to automatically detect that. Therefore it's possible to set the

1237 namespace explicitly for a more meaningful ``repr`` output.

1238 :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*,

1239 *order*, and *hash* arguments explicitly, assume they are set to

1240 ``True`` **unless any** of the involved methods for one of the

1241 arguments is implemented in the *current* class (i.e. it is *not*

1242 inherited from some base class).

1243

1244 So for example by implementing ``__eq__`` on a class yourself,

1245 ``attrs`` will deduce ``eq=False`` and will create *neither*

1246 ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible

1247 ``__ne__`` by default, so it *should* be enough to only implement

1248 ``__eq__`` in most cases).

1249

1250 .. warning::

1251

1252 If you prevent ``attrs`` from creating the ordering methods for you

1253 (``order=False``, e.g. by implementing ``__le__``), it becomes

1254 *your* responsibility to make sure its ordering is sound. The best

1255 way is to use the `functools.total_ordering` decorator.

1256

1257

1258 Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*,

1259 *cmp*, or *hash* overrides whatever *auto_detect* would determine.

1260

1261 :param bool repr: Create a ``__repr__`` method with a human readable

1262 representation of ``attrs`` attributes..

1263 :param bool str: Create a ``__str__`` method that is identical to

1264 ``__repr__``. This is usually not necessary except for

1265 `Exception`\ s.

1266 :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__``

1267 and ``__ne__`` methods that check two instances for equality.

1268

1269 They compare the instances as if they were tuples of their ``attrs``

1270 attributes if and only if the types of both classes are *identical*!

1271 :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``,

1272 ``__gt__``, and ``__ge__`` methods that behave like *eq* above and

1273 allow instances to be ordered. If ``None`` (default) mirror value of

1274 *eq*.

1275 :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq*

1276 and *order* to the same value. Must not be mixed with *eq* or *order*.

1277 :param Optional[bool] unsafe_hash: If ``None`` (default), the ``__hash__``

1278 method is generated according how *eq* and *frozen* are set.

1279

1280 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you.

1281 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to

1282 None, marking it unhashable (which it is).

1283 3. If *eq* is False, ``__hash__`` will be left untouched meaning the

1284 ``__hash__`` method of the base class will be used (if base class is

1285 ``object``, this means it will fall back to id-based hashing.).

1286

1287 Although not recommended, you can decide for yourself and force

1288 ``attrs`` to create one (e.g. if the class is immutable even though you

1289 didn't freeze it programmatically) by passing ``True`` or not. Both of

1290 these cases are rather special and should be used carefully.

1291

1292 See our documentation on `hashing`, Python's documentation on

1293 `object.__hash__`, and the `GitHub issue that led to the default \

1294 behavior <https://github.com/python-attrs/attrs/issues/136>`_ for more

1295 details.

1296 :param Optional[bool] hash: Alias for *unsafe_hash*. *unsafe_hash* takes

1297 precedence.

1298 :param bool init: Create a ``__init__`` method that initializes the

1299 ``attrs`` attributes. Leading underscores are stripped for the argument

1300 name. If a ``__attrs_pre_init__`` method exists on the class, it will

1301 be called before the class is initialized. If a ``__attrs_post_init__``

1302 method exists on the class, it will be called after the class is fully

1303 initialized.

1304

1305 If ``init`` is ``False``, an ``__attrs_init__`` method will be

1306 injected instead. This allows you to define a custom ``__init__``

1307 method that can do pre-init work such as ``super().__init__()``,

1308 and then call ``__attrs_init__()`` and ``__attrs_post_init__()``.

1309 :param bool slots: Create a :term:`slotted class <slotted classes>` that's

1310 more memory-efficient. Slotted classes are generally superior to the

1311 default dict classes, but have some gotchas you should know about, so

1312 we encourage you to read the :term:`glossary entry <slotted classes>`.

1313 :param bool frozen: Make instances immutable after initialization. If

1314 someone attempts to modify a frozen instance,

1315 `attr.exceptions.FrozenInstanceError` is raised.

1316

1317 .. note::

1318

1319 1. This is achieved by installing a custom ``__setattr__`` method

1320 on your class, so you can't implement your own.

1321

1322 2. True immutability is impossible in Python.

1323

1324 3. This *does* have a minor a runtime performance `impact

1325 <how-frozen>` when initializing new instances. In other words:

1326 ``__init__`` is slightly slower with ``frozen=True``.

1327

1328 4. If a class is frozen, you cannot modify ``self`` in

1329 ``__attrs_post_init__`` or a self-written ``__init__``. You can

1330 circumvent that limitation by using

1331 ``object.__setattr__(self, "attribute_name", value)``.

1332

1333 5. Subclasses of a frozen class are frozen too.

1334

1335 :param bool weakref_slot: Make instances weak-referenceable. This has no

1336 effect unless ``slots`` is also enabled.

1337 :param bool auto_attribs: If ``True``, collect :pep:`526`-annotated

1338 attributes from the class body.

1339

1340 In this case, you **must** annotate every field. If ``attrs``

1341 encounters a field that is set to an `attr.ib` but lacks a type

1342 annotation, an `attr.exceptions.UnannotatedAttributeError` is

1343 raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't

1344 want to set a type.

1345

1346 If you assign a value to those attributes (e.g. ``x: int = 42``), that

1347 value becomes the default value like if it were passed using

1348 ``attr.ib(default=42)``. Passing an instance of `attrs.Factory` also

1349 works as expected in most cases (see warning below).

1350

1351 Attributes annotated as `typing.ClassVar`, and attributes that are

1352 neither annotated nor set to an `attr.ib` are **ignored**.

1353

1354 .. warning::

1355 For features that use the attribute name to create decorators (e.g.

1356 `validators <validators>`), you still *must* assign `attr.ib` to

1357 them. Otherwise Python will either not find the name or try to use

1358 the default value to call e.g. ``validator`` on it.

1359

1360 These errors can be quite confusing and probably the most common bug

1361 report on our bug tracker.

1362

1363 :param bool kw_only: Make all attributes keyword-only

1364 in the generated ``__init__`` (if ``init`` is ``False``, this

1365 parameter is ignored).

1366 :param bool cache_hash: Ensure that the object's hash code is computed

1367 only once and stored on the object. If this is set to ``True``,

1368 hashing must be either explicitly or implicitly enabled for this

1369 class. If the hash code is cached, avoid any reassignments of

1370 fields involved in hash code computation or mutations of the objects

1371 those fields point to after object creation. If such changes occur,

1372 the behavior of the object's hash code is undefined.

1373 :param bool auto_exc: If the class subclasses `BaseException`

1374 (which implicitly includes any subclass of any exception), the

1375 following happens to behave like a well-behaved Python exceptions

1376 class:

1377

1378 - the values for *eq*, *order*, and *hash* are ignored and the

1379 instances compare and hash by the instance's ids (N.B. ``attrs`` will

1380 *not* remove existing implementations of ``__hash__`` or the equality

1381 methods. It just won't add own ones.),

1382 - all attributes that are either passed into ``__init__`` or have a

1383 default value are additionally available as a tuple in the ``args``

1384 attribute,

1385 - the value of *str* is ignored leaving ``__str__`` to base classes.

1386 :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs``

1387 collects attributes from base classes. The default behavior is

1388 incorrect in certain cases of multiple inheritance. It should be on by

1389 default but is kept off for backward-compatibility.

1390

1391 See issue `#428 <https://github.com/python-attrs/attrs/issues/428>`_ for

1392 more details.

1393

1394 :param Optional[bool] getstate_setstate:

1395 .. note::

1396 This is usually only interesting for slotted classes and you should

1397 probably just set *auto_detect* to `True`.

1398

1399 If `True`, ``__getstate__`` and

1400 ``__setstate__`` are generated and attached to the class. This is

1401 necessary for slotted classes to be pickleable. If left `None`, it's

1402 `True` by default for slotted classes and ``False`` for dict classes.

1403

1404 If *auto_detect* is `True`, and *getstate_setstate* is left `None`,

1405 and **either** ``__getstate__`` or ``__setstate__`` is detected directly

1406 on the class (i.e. not inherited), it is set to `False` (this is usually

1407 what you want).

1408

1409 :param on_setattr: A callable that is run whenever the user attempts to set

1410 an attribute (either by assignment like ``i.x = 42`` or by using

1411 `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments

1412 as validators: the instance, the attribute that is being modified, and

1413 the new value.

1414

1415 If no exception is raised, the attribute is set to the return value of

1416 the callable.

1417

1418 If a list of callables is passed, they're automatically wrapped in an

1419 `attrs.setters.pipe`.

1420 :type on_setattr: `callable`, or a list of callables, or `None`, or

1421 `attrs.setters.NO_OP`

1422

1423 :param Optional[callable] field_transformer:

1424 A function that is called with the original class object and all

1425 fields right before ``attrs`` finalizes the class. You can use

1426 this, e.g., to automatically add converters or validators to

1427 fields based on their types. See `transform-fields` for more details.

1428

1429 :param bool match_args:

1430 If `True` (default), set ``__match_args__`` on the class to support

1431 :pep:`634` (Structural Pattern Matching). It is a tuple of all

1432 non-keyword-only ``__init__`` parameter names on Python 3.10 and later.

1433 Ignored on older Python versions.

1434

1435 .. versionadded:: 16.0.0 *slots*

1436 .. versionadded:: 16.1.0 *frozen*

1437 .. versionadded:: 16.3.0 *str*

1438 .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``.

1439 .. versionchanged:: 17.1.0

1440 *hash* supports ``None`` as value which is also the default now.

1441 .. versionadded:: 17.3.0 *auto_attribs*

1442 .. versionchanged:: 18.1.0

1443 If *these* is passed, no attributes are deleted from the class body.

1444 .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained.

1445 .. versionadded:: 18.2.0 *weakref_slot*

1446 .. deprecated:: 18.2.0

1447 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a

1448 `DeprecationWarning` if the classes compared are subclasses of

1449 each other. ``__eq`` and ``__ne__`` never tried to compared subclasses

1450 to each other.

1451 .. versionchanged:: 19.2.0

1452 ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider

1453 subclasses comparable anymore.

1454 .. versionadded:: 18.2.0 *kw_only*

1455 .. versionadded:: 18.2.0 *cache_hash*

1456 .. versionadded:: 19.1.0 *auto_exc*

1457 .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.

1458 .. versionadded:: 19.2.0 *eq* and *order*

1459 .. versionadded:: 20.1.0 *auto_detect*

1460 .. versionadded:: 20.1.0 *collect_by_mro*

1461 .. versionadded:: 20.1.0 *getstate_setstate*

1462 .. versionadded:: 20.1.0 *on_setattr*

1463 .. versionadded:: 20.3.0 *field_transformer*

1464 .. versionchanged:: 21.1.0

1465 ``init=False`` injects ``__attrs_init__``

1466 .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__``

1467 .. versionchanged:: 21.1.0 *cmp* undeprecated

1468 .. versionadded:: 21.3.0 *match_args*

1469 .. versionadded:: 22.2.0

1470 *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).

1471 """

1472 eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None)

1473

1474 # unsafe_hash takes precedence due to PEP 681.

1475 if unsafe_hash is not None:

1476 hash = unsafe_hash

1477

1478 if isinstance(on_setattr, (list, tuple)):

1479 on_setattr = setters.pipe(*on_setattr)

1480

1481 def wrap(cls):

1482 is_frozen = frozen or _has_frozen_base_class(cls)

1483 is_exc = auto_exc is True and issubclass(cls, BaseException)

1484 has_own_setattr = auto_detect and _has_own_attribute(

1485 cls, "__setattr__"

1486 )

1487

1488 if has_own_setattr and is_frozen:

1489 raise ValueError("Can't freeze a class with a custom __setattr__.")

1490

1491 builder = _ClassBuilder(

1492 cls,

1493 these,

1494 slots,

1495 is_frozen,

1496 weakref_slot,

1497 _determine_whether_to_implement(

1498 cls,

1499 getstate_setstate,

1500 auto_detect,

1501 ("__getstate__", "__setstate__"),

1502 default=slots,

1503 ),

1504 auto_attribs,

1505 kw_only,

1506 cache_hash,

1507 is_exc,

1508 collect_by_mro,

1509 on_setattr,

1510 has_own_setattr,

1511 field_transformer,

1512 )

1513 if _determine_whether_to_implement(

1514 cls, repr, auto_detect, ("__repr__",)

1515 ):

1516 builder.add_repr(repr_ns)

1517 if str is True:

1518 builder.add_str()

1519

1520 eq = _determine_whether_to_implement(

1521 cls, eq_, auto_detect, ("__eq__", "__ne__")

1522 )

1523 if not is_exc and eq is True:

1524 builder.add_eq()

1525 if not is_exc and _determine_whether_to_implement(

1526 cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__")

1527 ):

1528 builder.add_order()

1529

1530 builder.add_setattr()

1531

1532 nonlocal hash

1533 if (

1534 hash is None

1535 and auto_detect is True

1536 and _has_own_attribute(cls, "__hash__")

1537 ):

1538 hash = False

1539

1540 if hash is not True and hash is not False and hash is not None:

1541 # Can't use `hash in` because 1 == True for example.

1542 raise TypeError(

1543 "Invalid value for hash. Must be True, False, or None."

1544 )

1545 elif hash is False or (hash is None and eq is False) or is_exc:

1546 # Don't do anything. Should fall back to __object__'s __hash__

1547 # which is by id.

1548 if cache_hash:

1549 raise TypeError(

1550 "Invalid value for cache_hash. To use hash caching,"

1551 " hashing must be either explicitly or implicitly "

1552 "enabled."

1553 )

1554 elif hash is True or (

1555 hash is None and eq is True and is_frozen is True

1556 ):

1557 # Build a __hash__ if told so, or if it's safe.

1558 builder.add_hash()

1559 else:

1560 # Raise TypeError on attempts to hash.

1561 if cache_hash:

1562 raise TypeError(

1563 "Invalid value for cache_hash. To use hash caching,"

1564 " hashing must be either explicitly or implicitly "

1565 "enabled."

1566 )

1567 builder.make_unhashable()

1568

1569 if _determine_whether_to_implement(

1570 cls, init, auto_detect, ("__init__",)

1571 ):

1572 builder.add_init()

1573 else:

1574 builder.add_attrs_init()

1575 if cache_hash:

1576 raise TypeError(

1577 "Invalid value for cache_hash. To use hash caching,"

1578 " init must be True."

1579 )

1580

1581 if (

1582 PY310

1583 and match_args

1584 and not _has_own_attribute(cls, "__match_args__")

1585 ):

1586 builder.add_match_args()

1587

1588 return builder.build_class()

1589

1590 # maybe_cls's type depends on the usage of the decorator. It's a class

1591 # if it's used as `@attrs` but ``None`` if used as `@attrs()`.

1592 if maybe_cls is None:

1593 return wrap

1594 else:

1595 return wrap(maybe_cls)

1596

1597

1598_attrs = attrs

1599"""

1600 Internal alias so we can use it in functions that take an argument called

1601*attrs*.

1602"""

1603

1604

1605 def _has_frozen_base_class(cls):

1606 """

1607 Check whether *cls* has a frozen ancestor by looking at its

1608 __setattr__.

1609 """

1610 return cls.__setattr__ is _frozen_setattrs

1611

1612

1613 def _generate_unique_filename(cls, func_name):

1614 """

1615 Create a "filename" suitable for a function being generated.

1616 """

1617 return (

1618 f"<attrs generated {func_name} {cls.__module__}."

1619 f"{getattr(cls, '__qualname__', cls.__name__)}>"

1620 )

1621

1622

1623 def _make_hash(cls, attrs, frozen, cache_hash):

1624 attrs = tuple(

1625 a for a in attrs if a.hash is True or (a.hash is None and a.eq is True)

1626 )

1627

1628 tab = " "

1629

1630 unique_filename = _generate_unique_filename(cls, "hash")

1631 type_hash = hash(unique_filename)

1632 # If eq is custom generated, we need to include the functions in globs

1633 globs = {}

1634

1635 hash_def = "def __hash__(self"

1636 hash_func = "hash(("

1637 closing_braces = "))"

1638 if not cache_hash:

1639 hash_def += "):"

1640 else:

1641 hash_def += ", *"

1642

1643 hash_def += (

1644 ", _cache_wrapper="

1645 + "__import__('attr._make')._make._CacheHashWrapper):"

1646 )

1647 hash_func = "_cache_wrapper(" + hash_func

1648 closing_braces += ")"

1649

1650 method_lines = [hash_def]

1651

1652 def append_hash_computation_lines(prefix, indent):

1653 """

1654 Generate the code for actually computing the hash code.

1655 Below this will either be returned directly or used to compute

1656 a value which is then cached, depending on the value of cache_hash

1657 """

1658

1659 method_lines.extend(

1660 [

1661 indent + prefix + hash_func,

1662 indent + f" {type_hash},",

1663 ]

1664 )

1665

1666 for a in attrs:

1667 if a.eq_key:

1668 cmp_name = f"_{a.name}_key"

1669 globs[cmp_name] = a.eq_key

1670 method_lines.append(

1671 indent + f" {cmp_name}(self.{a.name}),"

1672 )

1673 else:

1674 method_lines.append(indent + f" self.{a.name},")

1675

1676 method_lines.append(indent + " " + closing_braces)

1677

1678 if cache_hash:

1679 method_lines.append(tab + f"if self.{_hash_cache_field} is None:")

1680 if frozen:

1681 append_hash_computation_lines(

1682 f"object.__setattr__(self, '{_hash_cache_field}', ", tab * 2

1683 )

1684 method_lines.append(tab * 2 + ")") # close __setattr__

1685 else:

1686 append_hash_computation_lines(

1687 f"self.{_hash_cache_field} = ", tab * 2

1688 )

1689 method_lines.append(tab + f"return self.{_hash_cache_field}")

1690 else:

1691 append_hash_computation_lines("return ", tab)

1692

1693 script = "\n".join(method_lines)

1694 return _make_method("__hash__", script, unique_filename, globs)

1695

1696

1697 def _add_hash(cls, attrs):

1698 """

1699 Add a hash method to *cls*.

1700 """

1701 cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False)

1702 return cls

1703

1704

1705 def _make_ne():

1706 """

1707 Create __ne__ method.

1708 """

1709

1710 def __ne__(self, other):

1711 """

1712 Check equality and either forward a NotImplemented or

1713 return the result negated.

1714 """

1715 result = self.__eq__(other)

1716 if result is NotImplemented:

1717 return NotImplemented

1718

1719 return not result

1720

1721 return __ne__

1722

1723

1724 def _make_eq(cls, attrs):

1725 """

1726 Create __eq__ method for *cls* with *attrs*.

1727 """

1728 attrs = [a for a in attrs if a.eq]

1729

1730 unique_filename = _generate_unique_filename(cls, "eq")

1731 lines = [

1732 "def __eq__(self, other):",

1733 " if other.__class__ is not self.__class__:",

1734 " return NotImplemented",

1735 ]

1736

1737 # We can't just do a big self.x = other.x and... clause due to

1738 # irregularities like nan == nan is false but (nan,) == (nan,) is true.

1739 globs = {}

1740 if attrs:

1741 lines.append(" return (")

1742 others = [" ) == ("]

1743 for a in attrs:

1744 if a.eq_key:

1745 cmp_name = f"_{a.name}_key"

1746 # Add the key function to the global namespace

1747 # of the evaluated function.

1748 globs[cmp_name] = a.eq_key

1749 lines.append(f" {cmp_name}(self.{a.name}),")

1750 others.append(f" {cmp_name}(other.{a.name}),")

1751 else:

1752 lines.append(f" self.{a.name},")

1753 others.append(f" other.{a.name},")

1754

1755 lines += others + [" )"]

1756 else:

1757 lines.append(" return True")

1758

1759 script = "\n".join(lines)

1760

1761 return _make_method("__eq__", script, unique_filename, globs)

1762

1763

1764 def _make_order(cls, attrs):

1765 """

1766 Create ordering methods for *cls* with *attrs*.

1767 """

1768 attrs = [a for a in attrs if a.order]

1769

1770 def attrs_to_tuple(obj):

1771 """

1772 Save us some typing.

1773 """

1774 return tuple(

1775 key(value) if key else value

1776 for value, key in (

1777 (getattr(obj, a.name), a.order_key) for a in attrs

1778 )

1779 )

1780

1781 def __lt__(self, other):

1782 """

1783 Automatically created by attrs.

1784 """

1785 if other.__class__ is self.__class__:

1786 return attrs_to_tuple(self) < attrs_to_tuple(other)

1787

1788 return NotImplemented

1789

1790 def __le__(self, other):

1791 """

1792 Automatically created by attrs.

1793 """

1794 if other.__class__ is self.__class__:

1795 return attrs_to_tuple(self) <= attrs_to_tuple(other)

1796

1797 return NotImplemented

1798

1799 def __gt__(self, other):

1800 """

1801 Automatically created by attrs.

1802 """

1803 if other.__class__ is self.__class__:

1804 return attrs_to_tuple(self) > attrs_to_tuple(other)

1805

1806 return NotImplemented

1807

1808 def __ge__(self, other):

1809 """

1810 Automatically created by attrs.

1811 """

1812 if other.__class__ is self.__class__:

1813 return attrs_to_tuple(self) >= attrs_to_tuple(other)

1814

1815 return NotImplemented

1816

1817 return __lt__, __le__, __gt__, __ge__

1818

1819

1820 def _add_eq(cls, attrs=None):

1821 """

1822 Add equality methods to *cls* with *attrs*.

1823 """

1824 if attrs is None:

1825 attrs = cls.__attrs_attrs__

1826

1827 cls.__eq__ = _make_eq(cls, attrs)

1828 cls.__ne__ = _make_ne()

1829

1830 return cls

1831

1832

1833 def _make_repr(attrs, ns, cls):

1834 unique_filename = _generate_unique_filename(cls, "repr")

1835 # Figure out which attributes to include, and which function to use to

1836 # format them. The a.repr value can be either bool or a custom

1837 # callable.

1838 attr_names_with_reprs = tuple(

1839 (a.name, (repr if a.repr is True else a.repr), a.init)

1840 for a in attrs

1841 if a.repr is not False

1842 )

1843 globs = {

1844 name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr

1845 }

1846 globs["_compat"] = _compat

1847 globs["AttributeError"] = AttributeError

1848 globs["NOTHING"] = NOTHING

1849 attribute_fragments = []

1850 for name, r, i in attr_names_with_reprs:

1851 accessor = (

1852 "self." + name if i else 'getattr(self, "' + name + '", NOTHING)'

1853 )

1854 fragment = (

1855 "%s={%s!r}" % (name, accessor)

1856 if r == repr

1857 else "%s={%s_repr(%s)}" % (name, name, accessor)

1858 )

1859 attribute_fragments.append(fragment)

1860 repr_fragment = ", ".join(attribute_fragments)

1861

1862 if ns is None:

1863 cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}'

1864 else:

1865 cls_name_fragment = ns + ".{self.__class__.__name__}"

1866

1867 lines = [

1868 "def __repr__(self):",

1869 " try:",

1870 " already_repring = _compat.repr_context.already_repring",

1871 " except AttributeError:",

1872 " already_repring = {id(self),}",

1873 " _compat.repr_context.already_repring = already_repring",

1874 " else:",

1875 " if id(self) in already_repring:",

1876 " return '...'",

1877 " else:",

1878 " already_repring.add(id(self))",

1879 " try:",

1880 f" return f'{cls_name_fragment}({repr_fragment})'",

1881 " finally:",

1882 " already_repring.remove(id(self))",

1883 ]

1884

1885 return _make_method(

1886 "__repr__", "\n".join(lines), unique_filename, globs=globs

1887 )

1888

1889

1890 def _add_repr(cls, ns=None, attrs=None):

1891 """

1892 Add a repr method to *cls*.

1893 """

1894 if attrs is None:

1895 attrs = cls.__attrs_attrs__

1896

1897 cls.__repr__ = _make_repr(attrs, ns, cls)

1898 return cls

1899

1900

1901 def fields(cls):

1902 """

1903 Return the tuple of ``attrs`` attributes for a class.

1904

1905 The tuple also allows accessing the fields by their names (see below for

1906 examples).

1907

1908 :param type cls: Class to introspect.

1909

1910 :raise TypeError: If *cls* is not a class.

1911 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``

1912 class.

1913

1914 :rtype: tuple (with name accessors) of `attrs.Attribute`

1915

1916 .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields

1917 by name.

1918 """

1919 if not isinstance(cls, type):

1920 raise TypeError("Passed object must be a class.")

1921 attrs = getattr(cls, "__attrs_attrs__", None)

1922 if attrs is None:

1923 raise NotAnAttrsClassError(f"{cls!r} is not an attrs-decorated class.")

1924 return attrs

1925

1926

1927 def fields_dict(cls):

1928 """

1929 Return an ordered dictionary of ``attrs`` attributes for a class, whose

1930 keys are the attribute names.

1931

1932 :param type cls: Class to introspect.

1933

1934 :raise TypeError: If *cls* is not a class.

1935 :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``

1936 class.

1937

1938 :rtype: dict

1939

1940 .. versionadded:: 18.1.0

1941 """

1942 if not isinstance(cls, type):

1943 raise TypeError("Passed object must be a class.")

1944 attrs = getattr(cls, "__attrs_attrs__", None)

1945 if attrs is None:

1946 raise NotAnAttrsClassError(f"{cls!r} is not an attrs-decorated class.")

1947 return {a.name: a for a in attrs}

1948

1949

1950 def validate(inst):

1951 """

1952 Validate all attributes on *inst* that have a validator.

1953

1954 Leaves all exceptions through.

1955

1956 :param inst: Instance of a class with ``attrs`` attributes.

1957 """

1958 if _config._run_validators is False:

1959 return

1960

1961 for a in fields(inst.__class__):

1962 v = a.validator

1963 if v is not None:

1964 v(inst, a, getattr(inst, a.name))

1965

1966

1967 def _is_slot_cls(cls):

1968 return "__slots__" in cls.__dict__

1969

1970

1971 def _is_slot_attr(a_name, base_attr_map):

1972 """

1973 Check if the attribute name comes from a slot class.

1974 """

1975 return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name])

1976

1977

1978 def _make_init(

1979 cls,

1980 attrs,

1981 pre_init,

1982 post_init,

1983 frozen,

1984 slots,

1985 cache_hash,

1986 base_attr_map,

1987 is_exc,

1988 cls_on_setattr,

1989 attrs_init,

1990):

1991 has_cls_on_setattr = (

1992 cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP

1993 )

1994

1995 if frozen and has_cls_on_setattr:

1996 raise ValueError("Frozen classes can't use on_setattr.")

1997

1998 needs_cached_setattr = cache_hash or frozen

1999 filtered_attrs = []

2000 attr_dict = {}

2001 for a in attrs:

2002 if not a.init and a.default is NOTHING:

2003 continue

2004

2005 filtered_attrs.append(a)

2006 attr_dict[a.name] = a

2007

2008 if a.on_setattr is not None:

2009 if frozen is True:

2010 raise ValueError("Frozen classes can't use on_setattr.")

2011

2012 needs_cached_setattr = True

2013 elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP:

2014 needs_cached_setattr = True

2015

2016 unique_filename = _generate_unique_filename(cls, "init")

2017

2018 script, globs, annotations = _attrs_to_init_script(

2019 filtered_attrs,

2020 frozen,

2021 slots,

2022 pre_init,

2023 post_init,

2024 cache_hash,

2025 base_attr_map,

2026 is_exc,

2027 needs_cached_setattr,

2028 has_cls_on_setattr,

2029 attrs_init,

2030 )

2031 if cls.__module__ in sys.modules:

2032 # This makes typing.get_type_hints(CLS.__init__) resolve string types.

2033 globs.update(sys.modules[cls.__module__].__dict__)

2034

2035 globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict})

2036

2037 if needs_cached_setattr:

2038 # Save the lookup overhead in __init__ if we need to circumvent

2039 # setattr hooks.

2040 globs["_cached_setattr_get"] = _obj_setattr.__get__

2041

2042 init = _make_method(

2043 "__attrs_init__" if attrs_init else "__init__",

2044 script,

2045 unique_filename,

2046 globs,

2047 )

2048 init.__annotations__ = annotations

2049

2050 return init

2051

2052

2053 def _setattr(attr_name, value_var, has_on_setattr):

2054 """

2055 Use the cached object.setattr to set *attr_name* to *value_var*.

2056 """

2057 return f"_setattr('{attr_name}', {value_var})"

2058

2059

2060 def _setattr_with_converter(attr_name, value_var, has_on_setattr):

2061 """

2062 Use the cached object.setattr to set *attr_name* to *value_var*, but run

2063 its converter first.

2064 """

2065 return "_setattr('%s', %s(%s))" % (

2066 attr_name,

2067 _init_converter_pat % (attr_name,),

2068 value_var,

2069 )

2070

2071

2072 def _assign(attr_name, value, has_on_setattr):

2073 """

2074 Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise

2075 relegate to _setattr.

2076 """

2077 if has_on_setattr:

2078 return _setattr(attr_name, value, True)

2079

2080 return f"self.{attr_name} = {value}"

2081

2082

2083 def _assign_with_converter(attr_name, value_var, has_on_setattr):

2084 """

2085 Unless *attr_name* has an on_setattr hook, use normal assignment after

2086 conversion. Otherwise relegate to _setattr_with_converter.

2087 """

2088 if has_on_setattr:

2089 return _setattr_with_converter(attr_name, value_var, True)

2090

2091 return "self.%s = %s(%s)" % (

2092 attr_name,

2093 _init_converter_pat % (attr_name,),

2094 value_var,

2095 )

2096

2097

2098 def _attrs_to_init_script(

2099 attrs,

2100 frozen,

2101 slots,

2102 pre_init,

2103 post_init,

2104 cache_hash,

2105 base_attr_map,

2106 is_exc,

2107 needs_cached_setattr,

2108 has_cls_on_setattr,

2109 attrs_init,

2110):

2111 """

2112 Return a script of an initializer for *attrs* and a dict of globals.

2113

2114 The globals are expected by the generated script.

2115

2116 If *frozen* is True, we cannot set the attributes directly so we use

2117 a cached ``object.__setattr__``.

2118 """

2119 lines = []

2120 if pre_init:

2121 lines.append("self.__attrs_pre_init__()")

2122

2123 if needs_cached_setattr:

2124 lines.append(

2125 # Circumvent the __setattr__ descriptor to save one lookup per

2126 # assignment.

2127 # Note _setattr will be used again below if cache_hash is True

2128 "_setattr = _cached_setattr_get(self)"

2129 )

2130

2131 if frozen is True:

2132 if slots is True:

2133 fmt_setter = _setattr

2134 fmt_setter_with_converter = _setattr_with_converter

2135 else:

2136 # Dict frozen classes assign directly to __dict__.

2137 # But only if the attribute doesn't come from an ancestor slot

2138 # class.

2139 # Note _inst_dict will be used again below if cache_hash is True

2140 lines.append("_inst_dict = self.__dict__")

2141

2142 def fmt_setter(attr_name, value_var, has_on_setattr):

2143 if _is_slot_attr(attr_name, base_attr_map):

2144 return _setattr(attr_name, value_var, has_on_setattr)

2145

2146 return f"_inst_dict['{attr_name}'] = {value_var}"

2147

2148 def fmt_setter_with_converter(

2149 attr_name, value_var, has_on_setattr

2150 ):

2151 if has_on_setattr or _is_slot_attr(attr_name, base_attr_map):

2152 return _setattr_with_converter(

2153 attr_name, value_var, has_on_setattr

2154 )

2155

2156 return "_inst_dict['%s'] = %s(%s)" % (

2157 attr_name,

2158 _init_converter_pat % (attr_name,),

2159 value_var,

2160 )

2161

2162 else:

2163 # Not frozen.

2164 fmt_setter = _assign

2165 fmt_setter_with_converter = _assign_with_converter

2166

2167 args = []

2168 kw_only_args = []

2169 attrs_to_validate = []

2170

2171 # This is a dictionary of names to validator and converter callables.

2172 # Injecting this into __init__ globals lets us avoid lookups.

2173 names_for_globals = {}

2174 annotations = {"return": None}

2175

2176 for a in attrs:

2177 if a.validator:

2178 attrs_to_validate.append(a)

2179

2180 attr_name = a.name

2181 has_on_setattr = a.on_setattr is not None or (

2182 a.on_setattr is not setters.NO_OP and has_cls_on_setattr

2183 )

2184 # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not

2185 # explicitly provided

2186 arg_name = a.alias

2187

2188 has_factory = isinstance(a.default, Factory)

2189 if has_factory and a.default.takes_self:

2190 maybe_self = "self"

2191 else:

2192 maybe_self = ""

2193

2194 if a.init is False:

2195 if has_factory:

2196 init_factory_name = _init_factory_pat % (a.name,)

2197 if a.converter is not None:

2198 lines.append(

2199 fmt_setter_with_converter(

2200 attr_name,

2201 init_factory_name + f"({maybe_self})",

2202 has_on_setattr,

2203 )

2204 )

2205 conv_name = _init_converter_pat % (a.name,)

2206 names_for_globals[conv_name] = a.converter

2207 else:

2208 lines.append(

2209 fmt_setter(

2210 attr_name,

2211 init_factory_name + f"({maybe_self})",

2212 has_on_setattr,

2213 )

2214 )

2215 names_for_globals[init_factory_name] = a.default.factory

2216 else:

2217 if a.converter is not None:

2218 lines.append(

2219 fmt_setter_with_converter(

2220 attr_name,

2221 f"attr_dict['{attr_name}'].default",

2222 has_on_setattr,

2223 )

2224 )

2225 conv_name = _init_converter_pat % (a.name,)

2226 names_for_globals[conv_name] = a.converter

2227 else:

2228 lines.append(

2229 fmt_setter(

2230 attr_name,

2231 f"attr_dict['{attr_name}'].default",

2232 has_on_setattr,

2233 )

2234 )

2235 elif a.default is not NOTHING and not has_factory:

2236 arg = f"{arg_name}=attr_dict['{attr_name}'].default"

2237 if a.kw_only:

2238 kw_only_args.append(arg)

2239 else:

2240 args.append(arg)

2241

2242 if a.converter is not None:

2243 lines.append(

2244 fmt_setter_with_converter(

2245 attr_name, arg_name, has_on_setattr

2246 )

2247 )

2248 names_for_globals[

2249 _init_converter_pat % (a.name,)

2250 ] = a.converter

2251 else:

2252 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))

2253

2254 elif has_factory:

2255 arg = f"{arg_name}=NOTHING"

2256 if a.kw_only:

2257 kw_only_args.append(arg)

2258 else:

2259 args.append(arg)

2260 lines.append(f"if {arg_name} is not NOTHING:")

2261

2262 init_factory_name = _init_factory_pat % (a.name,)

2263 if a.converter is not None:

2264 lines.append(

2265 " "

2266 + fmt_setter_with_converter(

2267 attr_name, arg_name, has_on_setattr

2268 )

2269 )

2270 lines.append("else:")

2271 lines.append(

2272 " "

2273 + fmt_setter_with_converter(

2274 attr_name,

2275 init_factory_name + "(" + maybe_self + ")",

2276 has_on_setattr,

2277 )

2278 )

2279 names_for_globals[

2280 _init_converter_pat % (a.name,)

2281 ] = a.converter

2282 else:

2283 lines.append(

2284 " " + fmt_setter(attr_name, arg_name, has_on_setattr)

2285 )

2286 lines.append("else:")

2287 lines.append(

2288 " "

2289 + fmt_setter(

2290 attr_name,

2291 init_factory_name + "(" + maybe_self + ")",

2292 has_on_setattr,

2293 )

2294 )

2295 names_for_globals[init_factory_name] = a.default.factory

2296 else:

2297 if a.kw_only:

2298 kw_only_args.append(arg_name)

2299 else:

2300 args.append(arg_name)

2301

2302 if a.converter is not None:

2303 lines.append(

2304 fmt_setter_with_converter(

2305 attr_name, arg_name, has_on_setattr

2306 )

2307 )

2308 names_for_globals[

2309 _init_converter_pat % (a.name,)

2310 ] = a.converter

2311 else:

2312 lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))

2313

2314 if a.init is True:

2315 if a.type is not None and a.converter is None:

2316 annotations[arg_name] = a.type

2317 elif a.converter is not None:

2318 # Try to get the type from the converter.

2319 t = _AnnotationExtractor(a.converter).get_first_param_type()

2320 if t:

2321 annotations[arg_name] = t

2322

2323 if attrs_to_validate: # we can skip this if there are no validators.

2324 names_for_globals["_config"] = _config

2325 lines.append("if _config._run_validators is True:")

2326 for a in attrs_to_validate:

2327 val_name = "__attr_validator_" + a.name

2328 attr_name = "__attr_" + a.name

2329 lines.append(f" {val_name}(self, {attr_name}, self.{a.name})")

2330 names_for_globals[val_name] = a.validator

2331 names_for_globals[attr_name] = a

2332

2333 if post_init:

2334 lines.append("self.__attrs_post_init__()")

2335

2336 # because this is set only after __attrs_post_init__ is called, a crash

2337 # will result if post-init tries to access the hash code. This seemed

2338 # preferable to setting this beforehand, in which case alteration to

2339 # field values during post-init combined with post-init accessing the

2340 # hash code would result in silent bugs.

2341 if cache_hash:

2342 if frozen:

2343 if slots:

2344 # if frozen and slots, then _setattr defined above

2345 init_hash_cache = "_setattr('%s', %s)"

2346 else:

2347 # if frozen and not slots, then _inst_dict defined above

2348 init_hash_cache = "_inst_dict['%s'] = %s"

2349 else:

2350 init_hash_cache = "self.%s = %s"

2351 lines.append(init_hash_cache % (_hash_cache_field, "None"))

2352

2353 # For exceptions we rely on BaseException.__init__ for proper

2354 # initialization.

2355 if is_exc:

2356 vals = ",".join(f"self.{a.name}" for a in attrs if a.init)

2357

2358 lines.append(f"BaseException.__init__(self, {vals})")

2359

2360 args = ", ".join(args)

2361 if kw_only_args:

2362 args += "%s*, %s" % (

2363 ", " if args else "", # leading comma

2364 ", ".join(kw_only_args), # kw_only args

2365 )

2366

2367 return (

2368 "def %s(self, %s):\n %s\n"

2369 % (

2370 ("__attrs_init__" if attrs_init else "__init__"),

2371 args,

2372 "\n ".join(lines) if lines else "pass",

2373 ),

2374 names_for_globals,

2375 annotations,

2376 )

2377

2378

2379 def _default_init_alias_for(name: str) -> str:

2380 """

2381 The default __init__ parameter name for a field.

2382

2383 This performs private-name adjustment via leading-unscore stripping,

2384 and is the default value of Attribute.alias if not provided.

2385 """

2386

2387 return name.lstrip("_")

2388

2389

2390 class Attribute:

2391 """

2392 *Read-only* representation of an attribute.

2393

2394 The class has *all* arguments of `attr.ib` (except for ``factory``

2395 which is only syntactic sugar for ``default=Factory(...)`` plus the

2396 following:

2397

2398 - ``name`` (`str`): The name of the attribute.

2399 - ``alias`` (`str`): The __init__ parameter name of the attribute, after

2400 any explicit overrides and default private-attribute-name handling.

2401 - ``inherited`` (`bool`): Whether or not that attribute has been inherited

2402 from a base class.

2403 - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The callables

2404 that are used for comparing and ordering objects by this attribute,

2405 respectively. These are set by passing a callable to `attr.ib`'s ``eq``,

2406 ``order``, or ``cmp`` arguments. See also :ref:`comparison customization

2407 <custom-comparison>`.

2408

2409 Instances of this class are frequently used for introspection purposes

2410 like:

2411

2412 - `fields` returns a tuple of them.

2413 - Validators get them passed as the first argument.

2414 - The :ref:`field transformer <transform-fields>` hook receives a list of

2415 them.

2416 - The ``alias`` property exposes the __init__ parameter name of the field,

2417 with any overrides and default private-attribute handling applied.

2418

2419

2420 .. versionadded:: 20.1.0 *inherited*

2421 .. versionadded:: 20.1.0 *on_setattr*

2422 .. versionchanged:: 20.2.0 *inherited* is not taken into account for

2423 equality checks and hashing anymore.

2424 .. versionadded:: 21.1.0 *eq_key* and *order_key*

2425 .. versionadded:: 22.2.0 *alias*

2426

2427 For the full version history of the fields, see `attr.ib`.

2428 """

2429

2430 __slots__ = (

2431 "name",

2432 "default",

2433 "validator",

2434 "repr",

2435 "eq",

2436 "eq_key",

2437 "order",

2438 "order_key",

2439 "hash",

2440 "init",

2441 "metadata",

2442 "type",

2443 "converter",

2444 "kw_only",

2445 "inherited",

2446 "on_setattr",

2447 "alias",

2448 )

2449

2450 def __init__(

2451 self,

2452 name,

2453 default,

2454 validator,

2455 repr,

2456 cmp, # XXX: unused, remove along with other cmp code.

2457 hash,

2458 init,

2459 inherited,

2460 metadata=None,

2461 type=None,

2462 converter=None,

2463 kw_only=False,

2464 eq=None,

2465 eq_key=None,

2466 order=None,

2467 order_key=None,

2468 on_setattr=None,

2469 alias=None,

2470 ):

2471 eq, eq_key, order, order_key = _determine_attrib_eq_order(

2472 cmp, eq_key or eq, order_key or order, True

2473 )

2474

2475 # Cache this descriptor here to speed things up later.

2476 bound_setattr = _obj_setattr.__get__(self)

2477

2478 # Despite the big red warning, people *do* instantiate `Attribute`

2479 # themselves.

2480 bound_setattr("name", name)

2481 bound_setattr("default", default)

2482 bound_setattr("validator", validator)

2483 bound_setattr("repr", repr)

2484 bound_setattr("eq", eq)

2485 bound_setattr("eq_key", eq_key)

2486 bound_setattr("order", order)

2487 bound_setattr("order_key", order_key)

2488 bound_setattr("hash", hash)

2489 bound_setattr("init", init)

2490 bound_setattr("converter", converter)

2491 bound_setattr(

2492 "metadata",

2493 (

2494 types.MappingProxyType(dict(metadata)) # Shallow copy

2495 if metadata

2496 else _empty_metadata_singleton

2497 ),

2498 )

2499 bound_setattr("type", type)

2500 bound_setattr("kw_only", kw_only)

2501 bound_setattr("inherited", inherited)

2502 bound_setattr("on_setattr", on_setattr)

2503 bound_setattr("alias", alias)

2504

2505 def __setattr__(self, name, value):

2506 raise FrozenInstanceError()

2507

2508 @classmethod

2509 def from_counting_attr(cls, name, ca, type=None):

2510 # type holds the annotated value. deal with conflicts:

2511 if type is None:

2512 type = ca.type

2513 elif ca.type is not None:

2514 raise ValueError(

2515 "Type annotation and type argument cannot both be present"

2516 )

2517 inst_dict = {

2518 k: getattr(ca, k)

2519 for k in Attribute.__slots__

2520 if k

2521 not in (

2522 "name",

2523 "validator",

2524 "default",

2525 "type",

2526 "inherited",

2527 ) # exclude methods and deprecated alias

2528 }

2529 return cls(

2530 name=name,

2531 validator=ca._validator,

2532 default=ca._default,

2533 type=type,

2534 cmp=None,

2535 inherited=False,

2536 **inst_dict,

2537 )

2538

2539 # Don't use attr.evolve since fields(Attribute) doesn't work

2540 def evolve(self, **changes):

2541 """

2542 Copy *self* and apply *changes*.

2543

2544 This works similarly to `attr.evolve` but that function does not work

2545 with ``Attribute``.

2546

2547 It is mainly meant to be used for `transform-fields`.

2548

2549 .. versionadded:: 20.3.0

2550 """

2551 new = copy.copy(self)

2552

2553 new._setattrs(changes.items())

2554

2555 return new

2556

2557 # Don't use _add_pickle since fields(Attribute) doesn't work

2558 def __getstate__(self):

2559 """

2560 Play nice with pickle.

2561 """

2562 return tuple(

2563 getattr(self, name) if name != "metadata" else dict(self.metadata)

2564 for name in self.__slots__

2565 )

2566

2567 def __setstate__(self, state):

2568 """

2569 Play nice with pickle.

2570 """

2571 self._setattrs(zip(self.__slots__, state))

2572

2573 def _setattrs(self, name_values_pairs):

2574 bound_setattr = _obj_setattr.__get__(self)

2575 for name, value in name_values_pairs:

2576 if name != "metadata":

2577 bound_setattr(name, value)

2578 else:

2579 bound_setattr(

2580 name,

2581 types.MappingProxyType(dict(value))

2582 if value

2583 else _empty_metadata_singleton,

2584 )

2585

2586

2587_a = [

2588 Attribute(

2589 name=name,

2590 default=NOTHING,

2591 validator=None,

2592 repr=True,

2593 cmp=None,

2594 eq=True,

2595 order=False,

2596 hash=(name != "metadata"),

2597 init=True,

2598 inherited=False,

2599 alias=_default_init_alias_for(name),

2600 )

2601 for name in Attribute.__slots__

2602]

2603

2604 Attribute = _add_hash(

2605 _add_eq(

2606 _add_repr(Attribute, attrs=_a),

2607 attrs=[a for a in _a if a.name != "inherited"],

2608 ),

2609 attrs=[a for a in _a if a.hash and a.name != "inherited"],

2610)

2611

2612

2613 class _CountingAttr:

2614 """

2615 Intermediate representation of attributes that uses a counter to preserve

2616 the order in which the attributes have been defined.

2617

2618 *Internal* data structure of the attrs library. Running into is most

2619 likely the result of a bug like a forgotten `@attr.s` decorator.

2620 """

2621

2622 __slots__ = (

2623 "counter",

2624 "_default",

2625 "repr",

2626 "eq",

2627 "eq_key",

2628 "order",

2629 "order_key",

2630 "hash",

2631 "init",

2632 "metadata",

2633 "_validator",

2634 "converter",

2635 "type",

2636 "kw_only",

2637 "on_setattr",

2638 "alias",

2639 )

2640 __attrs_attrs__ = tuple(

2641 Attribute(

2642 name=name,

2643 alias=_default_init_alias_for(name),

2644 default=NOTHING,

2645 validator=None,

2646 repr=True,

2647 cmp=None,

2648 hash=True,

2649 init=True,

2650 kw_only=False,

2651 eq=True,

2652 eq_key=None,

2653 order=False,

2654 order_key=None,

2655 inherited=False,

2656 on_setattr=None,

2657 )

2658 for name in (

2659 "counter",

2660 "_default",

2661 "repr",

2662 "eq",

2663 "order",

2664 "hash",

2665 "init",

2666 "on_setattr",

2667 "alias",

2668 )

2669 ) + (

2670 Attribute(

2671 name="metadata",

2672 alias="metadata",

2673 default=None,

2674 validator=None,

2675 repr=True,

2676 cmp=None,

2677 hash=False,

2678 init=True,

2679 kw_only=False,

2680 eq=True,

2681 eq_key=None,

2682 order=False,

2683 order_key=None,

2684 inherited=False,

2685 on_setattr=None,

2686 ),

2687 )

2688 cls_counter = 0

2689

2690 def __init__(

2691 self,

2692 default,

2693 validator,

2694 repr,

2695 cmp,

2696 hash,

2697 init,

2698 converter,

2699 metadata,

2700 type,

2701 kw_only,

2702 eq,

2703 eq_key,

2704 order,

2705 order_key,

2706 on_setattr,

2707 alias,

2708 ):

2709 _CountingAttr.cls_counter += 1

2710 self.counter = _CountingAttr.cls_counter

2711 self._default = default

2712 self._validator = validator

2713 self.converter = converter

2714 self.repr = repr

2715 self.eq = eq

2716 self.eq_key = eq_key

2717 self.order = order

2718 self.order_key = order_key

2719 self.hash = hash

2720 self.init = init

2721 self.metadata = metadata

2722 self.type = type

2723 self.kw_only = kw_only

2724 self.on_setattr = on_setattr

2725 self.alias = alias

2726

2727 def validator(self, meth):

2728 """

2729 Decorator that adds *meth* to the list of validators.

2730

2731 Returns *meth* unchanged.

2732

2733 .. versionadded:: 17.1.0

2734 """

2735 if self._validator is None:

2736 self._validator = meth

2737 else:

2738 self._validator = and_(self._validator, meth)

2739 return meth

2740

2741 def default(self, meth):

2742 """

2743 Decorator that allows to set the default for an attribute.

2744

2745 Returns *meth* unchanged.

2746

2747 :raises DefaultAlreadySetError: If default has been set before.

2748

2749 .. versionadded:: 17.1.0

2750 """

2751 if self._default is not NOTHING:

2752 raise DefaultAlreadySetError()

2753

2754 self._default = Factory(meth, takes_self=True)

2755

2756 return meth

2757

2758

2759_CountingAttr = _add_eq(_add_repr(_CountingAttr))

2760

2761

2762 class Factory:

2763 """

2764 Stores a factory callable.

2765

2766 If passed as the default value to `attrs.field`, the factory is used to

2767 generate a new value.

2768

2769 :param callable factory: A callable that takes either none or exactly one

2770 mandatory positional argument depending on *takes_self*.

2771 :param bool takes_self: Pass the partially initialized instance that is

2772 being initialized as a positional argument.

2773

2774 .. versionadded:: 17.1.0 *takes_self*

2775 """

2776

2777 __slots__ = ("factory", "takes_self")

2778

2779 def __init__(self, factory, takes_self=False):

2780 """

2781 `Factory` is part of the default machinery so if we want a default

2782 value here, we have to implement it ourselves.

2783 """

2784 self.factory = factory

2785 self.takes_self = takes_self

2786

2787 def __getstate__(self):

2788 """

2789 Play nice with pickle.

2790 """

2791 return tuple(getattr(self, name) for name in self.__slots__)

2792

2793 def __setstate__(self, state):

2794 """

2795 Play nice with pickle.

2796 """

2797 for name, value in zip(self.__slots__, state):

2798 setattr(self, name, value)

2799

2800

2801_f = [

2802 Attribute(

2803 name=name,

2804 default=NOTHING,

2805 validator=None,

2806 repr=True,

2807 cmp=None,

2808 eq=True,

2809 order=False,

2810 hash=True,

2811 init=True,

2812 inherited=False,

2813 )

2814 for name in Factory.__slots__

2815]

2816

2817 Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)

2818

2819

2820 def make_class(name, attrs, bases=(object,), **attributes_arguments):

2821 """

2822 A quick way to create a new class called *name* with *attrs*.

2823

2824 :param str name: The name for the new class.

2825

2826 :param attrs: A list of names or a dictionary of mappings of names to

2827 attributes.

2828

2829 The order is deduced from the order of the names or attributes inside

2830 *attrs*. Otherwise the order of the definition of the attributes is

2831 used.

2832 :type attrs: `list` or `dict`

2833

2834 :param tuple bases: Classes that the new class will subclass.

2835

2836 :param attributes_arguments: Passed unmodified to `attr.s`.

2837

2838 :return: A new class with *attrs*.

2839 :rtype: type

2840

2841 .. versionadded:: 17.1.0 *bases*

2842 .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.

2843 """

2844 if isinstance(attrs, dict):

2845 cls_dict = attrs

2846 elif isinstance(attrs, (list, tuple)):

2847 cls_dict = {a: attrib() for a in attrs}

2848 else:

2849 raise TypeError("attrs argument must be a dict or a list.")

2850

2851 pre_init = cls_dict.pop("__attrs_pre_init__", None)

2852 post_init = cls_dict.pop("__attrs_post_init__", None)

2853 user_init = cls_dict.pop("__init__", None)

2854

2855 body = {}

2856 if pre_init is not None:

2857 body["__attrs_pre_init__"] = pre_init

2858 if post_init is not None:

2859 body["__attrs_post_init__"] = post_init

2860 if user_init is not None:

2861 body["__init__"] = user_init

2862

2863 type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body))

2864

2865 # For pickling to work, the __module__ variable needs to be set to the

2866 # frame where the class is created. Bypass this step in environments where

2867 # sys._getframe is not defined (Jython for example) or sys._getframe is not

2868 # defined for arguments greater than 0 (IronPython).

2869 try:

2870 type_.__module__ = sys._getframe(1).f_globals.get(

2871 "__name__", "__main__"

2872 )

2873 except (AttributeError, ValueError):

2874 pass

2875

2876 # We do it here for proper warnings with meaningful stacklevel.

2877 cmp = attributes_arguments.pop("cmp", None)

2878 (

2879 attributes_arguments["eq"],

2880 attributes_arguments["order"],

2881 ) = _determine_attrs_eq_order(

2882 cmp,

2883 attributes_arguments.get("eq"),

2884 attributes_arguments.get("order"),

2885 True,

2886 )

2887

2888 return _attrs(these=cls_dict, **attributes_arguments)(type_)

2889

2890

2891# These are required by within this module so we define them here and merely

2892# import into .validators / .converters.

2893

2894

2895@attrs(slots=True, hash=True)

2896 class _AndValidator:

2897 """

2898 Compose many validators to a single one.

2899 """

2900

2901 _validators = attrib()

2902

2903 def __call__(self, inst, attr, value):

2904 for v in self._validators:

2905 v(inst, attr, value)

2906

2907

2908 def and_(*validators):

2909 """

2910 A validator that composes multiple validators into one.

2911

2912 When called on a value, it runs all wrapped validators.

2913

2914 :param callables validators: Arbitrary number of validators.

2915

2916 .. versionadded:: 17.1.0

2917 """

2918 vals = []

2919 for validator in validators:

2920 vals.extend(

2921 validator._validators

2922 if isinstance(validator, _AndValidator)

2923 else [validator]

2924 )

2925

2926 return _AndValidator(tuple(vals))

2927

2928

2929 def pipe(*converters):

2930 """

2931 A converter that composes multiple converters into one.

2932

2933 When called on a value, it runs all wrapped converters, returning the

2934 *last* value.

2935

2936 Type annotations will be inferred from the wrapped converters', if

2937 they have any.

2938

2939 :param callables converters: Arbitrary number of converters.

2940

2941 .. versionadded:: 20.1.0

2942 """

2943

2944 def pipe_converter(val):

2945 for converter in converters:

2946 val = converter(val)

2947

2948 return val

2949

2950 if not converters:

2951 # If the converter list is empty, pipe_converter is the identity.

2952 A = typing.TypeVar("A")

2953 pipe_converter.__annotations__ = {"val": A, "return": A}

2954 else:

2955 # Get parameter type from first converter.

2956 t = _AnnotationExtractor(converters[0]).get_first_param_type()

2957 if t:

2958 pipe_converter.__annotations__["val"] = t

2959

2960 # Get return type from last converter.

2961 rt = _AnnotationExtractor(converters[-1]).get_return_type()

2962 if rt:

2963 pipe_converter.__annotations__["return"] = rt

2964

2965 return pipe_converter