@@ -35,6 +35,8 @@ specification:
3535 (which were added by :pep: `695 ` and :pep: `696 `) using PEP 649-like semantics.
3636* The ``SOURCE `` format is renamed to ``STRING `` to improve clarity and reduce the risk of
3737 user confusion.
38+ * Conditionally defined class and module annotations are handled correctly.
39+ * Accessing annotations on a partially executed module will raise :py:exc: `RuntimeError `.
3840
3941Motivation
4042==========
@@ -752,6 +754,125 @@ PEP, the four supported formats are now:
752754- ``FORWARDREF ``: replaces undefined names with ``ForwardRef `` objects.
753755- ``STRING ``: returns strings, attempts to recreate code close to the original source.
754756
757+ Conditionally defined annotations
758+ =================================
759+
760+ :pep: `649 ` does not support annotations that are conditionally defined
761+ in the body of a class or module:
762+
763+ It's currently possible to set module and class attributes with
764+ annotations inside an ``if `` or ``try `` statement, and it works
765+ as one would expect. It's untenable to support this behavior
766+ when this PEP is active.
767+
768+ However, the maintainer of the widely used SQLAlchemy library
769+ `reported <https://github.com/python/cpython/issues/130881 >`__
770+ that this pattern is actually common and important:
771+
772+ .. code :: python
773+
774+ from typing import TYPE_CHECKING
775+
776+ if TYPE_CHECKING :
777+ from some_module import SpecialType
778+
779+ class MyClass :
780+ somevalue: str
781+ if TYPE_CHECKING :
782+ someothervalue: SpecialType
783+
784+ Under the behavior envisioned in :pep: `649 `, the ``__annotations__ `` for
785+ ``MyClass `` would contain keys for both ``somevalue `` and ``someothervalue ``.
786+
787+ Fortunately, there is a tractable implementation strategy for making
788+ this code behave as expected again. This strategy relies on a few fortuitous
789+ circumstances:
790+
791+ * This behavior change is only relevant to module and class annotations,
792+ because annotations in local scopes are ignored.
793+ * Module and class bodies are only executed once.
794+ * The annotations of a class are not externally visible until execution of the
795+ class body is complete. For modules, this is not quite true, because a partially
796+ executed module can be visible to other imported modules, but this is an
797+ unusual case that is problematic for other reasons (see the next section).
798+
799+ This allows the following implementation strategy:
800+
801+ * Each annotated assignment is assigned a unique identifier (e.g., an integer).
802+ * During execution of a class or module body, a set, initially empty, is created
803+ to hold the identifiers of the annotations that have been defined.
804+ * When an annotated assignment is executed, its identifier is added to the set.
805+ * The generated ``__annotate__ `` function uses the set to determine
806+ which annotations were defined in the class or module body, and return only those.
807+
808+ This was implemented in :gh:pr: `130935 `.
809+
810+ Specification
811+ -------------
812+
813+ For classes and modules, the ``__annotate__ `` function will return only
814+ annotations for those assignments that were executed when the class or module body
815+ was executed.
816+
817+ Caching of annotations on partially executed modules
818+ ====================================================
819+
820+ :pep: `649 ` specifies that the value of the ``__annotations__ `` attribute
821+ on classes and modules is determined on first access by calling the
822+ ``__annotate__ `` function, and then it is cached for later access.
823+ This is correct in most cases and preserves compatibility, but there is
824+ one edge case where it can lead to surprising behavior: partially executed
825+ modules.
826+
827+ Consider this example:
828+
829+ .. code :: python
830+
831+ # recmod/__main__.py
832+ from . import a
833+ print (" in __main__:" , a.__annotations__ )
834+
835+ # recmod/a.py
836+ v1: int
837+ from . import b
838+ v2: int
839+
840+ # recmod/b.py
841+ from . import a
842+ print (" in b:" , a.__annotations__ )
843+
844+ Note that while ``.py `` executes, the ``recmod.a `` module is defined,
845+ but has not yet finished execution.
846+
847+ On 3.13, this produces:
848+
849+ .. code :: shell
850+
851+ $ python3.13 -m recmod
852+ in b: {' v1' : < class ' int' > }
853+ in __main__: {' v1' : < class ' int' > , ' v2' : < class ' int' > }
854+
855+ But with :pep: `649 ` implemented as originally proposed, this would
856+ print an empty dictionary twice, because the ``__annotate__ `` function
857+ is set only when module execution is complete. This is obviously
858+ unintuitive.
859+
860+ See :gh:issue: `130907 ` for implementation.
861+
862+ Specification
863+ -------------
864+
865+ Accessing ``__annotations__ `` on a partially executed module will
866+ raise :py:exc: `RuntimeError `. After module execution is complete,
867+ accessing ``__annotations__ `` will execute and cache the annotations as
868+ normal.
869+
870+ This is technically a compatibility break for code that introspects
871+ annotations on partially executed modules, but that should be a rare
872+ case. It is better to couple this compatibility break with the other
873+ changes in annotations behavior introduced by this PEP and :pep: `649 `.
874+
875+
755876Miscellaneous implementation details
756877====================================
757878
0 commit comments