Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/attr/_make.py: 67%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

988 statements  

1# SPDX-License-Identifier: MIT 

2 

3from __future__ import annotations 

4 

5import abc 

6import contextlib 

7import copy 

8import enum 

9import functools 

10import inspect 

11import itertools 

12import linecache 

13import sys 

14import types 

15import typing 

16 

17from operator import itemgetter 

18 

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

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

21from . import _compat, _config, setters 

22from ._compat import ( 

23 PY_3_8_PLUS, 

24 PY_3_10_PLUS, 

25 PY_3_11_PLUS, 

26 _AnnotationExtractor, 

27 _get_annotations, 

28 get_generic_base, 

29) 

30from .exceptions import ( 

31 DefaultAlreadySetError, 

32 FrozenInstanceError, 

33 NotAnAttrsClassError, 

34 UnannotatedAttributeError, 

35) 

36 

37 

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

39_OBJ_SETATTR = object.__setattr__ 

40_INIT_FACTORY_PAT = "__attr_factory_%s" 

41_CLASSVAR_PREFIXES = ( 

42 "typing.ClassVar", 

43 "t.ClassVar", 

44 "ClassVar", 

45 "typing_extensions.ClassVar", 

46) 

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

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

49# (when slots=True) 

50_HASH_CACHE_FIELD = "_attrs_cached_hash" 

51 

52_EMPTY_METADATA_SINGLETON = types.MappingProxyType({}) 

53 

54# Unique object for unequivocal getattr() defaults. 

55_SENTINEL = object() 

56 

57_DEFAULT_ON_SETATTR = setters.pipe(setters.convert, setters.validate) 

58 

59 

60class _Nothing(enum.Enum): 

61 """ 

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

63 

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

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

66 

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

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

69 """ 

70 

71 NOTHING = enum.auto() 

72 

73 def __repr__(self): 

74 return "NOTHING" 

75 

76 def __bool__(self): 

77 return False 

78 

79 

80NOTHING = _Nothing.NOTHING 

81""" 

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

83""" 

84 

85 

86class _CacheHashWrapper(int): 

87 """ 

88 An integer subclass that pickles / copies as None 

89 

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

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

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

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

94 

95 See GH #613 for more details. 

96 """ 

97 

98 def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008 

99 return _none_constructor, _args 

100 

101 

102def attrib( 

103 default=NOTHING, 

104 validator=None, 

105 repr=True, 

106 cmp=None, 

107 hash=None, 

108 init=True, 

109 metadata=None, 

110 type=None, 

111 converter=None, 

112 factory=None, 

113 kw_only=False, 

114 eq=None, 

115 order=None, 

116 on_setattr=None, 

117 alias=None, 

118): 

119 """ 

120 Create a new field / attribute on a class. 

121 

122 Identical to `attrs.field`, except it's not keyword-only. 

123 

124 Consider using `attrs.field` in new code (``attr.ib`` will *never* go away, 

125 though). 

126 

127 .. warning:: 

128 

129 Does **nothing** unless the class is also decorated with 

130 `attr.s` (or similar)! 

131 

132 

133 .. versionadded:: 15.2.0 *convert* 

134 .. versionadded:: 16.3.0 *metadata* 

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

136 .. versionchanged:: 17.1.0 

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

138 .. versionadded:: 17.3.0 *type* 

139 .. deprecated:: 17.4.0 *convert* 

140 .. versionadded:: 17.4.0 

141 *converter* as a replacement for the deprecated *convert* to achieve 

142 consistency with other noun-based arguments. 

143 .. versionadded:: 18.1.0 

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

145 .. versionadded:: 18.2.0 *kw_only* 

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

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

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

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

150 .. versionadded:: 20.1.0 *on_setattr* 

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

152 .. versionchanged:: 21.1.0 

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

154 .. versionchanged:: 21.1.0 *cmp* undeprecated 

155 .. versionadded:: 22.2.0 *alias* 

156 """ 

157 eq, eq_key, order, order_key = _determine_attrib_eq_order( 

158 cmp, eq, order, True 

159 ) 

160 

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

162 msg = "Invalid value for hash. Must be True, False, or None." 

163 raise TypeError(msg) 

164 

165 if factory is not None: 

166 if default is not NOTHING: 

167 msg = ( 

168 "The `default` and `factory` arguments are mutually exclusive." 

169 ) 

170 raise ValueError(msg) 

171 if not callable(factory): 

172 msg = "The `factory` argument must be a callable." 

173 raise ValueError(msg) 

174 default = Factory(factory) 

175 

176 if metadata is None: 

177 metadata = {} 

178 

179 # Apply syntactic sugar by auto-wrapping. 

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

181 on_setattr = setters.pipe(*on_setattr) 

182 

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

184 validator = and_(*validator) 

185 

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

187 converter = pipe(*converter) 

188 

189 return _CountingAttr( 

190 default=default, 

191 validator=validator, 

192 repr=repr, 

193 cmp=None, 

194 hash=hash, 

195 init=init, 

196 converter=converter, 

197 metadata=metadata, 

198 type=type, 

199 kw_only=kw_only, 

200 eq=eq, 

201 eq_key=eq_key, 

202 order=order, 

203 order_key=order_key, 

204 on_setattr=on_setattr, 

205 alias=alias, 

206 ) 

207 

208 

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

210 """ 

211 Evaluate the script with the given global (globs) and local (locs) 

212 variables. 

213 """ 

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

215 eval(bytecode, globs, locs) 

216 

217 

218def _make_method(name, script, filename, globs, locals=None): 

219 """ 

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

221 """ 

222 locs = {} if locals is None else locals 

223 

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

225 # we add a fake linecache entry. 

226 count = 1 

227 base_filename = filename 

228 while True: 

229 linecache_tuple = ( 

230 len(script), 

231 None, 

232 script.splitlines(True), 

233 filename, 

234 ) 

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

236 if old_val == linecache_tuple: 

237 break 

238 

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

240 count += 1 

241 

242 _compile_and_eval(script, globs, locs, filename) 

243 

244 return locs[name] 

245 

246 

247def _make_attr_tuple_class(cls_name, attr_names): 

248 """ 

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

250 

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

252 

253 class MyClassAttributes(tuple): 

254 __slots__ = () 

255 x = property(itemgetter(0)) 

256 """ 

257 attr_class_name = f"{cls_name}Attributes" 

258 attr_class_template = [ 

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

260 " __slots__ = ()", 

261 ] 

262 if attr_names: 

263 for i, attr_name in enumerate(attr_names): 

264 attr_class_template.append( 

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

266 ) 

267 else: 

268 attr_class_template.append(" pass") 

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

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

271 return globs[attr_class_name] 

272 

273 

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

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

276_Attributes = _make_attr_tuple_class( 

277 "_Attributes", 

278 [ 

279 # all attributes to build dunder methods for 

280 "attrs", 

281 # attributes that have been inherited 

282 "base_attrs", 

283 # map inherited attributes to their originating classes 

284 "base_attrs_map", 

285 ], 

286) 

287 

288 

289def _is_class_var(annot): 

290 """ 

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

292 

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

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

295 disadvantage compared to plain old classes. 

296 """ 

297 annot = str(annot) 

298 

299 # Annotation can be quoted. 

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

301 annot = annot[1:-1] 

302 

303 return annot.startswith(_CLASSVAR_PREFIXES) 

304 

305 

306def _has_own_attribute(cls, attrib_name): 

307 """ 

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

309 """ 

310 return attrib_name in cls.__dict__ 

311 

312 

313def _collect_base_attrs(cls, taken_attr_names): 

314 """ 

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

316 """ 

317 base_attrs = [] 

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

319 

320 # Traverse the MRO and collect attributes. 

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

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

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

324 continue 

325 

326 a = a.evolve(inherited=True) # noqa: PLW2901 

327 base_attrs.append(a) 

328 base_attr_map[a.name] = base_cls 

329 

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

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

332 # instance. 

333 filtered = [] 

334 seen = set() 

335 for a in reversed(base_attrs): 

336 if a.name in seen: 

337 continue 

338 filtered.insert(0, a) 

339 seen.add(a.name) 

340 

341 return filtered, base_attr_map 

342 

343 

344def _collect_base_attrs_broken(cls, taken_attr_names): 

345 """ 

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

347 

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

349 

350 Adhere to the old incorrect behavior. 

351 

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

353 leads to the buggy behavior reported in #428. 

354 """ 

355 base_attrs = [] 

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

357 

358 # Traverse the MRO and collect attributes. 

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

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

361 if a.name in taken_attr_names: 

362 continue 

363 

364 a = a.evolve(inherited=True) # noqa: PLW2901 

365 taken_attr_names.add(a.name) 

366 base_attrs.append(a) 

367 base_attr_map[a.name] = base_cls 

368 

369 return base_attrs, base_attr_map 

370 

371 

372def _transform_attrs( 

373 cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer 

374): 

375 """ 

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

377 

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

379 

380 If *collect_by_mro* is True, collect them in the correct MRO order, 

381 otherwise use the old -- incorrect -- order. See #428. 

382 

383 Return an `_Attributes`. 

384 """ 

385 cd = cls.__dict__ 

386 anns = _get_annotations(cls) 

387 

388 if these is not None: 

389 ca_list = list(these.items()) 

390 elif auto_attribs is True: 

391 ca_names = { 

392 name 

393 for name, attr in cd.items() 

394 if isinstance(attr, _CountingAttr) 

395 } 

396 ca_list = [] 

397 annot_names = set() 

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

399 if _is_class_var(type): 

400 continue 

401 annot_names.add(attr_name) 

402 a = cd.get(attr_name, NOTHING) 

403 

404 if not isinstance(a, _CountingAttr): 

405 a = attrib() if a is NOTHING else attrib(default=a) 

406 ca_list.append((attr_name, a)) 

407 

408 unannotated = ca_names - annot_names 

409 if len(unannotated) > 0: 

410 raise UnannotatedAttributeError( 

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

412 + ", ".join( 

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

414 ) 

415 + "." 

416 ) 

