Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/attr/_next_gen.py: 69%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

45 statements  

1# SPDX-License-Identifier: MIT 

2 

3""" 

4These are keyword-only APIs that call `attr.s` and `attr.ib` with different 

5default values. 

6""" 

7 

8from functools import partial 

9 

10from . import setters 

11from ._funcs import asdict as _asdict 

12from ._funcs import astuple as _astuple 

13from ._make import ( 

14 _DEFAULT_ON_SETATTR, 

15 NOTHING, 

16 _frozen_setattrs, 

17 attrib, 

18 attrs, 

19) 

20from .exceptions import NotAnAttrsClassError, UnannotatedAttributeError 

21 

22 

23def define( 

24 maybe_cls=None, 

25 *, 

26 these=None, 

27 repr=None, 

28 unsafe_hash=None, 

29 hash=None, 

30 init=None, 

31 slots=True, 

32 frozen=False, 

33 weakref_slot=True, 

34 str=False, 

35 auto_attribs=None, 

36 kw_only=False, 

37 cache_hash=False, 

38 auto_exc=True, 

39 eq=None, 

40 order=False, 

41 auto_detect=True, 

42 getstate_setstate=None, 

43 on_setattr=None, 

44 field_transformer=None, 

45 match_args=True, 

46 force_kw_only=False, 

47): 

