https://github.com/python/cpython/commit/922049b613d155ade4c4a8f83452767bea003a9f
commit: 922049b613d155ade4c4a8f83452767bea003a9f
branch: main
author: Jelle Zijlstra <jelle.zijls...@gmail.com>
committer: JelleZijlstra <jelle.zijls...@gmail.com>
date: 2025-04-28T06:10:28-07:00
summary:

gh-130907: Treat all module-level annotations as conditional (#131550)

files:
A Lib/test/typinganndata/partialexecution/__init__.py
A Lib/test/typinganndata/partialexecution/a.py
A Lib/test/typinganndata/partialexecution/b.py
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-03-21-08-47-36.gh-issue-130907.rGg-ge.rst
M Include/internal/pycore_compile.h
M Include/internal/pycore_instruction_sequence.h
M Include/internal/pycore_opcode_metadata.h
M Include/opcode_ids.h
M Lib/_opcode_metadata.py
M Lib/test/test_compiler_codegen.py
M Lib/test/test_dis.py
M Lib/test/test_grammar.py
M Lib/test/test_type_annotations.py
M Makefile.pre.in
M Objects/moduleobject.c
M Python/bytecodes.c
M Python/codegen.c
M Python/compile.c
M Python/flowgraph.c
M Python/instruction_sequence.c
M Python/symtable.c

diff --git a/Include/internal/pycore_compile.h 
b/Include/internal/pycore_compile.h
index b98dfb0cebbd3f..a606c2afe0a234 100644
--- a/Include/internal/pycore_compile.h
+++ b/Include/internal/pycore_compile.h
@@ -133,6 +133,8 @@ int _PyCompile_EnterScope(struct _PyCompiler *c, identifier 
name, int scope_type
 void _PyCompile_ExitScope(struct _PyCompiler *c);
 Py_ssize_t _PyCompile_AddConst(struct _PyCompiler *c, PyObject *o);
 _PyInstructionSequence *_PyCompile_InstrSequence(struct _PyCompiler *c);
+int _PyCompile_StartAnnotationSetup(struct _PyCompiler *c);
+int _PyCompile_EndAnnotationSetup(struct _PyCompiler *c);
 int _PyCompile_FutureFeatures(struct _PyCompiler *c);
 void _PyCompile_DeferredAnnotations(
     struct _PyCompiler *c, PyObject **deferred_annotations,
diff --git a/Include/internal/pycore_instruction_sequence.h 
b/Include/internal/pycore_instruction_sequence.h
index 099f2fd124007a..b5c927735374be 100644
--- a/Include/internal/pycore_instruction_sequence.h
+++ b/Include/internal/pycore_instruction_sequence.h
@@ -45,6 +45,9 @@ typedef struct instruction_sequence {
 
     /* PyList of instruction sequences of nested functions */
     PyObject *s_nested;
+
+    /* Code for creating annotations, spliced into the main sequence later */
+    struct instruction_sequence *s_annotations_code;
 } _PyInstructionSequence;
 
 typedef struct {
@@ -66,6 +69,8 @@ _PyJumpTargetLabel 
_PyInstructionSequence_NewLabel(_PyInstructionSequence *seq);
 int _PyInstructionSequence_ApplyLabelMap(_PyInstructionSequence *seq);
 int _PyInstructionSequence_InsertInstruction(_PyInstructionSequence *seq, int 
pos,
                                              int opcode, int oparg, 
_Py_SourceLocation loc);
+int _PyInstructionSequence_SetAnnotationsCode(_PyInstructionSequence *seq,
+                                              _PyInstructionSequence 
*annotations);
 int _PyInstructionSequence_AddNested(_PyInstructionSequence *seq, 
_PyInstructionSequence *nested);
 void PyInstructionSequence_Fini(_PyInstructionSequence *seq);
 
diff --git a/Include/internal/pycore_opcode_metadata.h 
b/Include/internal/pycore_opcode_metadata.h
index c43d60175d5449..3b881b30b054f1 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -20,6 +20,7 @@ extern "C" {
 #define IS_PSEUDO_INSTR(OP)  ( \
     ((OP) == LOAD_CLOSURE) || \
     ((OP) == STORE_FAST_MAYBE_NULL) || \
+    ((OP) == ANNOTATIONS_PLACEHOLDER) || \
     ((OP) == JUMP) || \
     ((OP) == JUMP_NO_INTERRUPT) || \
     ((OP) == JUMP_IF_FALSE) || \
@@ -35,6 +36,8 @@ extern int _PyOpcode_num_popped(int opcode, int oparg);
 #ifdef NEED_OPCODE_METADATA
 int _PyOpcode_num_popped(int opcode, int oparg)  {
     switch(opcode) {
+        case ANNOTATIONS_PLACEHOLDER:
+            return 0;
         case BINARY_OP:
             return 2;
         case BINARY_OP_ADD_FLOAT:
@@ -514,6 +517,8 @@ extern int _PyOpcode_num_pushed(int opcode, int oparg);
 #ifdef NEED_OPCODE_METADATA
 int _PyOpcode_num_pushed(int opcode, int oparg)  {
     switch(opcode) {
+        case ANNOTATIONS_PLACEHOLDER:
+            return 0;
         case BINARY_OP:
             return 1;
         case BINARY_OP_ADD_FLOAT:
@@ -1004,7 +1009,7 @@ enum InstructionFormat {
 };
 
 #define IS_VALID_OPCODE(OP) \
-    (((OP) >= 0) && ((OP) < 266) && \
+    (((OP) >= 0) && ((OP) < 267) && \
      (_PyOpcode_opcode_metadata[(OP)].valid_entry))
 
 #define HAS_ARG_FLAG (1)
@@ -1058,9 +1063,9 @@ struct opcode_metadata {
     uint16_t flags;
 };
 
-extern const struct opcode_metadata _PyOpcode_opcode_metadata[266];
+extern const struct opcode_metadata _PyOpcode_opcode_metadata[267];
 #ifdef NEED_OPCODE_METADATA
-const struct opcode_metadata _PyOpcode_opcode_metadata[266] = {
+const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
     [BINARY_OP] = { true, INSTR_FMT_IBC0000, HAS_ARG_FLAG | HAS_ERROR_FLAG | 
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
     [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | 
HAS_ERROR_FLAG },
     [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | 
HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
@@ -1285,6 +1290,7 @@ const struct opcode_metadata 
_PyOpcode_opcode_metadata[266] = {
     [UNPACK_SEQUENCE_TWO_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | 
HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
     [WITH_EXCEPT_START] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | 
HAS_ESCAPES_FLAG },
     [YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
+    [ANNOTATIONS_PLACEHOLDER] = { true, -1, HAS_PURE_FLAG },
     [JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | 
HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [JUMP_IF_FALSE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | 
HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [JUMP_IF_TRUE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG 
| HAS_ESCAPES_FLAG },
@@ -1491,9 +1497,10 @@ _PyOpcode_macro_expansion[256] = {
 };
 #endif // NEED_OPCODE_METADATA
 
-extern const char *_PyOpcode_OpName[266];
+extern const char *_PyOpcode_OpName[267];
 #ifdef NEED_OPCODE_METADATA
-const char *_PyOpcode_OpName[266] = {
+const char *_PyOpcode_OpName[267] = {
+    [ANNOTATIONS_PLACEHOLDER] = "ANNOTATIONS_PLACEHOLDER",
     [BINARY_OP] = "BINARY_OP",
     [BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
     [BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
@@ -2025,11 +2032,12 @@ struct pseudo_targets {
     uint8_t as_sequence;
     uint8_t targets[4];
 };
-extern const struct pseudo_targets _PyOpcode_PseudoTargets[10];
+extern const struct pseudo_targets _PyOpcode_PseudoTargets[11];
 #ifdef NEED_OPCODE_METADATA
-const struct pseudo_targets _PyOpcode_PseudoTargets[10] = {
+const struct pseudo_targets _PyOpcode_PseudoTargets[11] = {
     [LOAD_CLOSURE-256] = { 0, { LOAD_FAST, 0, 0, 0 } },
     [STORE_FAST_MAYBE_NULL-256] = { 0, { STORE_FAST, 0, 0, 0 } },
+    [ANNOTATIONS_PLACEHOLDER-256] = { 0, { NOP, 0, 0, 0 } },
     [JUMP-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD, 0, 0 } },
     [JUMP_NO_INTERRUPT-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD_NO_INTERRUPT, 
0, 0 } },
     [JUMP_IF_FALSE-256] = { 1, { COPY, TO_BOOL, POP_JUMP_IF_FALSE, 0 } },
@@ -2043,7 +2051,7 @@ const struct pseudo_targets _PyOpcode_PseudoTargets[10] = 
{
 #endif // NEED_OPCODE_METADATA
 static inline bool
 is_pseudo_target(int pseudo, int target) {
-    if (pseudo < 256 || pseudo >= 266) {
+    if (pseudo < 256 || pseudo >= 267) {
         return false;
     }
     for (int i = 0; _PyOpcode_PseudoTargets[pseudo-256].targets[i]; i++) {
diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h
index 898dc580f4148e..d93f028b732406 100644
--- a/Include/opcode_ids.h
+++ b/Include/opcode_ids.h
@@ -234,16 +234,17 @@ extern "C" {
 #define INSTRUMENTED_JUMP_BACKWARD             253
 #define INSTRUMENTED_LINE                      254
 #define ENTER_EXECUTOR                         255
-#define JUMP                                   256
-#define JUMP_IF_FALSE                          257
-#define JUMP_IF_TRUE                           258
-#define JUMP_NO_INTERRUPT                      259
-#define LOAD_CLOSURE                           260
-#define POP_BLOCK                              261
-#define SETUP_CLEANUP                          262
-#define SETUP_FINALLY                          263
-#define SETUP_WITH                             264
-#define STORE_FAST_MAYBE_NULL                  265
+#define ANNOTATIONS_PLACEHOLDER                256
+#define JUMP                                   257
+#define JUMP_IF_FALSE                          258
+#define JUMP_IF_TRUE                           259
+#define JUMP_NO_INTERRUPT                      260
+#define LOAD_CLOSURE                           261
+#define POP_BLOCK                              262
+#define SETUP_CLEANUP                          263
+#define SETUP_FINALLY                          264
+#define SETUP_WITH                             265
+#define STORE_FAST_MAYBE_NULL                  266
 
 #define HAVE_ARGUMENT                           42
 #define MIN_SPECIALIZED_OPCODE                 129
diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py
index 15900265a01270..4d30b6503fd1e9 100644
--- a/Lib/_opcode_metadata.py
+++ b/Lib/_opcode_metadata.py
@@ -350,16 +350,17 @@
     'INSTRUMENTED_CALL_KW': 251,
     'INSTRUMENTED_CALL_FUNCTION_EX': 252,
     'INSTRUMENTED_JUMP_BACKWARD': 253,
-    'JUMP': 256,
-    'JUMP_IF_FALSE': 257,
-    'JUMP_IF_TRUE': 258,
-    'JUMP_NO_INTERRUPT': 259,
-    'LOAD_CLOSURE': 260,
-    'POP_BLOCK': 261,
-    'SETUP_CLEANUP': 262,
-    'SETUP_FINALLY': 263,
-    'SETUP_WITH': 264,
-    'STORE_FAST_MAYBE_NULL': 265,
+    'ANNOTATIONS_PLACEHOLDER': 256,
+    'JUMP': 257,
+    'JUMP_IF_FALSE': 258,
+    'JUMP_IF_TRUE': 259,
+    'JUMP_NO_INTERRUPT': 260,
+    'LOAD_CLOSURE': 261,
+    'POP_BLOCK': 262,
+    'SETUP_CLEANUP': 263,
+    'SETUP_FINALLY': 264,
+    'SETUP_WITH': 265,
+    'STORE_FAST_MAYBE_NULL': 266,
 }
 
 HAVE_ARGUMENT = 42
diff --git a/Lib/test/test_compiler_codegen.py 
b/Lib/test/test_compiler_codegen.py
index cc9ecc7e38917b..d02937c84d9534 100644
--- a/Lib/test/test_compiler_codegen.py
+++ b/Lib/test/test_compiler_codegen.py
@@ -26,6 +26,7 @@ def test_if_expression(self):
         false_lbl = self.Label()
         expected = [
             ('RESUME', 0, 0),
+            ('ANNOTATIONS_PLACEHOLDER', None),
             ('LOAD_CONST', 0, 1),
             ('TO_BOOL', 0, 1),
             ('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1),
@@ -45,6 +46,7 @@ def test_for_loop(self):
         false_lbl = self.Label()
         expected = [
             ('RESUME', 0, 0),
+            ('ANNOTATIONS_PLACEHOLDER', None),
             ('LOAD_NAME', 0, 1),
             ('GET_ITER', None, 1),
             loop_lbl := self.Label(),
@@ -73,6 +75,7 @@ def f(x):
         expected = [
             # Function definition
             ('RESUME', 0),
+            ('ANNOTATIONS_PLACEHOLDER', None),
             ('LOAD_CONST', 0),
             ('MAKE_FUNCTION', None),
             ('STORE_NAME', 0),
@@ -106,6 +109,7 @@ def g():
         expected = [
             # Function definition
             ('RESUME', 0),
+            ('ANNOTATIONS_PLACEHOLDER', None),
             ('LOAD_CONST', 0),
             ('MAKE_FUNCTION', None),
             ('STORE_NAME', 0),
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index b53a3656429300..f2586fcee57d87 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -381,24 +381,36 @@ def wrap_func_w_kwargs():
 # leading newline is for a reason (tests lineno)
 
 dis_annot_stmt_str = """\
-  0           RESUME                   0
+  --           MAKE_CELL                0 (__conditional_annotations__)
 
-  2           LOAD_SMALL_INT           1
-              STORE_NAME               0 (x)
+   0           RESUME                   0
 
-  4           LOAD_SMALL_INT           1
-              LOAD_NAME                1 (lst)
-              LOAD_NAME                2 (fun)
-              PUSH_NULL
-              LOAD_SMALL_INT           0
-              CALL                     1
-              STORE_SUBSCR
+   2           LOAD_CONST               1 (<code object __annotate__ at 0x..., 
file "<dis>", line 2>)
+               MAKE_FUNCTION
+               STORE_NAME               4 (__annotate__)
+               BUILD_SET                0
+               STORE_NAME               0 (__conditional_annotations__)
+               LOAD_SMALL_INT           1
+               STORE_NAME               1 (x)
+               LOAD_NAME                0 (__conditional_annotations__)
+               LOAD_SMALL_INT           0
+               SET_ADD                  1
+               POP_TOP
 
-  2           LOAD_CONST               1 (<code object __annotate__ at 0x..., 
file "<dis>", line 2>)
-              MAKE_FUNCTION
-              STORE_NAME               3 (__annotate__)
-              LOAD_CONST               2 (None)
-              RETURN_VALUE
+   3           LOAD_NAME                0 (__conditional_annotations__)
+               LOAD_SMALL_INT           1
+               SET_ADD                  1
+               POP_TOP
+
+   4           LOAD_SMALL_INT           1
+               LOAD_NAME                2 (lst)
+               LOAD_NAME                3 (fun)
+               PUSH_NULL
+               LOAD_SMALL_INT           0
+               CALL                     1
+               STORE_SUBSCR
+               LOAD_CONST               2 (None)
+               RETURN_VALUE
 """
 
 fn_with_annotate_str = """
@@ -995,7 +1007,8 @@ def test_boundaries(self):
     def test_widths(self):
         long_opcodes = set(['JUMP_BACKWARD_NO_INTERRUPT',
                             'LOAD_FAST_BORROW_LOAD_FAST_BORROW',
-                            'INSTRUMENTED_CALL_FUNCTION_EX'])
+                            'INSTRUMENTED_CALL_FUNCTION_EX',
+                            'ANNOTATIONS_PLACEHOLDER'])
         for op, opname in enumerate(dis.opname):
             if opname in long_opcodes or opname.startswith("INSTRUMENTED"):
                 continue
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 3ea4e47ca50a16..35cd6984267b3b 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -383,9 +383,10 @@ def test_var_annot_simple_exec(self):
         gns = {}; lns = {}
         exec("'docstring'\n"
              "x: int = 5\n", gns, lns)
+        self.assertNotIn('__annotate__', gns)
+
+        gns.update(lns)  # __annotate__ looks at globals
         self.assertEqual(lns["__annotate__"](annotationlib.Format.VALUE), 
{'x': int})
-        with self.assertRaises(KeyError):
-            gns['__annotate__']
 
     def test_var_annot_rhs(self):
         ns = {}
diff --git a/Lib/test/test_type_annotations.py 
b/Lib/test/test_type_annotations.py
index fb441976694b34..b72d3dbe516974 100644
--- a/Lib/test/test_type_annotations.py
+++ b/Lib/test/test_type_annotations.py
@@ -3,7 +3,7 @@
 import textwrap
 import types
 import unittest
-from test.support import run_code, check_syntax_error, cpython_only
+from test.support import run_code, check_syntax_error, import_helper, 
cpython_only
 from test.test_inspect import inspect_stringized_annotations
 
 
@@ -151,6 +151,14 @@ class D(metaclass=C):
             del D.__annotations__
         self.assertEqual(D.__annotations__, {})
 
+    def test_partially_executed_module(self):
+        partialexe = 
import_helper.import_fresh_module("test.typinganndata.partialexecution")
+        self.assertEqual(
+            partialexe.a.__annotations__,
+            {"v1": int, "v2": int},
+        )
+        self.assertEqual(partialexe.b.annos, {"v1": int})
+
     @cpython_only
     def test_no_cell(self):
         # gh-130924: Test that uses of annotations in local scopes do not
diff --git a/Lib/test/typinganndata/partialexecution/__init__.py 
b/Lib/test/typinganndata/partialexecution/__init__.py
new file mode 100644
index 00000000000000..c39074ea84b7e6
--- /dev/null
+++ b/Lib/test/typinganndata/partialexecution/__init__.py
@@ -0,0 +1 @@
+from . import a
diff --git a/Lib/test/typinganndata/partialexecution/a.py 
b/Lib/test/typinganndata/partialexecution/a.py
new file mode 100644
index 00000000000000..ed0b8dcbd55530
--- /dev/null
+++ b/Lib/test/typinganndata/partialexecution/a.py
@@ -0,0 +1,5 @@
+v1: int
+
+from . import b
+
+v2: int
diff --git a/Lib/test/typinganndata/partialexecution/b.py 
b/Lib/test/typinganndata/partialexecution/b.py
new file mode 100644
index 00000000000000..36b8d2e52a3c63
--- /dev/null
+++ b/Lib/test/typinganndata/partialexecution/b.py
@@ -0,0 +1,3 @@
+from . import a
+
+annos = a.__annotations__
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 8ec3173a8847b1..925e0a243c9e96 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2679,6 +2679,7 @@ TESTSUBDIRS=      idlelib/idle_test \
                test/translationdata/getopt \
                test/translationdata/optparse \
                test/typinganndata \
+               test/typinganndata/partialexecution \
                test/wheeldata \
                test/xmltestdata \
                test/xmltestdata/c14n-20 \
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-21-08-47-36.gh-issue-130907.rGg-ge.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-21-08-47-36.gh-issue-130907.rGg-ge.rst
new file mode 100644
index 00000000000000..587627e979adbb
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-21-08-47-36.gh-issue-130907.rGg-ge.rst
@@ -0,0 +1,3 @@
+If the ``__annotations__`` of a module object are accessed while the
+module is executing, return the annotations that have been defined so far,
+without caching them.
diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c
index 46dea1534cbcd6..16f1b2cacd4f83 100644
--- a/Objects/moduleobject.c
+++ b/Objects/moduleobject.c
@@ -1246,6 +1246,25 @@ module_get_annotations(PyObject *self, void 
*Py_UNUSED(ignored))
 
     PyObject *annotations;
     if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) == 0) {
+        PyObject *spec;
+        if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__spec__), &spec) < 0) {
+            Py_DECREF(dict);
+            return NULL;
+        }
+        bool is_initializing = false;
+        if (spec != NULL) {
+            int rc = _PyModuleSpec_IsInitializing(spec);
+            if (rc < 0) {
+                Py_DECREF(spec);
+                Py_DECREF(dict);
+                return NULL;
+            }
+            Py_DECREF(spec);
+            if (rc) {
+                is_initializing = true;
+            }
+        }
+
         PyObject *annotate;
         int annotate_result = PyDict_GetItemRef(dict, &_Py_ID(__annotate__), 
&annotate);
         if (annotate_result < 0) {
@@ -1273,7 +1292,8 @@ module_get_annotations(PyObject *self, void 
*Py_UNUSED(ignored))
             annotations = PyDict_New();
         }
         Py_XDECREF(annotate);
-        if (annotations) {
+        // Do not cache annotations if the module is still initializing
+        if (annotations && !is_initializing) {
             int result = PyDict_SetItem(
                     dict, &_Py_ID(__annotations__), annotations);
             if (result) {
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index f5c6e734a79960..40fb0d769afcca 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2026,6 +2026,10 @@ dummy_func(
             }
         }
 
+        pseudo(ANNOTATIONS_PLACEHOLDER, (--)) = {
+            NOP,
+        };
+
         inst(DICT_UPDATE, (dict, unused[oparg - 1], update -- dict, 
unused[oparg - 1])) {
             PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
             PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
diff --git a/Python/codegen.c b/Python/codegen.c
index 35b46dcdc40950..a7e412f7032c45 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -654,6 +654,9 @@ codegen_enter_scope(compiler *c, identifier name, int 
scope_type,
         loc.lineno = 0;
     }
     ADDOP_I(c, loc, RESUME, RESUME_AT_FUNC_START);
+    if (scope_type == COMPILE_SCOPE_MODULE) {
+        ADDOP(c, loc, ANNOTATIONS_PLACEHOLDER);
+    }
     return SUCCESS;
 }
 
@@ -792,6 +795,14 @@ codegen_process_deferred_annotations(compiler *c, location 
loc)
         return SUCCESS;
     }
 
+    int scope_type = SCOPE_TYPE(c);
+    bool need_separate_block = scope_type == COMPILE_SCOPE_MODULE;
+    if (need_separate_block) {
+        if (_PyCompile_StartAnnotationSetup(c) == ERROR) {
+            goto error;
+        }
+    }
+
     // It's possible that ste_annotations_block is set but
     // u_deferred_annotations is not, because the former is still
     // set if there are only non-simple annotations (i.e., annotations
@@ -800,7 +811,6 @@ codegen_process_deferred_annotations(compiler *c, location 
loc)
     PySTEntryObject *ste = SYMTABLE_ENTRY(c);
     assert(ste->ste_annotation_block != NULL);
     void *key = (void *)((uintptr_t)ste->ste_id + 1);
-    int scope_type = SCOPE_TYPE(c);
     if (codegen_setup_annotations_scope(c, loc, key,
                                         ste->ste_annotation_block->ste_name) < 
0) {
         goto error;
@@ -820,6 +830,10 @@ codegen_process_deferred_annotations(compiler *c, location 
loc)
         ste->ste_type == ClassBlock ? &_Py_ID(__annotate_func__) : 
&_Py_ID(__annotate__),
         Store));
 
+    if (need_separate_block) {
+        RETURN_IF_ERROR(_PyCompile_EndAnnotationSetup(c));
+    }
+
     return SUCCESS;
 error:
     Py_XDECREF(deferred_anno);
diff --git a/Python/compile.c b/Python/compile.c
index 430898b5966842..15ef7214d44b9c 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -64,6 +64,7 @@ struct compiler_unit {
     long u_next_conditional_annotation_index;  /* index of the next 
conditional annotation */
 
     instr_sequence *u_instr_sequence; /* codegen output */
+    instr_sequence *u_stashed_instr_sequence; /* temporarily stashed parent 
instruction sequence */
 
     int u_nfblocks;
     int u_in_inlined_comp;
@@ -178,6 +179,7 @@ static void
 compiler_unit_free(struct compiler_unit *u)
 {
     Py_CLEAR(u->u_instr_sequence);
+    Py_CLEAR(u->u_stashed_instr_sequence);
     Py_CLEAR(u->u_ste);
     Py_CLEAR(u->u_metadata.u_name);
     Py_CLEAR(u->u_metadata.u_qualname);
@@ -681,6 +683,7 @@ _PyCompile_EnterScope(compiler *c, identifier name, int 
scope_type,
         compiler_unit_free(u);
         return ERROR;
     }
+    u->u_stashed_instr_sequence = NULL;
 
     /* Push the old compiler_unit on the stack. */
     if (c->u) {
@@ -1154,7 +1157,7 @@ _PyCompile_AddDeferredAnnotation(compiler *c, stmt_ty s,
     }
     Py_DECREF(ptr);
     PyObject *index;
-    if (c->u->u_in_conditional_block) {
+    if (c->u->u_scope_type == COMPILE_SCOPE_MODULE || 
c->u->u_in_conditional_block) {
         index = PyLong_FromLong(c->u->u_next_conditional_annotation_index);
         if (index == NULL) {
             return ERROR;
@@ -1231,6 +1234,35 @@ _PyCompile_InstrSequence(compiler *c)
     return c->u->u_instr_sequence;
 }
 
+int
+_PyCompile_StartAnnotationSetup(struct _PyCompiler *c)
+{
+    instr_sequence *new_seq = (instr_sequence *)_PyInstructionSequence_New();
+    if (new_seq == NULL) {
+        return ERROR;
+    }
+    assert(c->u->u_stashed_instr_sequence == NULL);
+    c->u->u_stashed_instr_sequence = c->u->u_instr_sequence;
+    c->u->u_instr_sequence = new_seq;
+    return SUCCESS;
+}
+
+int
+_PyCompile_EndAnnotationSetup(struct _PyCompiler *c)
+{
+    assert(c->u->u_stashed_instr_sequence != NULL);
+    instr_sequence *parent_seq = c->u->u_stashed_instr_sequence;
+    instr_sequence *anno_seq = c->u->u_instr_sequence;
+    c->u->u_stashed_instr_sequence = NULL;
+    c->u->u_instr_sequence = parent_seq;
+    if (_PyInstructionSequence_SetAnnotationsCode(parent_seq, anno_seq) == 
ERROR) {
+        Py_DECREF(anno_seq);
+        return ERROR;
+    }
+    return SUCCESS;
+}
+
+
 int
 _PyCompile_FutureFeatures(compiler *c)
 {
diff --git a/Python/flowgraph.c b/Python/flowgraph.c
index a0d5690250cffb..9e714bf6c4c54d 100644
--- a/Python/flowgraph.c
+++ b/Python/flowgraph.c
@@ -3847,16 +3847,38 @@ _PyCfg_FromInstructionSequence(_PyInstructionSequence 
*seq)
             seq->s_instrs[instr->i_oparg].i_target = 1;
         }
     }
+    int offset = 0;
     for (int i = 0; i < seq->s_used; i++) {
         _PyInstruction *instr = &seq->s_instrs[i];
+        if (instr->i_opcode == ANNOTATIONS_PLACEHOLDER) {
+            if (seq->s_annotations_code != NULL) {
+                assert(seq->s_annotations_code->s_labelmap_size == 0
+                    && seq->s_annotations_code->s_nested == NULL);
+                for (int j = 0; j < seq->s_annotations_code->s_used; j++) {
+                    _PyInstruction *ann_instr = 
&seq->s_annotations_code->s_instrs[j];
+                    assert(!HAS_TARGET(ann_instr->i_opcode));
+                    if (_PyCfgBuilder_Addop(g, ann_instr->i_opcode, 
ann_instr->i_oparg, ann_instr->i_loc) < 0) {
+                        goto error;
+                    }
+                }
+                offset += seq->s_annotations_code->s_used - 1;
+            }
+            else {
+                offset -= 1;
+            }
+            continue;
+        }
         if (instr->i_target) {
-            jump_target_label lbl_ = {i};
+            jump_target_label lbl_ = {i + offset};
             if (_PyCfgBuilder_UseLabel(g, lbl_) < 0) {
                 goto error;
             }
         }
         int opcode = instr->i_opcode;
         int oparg = instr->i_oparg;
+        if (HAS_TARGET(opcode)) {
+            oparg += offset;
+        }
         if (_PyCfgBuilder_Addop(g, opcode, oparg, instr->i_loc) < 0) {
             goto error;
         }
diff --git a/Python/instruction_sequence.c b/Python/instruction_sequence.c
index 4ca85eec345d38..b068e4fa3dbf43 100644
--- a/Python/instruction_sequence.c
+++ b/Python/instruction_sequence.c
@@ -154,6 +154,15 @@ _PyInstructionSequence_InsertInstruction(instr_sequence 
*seq, int pos,
     return SUCCESS;
 }
 
+int
+_PyInstructionSequence_SetAnnotationsCode(instr_sequence *seq,
+                                          instr_sequence *annotations)
+{
+    assert(seq->s_annotations_code == NULL);
+    seq->s_annotations_code = annotations;
+    return SUCCESS;
+}
+
 int
 _PyInstructionSequence_AddNested(instr_sequence *seq, instr_sequence *nested)
 {
@@ -178,6 +187,12 @@ PyInstructionSequence_Fini(instr_sequence *seq) {
 
     PyMem_Free(seq->s_instrs);
     seq->s_instrs = NULL;
+
+    if (seq->s_annotations_code != NULL) {
+        PyInstructionSequence_Fini(seq->s_annotations_code);
+        Py_CLEAR(seq->s_annotations_code);
+    }
+
 }
 
 /*[clinic input]
@@ -200,6 +215,7 @@ inst_seq_create(void)
     seq->s_labelmap = NULL;
     seq->s_labelmap_size = 0;
     seq->s_nested = NULL;
+    seq->s_annotations_code = NULL;
 
     PyObject_GC_Track(seq);
     return seq;
@@ -414,6 +430,7 @@ inst_seq_traverse(PyObject *op, visitproc visit, void *arg)
 {
     _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
     Py_VISIT(seq->s_nested);
+    Py_VISIT((PyObject *)seq->s_annotations_code);
     return 0;
 }
 
@@ -422,6 +439,7 @@ inst_seq_clear(PyObject *op)
 {
     _PyInstructionSequence *seq = (_PyInstructionSequence *)op;
     Py_CLEAR(seq->s_nested);
+    Py_CLEAR(seq->s_annotations_code);
     return 0;
 }
 
diff --git a/Python/symtable.c b/Python/symtable.c
index 66f6c4a89aaee3..2f13f35072aa5e 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -2749,8 +2749,10 @@ symtable_visit_annotation(struct symtable *st, expr_ty 
annotation, void *key)
     // Annotations in local scopes are not executed and should not affect the 
symtable
     bool is_unevaluated = st->st_cur->ste_type == FunctionBlock;
 
-    if ((st->st_cur->ste_type == ClassBlock || st->st_cur->ste_type == 
ModuleBlock)
-            && st->st_cur->ste_in_conditional_block
+    // Module-level annotations are always considered conditional because the 
module
+    // may be partially executed.
+    if ((((st->st_cur->ste_type == ClassBlock && 
st->st_cur->ste_in_conditional_block)
+            || st->st_cur->ste_type == ModuleBlock))
             && !st->st_cur->ste_has_conditional_annotations)
     {
         st->st_cur->ste_has_conditional_annotations = 1;

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to