@@ -187,6 +187,10 @@ The module will contain the following functionality:
187
187
module, or class. This will replace :py:func: `inspect.get_annotations `. The latter
188
188
will delegate to the new function. It may eventually be deprecated, but to
189
189
minimize disruption, we do not propose an immediate deprecation.
190
+ * ``get_annotate_function() ``: A function that returns the ``__annotate__ `` function
191
+ of an object, if it has one, or ``None `` if it does not. This is usually equivalent
192
+ to accessing the ``.__annotate__ `` attribute, except in the presence of metaclasses
193
+ (see :ref: `below <pep749-metaclasses >`).
190
194
* ``Format ``: an enum that contains the possible formats of annotations. This will
191
195
replace the ``VALUE ``, ``FORWARDREF ``, and ``SOURCE `` formats in :pep: `649 `.
192
196
PEP 649 proposed to make these values global members of the :py:mod: `inspect `
@@ -390,6 +394,163 @@ More specifically:
390
394
``__dict__ ``. Writing to these attributes will directly update the ``__dict__ ``,
391
395
without affecting the wrapped callable.
392
396
397
+ .. _pep749-metaclasses :
398
+
399
+ Annotations and metaclasses
400
+ ===========================
401
+
402
+ Testing of the initial implementation of this PEP revealed serious problems with
403
+ the interaction between metaclasses and class annotations.
404
+
405
+ Pre-existing bugs
406
+ -----------------
407
+
408
+ We found several bugs in the existing behavior of ``__annotations__ `` on classes
409
+ while investigating the behaviors to be specified in this PEP. Fixing these bugs
410
+ on Python 3.13 and earlier is outside the scope of this PEP, but they are noted here
411
+ to explain the corner cases that need to be dealt with.
412
+
413
+ For context, on Python 3.10 through 3.13 the ``__annotations__ `` dictionary is
414
+ placed in the class namespace if the class has any annotations. If it does not,
415
+ there is no ``__annotations__ `` class dictionary key when the class is created,
416
+ but accessing ``cls.__annotations__ `` invokes a descriptor defined on ``type ``
417
+ that returns an empty dictionary and stores it in the class dictionary.
418
+ :py:ref: `Static types <static-types >` are an exception: they never have
419
+ annotations, and accessing ``.__annotations__ `` raises :py:exc: `AttributeError `.
420
+ On Python 3.9 and earlier, the behavior was different; see
421
+ `gh-88067 <https://github.com/python/cpython/issues/88067 >`__.
422
+
423
+ The following code fails identically on Python 3.10 through 3.13::
424
+
425
+ class Meta(type): pass
426
+
427
+ class X(metaclass=Meta):
428
+ a: str
429
+
430
+ class Y(X): pass
431
+
432
+ Meta.__annotations__ # important
433
+ assert Y.__annotations__ == {}, Y.__annotations__ # fails: {'a': <class 'str'>}
434
+
435
+ If the annotations on the metaclass ``Meta `` are accessed before the annotations
436
+ on ``Y ``, then the annotations for the base class ``X `` are leaked to ``Y ``.
437
+ However, if the metaclass's annotations are *not * accessed (i.e., the line ``Meta.__annotations__ ``
438
+ above is removed), then the annotations for ``Y `` are correctly empty.
439
+
440
+ Similarly, annotations from annotated metaclasses leak to unannotated
441
+ classes that are instances of the metaclass::
442
+
443
+ class Meta(type):
444
+ a: str
445
+
446
+ class X(metaclass=Meta):
447
+ pass
448
+
449
+ assert X.__annotations__ == {}, X.__annotations__ # fails: {'a': <class 'str'>}
450
+
451
+ The reason for these behaviors is that if the metaclass contains an
452
+ ``__annotations__ `` entry in its class dictionary, this prevents
453
+ instances of the metaclass from using the ``__annotations__ `` data descriptor
454
+ on the base :py:class: `type ` class. In the first case, accessing ``Meta.__annotations__ ``
455
+ sets ``Meta.__dict__["__annotations__"] = {} `` as a side effect. Then, looking
456
+ up the ``__annotations__ `` attribute on ``Y `` first sees the metaclass attribute,
457
+ but skips it because it is a data descriptor. Next, it looks in the class dictionaries
458
+ of the classes in its method resolution order (MRO), finds ``X.__annotations__ ``,
459
+ and returns it. In the second example, there are no annotations
460
+ anywhere in the MRO, so ``type.__getattribute__ `` falls back to
461
+ returning the metaclass attribute.
462
+
463
+ Metaclass behavior with PEP 649
464
+ -------------------------------
465
+
466
+ With :pep: `649 `, the behavior of accessing the ``.__annotations__ `` attribute
467
+ on classes when metaclasses are involved becomes even more erratic, because now
468
+ ``__annotations__ `` is only lazily added to the class dictionary even for classes
469
+ with annotations. The new ``__annotate__ `` attribute is also lazily created
470
+ on classes without annotations, which causes further misbehaviors when
471
+ metaclasses are involved.
472
+
473
+ The cause of these problems is that we set the ``__annotate__ `` and ``__annotations__ ``
474
+ class dictionary entries only under some circumstances, and rely on descriptors
475
+ defined on :py:class: `type ` to fill them in if they are not set. When normal
476
+ attribute lookup is used, this approach breaks down in the presence of
477
+ metaclasses, because entries in the metaclass's own class dictionary can render
478
+ the descriptors invisible.
479
+
480
+ While we considered several approaches that would allow ``cls.__annotations__ ``
481
+ and ``cls.__annotate__ `` to work reliably when ``cls `` is a type with a custom
482
+ metaclass, any such approach would expose significant complexity to advanced users.
483
+ Instead, we recommend a simpler approach that confines the complexity to the
484
+ ``annotationlib `` module: in ``annotationlib.get_annotations ``, we bypass normal
485
+ attribute lookup by using the ``type.__annotations__ `` descriptor directly.
486
+
487
+ Specification
488
+ -------------
489
+
490
+ Users should always use ``annotationlib.get_annotations `` to access the
491
+ annotations of a class object, and ``annotationlib.get_annotate_function ``
492
+ to access the ``__annotate__ `` function. These functions will return only
493
+ the class's own annotations, even when metaclasses are involved.
494
+
495
+ The behavior of accessing the ``__annotations__ `` and ``__annotate__ ``
496
+ attributes on classes with a metaclass other than ``builtins.type `` is
497
+ unspecified. The documentation should warn against direct use of these
498
+ attributes and recommend using the ``annotationlib `` module instead.
499
+
500
+ Similarly, the presence of ``__annotations__ `` and ``__annotate__ `` keys
501
+ in the class dictionary is an implementation detail and should not be relied
502
+ upon.
503
+
504
+ Rejected alternatives
505
+ ---------------------
506
+
507
+ We considered two broad approaches for dealing with the behavior
508
+ of the ``__annotations__ `` and ``__annotate__ `` entries in classes:
509
+
510
+ * Ensure that the entry is *always * present in the class dictionary, even if it
511
+ is empty or has not yet been evaluated. This means we do not have to rely on
512
+ the descriptors defined on :py:class: `type ` to fill in the field, and
513
+ therefore the metaclass's attributes will not interfere. (Prototype
514
+ in `gh-120719 <https://github.com/python/cpython/pull/120719 >`__.)
515
+ * Ensure that the entry is *never * present in the class dictionary, or at least
516
+ never added by logic in the language core. This means that the descriptors
517
+ on :py:class: `type ` will always be used, without interference from the metaclass.
518
+ (Prototype in `gh-120816 <https://github.com/python/cpython/pull/120816 >`__.)
519
+
520
+ Alex Waygood suggested an implementation using the first approach. When a
521
+ heap type (such as a class created through the ``class `` statement) is created,
522
+ ``cls.__dict__["__annotations__"] `` is set to a special descriptor.
523
+ On ``__get__ ``, the descriptor evaluates the annotations by calling ``__annotate__ ``
524
+ and returning the result. The annotations dictionary is cached within the
525
+ descriptor instance. The descriptor also behaves like a mapping,
526
+ so that code that uses ``cls.__dict__["__annotations__"] `` will still usually
527
+ work: treating the object as a mapping will evaluate the annotations and behave
528
+ as if the descriptor itself was the annotations dictionary. (Code that assumes
529
+ that ``cls.__dict__["__annotations__"] `` is specifically an instance of ``dict ``
530
+ may break, however.)
531
+
532
+ This approach is also straightforward to implement for ``__annotate__ ``: this
533
+ attribute is already always set for classes with annotations, and we can set
534
+ it explicitly to ``None `` for classes without annotations.
535
+
536
+ While this approach would fix the known edge cases with metaclasses, it
537
+ introduces significant complexity to all classes, including a new built-in type
538
+ (for the annotations descriptor) with unusual behavior.
539
+
540
+ The alternative approach would be to never set ``__dict__["__annotations__"] ``
541
+ and use some other storage to store the cached annotations. This behavior
542
+ change would have to apply even to classes defined under
543
+ ``from __future__ import annotations ``, because otherwise there could be buggy
544
+ behavior if a class is defined without ``from __future__ import annotations ``
545
+ but its metaclass does have the future enabled. As :pep: `649 ` previously noted,
546
+ removing ``__annotations__ `` from class dictionaries also has backwards compatibility
547
+ implications: ``cls.__dict__.get("__annotations__") `` is a common idiom to
548
+ retrieve annotations.
549
+
550
+ This approach would also mean that accessing ``.__annotations__ `` on an instance
551
+ of an annotated class no longer works. While this behavior is not documented,
552
+ it is a long-standing feature of Python and is relied upon by some users.
553
+
393
554
Remove code flag for marking ``__annotate__ `` functions
394
555
=======================================================
395
556
@@ -695,7 +856,9 @@ Acknowledgments
695
856
First of all, I thank Larry Hastings for writing :pep: `649 `. This PEP modifies some of his
696
857
initial decisions, but the overall design is still his.
697
858
698
- I thank Carl Meyer and Alex Waygood for feedback on early drafts of this PEP.
859
+ I thank Carl Meyer and Alex Waygood for feedback on early drafts of this PEP. Alex Waygood,
860
+ Alyssa Coghlan, and David Ellis provided insightful feedback and suggestions on the
861
+ interaction between metaclasses and ``__annotations__ ``.
699
862
700
863
Appendix
701
864
========
0 commit comments