48 r""" 

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

50 :term:`fields <field>` specified using :doc:`type annotations <types>`, 

51 `field()` calls, or the *these* argument. 

52 

53 Since *attrs* patches or replaces an existing class, you cannot use 

54 `object.__init_subclass__` with *attrs* classes, because it runs too early. 

55 As a replacement, you can define ``__attrs_init_subclass__`` on your class. 

56 It will be called by *attrs* classes that subclass it after they're 

57 created. See also :ref:`init-subclass`. 

58 

59 Args: 

60 slots (bool): 

61 Create a :term:`slotted class <slotted classes>` that's more 

62 memory-efficient. Slotted classes are generally superior to the 

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

64 so we encourage you to read the :term:`glossary entry <slotted 

65 classes>`. 

66 

67 auto_detect (bool): 

68 Instead of setting the *init*, *repr*, *eq*, and *hash* arguments 

69 explicitly, assume they are set to True **unless any** of the 

70 involved methods for one of the arguments is implemented in the 

71 *current* class (meaning, it is *not* inherited from some base 

72 class). 

73 

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

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

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

77 sensible ``__ne__`` by default, so it *should* be enough to only 

78 implement ``__eq__`` in most cases). 

79 

80 Passing :data:`True` or :data:`False` to *init*, *repr*, *eq*, or *hash* 

81 overrides whatever *auto_detect* would determine. 

82 

83 auto_exc (bool): 

84 If the class subclasses `BaseException` (which implicitly includes 

85 any subclass of any exception), the following happens to behave 

86 like a well-behaved Python exception class: 

87 

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

89 instances compare and hash by the instance's ids [#]_ , 

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

91 default value are additionally available as a tuple in the 

92 ``args`` attribute, 

93 - the value of *str* is ignored leaving ``__str__`` to base 

94 classes. 

95 

96 .. [#] 

97 Note that *attrs* will *not* remove existing implementations of 

98 ``__hash__`` or the equality methods. It just won't add own 

99 ones. 

100 

101 on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): 

102 A callable that is run whenever the user attempts to set an 

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

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

105 arguments as validators: the instance, the attribute that is being 

106 modified, and the new value. 

107 

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

109 of the callable. 

110 

111 If the callable is a generator, it may yield exactly once and runs 

112 before and after the assignment and its yield value is used as the 

113 new value. 

114 

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

116 an `attrs.setters.pipe`. 

117 

118 If left None, the default behavior is to run converters and 

119 validators whenever an attribute is set. 

120 

121 init (bool): 

122 Create a ``__init__`` method that initializes the *attrs* 

123 attributes. Leading underscores are stripped for the argument name, 

124 unless an alias is set on the attribute. 

125 

126 .. seealso:: 

127 `init` shows advanced ways to customize the generated 

128 ``__init__`` method, including executing code before and after. 

129 

130 repr(bool): 

131 Create a ``__repr__`` method with a human readable representation 

132 of *attrs* attributes. 

133 

134 str (bool): 

135 Create a ``__str__`` method that is identical to ``__repr__``. This 

136 is usually not necessary except for `Exception`\ s. 

137 

138 eq (bool | None): 

139 If True or None (default), add ``__eq__`` and ``__ne__`` methods 

140 that check two instances for equality. 

141 

142 .. seealso:: 

143 `comparison` describes how to customize the comparison behavior 

144 going as far comparing NumPy arrays. 

145 

146 order (bool | None): 

147 If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` 

148 methods that behave like *eq* above and allow instances to be 

149 ordered. 

150 

151 They compare the instances as if they were tuples of their *attrs* 

152 attributes if and only if the types of both classes are 

153 *identical*. 

154 

155 If `None` mirror value of *eq*. 

156 

157 .. seealso:: `comparison` 

158 

159 unsafe_hash (bool | None): 

160 If None (default), the ``__hash__`` method is generated according 

161 how *eq* and *frozen* are set. 

162 

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

164 you. 

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

166 to None, marking it unhashable (which it is). 

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

168 the ``__hash__`` method of the base class will be used. If the 

169 base class is `object`, this means it will fall back to id-based 

170 hashing. 

171 

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

173 *attrs* to create one (for example, if the class is immutable even 

174 though you didn't freeze it programmatically) by passing True or 

175 not. Both of these cases are rather special and should be used 

176 carefully. 

177 

178 .. seealso:: 

179 

180 - Our documentation on `hashing`, 

181 - Python's documentation on `object.__hash__`, 

182 - and the `GitHub issue that led to the default \ behavior 

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

184 details. 

185 

186 hash (bool | None): 

187 Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence. 

188 

189 cache_hash (bool): 

190 Ensure that the object's hash code is computed only once and stored 

191 on the object. If this is set to True, hashing must be either 

192 explicitly or implicitly enabled for this class. If the hash code 

193 is cached, avoid any reassignments of fields involved in hash code 

194 computation or mutations of the objects those fields point to after 

195 object creation. If such changes occur, the behavior of the 

196 object's hash code is undefined. 

197 

198 frozen (bool): 

199 Make instances immutable after initialization. If someone attempts 

200 to modify a frozen instance, `attrs.exceptions.FrozenInstanceError` 

201 is raised. 

202 

203 .. note:: 

204 

205 1. This is achieved by installing a custom ``__setattr__`` 

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

207 

208 2. True immutability is impossible in Python. 

209 

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

211 <how-frozen>` when initializing new instances. In other 

212 words: ``__init__`` is slightly slower with ``frozen=True``. 

213 

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

215 ``__attrs_post_init__`` or a self-written ``__init__``. You 

216 can circumvent that limitation by using 

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

218 

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

220 

221 kw_only (bool): 

222 Make attributes keyword-only in the generated ``__init__`` (if 

223 *init* is False, this parameter is ignored). Attributes that 

224 explicitly set ``kw_only=False`` are not affected; base class 

225 attributes are also not affected. 

226 

227 Also see *force_kw_only*. 

228 

229 weakref_slot (bool): 

230 Make instances weak-referenceable. This has no effect unless 

231 *slots* is True. 

232 

233 field_transformer (~typing.Callable | None): 

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

235 fields right before *attrs* finalizes the class. You can use this, 

236 for example, to automatically add converters or validators to 

237 fields based on their types. 

238 

239 .. seealso:: `transform-fields` 

240 

241 match_args (bool): 

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

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

244 non-keyword-only ``__init__`` parameter names. 

245 

246 force_kw_only (bool): 

247 A back-compat flag for restoring pre-25.4.0 behavior. If True and 

248 ``kw_only=True``, all attributes are made keyword-only, including 

249 base class attributes, and those set to ``kw_only=False`` at the 

250 attribute level. Defaults to False. 

251 

252 See also `issue #980 

253 <https://github.com/python-attrs/attrs/issues/980>`_. 

254 

255 getstate_setstate (bool | None): 

256 .. note:: 

257 

258 This is usually only interesting for slotted classes and you 

259 should probably just set *auto_detect* to True. 

260 

261 If True, ``__getstate__`` and ``__setstate__`` are generated and 

262 attached to the class. This is necessary for slotted classes to be 

263 pickleable. If left None, it's True by default for slotted classes 

264 and False for dict classes. 

265 

266 If *auto_detect* is True, and *getstate_setstate* is left None, and 

267 **either** ``__getstate__`` or ``__setstate__`` is detected 

268 directly on the class (meaning: not inherited), it is set to False 

269 (this is usually what you want). 

270 

271 auto_attribs (bool | None): 

272 If True, look at type annotations to determine which attributes to 

273 use, like `dataclasses`. If False, it will only look for explicit 

274 :func:`field` class attributes, like classic *attrs*. 

275 

276 If left None, it will guess: 

277 

278 1. If any attributes are annotated and no unannotated 

279 `attrs.field`\ s are found, it assumes *auto_attribs=True*. 

280 2. Otherwise it assumes *auto_attribs=False* and tries to collect 

281 `attrs.field`\ s. 

282 

283 If *attrs* decides to look at type annotations, **all** fields 

284 **must** be annotated. If *attrs* encounters a field that is set to 

285 a :func:`field` / `attr.ib` but lacks a type annotation, an 

286 `attrs.exceptions.UnannotatedAttributeError` is raised. Use 

287 ``field_name: typing.Any = field(...)`` if you don't want to set a 

288 type. 

289 

290 .. warning:: 

291 

292 For features that use the attribute name to create decorators 

293 (for example, :ref:`validators <validators>`), you still *must* 

294 assign :func:`field` / `attr.ib` to them. Otherwise Python will 

295 either not find the name or try to use the default value to 

296 call, for example, ``validator`` on it. 

297 

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

299 neither annotated nor set to an `field()` are **ignored**. 

300 

301 these (dict[str, object]): 

302 A dictionary of name to the (private) return value of `field()` 

303 mappings. This is useful to avoid the definition of your attributes 

304 within the class body because you can't (for example, if you want 

305 to add ``__repr__`` methods to Django models) or don't want to. 

306 

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

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

309 

310 The order is deduced from the order of the attributes inside 

311 *these*. 

312 

313 Arguably, this is a rather obscure feature. 

314 

315 .. versionadded:: 20.1.0 

316 .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. 

317 .. versionadded:: 22.2.0 

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

319 .. versionchanged:: 24.1.0 

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

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

322 uncomparable values like `math.nan`. 

323 .. versionadded:: 24.1.0 

324 If a class has an *inherited* classmethod called 

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

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

327 .. versionadded:: 24.3.0 

328 Unless already present, a ``__replace__`` method is automatically 

329 created for `copy.replace` (Python 3.13+ only). 

330 .. versionchanged:: 25.4.0 

331 *kw_only* now only applies to attributes defined in the current class, 

332 and respects attribute-level ``kw_only=False`` settings. 

333 .. versionadded:: 25.4.0 

334 Added *force_kw_only* to go back to the previous *kw_only* behavior. 

335 .. versionchanged:: 26.2.0 

336 *on_setattr* hooks can now be generator functions that yield exactly 

337 once. 

338 

339 .. note:: 

340 

341 The main differences to the classic `attr.s` are: 

342 

343 - Automatically detect whether or not *auto_attribs* should be `True` 

344 (c.f. *auto_attribs* parameter). 

345 - Converters and validators run when attributes are set by default -- 

346 if *frozen* is `False`. 

347 - *slots=True* 

348 

349 Usually, this has only upsides and few visible effects in everyday 

350 programming. But it *can* lead to some surprising behaviors, so 

351 please make sure to read :term:`slotted classes`. 

352 

353 - *auto_exc=True* 

354 - *auto_detect=True* 

355 - *order=False* 

356 - *force_kw_only=False* 

357 - Some options that were only relevant on Python 2 or were kept around 

358 for backwards-compatibility have been removed. 

359 

360 """ 