417 else: 

418 ca_list = sorted( 

419 ( 

420 (name, attr) 

421 for name, attr in cd.items() 

422 if isinstance(attr, _CountingAttr) 

423 ), 

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

425 ) 

426 

427 own_attrs = [ 

428 Attribute.from_counting_attr( 

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

430 ) 

431 for attr_name, ca in ca_list 

432 ] 

433 

434 if collect_by_mro: 

435 base_attrs, base_attr_map = _collect_base_attrs( 

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

437 ) 

438 else: 

439 base_attrs, base_attr_map = _collect_base_attrs_broken( 

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

441 ) 

442 

443 if kw_only: 

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

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

446 

447 attrs = base_attrs + own_attrs 

448 

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

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

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

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

453 had_default = False 

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

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

456 msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}" 

457 raise ValueError(msg) 

458 

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

460 had_default = True 

461 

462 if field_transformer is not None: 

463 attrs = field_transformer(cls, attrs) 

464 

465 # Resolve default field alias after executing field_transformer. 

466 # This allows field_transformer to differentiate between explicit vs 

467 # default aliases and supply their own defaults. 

468 attrs = [ 

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

470 for a in attrs 

471 ] 

472 

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

474 # add or remove attributes! 

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

476 AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) 

477 

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

479 

480 

481def _make_cached_property_getattr(cached_properties, original_getattr, cls): 

482 lines = [ 

483 # Wrapped to get `__class__` into closure cell for super() 

484 # (It will be replaced with the newly constructed class after construction). 

485 "def wrapper(_cls):", 

486 " __class__ = _cls", 

487 " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):", 

488 " func = cached_properties.get(item)", 

489 " if func is not None:", 

490 " result = func(self)", 

491 " _setter = _cached_setattr_get(self)", 

492 " _setter(item, result)", 

493 " return result", 

494 ] 

495 if original_getattr is not None: 

496 lines.append( 

497 " return original_getattr(self, item)", 

498 ) 

499 else: 

500 lines.extend( 

501 [ 

502 " try:", 

503 " return super().__getattribute__(item)", 

504 " except AttributeError:", 

505 " if not hasattr(super(), '__getattr__'):", 

506 " raise", 

507 " return super().__getattr__(item)", 

508 " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"", 

509 " raise AttributeError(original_error)", 

510 ] 

511 ) 

512 

513 lines.extend( 

514 [ 

515 " return __getattr__", 

516 "__getattr__ = wrapper(_cls)", 

517 ] 

518 ) 

519 

520 unique_filename = _generate_unique_filename(cls, "getattr") 

521 

522 glob = { 

523 "cached_properties": cached_properties, 

524 "_cached_setattr_get": _OBJ_SETATTR.__get__, 

525 "original_getattr": original_getattr, 

526 } 

527 

528 return _make_method( 

529 "__getattr__", 

530 "\n".join(lines), 

531 unique_filename, 

532 glob, 

533 locals={ 

534 "_cls": cls, 

535 }, 

536 ) 

537 

538 

539def _frozen_setattrs(self, name, value): 

540 """ 

541 Attached to frozen classes as __setattr__. 

542 """ 

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

544 "__cause__", 

545 "__context__", 

546 "__traceback__", 

547 ): 

548 BaseException.__setattr__(self, name, value) 

549 return 

550 

551 raise FrozenInstanceError() 

552 

553 

554def _frozen_delattrs(self, name): 

555 """ 

556 Attached to frozen classes as __delattr__. 

557 """ 

558 raise FrozenInstanceError() 

559 

560 

561class _ClassBuilder: 

562 """ 

563 Iteratively build *one* class. 

564 """ 

565 

566 __slots__ = ( 

567 "_attr_names", 

568 "_attrs", 

569 "_base_attr_map", 

570 "_base_names", 

571 "_cache_hash", 

572 "_cls", 

573 "_cls_dict", 

574 "_delete_attribs", 

575 "_frozen", 

576 "_has_pre_init", 

577 "_pre_init_has_args", 

578 "_has_post_init", 

579 "_is_exc", 

580 "_on_setattr", 

581 "_slots", 

582 "_weakref_slot", 

583 "_wrote_own_setattr", 

584 "_has_custom_setattr", 

585 ) 

586 

587 def __init__( 

588 self, 

589 cls, 

590 these, 

591 slots, 

592 frozen, 

593 weakref_slot, 

594 getstate_setstate, 

595 auto_attribs, 

596 kw_only, 

597 cache_hash, 

598 is_exc, 

599 collect_by_mro, 

600 on_setattr, 

601 has_custom_setattr, 

602 field_transformer, 

603 ): 

604 attrs, base_attrs, base_map = _transform_attrs( 

605 cls, 

606 these, 

607 auto_attribs, 

608 kw_only, 

609 collect_by_mro, 

610 field_transformer, 

611 ) 

612 

613 self._cls = cls 

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

615 self._attrs = attrs 

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

617 self._base_attr_map = base_map 

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

619 self._slots = slots 

620 self._frozen = frozen 

621 self._weakref_slot = weakref_slot 

622 self._cache_hash = cache_hash 

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

624 self._pre_init_has_args = False 

625 if self._has_pre_init: 

626 # Check if the pre init method has more arguments than just `self` 

627 # We want to pass arguments if pre init expects arguments 

628 pre_init_func = cls.__attrs_pre_init__ 

629 pre_init_signature = inspect.signature(pre_init_func) 

630 self._pre_init_has_args = len(pre_init_signature.parameters) > 1 

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

632 self._delete_attribs = not bool(these) 

633 self._is_exc = is_exc 

634 self._on_setattr = on_setattr 

635 

636 self._has_custom_setattr = has_custom_setattr 

637 self._wrote_own_setattr = False 

638 

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

640 

641 if frozen: 

642 self._cls_dict["__setattr__"] = _frozen_setattrs 

643 self._cls_dict["__delattr__"] = _frozen_delattrs 

644 

645 self._wrote_own_setattr = True 

646 elif on_setattr in ( 

647 _DEFAULT_ON_SETATTR, 

648 setters.validate, 

649 setters.convert, 

650 ): 

651 has_validator = has_converter = False 

652 for a in attrs: 

653 if a.validator is not None: 

654 has_validator = True 

655 if a.converter is not None: 

656 has_converter = True 

657 

658 if has_validator and has_converter: 

659 break 

660 if ( 

661 ( 

662 on_setattr == _DEFAULT_ON_SETATTR 

663 and not (has_validator or has_converter) 

664 ) 

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

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

667 ): 

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

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

670 # no on_setattr. 

671 self._on_setattr = None 

672 

673 if getstate_setstate: 

674 ( 

675 self._cls_dict["__getstate__"], 

676 self._cls_dict["__setstate__"], 

677 ) = self._make_getstate_setstate() 

678 

679 def __repr__(self): 

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

681 

682 def build_class(self): 

683 """ 

684 Finalize class based on the accumulated configuration. 

685 

686 Builder cannot be used after calling this method. 

687 """ 

688 if self._slots is True: 

689 cls = self._create_slots_class() 

690 else: 

691 cls = self._patch_original_class() 

692 if PY_3_10_PLUS: 

693 cls = abc.update_abstractmethods(cls) 

694 

695 # The method gets only called if it's not inherited from a base class. 

696 # _has_own_attribute does NOT work properly for classmethods. 

697 if ( 

698 getattr(cls, "__attrs_init_subclass__", None) 

699 and "__attrs_init_subclass__" not in cls.__dict__ 

700 ): 

701 cls.__attrs_init_subclass__() 

702 

703 return cls 

704 

705 def _patch_original_class(self): 

706 """ 

707 Apply accumulated methods and return the class. 

708 """ 

709 cls = self._cls 

710 base_names = self._base_names 

711 

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

713 if self._delete_attribs: 

714 for name in self._attr_names: 

715 if ( 

716 name not in base_names 

717 and getattr(cls, name, _SENTINEL) is not _SENTINEL 

718 ): 

719 # An AttributeError can happen if a base class defines a 

720 # class variable and we want to set an attribute with the 

721 # same name by using only a type annotation. 

722 with contextlib.suppress(AttributeError): 

723 delattr(cls, name) 

724 

725 # Attach our dunder methods. 

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

727 setattr(cls, name, value) 

728 

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

730 # reset it to object's. 

731 if not self._wrote_own_setattr and getattr( 

732 cls, "__attrs_own_setattr__", False 

733 ): 

734 cls.__attrs_own_setattr__ = False 

735 

736 if not self._has_custom_setattr: 

737 cls.__setattr__ = _OBJ_SETATTR 

738 

739 return cls 

740 

741 def _create_slots_class(self): 

742 """ 

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

744 """ 

745 cd = { 

746 k: v 

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

748 if k not in (*tuple(self._attr_names), "__dict__", "__weakref__") 

749 } 

750 

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

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

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

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

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

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

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

758 # XXX: OK with us. 

759 if not self._wrote_own_setattr: 

760 cd["__attrs_own_setattr__"] = False 

761 

762 if not self._has_custom_setattr: 

763 for base_cls in self._cls.__bases__: 

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

765 cd["__setattr__"] = _OBJ_SETATTR 

766 break 

767 

768 # Traverse the MRO to collect existing slots 

769 # and check for an existing __weakref__. 

770 existing_slots = {} 

771 weakref_inherited = False 

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

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

774 weakref_inherited = True 

775 existing_slots.update( 

776 { 

777 name: getattr(base_cls, name) 

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

779 } 

780 ) 

781 

782 base_names = set(self._base_names) 

783 

784 names = self._attr_names 

785 if ( 

786 self._weakref_slot 

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

788 and "__weakref__" not in names 

789 and not weakref_inherited 

790 ): 

791 names += ("__weakref__",) 

792 

793 if PY_3_8_PLUS: 

794 cached_properties = { 

795 name: cached_property.func 

796 for name, cached_property in cd.items() 

797 if isinstance(cached_property, functools.cached_property) 

798 } 

799 else: 

