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

901 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# SPDX-License-Identifier: MIT 

2 

3import copy 

4import enum 

5import linecache 

6import sys 

7import types 

8import typing 

9 

10from 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. 

14from . import _compat, _config, setters 

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

16from .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 

47class _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 

67NOTHING = _Nothing.NOTHING 

68""" 

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

70""" 

71 

72 

73class _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 

89def 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 

290def _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 

298def _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 

327def _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 

369def _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 

386def _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 

402def _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 

412def _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 

443def _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 

471def _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 

585if 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 

600else: 

601 

602 def _frozen_setattrs(self, name, value): 

603 """ 

604 Attached to frozen classes as __setattr__. 

605 """ 

606 raise FrozenInstanceError() 

607 

608 

609def _frozen_delattrs(self, name): 

610 """ 

611 Attached to frozen classes as __delattr__. 

612 """ 

613 raise FrozenInstanceError() 

614 

615 

616class _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 

1100def _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 

1126def _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 

1167def _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 

1193def 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""" 

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

1601*attrs*. 

1602""" 

1603 

1604 

1605def _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 

1613def _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 

1623def _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 

1697def _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 

1705def _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 

1724def _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 

1764def _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 

1820def _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 

1833def _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 

1890def _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 

1901def 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 

1927def 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 

1950def 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 

1967def _is_slot_cls(cls): 

1968 return "__slots__" in cls.__dict__ 

1969 

1970 

1971def _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 

1978def _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 

2053def _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 

2060def _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 

2072def _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 

2083def _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 

2098def _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 

2379def _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 

2390class 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 

2604Attribute = _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 

2613class _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 

2762class 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 

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

2818 

2819 

2820def 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) 

2896class _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 

2908def 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 

2929def 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