361 

362 def do_it(cls, auto_attribs): 

363 return attrs( 

364 maybe_cls=cls, 

365 these=these, 

366 repr=repr, 

367 hash=hash, 

368 unsafe_hash=unsafe_hash, 

369 init=init, 

370 slots=slots, 

371 frozen=frozen, 

372 weakref_slot=weakref_slot, 

373 str=str, 

374 auto_attribs=auto_attribs, 

375 kw_only=kw_only, 

376 cache_hash=cache_hash, 

377 auto_exc=auto_exc, 

378 eq=eq, 

379 order=order, 

380 auto_detect=auto_detect, 

381 collect_by_mro=True, 

382 getstate_setstate=getstate_setstate, 

383 on_setattr=on_setattr, 

384 field_transformer=field_transformer, 

385 match_args=match_args, 

386 force_kw_only=force_kw_only, 

387 ) 

388 

389 def wrap(cls): 

390 """ 

391 Making this a wrapper ensures this code runs during class creation. 

392 

393 We also ensure that frozen-ness of classes is inherited. 

394 """ 

395 nonlocal frozen, on_setattr 

396 

397 had_on_setattr = on_setattr not in (None, setters.NO_OP) 

398 

399 # By default, mutable classes convert & validate on setattr. 

400 if frozen is False and on_setattr is None: 