800 # `functools.cached_property` was introduced in 3.8. 

801 # So can't be used before this. 

802 cached_properties = {} 

803 

804 # Collect methods with a `__class__` reference that are shadowed in the new class. 

805 # To know to update them. 

806 additional_closure_functions_to_update = [] 

807 if cached_properties: 

808 class_annotations = _get_annotations(self._cls) 

809 for name, func in cached_properties.items(): 

810 # Add cached properties to names for slotting. 

811 names += (name,) 

812 # Clear out function from class to avoid clashing. 

813 del cd[name] 

814 additional_closure_functions_to_update.append(func) 

815 annotation = inspect.signature(func).return_annotation 

816 if annotation is not inspect.Parameter.empty: 

817 class_annotations[name] = annotation 

818 

819 original_getattr = cd.get("__getattr__") 

820 if original_getattr is not None: 

821 additional_closure_functions_to_update.append(original_getattr) 

822 

823 cd["__getattr__"] = _make_cached_property_getattr( 

824 cached_properties, original_getattr, self._cls 

825 ) 

826 

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

828 # Setting __slots__ to inherited attributes wastes memory. 

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

830 

831 # There are slots for attributes from current class 

832 # that are defined in parent classes. 

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

834 # we collect them here and update the class dict 

835 reused_slots = { 

836 slot: slot_descriptor 

837 for slot, slot_descriptor in existing_slots.items() 

838 if slot in slot_names 

839 } 

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

841 cd.update(reused_slots) 

842 if self._cache_hash: 

843 slot_names.append(_HASH_CACHE_FIELD) 

844 

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

846 

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

848 

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

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

851 

852 # The following is a fix for 

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

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

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

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

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

858 for item in itertools.chain( 

859 cls.__dict__.values(), additional_closure_functions_to_update 

860 ): 

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

862 # Class- and staticmethods hide their functions inside. 

863 # These might need to be rewritten as well. 

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

865 elif isinstance(item, property): 

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

867 # There is no universal way for other descriptors. 

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

869 else: 

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

871 

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

873 continue 

874 for cell in closure_cells: 

875 try: 

876 match = cell.cell_contents is self._cls 

877 except ValueError: # noqa: PERF203 

878 # ValueError: Cell is empty 

879 pass 

880 else: 

881 if match: 

882 cell.cell_contents = cls 

883 return cls 

884 

885 def add_repr(self, ns): 

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

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

888 ) 

889 return self 

890 

891 def add_str(self): 

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

893 if repr is None: 

894 msg = "__str__ can only be generated if a __repr__ exists." 

895 raise ValueError(msg) 

896 

897 def __str__(self): 

898 return self.__repr__() 

899 

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

901 return self 

902 

903 def _make_getstate_setstate(self): 

904 """ 

905 Create custom __setstate__ and __getstate__ methods. 

906 """ 

907 # __weakref__ is not writable. 

908 state_attr_names = tuple( 

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

910 ) 

911 

912 def slots_getstate(self): 

913 """ 

914 Automatically created by attrs. 

915 """ 

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

917 

918 hash_caching_enabled = self._cache_hash 

919 

920 def slots_setstate(self, state): 

921 """ 

922 Automatically created by attrs. 

923 """ 

924 __bound_setattr = _OBJ_SETATTR.__get__(self) 

925 if isinstance(state, tuple): 

926 # Backward compatibility with attrs instances pickled with 

927 # attrs versions before v22.2.0 which stored tuples. 

928 for name, value in zip(state_attr_names, state): 

929 __bound_setattr(name, value) 

930 else: 

931 for name in state_attr_names: 

932 if name in state: 

933 __bound_setattr(name, state[name]) 

934 

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

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

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

938 # miss. 

939 if hash_caching_enabled: 

940 __bound_setattr(_HASH_CACHE_FIELD, None) 

941 

942 return slots_getstate, slots_setstate 

943 

944 def make_unhashable(self): 

945 self._cls_dict["__hash__"] = None 

946 return self 

947 

948 def add_hash(self): 

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

950 _make_hash( 

951 self._cls, 

952 self._attrs, 

953 frozen=self._frozen, 

954 cache_hash=self._cache_hash, 

955 ) 

956 ) 

957 

958 return self 

959 

960 def add_init(self): 

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

962 _make_init( 

963 self._cls, 

964 self._attrs, 

965 self._has_pre_init, 

966 self._pre_init_has_args, 

967 self._has_post_init, 

968 self._frozen, 

969 self._slots, 

970 self._cache_hash, 

971 self._base_attr_map, 

972 self._is_exc, 

973 self._on_setattr, 

974 attrs_init=False, 

975 ) 

976 ) 

977 

978 return self 

979 

980 def add_match_args(self): 

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

982 field.name 

983 for field in self._attrs 

984 if field.init and not field.kw_only 

985 ) 

986 

987 def add_attrs_init(self): 

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

989 _make_init( 

990 self._cls, 

991 self._attrs, 

992 self._has_pre_init, 

993 self._pre_init_has_args, 

994 self._has_post_init, 

995 self._frozen, 

996 self._slots, 

997 self._cache_hash, 

998 self._base_attr_map, 

999 self._is_exc, 

1000 self._on_setattr, 

1001 attrs_init=True, 

1002 ) 

1003 ) 

1004 

1005 return self 

1006 

1007 def add_eq(self): 

1008 cd = self._cls_dict 

1009 

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

1011 _make_eq(self._cls, self._attrs) 

1012 ) 

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

1014 

1015 return self 

1016 

1017 def add_order(self): 

1018 cd = self._cls_dict 

1019 

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

1021 self._add_method_dunders(meth) 

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

1023 ) 

1024 

1025 return self 

1026 

1027 def add_setattr(self): 

1028 if self._frozen: 

1029 return self 

1030 

1031 sa_attrs = {} 

1032 for a in self._attrs: 

1033 on_setattr = a.on_setattr or self._on_setattr 

1034 if on_setattr and on_setattr is not setters.NO_OP: 

1035 sa_attrs[a.name] = a, on_setattr 

1036 

1037 if not sa_attrs: 

1038 return self 

1039 

1040 if self._has_custom_setattr: 

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

1042 msg = "Can't combine custom __setattr__ with on_setattr hooks." 

1043 raise ValueError(msg) 

1044 

1045 # docstring comes from _add_method_dunders 

1046 def __setattr__(self, name, val): 

1047 try: 

1048 a, hook = sa_attrs[name] 

1049 except KeyError: 

1050 nval = val 

1051 else: 

1052 nval = hook(self, a, val) 

1053 

1054 _OBJ_SETATTR(self, name, nval) 

1055 

1056 self._cls_dict["__attrs_own_setattr__"] = True 

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

1058 self._wrote_own_setattr = True 

1059 

1060 return self 

1061 

1062 def _add_method_dunders(self, method): 

1063 """ 

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

1065 """ 

1066 with contextlib.suppress(AttributeError): 

1067 method.__module__ = self._cls.__module__ 

1068 

1069 with contextlib.suppress(AttributeError): 

1070 method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" 

1071 

1072 with contextlib.suppress(AttributeError): 

1073 method.__doc__ = ( 

1074 "Method generated by attrs for class " 

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

1076 ) 

1077 

1078 return method 

1079 

1080 

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

1082 """ 

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

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

1085 """ 

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

1087 msg = "Don't mix `cmp` with `eq' and `order`." 

1088 raise ValueError(msg) 

1089 

1090 # cmp takes precedence due to bw-compatibility. 

1091 if cmp is not None: 

1092 return cmp, cmp 

1093 

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

1095 # mirrors equality. 

1096 if eq is None: 

1097 eq = default_eq 

1098 

1099 if order is None: 

1100 order = eq 

1101 

1102 if eq is False and order is True: 

1103 msg = "`order` can only be True if `eq` is True too." 

1104 raise ValueError(msg) 

1105 

1106 return eq, order 

1107 

1108 

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

1110 """ 

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

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

1113 """ 

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

1115 msg = "Don't mix `cmp` with `eq' and `order`." 

1116 raise ValueError(msg) 

1117 

1118 def decide_callable_or_boolean(value): 

1119 """ 

1120 Decide whether a key function is used. 

1121 """ 

1122 if callable(value): 

1123 value, key = True, value 

1124 else: 

1125 key = None 

1126 return value, key 

1127 

1128 # cmp takes precedence due to bw-compatibility. 

1129 if cmp is not None: 

1130 cmp, cmp_key = decide_callable_or_boolean(cmp) 

1131 return cmp, cmp_key, cmp, cmp_key 

1132 

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

1134 # mirrors equality. 

1135 if eq is None: 

1136 eq, eq_key = default_eq, None 

1137 else: 

1138 eq, eq_key = decide_callable_or_boolean(eq) 

1139 

1140 if order is None: 

1141 order, order_key = eq, eq_key 

1142 else: 

1143 order, order_key = decide_callable_or_boolean(order) 

1144 

1145 if eq is False and order is True: 

1146 msg = "`order` can only be True if `eq` is True too." 

1147 raise ValueError(msg) 

1148 

1149 return eq, eq_key, order, order_key 

1150 

1151 

1152def _determine_whether_to_implement( 

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

1154): 

1155 """ 

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

1157 

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

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

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

1161 

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

1163 """ 

1164 if flag is True or flag is False: 

1165 return flag 

1166 

1167 if flag is None and auto_detect is False: 

1168 return default 

1169 

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

1171 for dunder in dunders: 

1172 if _has_own_attribute(cls, dunder): 

1173 return False 

1174 

1175 return default 

1176 

1177 

1178def attrs( 

1179 maybe_cls=None, 

1180 these=None, 

1181 repr_ns=None, 

1182 repr=None, 

1183 cmp=None, 

1184 hash=None, 

1185 init=None, 

1186 slots=False, 

1187 frozen=False, 

1188 weakref_slot=True, 

1189 str=False, 

1190 auto_attribs=False, 

1191 kw_only=False, 

1192 cache_hash=False, 

1193 auto_exc=False, 

1194 eq=None, 

1195 order=None, 

1196 auto_detect=False, 

1197 collect_by_mro=False, 

1198 getstate_setstate=None, 

1199 on_setattr=None, 

1200 field_transformer=None, 

1201 match_args=True, 

1202 unsafe_hash=None, 

1203): 

1204 r""" 

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

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

1207 

1208 Consider using `attrs.define` / `attrs.frozen` in new code (``attr.s`` will 

1209 *never* go away, though). 

1210 

1211 Args: 

1212 repr_ns (str): 

1213 When using nested classes, there was no way in Python 2 to 

1214 automatically detect that. This argument allows to set a custom 

1215 name for a more meaningful ``repr`` output. This argument is 

1216 pointless in Python 3 and is therefore deprecated. 

1217 

1218 .. caution:: 

1219 Refer to `attrs.define` for the rest of the parameters, but note that they 

1220 can have different defaults. 

1221 

1222 Notably, leaving *on_setattr* as `None` will **not** add any hooks. 

1223 

1224 .. versionadded:: 16.0.0 *slots* 

1225 .. versionadded:: 16.1.0 *frozen* 

1226 .. versionadded:: 16.3.0 *str* 

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

1228 .. versionchanged:: 17.1.0 

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

1230 .. versionadded:: 17.3.0 *auto_attribs* 

1231 .. versionchanged:: 18.1.0 

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

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

1234 .. versionadded:: 18.2.0 *weakref_slot* 

1235 .. deprecated:: 18.2.0 

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

1237 `DeprecationWarning` if the classes compared are subclasses of 

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

1239 to each other. 

1240 .. versionchanged:: 19.2.0 

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

1242 subclasses comparable anymore. 

1243 .. versionadded:: 18.2.0 *kw_only* 

1244 .. versionadded:: 18.2.0 *cache_hash* 

1245 .. versionadded:: 19.1.0 *auto_exc* 

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

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

1248 .. versionadded:: 20.1.0 *auto_detect* 

1249 .. versionadded:: 20.1.0 *collect_by_mro* 

1250 .. versionadded:: 20.1.0 *getstate_setstate* 

1251 .. versionadded:: 20.1.0 *on_setattr* 

1252 .. versionadded:: 20.3.0 *field_transformer* 

1253 .. versionchanged:: 21.1.0 

1254 ``init=False`` injects ``__attrs_init__`` 

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

1256 .. versionchanged:: 21.1.0 *cmp* undeprecated 

1257 .. versionadded:: 21.3.0 *match_args* 

1258 .. versionadded:: 22.2.0 

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

1260 .. deprecated:: 24.1.0 *repr_ns* 

1261 .. versionchanged:: 24.1.0 

1262 Instances are not compared as tuples of attributes anymore, but using a 

1263 big ``and`` condition. This is faster and has more correct behavior for 

1264 uncomparable values like `math.nan`. 

1265 .. versionadded:: 24.1.0 

1266 If a class has an *inherited* classmethod called 

1267 ``__attrs_init_subclass__``, it is executed after the class is created. 

1268 .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. 

1269 """ 

1270 if repr_ns is not None: 

1271 import warnings 

1272 

1273 warnings.warn( 

1274 DeprecationWarning( 

1275 "The `repr_ns` argument is deprecated and will be removed in or after August 2025." 

1276 ), 

1277 stacklevel=2, 

1278 ) 

1279 

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

1281 

1282 # unsafe_hash takes precedence due to PEP 681. 

1283 if unsafe_hash is not None: 

1284 hash = unsafe_hash 

1285 

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

1287 on_setattr = setters.pipe(*on_setattr) 

1288 

1289 def wrap(cls): 

1290 is_frozen = frozen or _has_frozen_base_class(cls) 

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

1292 has_own_setattr = auto_detect and _has_own_attribute( 

1293 cls, "__setattr__" 

1294 ) 

1295 

1296 if has_own_setattr and is_frozen: 

1297 msg = "Can't freeze a class with a custom __setattr__." 

1298 raise ValueError(msg) 

1299 

1300 builder = _ClassBuilder( 

1301 cls, 

1302 these, 

1303 slots, 

1304 is_frozen, 

1305 weakref_slot, 

1306 _determine_whether_to_implement( 

1307 cls, 

1308 getstate_setstate, 

1309 auto_detect, 

1310 ("__getstate__", "__setstate__"), 

1311 default=slots, 

1312 ), 

1313 auto_attribs, 

1314 kw_only, 

1315 cache_hash, 

1316 is_exc, 

1317 collect_by_mro, 

1318 on_setattr, 

1319 has_own_setattr, 

1320 field_transformer, 

1321 ) 

1322 if _determine_whether_to_implement( 

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

1324 ): 

1325 builder.add_repr(repr_ns) 

1326 if str is True: 

1327 builder.add_str() 

1328 

1329 eq = _determine_whether_to_implement( 

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

1331 ) 

1332 if not is_exc and eq is True: 

1333 builder.add_eq() 

1334 if not is_exc and _determine_whether_to_implement( 

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

1336 ): 

1337 builder.add_order() 

1338 

1339 builder.add_setattr() 

1340 

1341 nonlocal hash 

1342 if ( 

1343 hash is None 

1344 and auto_detect is True 

1345 and _has_own_attribute(cls, "__hash__") 

1346 ): 

1347 hash = False 

1348 

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

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

1351 msg = "Invalid value for hash. Must be True, False, or None." 

1352 raise TypeError(msg) 

1353 

1354 if hash is False or (hash is None and eq is False) or is_exc: 

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

1356 # which is by id. 

1357 if cache_hash: 

1358 msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." 

1359 raise TypeError(msg) 

1360 elif hash is True or ( 

1361 hash is None and eq is True and is_frozen is True 

1362 ): 

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

1364 builder.add_hash() 

1365 else: 

1366 # Raise TypeError on attempts to hash. 

1367 if cache_hash: 

1368 msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." 

1369 raise TypeError(msg) 

1370 builder.make_unhashable() 

1371 

1372 if _determine_whether_to_implement( 

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

1374 ): 

1375 builder.add_init() 

1376 else: 

1377 builder.add_attrs_init() 

1378 if cache_hash: 

1379 msg = "Invalid value for cache_hash. To use hash caching, init must be True." 

1380 raise TypeError(msg) 

1381 

1382 if ( 

1383 PY_3_10_PLUS 

1384 and match_args 

1385 and not _has_own_attribute(cls, "__match_args__") 

1386 ): 

1387 builder.add_match_args() 

1388 

1389 return builder.build_class() 

1390 

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

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

1393 if maybe_cls is None: 

1394 return wrap 

1395 

1396 return wrap(maybe_cls) 

1397 

1398 

1399_attrs = attrs 

1400""" 

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

1402*attrs*. 

1403""" 

1404 

1405 

1406def _has_frozen_base_class(cls): 

1407 """ 

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

1409 __setattr__. 

1410 """ 

1411 return cls.__setattr__ is _frozen_setattrs 

1412 

1413 

1414def _generate_unique_filename(cls, func_name): 

1415 """ 

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

1417 """ 

1418 return ( 

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

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

1421 ) 

1422 

1423 

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

1425 attrs = tuple( 

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

1427 ) 

1428 

1429 tab = " " 

1430 

1431 unique_filename = _generate_unique_filename(cls, "hash") 

1432 type_hash = hash(unique_filename) 

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

1434 globs = {} 

1435 

1436 hash_def = "def __hash__(self" 

1437 hash_func = "hash((" 

1438 closing_braces = "))" 

1439 if not cache_hash: 

1440 hash_def += "):" 

1441 else: 

1442 hash_def += ", *" 

1443 

1444 hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):" 

1445 hash_func = "_cache_wrapper(" + hash_func 

1446 closing_braces += ")" 

1447 

1448 method_lines = [hash_def] 

1449 

1450 def append_hash_computation_lines(prefix, indent): 

1451 """ 

1452 Generate the code for actually computing the hash code. 

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

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

1455 """ 

1456 

1457 method_lines.extend( 

1458 [ 

1459 indent + prefix + hash_func, 

1460 indent + f" {type_hash},", 

1461 ] 

1462 ) 

1463 

1464 for a in attrs: 

1465 if a.eq_key: 

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

1467 globs[cmp_name] = a.eq_key 

1468 method_lines.append( 

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

1470 ) 

1471 else: 

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

1473 

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

1475 

1476 if cache_hash: 

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

1478 if frozen: 

1479 append_hash_computation_lines( 

1480 f"object.__setattr__(self, '{_HASH_CACHE_FIELD}', ", tab * 2 

1481 ) 

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

1483 else: 

1484 append_hash_computation_lines( 

1485 f"self.{_HASH_CACHE_FIELD} = ", tab * 2 

1486 ) 

1487 method_lines.append(tab + f"return self.{_HASH_CACHE_FIELD}") 

1488 else: 

1489 append_hash_computation_lines("return ", tab) 

1490 

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

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

1493 

1494 

1495def _add_hash(cls, attrs): 

1496 """ 

1497 Add a hash method to *cls*. 

1498 """ 

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

1500 return cls 

1501 

1502 

1503def _make_ne(): 

1504 """ 

1505 Create __ne__ method. 

1506 """ 

1507 

1508 def __ne__(self, other): 

1509 """ 

1510 Check equality and either forward a NotImplemented or 

1511 return the result negated. 

1512 """ 

1513 result = self.__eq__(other) 

1514 if result is NotImplemented: 

1515 return NotImplemented 

1516 

1517 return not result 

1518 

1519 return __ne__ 

1520 

1521 

1522def _make_eq(cls, attrs): 

1523 """ 

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

1525 """ 

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

1527 

1528 unique_filename = _generate_unique_filename(cls, "eq") 

1529 lines = [ 

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

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

1532 " return NotImplemented", 

1533 ] 

1534 

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

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

1537 globs = {} 

1538 if attrs: 

1539 lines.append(" return (") 

1540 for a in attrs: 

1541 if a.eq_key: 

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

1543 # Add the key function to the global namespace 