401 on_setattr = _DEFAULT_ON_SETATTR 

402 

403 # However, if we subclass a frozen class, we inherit the immutability 

404 # and disable on_setattr. 

405 for base_cls in cls.__bases__: 

406 if base_cls.__setattr__ is _frozen_setattrs: 

407 if had_on_setattr: 

408 msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)." 

409 raise ValueError(msg) 

410 

411 on_setattr = setters.NO_OP 

412 break 

413 

414 if auto_attribs is not None: 

415 return do_it(cls, auto_attribs) 

416 

417 try: 

418 return do_it(cls, True) 

419 except UnannotatedAttributeError: 

420 return do_it(cls, False) 

421 

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

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

424 if maybe_cls is None: 

425 return wrap 

426 

427 return wrap(maybe_cls) 

428 

429 

430mutable = define 

431frozen = partial(define, frozen=True, on_setattr=None) 

432 

433 

434def field( 

435 *, 

436 default=NOTHING, 

437 validator=None, 

438 repr=True, 

439 hash=None, 

440 init=True, 

441 metadata=None, 

442 type=None, 

443 converter=None, 

444 factory=None, 

445 kw_only=None, 

446 eq=None, 

447 order=None, 

448 on_setattr=None, 

449 alias=None, 

450): 

451 """ 

452 Create a new :term:`field` / :term:`attribute` on a class. 

453 

454 .. warning:: 

455 

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

457 `attrs.define` (or similar)! 

458 

459 Args: 

460 default: 

461 A value that is used if an *attrs*-generated ``__init__`` is used 

462 and no value is passed while instantiating or the attribute is 

463 excluded using ``init=False``. 

464 

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

466 be used to construct a new value (useful for mutable data types 

467 like lists or dicts). 

468 

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

470 value *must* be supplied when instantiating; otherwise a 

471 `TypeError` will be raised. 

472 

473 .. seealso:: `defaults` 

474 

475 factory (~typing.Callable): 

476 Syntactic sugar for ``default=attr.Factory(factory)``. 

477 

478 validator (~typing.Callable | list[~typing.Callable]): 

479 Callable that is called by *attrs*-generated ``__init__`` methods 

480 after the instance has been initialized. They receive the 

481 initialized instance, the :func:`~attrs.Attribute`, and the passed 

482 value. 

483 

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

485 an exception itself. 

486 

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

488 all pass. 

489 

490 Validators can be globally disabled and re-enabled using 

491 `attrs.validators.get_disabled` / `attrs.validators.set_disabled`. 

492 

493 The validator can also be set using decorator notation as shown 

494 below. 

495 

496 .. seealso:: :ref:`validators` 

497 

498 repr (bool | ~typing.Callable): 

499 Include this attribute in the generated ``__repr__`` method. If 

500 True, include the attribute; if False, omit it. By default, the 

501 built-in ``repr()`` function is used. To override how the attribute 

502 value is formatted, pass a ``callable`` that takes a single value 

503 and returns a string. Note that the resulting string is used as-is, 

504 which means it will be used directly *instead* of calling 

505 ``repr()`` (the default). 

506 

507 eq (bool | ~typing.Callable): 

508 If True (default), include this attribute in the generated 

509 ``__eq__`` and ``__ne__`` methods that check two instances for 

510 equality. To override how the attribute value is compared, pass a 

511 callable that takes a single value and returns the value to be 

512 compared. 

513 

514 .. seealso:: `comparison` 

515 

516 order (bool | ~typing.Callable): 

517 If True (default), include this attributes in the generated 

518 ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To 

519 override how the attribute value is ordered, pass a callable that 

520 takes a single value and returns the value to be ordered. 

521 

522 .. seealso:: `comparison` 

523 

524 hash (bool | None): 

525 Include this attribute in the generated ``__hash__`` method. If 

526 None (default), mirror *eq*'s value. This is the correct behavior 

527 according the Python spec. Setting this value to anything else 

528 than None is *discouraged*. 

529 

530 .. seealso:: `hashing` 

531 

532 init (bool): 

533 Include this attribute in the generated ``__init__`` method. 

534 

535 It is possible to set this to False and set a default value. In 

536 that case this attributed is unconditionally initialized with the 

537 specified default value or factory. 

538 

539 .. seealso:: `init` 

540 

541 converter (typing.Callable | Converter): 

542 A callable that is called by *attrs*-generated ``__init__`` methods 

543 to convert attribute's value to the desired format. 

544 

545 If a vanilla callable is passed, it is given the passed-in value as 

546 the only positional argument. It is possible to receive additional 

547 arguments by wrapping the callable in a `Converter`. 

548 

549 Either way, the returned value will be used as the new value of the 

550 attribute. The value is converted before being passed to the 

551 validator, if any. 

552 

553 .. seealso:: :ref:`converters` 

554 

555 metadata (dict | None): 

556 An arbitrary mapping, to be used by third-party code. 

557 

558 .. seealso:: `extending-metadata`. 

559 

560 type (type): 

561 The type of the attribute. Nowadays, the preferred method to 

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

563 This argument is provided for backwards-compatibility and for usage 

564 with `make_class`. Regardless of the approach used, the type will 

565 be stored on ``Attribute.type``. 

566 

567 Please note that *attrs* doesn't do anything with this metadata by 

568 itself. You can use it as part of your own code or for `static type 

569 checking <types>`. 

570 

571 kw_only (bool | None): 

572 Make this attribute keyword-only in the generated ``__init__`` (if 

573 *init* is False, this parameter is ignored). If None (default), 

574 mirror the setting from `attrs.define`. 

575 

576 on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): 

577 Allows to overwrite the *on_setattr* setting from `attr.s`. If left 

578 None, the *on_setattr* value from `attrs.define` is used. Set to 

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

580 attribute -- regardless of the setting in `define()`. 

581 

582 May be a generator function that yields exactly once and runs 

583 before and after the assignment and its yield value is used as the 

584 new value. 

585 

586 alias (str | None): 

587 Override this attribute's parameter name in the generated 

588 ``__init__`` method. If left None, default to ``name`` stripped 

589 of leading underscores. See `private-attributes`. 

590 

591 .. versionadded:: 20.1.0 

592 .. versionchanged:: 21.1.0 

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

594 .. versionadded:: 22.2.0 *alias* 

595 .. versionadded:: 23.1.0 

596 The *type* parameter has been re-added; mostly for `attrs.make_class`. 

597 Please note that type checkers ignore this metadata. 

598 .. versionchanged:: 25.4.0 

599 *kw_only* can now be None, and its default is also changed from False to 

600 None. 

601 .. versionchanged:: 26.2.0 

602 *on_setattr* hooks can now be generator functions that yield exactly 

603 once. 

604 

605 .. seealso:: 

606 

607 `attr.ib` 

608 """ 