1544 # of the evaluated function. 

1545 globs[cmp_name] = a.eq_key 

1546 lines.append( 

1547 f" {cmp_name}(self.{a.name}) == {cmp_name}(other.{a.name})" 

1548 ) 

1549 else: 

1550 lines.append(f" self.{a.name} == other.{a.name}") 

1551 if a is not attrs[-1]: 

1552 lines[-1] = f"{lines[-1]} and" 

1553 lines.append(" )") 

1554 else: 

1555 lines.append(" return True") 

1556 

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

1558 

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

1560 

1561 

1562def _make_order(cls, attrs): 

1563 """ 

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

1565 """ 

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

1567 

1568 def attrs_to_tuple(obj): 

1569 """ 

1570 Save us some typing. 

1571 """ 

1572 return tuple( 

1573 key(value) if key else value 

1574 for value, key in ( 

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

1576 ) 

1577 ) 

1578 

1579 def __lt__(self, other): 

1580 """ 

1581 Automatically created by attrs. 

1582 """ 

1583 if other.__class__ is self.__class__: 

1584 return attrs_to_tuple(self) < attrs_to_tuple(other) 

1585 

1586 return NotImplemented 

1587 

1588 def __le__(self, other): 

1589 """ 

1590 Automatically created by attrs. 

1591 """ 

1592 if other.__class__ is self.__class__: 

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

1594 

1595 return NotImplemented 

1596 

1597 def __gt__(self, other): 

1598 """ 

1599 Automatically created by attrs. 

1600 """ 

1601 if other.__class__ is self.__class__: 

1602 return attrs_to_tuple(self) > attrs_to_tuple(other) 

1603 

1604 return NotImplemented 

1605 

1606 def __ge__(self, other): 

1607 """ 

1608 Automatically created by attrs. 

1609 """ 

1610 if other.__class__ is self.__class__: 

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

1612 

1613 return NotImplemented 

1614 

1615 return __lt__, __le__, __gt__, __ge__ 

1616 

1617 

1618def _add_eq(cls, attrs=None): 

1619 """ 

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

1621 """ 

1622 if attrs is None: 

1623 attrs = cls.__attrs_attrs__ 

1624 

1625 cls.__eq__ = _make_eq(cls, attrs) 

1626 cls.__ne__ = _make_ne() 

1627 

1628 return cls 

1629 

1630 

1631def _make_repr(attrs, ns, cls): 

1632 unique_filename = _generate_unique_filename(cls, "repr") 

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

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

1635 # callable. 

1636 attr_names_with_reprs = tuple( 

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

1638 for a in attrs 

1639 if a.repr is not False 

1640 ) 

1641 globs = { 

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

1643 } 

1644 globs["_compat"] = _compat 

1645 globs["AttributeError"] = AttributeError 

1646 globs["NOTHING"] = NOTHING 

1647 attribute_fragments = [] 

1648 for name, r, i in attr_names_with_reprs: 

1649 accessor = ( 

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

1651 ) 

1652 fragment = ( 

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

1654 if r == repr 

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

1656 ) 

1657 attribute_fragments.append(fragment) 

1658 repr_fragment = ", ".join(attribute_fragments) 

1659 

1660 if ns is None: 

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

1662 else: 

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

1664 

1665 lines = [ 

1666 "def __repr__(self):", 

1667 " try:", 

1668 " already_repring = _compat.repr_context.already_repring", 

1669 " except AttributeError:", 

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

1671 " _compat.repr_context.already_repring = already_repring", 

1672 " else:", 

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

1674 " return '...'", 

1675 " else:", 

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

1677 " try:", 

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

1679 " finally:", 

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

1681 ] 

1682 

1683 return _make_method( 

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

1685 ) 

1686 

1687 

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

1689 """ 

1690 Add a repr method to *cls*. 

1691 """ 

1692 if attrs is None: 

1693 attrs = cls.__attrs_attrs__ 

1694 

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

1696 return cls 

1697 

1698 

1699def fields(cls): 

1700 """ 

1701 Return the tuple of *attrs* attributes for a class. 

1702 

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

1704 examples). 

1705 

1706 Args: 

1707 cls (type): Class to introspect. 

1708 

1709 Raises: 

1710 TypeError: If *cls* is not a class. 

1711 

1712 attrs.exceptions.NotAnAttrsClassError: 

1713 If *cls* is not an *attrs* class. 

1714 

1715 Returns: 

1716 tuple (with name accessors) of `attrs.Attribute` 

1717 

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

1719 by name. 

1720 .. versionchanged:: 23.1.0 Add support for generic classes. 

1721 """ 

1722 generic_base = get_generic_base(cls) 

1723 

1724 if generic_base is None and not isinstance(cls, type): 

1725 msg = "Passed object must be a class." 

1726 raise TypeError(msg) 

1727 

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

1729 

1730 if attrs is None: 

1731 if generic_base is not None: 

1732 attrs = getattr(generic_base, "__attrs_attrs__", None) 

1733 if attrs is not None: 

1734 # Even though this is global state, stick it on here to speed 

1735 # it up. We rely on `cls` being cached for this to be 

1736 # efficient. 

1737 cls.__attrs_attrs__ = attrs 

1738 return attrs 

1739 msg = f"{cls!r} is not an attrs-decorated class." 

1740 raise NotAnAttrsClassError(msg) 

1741 

1742 return attrs 

1743 

1744 

1745def fields_dict(cls): 

1746 """ 

1747 Return an ordered dictionary of *attrs* attributes for a class, whose keys 

1748 are the attribute names. 

1749 

1750 Args: 

1751 cls (type): Class to introspect. 

1752 

1753 Raises: 

1754 TypeError: If *cls* is not a class. 

1755 

1756 attrs.exceptions.NotAnAttrsClassError: 

1757 If *cls* is not an *attrs* class. 

1758 

1759 Returns: 

1760 dict[str, attrs.Attribute]: Dict of attribute name to definition 

1761 

1762 .. versionadded:: 18.1.0 

1763 """ 

1764 if not isinstance(cls, type): 

1765 msg = "Passed object must be a class." 

1766 raise TypeError(msg) 

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

1768 if attrs is None: 

1769 msg = f"{cls!r} is not an attrs-decorated class." 

1770 raise NotAnAttrsClassError(msg) 

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

1772 

1773 

1774def validate(inst): 

1775 """ 

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

1777 

1778 Leaves all exceptions through. 

1779 

1780 Args: 

1781 inst: Instance of a class with *attrs* attributes. 

1782 """ 

1783 if _config._run_validators is False: 

1784 return 

1785 

1786 for a in fields(inst.__class__): 

1787 v = a.validator 

1788 if v is not None: 

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

1790 

1791 

1792def _is_slot_attr(a_name, base_attr_map): 

1793 """ 

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

1795 """ 

1796 cls = base_attr_map.get(a_name) 

1797 return cls and "__slots__" in cls.__dict__ 

1798 

1799 

1800def _make_init( 

1801 cls, 

1802 attrs, 

1803 pre_init, 

1804 pre_init_has_args, 

1805 post_init, 

1806 frozen, 

1807 slots, 

1808 cache_hash, 

1809 base_attr_map, 

1810 is_exc, 

1811 cls_on_setattr, 

1812 attrs_init, 

1813): 

1814 has_cls_on_setattr = ( 

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

1816 ) 

1817 

1818 if frozen and has_cls_on_setattr: 

1819 msg = "Frozen classes can't use on_setattr." 

1820 raise ValueError(msg) 

1821 

1822 needs_cached_setattr = cache_hash or frozen 

1823 filtered_attrs = [] 

1824 attr_dict = {} 

1825 for a in attrs: 

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

1827 continue 

1828 

1829 filtered_attrs.append(a) 

1830 attr_dict[a.name] = a 

1831 

1832 if a.on_setattr is not None: 

1833 if frozen is True: 

1834 msg = "Frozen classes can't use on_setattr." 

1835 raise ValueError(msg) 

1836 

1837 needs_cached_setattr = True 

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

1839 needs_cached_setattr = True 

1840 

1841 unique_filename = _generate_unique_filename(cls, "init") 

1842 

1843 script, globs, annotations = _attrs_to_init_script( 

1844 filtered_attrs, 

1845 frozen, 

1846 slots, 

1847 pre_init, 

1848 pre_init_has_args, 

1849 post_init, 

1850 cache_hash, 

1851 base_attr_map, 

1852 is_exc, 

1853 needs_cached_setattr, 

1854 has_cls_on_setattr, 

1855 "__attrs_init__" if attrs_init else "__init__", 

1856 ) 

1857 if cls.__module__ in sys.modules: 

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

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

1860 

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

1862 

1863 if needs_cached_setattr: 

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

1865 # setattr hooks. 

1866 globs["_cached_setattr_get"] = _OBJ_SETATTR.__get__ 

1867 

1868 init = _make_method( 

1869 "__attrs_init__" if attrs_init else "__init__", 

1870 script, 

1871 unique_filename, 

1872 globs, 

1873 ) 

1874 init.__annotations__ = annotations 

1875 

1876 return init 

1877 

1878 

1879def _setattr(attr_name: str, value_var: str, has_on_setattr: bool) -> str: 

1880 """ 

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

1882 """ 

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

1884 

1885 

1886def _setattr_with_converter( 

1887 attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter 

1888) -> str: 

1889 """ 

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

1891 its converter first. 

1892 """ 

1893 return f"_setattr('{attr_name}', {converter._fmt_converter_call(attr_name, value_var)})" 

1894 

1895 

1896def _assign(attr_name: str, value: str, has_on_setattr: bool) -> str: 

1897 """ 

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

1899 relegate to _setattr. 

1900 """ 

1901 if has_on_setattr: 

1902 return _setattr(attr_name, value, True) 

1903 

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

1905 

1906 

1907def _assign_with_converter( 

1908 attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter 

1909) -> str: 

1910 """ 

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

1912 conversion. Otherwise relegate to _setattr_with_converter. 

1913 """ 

1914 if has_on_setattr: 

1915 return _setattr_with_converter(attr_name, value_var, True, converter) 

1916 

1917 return f"self.{attr_name} = {converter._fmt_converter_call(attr_name, value_var)}" 

1918 

1919 

1920def _determine_setters( 

1921 frozen: bool, slots: bool, base_attr_map: dict[str, type] 

1922): 

1923 """ 

1924 Determine the correct setter functions based on whether a class is frozen 

1925 and/or slotted. 

1926 """ 

1927 if frozen is True: 

1928 if slots is True: 

1929 return (), _setattr, _setattr_with_converter 

1930 

1931 # Dict frozen classes assign directly to __dict__. 

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

1933 # class. 

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

1935 

1936 def fmt_setter( 

1937 attr_name: str, value_var: str, has_on_setattr: bool 

1938 ) -> str: 

1939 if _is_slot_attr(attr_name, base_attr_map): 

1940 return _setattr(attr_name, value_var, has_on_setattr) 

1941 

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

1943 

1944 def fmt_setter_with_converter( 

1945 attr_name: str, 

1946 value_var: str, 

1947 has_on_setattr: bool, 

1948 converter: Converter, 

1949 ) -> str: 

1950 if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): 

1951 return _setattr_with_converter( 

1952 attr_name, value_var, has_on_setattr, converter 

1953 ) 

1954 

1955 return f"_inst_dict['{attr_name}'] = {converter._fmt_converter_call(attr_name, value_var)}" 

1956 

1957 return ( 

1958 ("_inst_dict = self.__dict__",), 

1959 fmt_setter, 

1960 fmt_setter_with_converter, 

1961 ) 

1962 

1963 # Not frozen -- we can just assign directly. 

1964 return (), _assign, _assign_with_converter 

1965 

1966 

1967def _attrs_to_init_script( 

1968 attrs: list[Attribute], 

1969 is_frozen: bool, 

1970 is_slotted: bool, 

1971 call_pre_init: bool, 

1972 pre_init_has_args: bool, 

1973 call_post_init: bool, 

1974 does_cache_hash: bool, 

1975 base_attr_map: dict[str, type], 

1976 is_exc: bool, 

1977 needs_cached_setattr: bool, 

1978 has_cls_on_setattr: bool, 

1979 method_name: str, 

1980) -> tuple[str, dict, dict]: 

1981 """ 

1982 Return a script of an initializer for *attrs*, a dict of globals, and 

1983 annotations for the initializer. 

1984 

1985 The globals are required by the generated script. 

1986 """ 

1987 lines = ["self.__attrs_pre_init__()"] if call_pre_init else [] 

1988 

1989 if needs_cached_setattr: 

1990 lines.append( 

1991 # Circumvent the __setattr__ descriptor to save one lookup per 

1992 # assignment. Note _setattr will be used again below if 

1993 # does_cache_hash is True. 

1994 "_setattr = _cached_setattr_get(self)" 

1995 ) 

1996 

1997 extra_lines, fmt_setter, fmt_setter_with_converter = _determine_setters( 

1998 is_frozen, is_slotted, base_attr_map 

1999 ) 

2000 lines.extend(extra_lines) 

2001 

2002 args = [] 

2003 kw_only_args = [] 

2004 attrs_to_validate = [] 

2005 

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

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

2008 names_for_globals = {} 

2009 annotations = {"return": None} 

2010 

2011 for a in attrs: 

2012 if a.validator: 

2013 attrs_to_validate.append(a) 

2014 

2015 attr_name = a.name 

2016 has_on_setattr = a.on_setattr is not None or ( 

2017 a.on_setattr is not setters.NO_OP and has_cls_on_setattr 

2018 ) 

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

2020 # explicitly provided 

2021 arg_name = a.alias 

2022 

2023 has_factory = isinstance(a.default, Factory) 

2024 maybe_self = "self" if has_factory and a.default.takes_self else "" 

2025 

2026 if a.converter and not isinstance(a.converter, Converter): 

2027 converter = Converter(a.converter) 

2028 else: 

2029 converter = a.converter 

2030 

2031 if a.init is False: 

2032 if has_factory: 

2033 init_factory_name = _INIT_FACTORY_PAT % (a.name,) 

2034 if converter is not None: 

2035 lines.append( 

2036 fmt_setter_with_converter( 

2037 attr_name, 

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

2039 has_on_setattr, 

2040 converter, 

2041 ) 

2042 ) 

2043 names_for_globals[converter._get_global_name(a.name)] = ( 

2044 converter.converter 

2045 ) 

2046 else: 

2047 lines.append( 

2048 fmt_setter( 

2049 attr_name, 

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

2051 has_on_setattr, 

2052 ) 

2053 ) 

2054 names_for_globals[init_factory_name] = a.default.factory 

2055 elif converter is not None: 

2056 lines.append( 

2057 fmt_setter_with_converter( 

2058 attr_name, 

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

2060 has_on_setattr, 

2061 converter, 

2062 ) 

2063 ) 

2064 names_for_globals[converter._get_global_name(a.name)] = ( 

2065 converter.converter 

2066 ) 

2067 else: 

2068 lines.append( 

2069 fmt_setter( 

2070 attr_name, 

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

2072 has_on_setattr, 

2073 ) 

2074 ) 

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

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

2077 if a.kw_only: 

2078 kw_only_args.append(arg) 

2079 else: 

2080 args.append(arg) 

2081 

2082 if converter is not None: 

2083 lines.append( 

2084 fmt_setter_with_converter( 

2085 attr_name, arg_name, has_on_setattr, converter 

2086 ) 

2087 ) 

2088 names_for_globals[converter._get_global_name(a.name)] = ( 

2089 converter.converter 

2090 ) 

2091 else: 

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

2093 

2094 elif has_factory: 

2095 arg = f"{arg_name}=NOTHING" 

2096 if a.kw_only: 

2097 kw_only_args.append(arg) 

2098 else: 

2099 args.append(arg) 

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

2101 

2102 init_factory_name = _INIT_FACTORY_PAT % (a.name,) 

2103 if converter is not None: 

2104 lines.append( 

2105 " " 

2106 + fmt_setter_with_converter( 

2107 attr_name, arg_name, has_on_setattr, converter 

2108 ) 

2109 ) 

2110 lines.append("else:") 

2111 lines.append( 

2112 " " 

2113 + fmt_setter_with_converter( 

2114 attr_name, 

2115 init_factory_name + "(" + maybe_self + ")", 

2116 has_on_setattr, 

2117 converter, 

2118 ) 

2119 ) 

2120 names_for_globals[converter._get_global_name(a.name)] = ( 

2121 converter.converter 

2122 ) 

2123 else: 

2124 lines.append( 

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

2126 ) 

2127 lines.append("else:") 

2128 lines.append( 

2129 " " 

2130 + fmt_setter( 

2131 attr_name, 

2132 init_factory_name + "(" + maybe_self + ")", 

2133 has_on_setattr, 

2134 ) 

2135 ) 

2136 names_for_globals[init_factory_name] = a.default.factory 

2137 else: 

2138 if a.kw_only: 

2139 kw_only_args.append(arg_name) 

2140 else: 

2141 args.append(arg_name) 

2142 

2143 if converter is not None: 

2144 lines.append( 

2145 fmt_setter_with_converter( 

2146 attr_name, arg_name, has_on_setattr, converter 

2147 ) 

2148 ) 

2149 names_for_globals[converter._get_global_name(a.name)] = ( 

2150 converter.converter 

2151 ) 

2152 else: 

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

2154 

2155 if a.init is True: 

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

2157 annotations[arg_name] = a.type 

2158 elif converter is not None and converter._first_param_type: 

2159 # Use the type from the converter if present. 

2160 annotations[arg_name] = converter._first_param_type 

2161 

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

2163 names_for_globals["_config"] = _config 

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

2165 for a in attrs_to_validate: 

2166 val_name = "__attr_validator_" + a.name 

2167 attr_name = "__attr_" + a.name 

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

2169 names_for_globals[val_name] = a.validator 

2170 names_for_globals[attr_name] = a 

2171 

2172 if call_post_init: 

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

2174 

2175 # Because this is set only after __attrs_post_init__ is called, a crash 

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

2177 # preferable to setting this beforehand, in which case alteration to field 

2178 # values during post-init combined with post-init accessing the hash code 

2179 # would result in silent bugs. 

2180 if does_cache_hash: 

2181 if is_frozen: 

2182 if is_slotted: 

2183 init_hash_cache = f"_setattr('{_HASH_CACHE_FIELD}', None)" 

2184 else: 

2185 init_hash_cache = f"_inst_dict['{_HASH_CACHE_FIELD}'] = None" 

2186 else: 

2187 init_hash_cache = f"self.{_HASH_CACHE_FIELD} = None" 

2188 lines.append(init_hash_cache) 

2189 

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

2191 # initialization. 

2192 if is_exc: 

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

2194 

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

2196 

2197 args = ", ".join(args) 

2198 pre_init_args = args 

2199 if kw_only_args: 

2200 # leading comma & kw_only args 

2201 args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}" 

2202 pre_init_kw_only_args = ", ".join( 

2203 [ 

2204 f"{kw_arg_name}={kw_arg_name}" 

2205 # We need to remove the defaults from the kw_only_args. 

2206 for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args) 

2207 ] 

2208 ) 

2209 pre_init_args += ", " if pre_init_args else "" 

2210 pre_init_args += pre_init_kw_only_args 

2211 

2212 if call_pre_init and pre_init_has_args: 

2213 # If pre init method has arguments, pass same arguments as `__init__`. 

2214 lines[0] = f"self.__attrs_pre_init__({pre_init_args})" 

2215 

2216 # Python 3.7 doesn't allow backslashes in f strings. 

2217 NL = "\n " 

2218 return ( 

2219 f"""def {method_name}(self, {args}): 

2220 {NL.join(lines) if lines else 'pass'} 

2221""", 

2222 names_for_globals, 

2223 annotations, 

2224 ) 

2225 

2226 

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