609 return attrib( 

610 default=default, 

611 validator=validator, 

612 repr=repr, 

613 hash=hash, 

614 init=init, 

615 metadata=metadata, 

616 type=type, 

617 converter=converter, 

618 factory=factory, 

619 kw_only=kw_only, 

620 eq=eq, 

621 order=order, 

622 on_setattr=on_setattr, 

623 alias=alias, 

624 ) 

625 

626 

627def asdict(inst, *, recurse=True, filter=None, value_serializer=None): 

628 """ 

629 Same as `attr.asdict`, except that collections types are always retained 

630 and dict is always used as *dict_factory*. 

631 

632 .. versionadded:: 21.3.0 

633 """ 

634 return _asdict( 

635 inst=inst, 

636 recurse=recurse, 

637 filter=filter, 

638 value_serializer=value_serializer, 

639 retain_collection_types=True, 

640 ) 

641 

642 

643def astuple(inst, *, recurse=True, filter=None): 

644 """ 

645 Same as `attr.astuple`, except that collections types are always retained 

646 and `tuple` is always used as the *tuple_factory*. 

647 

648 .. versionadded:: 21.3.0 

649 """ 

650 return _astuple( 

651 inst=inst, recurse=recurse, filter=filter, retain_collection_types=True 

652 ) 

653 

654 

655def inspect(cls): 

656 """ 

657 Inspect the class and return its effective build parameters. 

658 

659 Warning: 

660 This feature is currently **experimental** and is not covered by our 

661 strict backwards-compatibility guarantees. 

662 

663 Args: 

664 cls: The *attrs*-decorated class to inspect. 

665 

666 Returns: 

667 The effective build parameters of the class. 

668 

669 Raises: 

670 NotAnAttrsClassError: If the class is not an *attrs*-decorated class. 

671 

672 .. versionadded:: 25.4.0 

673 """ 

674 try: 

675 return cls.__dict__["__attrs_props__"] 

676 except KeyError: 

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

678 raise NotAnAttrsClassError(msg) from None