2228 """ 

2229 The default __init__ parameter name for a field. 

2230 

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

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

2233 """ 

2234 

2235 return name.lstrip("_") 

2236 

2237 

2238class Attribute: 

2239 """ 

2240 *Read-only* representation of an attribute. 

2241 

2242 .. warning:: 

2243 

2244 You should never instantiate this class yourself. 

2245 

2246 The class has *all* arguments of `attr.ib` (except for ``factory`` which is 

2247 only syntactic sugar for ``default=Factory(...)`` plus the following: 

2248 

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

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

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

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

2253 from a base class. 

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

2255 callables that are used for comparing and ordering objects by this 

2256 attribute, respectively. These are set by passing a callable to 

2257 `attr.ib`'s ``eq``, ``order``, or ``cmp`` arguments. See also 

2258 :ref:`comparison customization <custom-comparison>`. 

2259 

2260 Instances of this class are frequently used for introspection purposes 

2261 like: 

2262 

2263 - `fields` returns a tuple of them. 

2264 - Validators get them passed as the first argument. 

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

2266 them. 

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

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

2269 

2270 

2271 .. versionadded:: 20.1.0 *inherited* 

2272 .. versionadded:: 20.1.0 *on_setattr* 

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

2274 equality checks and hashing anymore. 

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

2276 .. versionadded:: 22.2.0 *alias* 

2277 

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

2279 """ 

2280 

2281 __slots__ = ( 

2282 "name", 

2283 "default", 

2284 "validator", 

2285 "repr", 

2286 "eq", 

2287 "eq_key", 

2288 "order", 

2289 "order_key", 

2290 "hash", 

2291 "init", 

2292 "metadata", 

2293 "type", 

2294 "converter", 

2295 "kw_only", 

2296 "inherited", 

2297 "on_setattr", 

2298 "alias", 

2299 ) 

2300 

2301 def __init__( 

2302 self, 

2303 name, 

2304 default, 

2305 validator, 

2306 repr, 

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

2308 hash, 

2309 init, 

2310 inherited, 

2311 metadata=None, 

2312 type=None, 

2313 converter=None, 

2314 kw_only=False, 

2315 eq=None, 

2316 eq_key=None, 

2317 order=None, 

2318 order_key=None, 

2319 on_setattr=None, 

2320 alias=None, 

2321 ): 

2322 eq, eq_key, order, order_key = _determine_attrib_eq_order( 

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

2324 ) 

2325 

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

2327 bound_setattr = _OBJ_SETATTR.__get__(self) 

2328 

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

2330 # themselves. 

2331 bound_setattr("name", name) 

2332 bound_setattr("default", default) 

2333 bound_setattr("validator", validator) 

2334 bound_setattr("repr", repr) 

2335 bound_setattr("eq", eq) 

2336 bound_setattr("eq_key", eq_key) 

2337 bound_setattr("order", order) 

2338 bound_setattr("order_key", order_key) 

2339 bound_setattr("hash", hash) 

2340 bound_setattr("init", init) 

2341 bound_setattr("converter", converter) 

2342 bound_setattr( 

2343 "metadata", 

2344 ( 

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

2346 if metadata 

2347 else _EMPTY_METADATA_SINGLETON 

2348 ), 

2349 ) 

2350 bound_setattr("type", type) 

2351 bound_setattr("kw_only", kw_only) 

2352 bound_setattr("inherited", inherited) 

2353 bound_setattr("on_setattr", on_setattr) 

2354 bound_setattr("alias", alias) 

2355 

2356 def __setattr__(self, name, value): 

2357 raise FrozenInstanceError() 

2358 

2359 @classmethod 

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

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

2362 if type is None: 

2363 type = ca.type 

2364 elif ca.type is not None: 

2365 msg = "Type annotation and type argument cannot both be present" 

2366 raise ValueError(msg) 

2367 inst_dict = { 

2368 k: getattr(ca, k) 

2369 for k in Attribute.__slots__ 

2370 if k 

2371 not in ( 

2372 "name", 

2373 "validator", 

2374 "default", 

2375 "type", 

2376 "inherited", 

2377 ) # exclude methods and deprecated alias 

2378 } 

2379 return cls( 

2380 name=name, 

2381 validator=ca._validator, 

2382 default=ca._default, 

2383 type=type, 

2384 cmp=None, 

2385 inherited=False, 

2386 **inst_dict, 

2387 ) 

2388 

2389 # Don't use attrs.evolve since fields(Attribute) doesn't work 

2390 def evolve(self, **changes): 

2391 """ 

2392 Copy *self* and apply *changes*. 

2393 

2394 This works similarly to `attrs.evolve` but that function does not work 

2395 with {class}`Attribute`. 

2396 

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

2398 

2399 .. versionadded:: 20.3.0 

2400 """ 

2401 new = copy.copy(self) 

2402 

2403 new._setattrs(changes.items()) 

2404 

2405 return new 

2406 

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

2408 def __getstate__(self): 

2409 """ 

2410 Play nice with pickle. 

2411 """ 

2412 return tuple( 

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

2414 for name in self.__slots__ 

2415 ) 

2416 

2417 def __setstate__(self, state): 

2418 """ 

2419 Play nice with pickle. 

2420 """ 

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

2422 

2423 def _setattrs(self, name_values_pairs): 

2424 bound_setattr = _OBJ_SETATTR.__get__(self) 

2425 for name, value in name_values_pairs: 

2426 if name != "metadata": 

2427 bound_setattr(name, value) 

2428 else: 

2429 bound_setattr( 

2430 name, 

2431 ( 

2432 types.MappingProxyType(dict(value)) 

2433 if value 

2434 else _EMPTY_METADATA_SINGLETON 

2435 ), 

2436 ) 

2437 

2438 

2439_a = [ 

2440 Attribute( 

2441 name=name, 

2442 default=NOTHING, 

2443 validator=None, 

2444 repr=True, 

2445 cmp=None, 

2446 eq=True, 

2447 order=False, 

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

2449 init=True, 

2450 inherited=False, 

2451 alias=_default_init_alias_for(name), 

2452 ) 

2453 for name in Attribute.__slots__ 

2454] 

2455 

2456Attribute = _add_hash( 

2457 _add_eq( 

2458 _add_repr(Attribute, attrs=_a), 

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

2460 ), 

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

2462) 

2463 

2464 

2465class _CountingAttr: 

2466 """ 

2467 Intermediate representation of attributes that uses a counter to preserve 

2468 the order in which the attributes have been defined. 

2469 

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

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

2472 """ 

2473 

2474 __slots__ = ( 

2475 "counter", 

2476 "_default", 

2477 "repr", 

2478 "eq", 

2479 "eq_key", 

2480 "order", 

2481 "order_key", 

2482 "hash", 

2483 "init", 

2484 "metadata", 

2485 "_validator", 

2486 "converter", 

2487 "type", 

2488 "kw_only", 

2489 "on_setattr", 

2490 "alias", 

2491 ) 

2492 __attrs_attrs__ = ( 

2493 *tuple( 

2494 Attribute( 

2495 name=name, 

2496 alias=_default_init_alias_for(name), 

2497 default=NOTHING, 

2498 validator=None, 

2499 repr=True, 

2500 cmp=None, 

2501 hash=True, 

2502 init=True, 

2503 kw_only=False, 

2504 eq=True, 

2505 eq_key=None, 

2506 order=False, 

2507 order_key=None, 

2508 inherited=False, 

2509 on_setattr=None, 

2510 ) 

2511 for name in ( 

2512 "counter", 

2513 "_default", 

2514 "repr", 

2515 "eq", 

2516 "order", 

2517 "hash", 

2518 "init", 

2519 "on_setattr", 

2520 "alias", 

2521 ) 

2522 ), 

2523 Attribute( 

2524 name="metadata", 

2525 alias="metadata", 

2526 default=None, 

2527 validator=None, 

2528 repr=True, 

2529 cmp=None, 

2530 hash=False, 

2531 init=True, 

2532 kw_only=False, 

2533 eq=True, 

2534 eq_key=None, 

2535 order=False, 

2536 order_key=None, 

2537 inherited=False, 

2538 on_setattr=None, 

2539 ), 

2540 ) 

2541 cls_counter = 0 

2542 

2543 def __init__( 

2544 self, 

2545 default, 

2546 validator, 

2547 repr, 

2548 cmp, 

2549 hash, 

2550 init, 

2551 converter, 

2552 metadata, 

2553 type, 

2554 kw_only, 

2555 eq, 

2556 eq_key, 

2557 order, 

2558 order_key, 

2559 on_setattr, 

2560 alias, 

2561 ): 

2562 _CountingAttr.cls_counter += 1 

2563 self.counter = _CountingAttr.cls_counter 

2564 self._default = default 

2565 self._validator = validator 

2566 self.converter = converter 

2567 self.repr = repr 

2568 self.eq = eq 

2569 self.eq_key = eq_key 

2570 self.order = order 

2571 self.order_key = order_key 

2572 self.hash = hash 

2573 self.init = init 

2574 self.metadata = metadata 

2575 self.type = type 

2576 self.kw_only = kw_only 

2577 self.on_setattr = on_setattr 

2578 self.alias = alias 

2579 

2580 def validator(self, meth): 

2581 """ 

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

2583 

2584 Returns *meth* unchanged. 

2585 

2586 .. versionadded:: 17.1.0 

2587 """ 

2588 if self._validator is None: 

2589 self._validator = meth 

2590 else: 

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

2592 return meth 

2593 

2594 def default(self, meth): 

2595 """ 

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

2597 

2598 Returns *meth* unchanged. 

2599 

2600 Raises: 

2601 DefaultAlreadySetError: If default has been set before. 

2602 

2603 .. versionadded:: 17.1.0 

2604 """ 

2605 if self._default is not NOTHING: 

2606 raise DefaultAlreadySetError() 

2607 

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

2609 

2610 return meth 

2611 

2612 

2613_CountingAttr = _add_eq(_add_repr(_CountingAttr)) 

2614 

2615 

2616class Factory: 

2617 """ 

2618 Stores a factory callable. 

2619 

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

2621 generate a new value. 

2622 

2623 Args: 

2624 factory (typing.Callable): 

2625 A callable that takes either none or exactly one mandatory 

2626 positional argument depending on *takes_self*. 

2627 

2628 takes_self (bool): 

2629 Pass the partially initialized instance that is being initialized 

2630 as a positional argument. 

2631 

2632 .. versionadded:: 17.1.0 *takes_self* 

2633 """ 

2634 

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

2636 

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

2638 self.factory = factory 

2639 self.takes_self = takes_self 

2640 

2641 def __getstate__(self): 

2642 """ 

2643 Play nice with pickle. 

2644 """ 

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

2646 

2647 def __setstate__(self, state): 

2648 """ 

2649 Play nice with pickle. 

2650 """ 

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

2652 setattr(self, name, value) 

2653 

2654 

2655_f = [ 

2656 Attribute( 

2657 name=name, 

2658 default=NOTHING, 

2659 validator=None, 

2660 repr=True, 

2661 cmp=None, 

2662 eq=True, 

2663 order=False, 

2664 hash=True, 

2665 init=True, 

2666 inherited=False, 

2667 ) 

2668 for name in Factory.__slots__ 

2669] 

2670 

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

2672 

2673 

2674class Converter: 

2675 """ 

2676 Stores a converter callable. 

2677 

2678 Allows for the wrapped converter to take additional arguments. The 

2679 arguments are passed in the order they are documented. 

2680 

2681 Args: 

2682 converter (Callable): A callable that converts the passed value. 

2683 

2684 takes_self (bool): 

2685 Pass the partially initialized instance that is being initialized 

2686 as a positional argument. (default: `False`) 

2687 

2688 takes_field (bool): 

2689 Pass the field definition (an :class:`Attribute`) into the 

2690 converter as a positional argument. (default: `False`) 

2691 

2692 .. versionadded:: 24.1.0 

2693 """ 

2694 

2695 __slots__ = ( 

2696 "converter", 

2697 "takes_self", 

2698 "takes_field", 

2699 "_first_param_type", 

2700 "_global_name", 

2701 "__call__", 

2702 ) 

2703 

2704 def __init__(self, converter, *, takes_self=False, takes_field=False): 

2705 self.converter = converter 

2706 self.takes_self = takes_self 

2707 self.takes_field = takes_field 

2708 

2709 ex = _AnnotationExtractor(converter) 

2710 self._first_param_type = ex.get_first_param_type() 

2711 

2712 if not (self.takes_self or self.takes_field): 

2713 self.__call__ = lambda value, _, __: self.converter(value) 

2714 elif self.takes_self and not self.takes_field: 

2715 self.__call__ = lambda value, instance, __: self.converter( 

2716 value, instance 

2717 ) 

2718 elif not self.takes_self and self.takes_field: 

2719 self.__call__ = lambda value, __, field: self.converter( 

2720 value, field 

2721 ) 

2722 else: 

2723 self.__call__ = lambda value, instance, field: self.converter( 

2724 value, instance, field 

2725 ) 

2726 

2727 rt = ex.get_return_type() 

2728 if rt is not None: 

2729 self.__call__.__annotations__["return"] = rt 

2730 

2731 @staticmethod 

2732 def _get_global_name(attr_name: str) -> str: 

2733 """ 

2734 Return the name that a converter for an attribute name *attr_name* 

2735 would have. 

2736 """ 

2737 return f"__attr_converter_{attr_name}" 

2738 

2739 def _fmt_converter_call(self, attr_name: str, value_var: str) -> str: 

2740 """ 

2741 Return a string that calls the converter for an attribute name 

2742 *attr_name* and the value in variable named *value_var* according to 

2743 `self.takes_self` and `self.takes_field`. 

2744 """ 

2745 if not (self.takes_self or self.takes_field): 

2746 return f"{self._get_global_name(attr_name)}({value_var})" 

2747 

2748 if self.takes_self and self.takes_field: 

2749 return f"{self._get_global_name(attr_name)}({value_var}, self, attr_dict['{attr_name}'])" 

2750 

2751 if self.takes_self: 

2752 return f"{self._get_global_name(attr_name)}({value_var}, self)" 

2753 

2754 return f"{self._get_global_name(attr_name)}({value_var}, attr_dict['{attr_name}'])" 

2755 

2756 def __getstate__(self): 

2757 """ 

2758 Return a dict containing only converter and takes_self -- the rest gets 

2759 computed when loading. 

2760 """ 

2761 return { 

2762 "converter": self.converter, 

2763 "takes_self": self.takes_self, 

2764 "takes_field": self.takes_field, 

2765 } 

2766 

2767 def __setstate__(self, state): 

2768 """ 

2769 Load instance from state. 

2770 """ 

2771 self.__init__(**state) 

2772 

2773 

2774_f = [ 

2775 Attribute( 

2776 name=name, 

2777 default=NOTHING, 

2778 validator=None, 

2779 repr=True, 

2780 cmp=None, 

2781 eq=True, 

2782 order=False, 

2783 hash=True, 

2784 init=True, 

2785 inherited=False, 

2786 ) 

2787 for name in ("converter", "takes_self", "takes_field") 

2788] 

2789 

2790Converter = _add_hash( 

2791 _add_eq(_add_repr(Converter, attrs=_f), attrs=_f), attrs=_f 

2792) 

2793 

2794 

2795def make_class( 

2796 name, attrs, bases=(object,), class_body=None, **attributes_arguments 

2797): 

2798 r""" 

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

2800 

2801 Args: 

2802 name (str): The name for the new class. 

2803 

2804 attrs( list | dict): 

2805 A list of names or a dictionary of mappings of names to `attr.ib`\ 

2806 s / `attrs.field`\ s. 

2807 

2808 The order is deduced from the order of the names or attributes 

2809 inside *attrs*. Otherwise the order of the definition of the 

2810 attributes is used. 

2811 

2812 bases (tuple[type, ...]): Classes that the new class will subclass. 

2813 

2814 class_body (dict): 

2815 An optional dictionary of class attributes for the new class. 

2816 

2817 attributes_arguments: Passed unmodified to `attr.s`. 

2818 

2819 Returns: 

2820 type: A new class with *attrs*. 

2821 

2822 .. versionadded:: 17.1.0 *bases* 

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

2824 .. versionchanged:: 23.2.0 *class_body* 

2825 """ 

2826 if isinstance(attrs, dict): 

2827 cls_dict = attrs 

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

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

2830 else: 

2831 msg = "attrs argument must be a dict or a list." 

2832 raise TypeError(msg) 

2833 

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

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

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

2837 

2838 body = {} 

2839 if class_body is not None: 

2840 body.update(class_body) 

2841 if pre_init is not None: 

2842 body["__attrs_pre_init__"] = pre_init 

2843 if post_init is not None: 

2844 body["__attrs_post_init__"] = post_init 

2845 if user_init is not None: 

2846 body["__init__"] = user_init 

2847 

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

2849 

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

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

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

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

2854 with contextlib.suppress(AttributeError, ValueError): 

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

2856 "__name__", "__main__" 

2857 ) 

2858 

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

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

2861 ( 

2862 attributes_arguments["eq"], 

2863 attributes_arguments["order"], 

2864 ) = _determine_attrs_eq_order( 

2865 cmp, 

2866 attributes_arguments.get("eq"), 

2867 attributes_arguments.get("order"), 

2868 True, 

2869 ) 

2870 

2871 cls = _attrs(these=cls_dict, **attributes_arguments)(type_) 

2872 # Only add type annotations now or "_attrs()" will complain: 

2873 cls.__annotations__ = { 

2874 k: v.type for k, v in cls_dict.items() if v.type is not None 

2875 } 

2876 return cls 

2877 

2878 

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

2880# import into .validators / .converters. 

2881 

2882 

2883@attrs(slots=True, unsafe_hash=True) 

2884class _AndValidator: 

2885 """ 

2886 Compose many validators to a single one. 

2887 """ 

2888 

2889 _validators = attrib() 

2890 

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

2892 for v in self._validators: 

2893 v(inst, attr, value) 

2894 

2895 

2896def and_(*validators): 

2897 """ 

2898 A validator that composes multiple validators into one. 

2899 

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

2901 

2902 Args: 

2903 validators (~collections.abc.Iterable[typing.Callable]): 

2904 Arbitrary number of validators. 

2905 

2906 .. versionadded:: 17.1.0 

2907 """ 

2908 vals = [] 

2909 for validator in validators: 

2910 vals.extend( 

2911 validator._validators 

2912 if isinstance(validator, _AndValidator) 

2913 else [validator] 

2914 ) 

2915 

2916 return _AndValidator(tuple(vals)) 

2917 

2918 

2919def pipe(*converters): 

2920 """ 

2921 A converter that composes multiple converters into one. 

2922 

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

2924 *last* value. 

2925 

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

2927 have any. 

2928 

2929 converters (~collections.abc.Iterable[typing.Callable]): 

2930 Arbitrary number of converters. 

2931 

2932 .. versionadded:: 20.1.0 

2933 """ 

2934 

2935 def pipe_converter(val, inst, field): 

2936 for c in converters: 

2937 val = c(val, inst, field) if isinstance(c, Converter) else c(val) 

2938 

2939 return val 

2940 

2941 if not converters: 

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

2943 A = typing.TypeVar("A") 

2944 pipe_converter.__annotations__.update({"val": A, "return": A}) 

2945 else: 

2946 # Get parameter type from first converter. 

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

2948 if t: 

2949 pipe_converter.__annotations__["val"] = t 

2950 

2951 last = converters[-1] 

2952 if not PY_3_11_PLUS and isinstance(last, Converter): 

2953 last = last.__call__ 

2954 

2955 # Get return type from last converter. 

2956 rt = _AnnotationExtractor(last).get_return_type() 

2957 if rt: 

2958 pipe_converter.__annotations__["return"] = rt 

2959 

2960 return Converter(pipe_converter, takes_self=True, takes_field=True)