https://github.com/python/cpython/commit/898e6b395e63ad7f8bbe421adf0af8947d429925
commit: 898e6b395e63ad7f8bbe421adf0af8947d429925
branch: main
author: Jelle Zijlstra <jelle.zijls...@gmail.com>
committer: JelleZijlstra <jelle.zijls...@gmail.com>
date: 2025-03-26T03:48:19Z
summary:

gh-130881: Handle conditionally defined annotations (#130935)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-03-06-17-04-27.gh-issue-130935.ss4lmS.rst
M Include/internal/pycore_compile.h
M Include/internal/pycore_global_objects_fini_generated.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Include/internal/pycore_symtable.h
M Include/internal/pycore_unicodeobject_generated.h
M Lib/test/test_type_annotations.py
M Python/codegen.c
M Python/compile.c
M Python/symtable.c

diff --git a/Include/internal/pycore_compile.h 
b/Include/internal/pycore_compile.h
index 467374c705486e..b98dfb0cebbd3f 100644
--- a/Include/internal/pycore_compile.h
+++ b/Include/internal/pycore_compile.h
@@ -134,7 +134,9 @@ void _PyCompile_ExitScope(struct _PyCompiler *c);
 Py_ssize_t _PyCompile_AddConst(struct _PyCompiler *c, PyObject *o);
 _PyInstructionSequence *_PyCompile_InstrSequence(struct _PyCompiler *c);
 int _PyCompile_FutureFeatures(struct _PyCompiler *c);
-PyObject *_PyCompile_DeferredAnnotations(struct _PyCompiler *c);
+void _PyCompile_DeferredAnnotations(
+    struct _PyCompiler *c, PyObject **deferred_annotations,
+    PyObject **conditional_annotation_indices);
 PyObject *_PyCompile_Mangle(struct _PyCompiler *c, PyObject *name);
 PyObject *_PyCompile_MaybeMangle(struct _PyCompiler *c, PyObject *name);
 int _PyCompile_MaybeAddStaticAttributeToClass(struct _PyCompiler *c, expr_ty 
e);
@@ -178,13 +180,16 @@ int _PyCompile_TweakInlinedComprehensionScopes(struct 
_PyCompiler *c, _Py_Source
                                                
_PyCompile_InlinedComprehensionState *state);
 int _PyCompile_RevertInlinedComprehensionScopes(struct _PyCompiler *c, 
_Py_SourceLocation loc,
                                                 
_PyCompile_InlinedComprehensionState *state);
-int _PyCompile_AddDeferredAnnotaion(struct _PyCompiler *c, stmt_ty s);
+int _PyCompile_AddDeferredAnnotation(struct _PyCompiler *c, stmt_ty s,
+                                     PyObject **conditional_annotation_index);
+void _PyCompile_EnterConditionalBlock(struct _PyCompiler *c);
+void _PyCompile_LeaveConditionalBlock(struct _PyCompiler *c);
 
 int _PyCodegen_AddReturnAtEnd(struct _PyCompiler *c, int addNone);
 int _PyCodegen_EnterAnonymousScope(struct _PyCompiler* c, mod_ty mod);
 int _PyCodegen_Expression(struct _PyCompiler *c, expr_ty e);
-int _PyCodegen_Body(struct _PyCompiler *c, _Py_SourceLocation loc, 
asdl_stmt_seq *stmts,
-                    bool is_interactive);
+int _PyCodegen_Module(struct _PyCompiler *c, _Py_SourceLocation loc, 
asdl_stmt_seq *stmts,
+                      bool is_interactive);
 
 int _PyCompile_ConstCacheMergeOne(PyObject *const_cache, PyObject **obj);
 
diff --git a/Include/internal/pycore_global_objects_fini_generated.h 
b/Include/internal/pycore_global_objects_fini_generated.h
index 0612184e10c70c..3579481ba43ba6 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -604,6 +604,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__classdict__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__classdictcell__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__complex__));
+    _PyStaticObject_CheckRefcnt((PyObject 
*)&_Py_ID(__conditional_annotations__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__contains__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ctypes_from_outparam__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__del__));
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index bc21fa5e4ca761..d83732fc43d65d 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -95,6 +95,7 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(__classdict__)
         STRUCT_FOR_ID(__classdictcell__)
         STRUCT_FOR_ID(__complex__)
+        STRUCT_FOR_ID(__conditional_annotations__)
         STRUCT_FOR_ID(__contains__)
         STRUCT_FOR_ID(__ctypes_from_outparam__)
         STRUCT_FOR_ID(__del__)
diff --git a/Include/internal/pycore_runtime_init_generated.h 
b/Include/internal/pycore_runtime_init_generated.h
index 4a7111a01bf00c..554fbdac5b0f51 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -602,6 +602,7 @@ extern "C" {
     INIT_ID(__classdict__), \
     INIT_ID(__classdictcell__), \
     INIT_ID(__complex__), \
+    INIT_ID(__conditional_annotations__), \
     INIT_ID(__contains__), \
     INIT_ID(__ctypes_from_outparam__), \
     INIT_ID(__del__), \
diff --git a/Include/internal/pycore_symtable.h 
b/Include/internal/pycore_symtable.h
index fd6b9bcbe5abd8..a347db16722961 100644
--- a/Include/internal/pycore_symtable.h
+++ b/Include/internal/pycore_symtable.h
@@ -124,6 +124,8 @@ typedef struct _symtable_entry {
                                              enclosing class scope */
     unsigned ste_has_docstring : 1; /* true if docstring present */
     unsigned ste_method : 1; /* true if block is a function block defined in 
class scope */
+    unsigned ste_has_conditional_annotations : 1; /* true if block has 
conditionally executed annotations */
+    unsigned ste_in_conditional_block : 1; /* set while we are inside a 
conditionally executed block */
     int ste_comp_iter_expr; /* non-zero if visiting a comprehension range 
expression */
     _Py_SourceLocation ste_loc; /* source location of block */
     struct _symtable_entry *ste_annotation_block; /* symbol table entry for 
this entry's annotations */
diff --git a/Include/internal/pycore_unicodeobject_generated.h 
b/Include/internal/pycore_unicodeobject_generated.h
index 1ec99a1b5b3a5c..58b36977485988 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -168,6 +168,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
     assert(PyUnicode_GET_LENGTH(string) != 1);
+    string = &_Py_ID(__conditional_annotations__);
+    _PyUnicode_InternStatic(interp, &string);
+    assert(_PyUnicode_CheckConsistency(string, 1));
+    assert(PyUnicode_GET_LENGTH(string) != 1);
     string = &_Py_ID(__contains__);
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
diff --git a/Lib/test/test_type_annotations.py 
b/Lib/test/test_type_annotations.py
index 0afcd76af153e7..60908546bb2653 100644
--- a/Lib/test/test_type_annotations.py
+++ b/Lib/test/test_type_annotations.py
@@ -457,3 +457,202 @@ class format: pass
             "cannot access free variable 'format' where it is not associated 
with a value in enclosing scope",
         ):
             ns["f"].__annotations__
+
+
+class ConditionalAnnotationTests(unittest.TestCase):
+    def check_scopes(self, code, true_annos, false_annos):
+        for scope in ("class", "module"):
+            for (cond, expected) in (
+                # Constants (so code might get optimized out)
+                (True, true_annos), (False, false_annos),
+                # Non-constant expressions
+                ("not not len", true_annos), ("not len", false_annos),
+            ):
+                with self.subTest(scope=scope, cond=cond):
+                    code_to_run = code.format(cond=cond)
+                    if scope == "class":
+                        code_to_run = "class Cls:\n" + 
textwrap.indent(textwrap.dedent(code_to_run), " " * 4)
+                    ns = run_code(code_to_run)
+                    if scope == "class":
+                        self.assertEqual(ns["Cls"].__annotations__, expected)
+                    else:
+                        
self.assertEqual(ns["__annotate__"](annotationlib.Format.VALUE),
+                                         expected)
+
+    def test_with(self):
+        code = """
+            class Swallower:
+                def __enter__(self):
+                    pass
+
+                def __exit__(self, *args):
+                    return True
+
+            with Swallower():
+                if {cond}:
+                    about_to_raise: int
+                    raise Exception
+                in_with: "with"
+        """
+        self.check_scopes(code, {"about_to_raise": int}, {"in_with": "with"})
+
+    def test_simple_if(self):
+        code = """
+            if {cond}:
+                in_if: "if"
+            else:
+                in_if: "else"
+        """
+        self.check_scopes(code, {"in_if": "if"}, {"in_if": "else"})
+
+    def test_if_elif(self):
+        code = """
+            if not len:
+                in_if: "if"
+            elif {cond}:
+                in_elif: "elif"
+            else:
+                in_else: "else"
+        """
+        self.check_scopes(
+            code,
+            {"in_elif": "elif"},
+            {"in_else": "else"}
+        )
+
+    def test_try(self):
+        code = """
+            try:
+                if {cond}:
+                    raise Exception
+                in_try: "try"
+            except Exception:
+                in_except: "except"
+            finally:
+                in_finally: "finally"
+        """
+        self.check_scopes(
+            code,
+            {"in_except": "except", "in_finally": "finally"},
+            {"in_try": "try", "in_finally": "finally"}
+        )
+
+    def test_try_star(self):
+        code = """
+            try:
+                if {cond}:
+                    raise Exception
+                in_try_star: "try"
+            except* Exception:
+                in_except_star: "except"
+            finally:
+                in_finally: "finally"
+        """
+        self.check_scopes(
+            code,
+            {"in_except_star": "except", "in_finally": "finally"},
+            {"in_try_star": "try", "in_finally": "finally"}
+        )
+
+    def test_while(self):
+        code = """
+            while {cond}:
+                in_while: "while"
+                break
+            else:
+                in_else: "else"
+        """
+        self.check_scopes(
+            code,
+            {"in_while": "while"},
+            {"in_else": "else"}
+        )
+
+    def test_for(self):
+        code = """
+            for _ in ([1] if {cond} else []):
+                in_for: "for"
+            else:
+                in_else: "else"
+        """
+        self.check_scopes(
+            code,
+            {"in_for": "for", "in_else": "else"},
+            {"in_else": "else"}
+        )
+
+    def test_match(self):
+        code = """
+            match {cond}:
+                case True:
+                    x: "true"
+                case False:
+                    x: "false"
+        """
+        self.check_scopes(
+            code,
+            {"x": "true"},
+            {"x": "false"}
+        )
+
+    def test_nesting_override(self):
+        code = """
+            if {cond}:
+                x: "foo"
+                if {cond}:
+                    x: "bar"
+        """
+        self.check_scopes(
+            code,
+            {"x": "bar"},
+            {}
+        )
+
+    def test_nesting_outer(self):
+        code = """
+            if {cond}:
+                outer_before: "outer_before"
+                if len:
+                    inner_if: "inner_if"
+                else:
+                    inner_else: "inner_else"
+                outer_after: "outer_after"
+        """
+        self.check_scopes(
+            code,
+            {"outer_before": "outer_before", "inner_if": "inner_if",
+             "outer_after": "outer_after"},
+            {}
+        )
+
+    def test_nesting_inner(self):
+        code = """
+            if len:
+                outer_before: "outer_before"
+                if {cond}:
+                    inner_if: "inner_if"
+                else:
+                    inner_else: "inner_else"
+                outer_after: "outer_after"
+        """
+        self.check_scopes(
+            code,
+            {"outer_before": "outer_before", "inner_if": "inner_if",
+             "outer_after": "outer_after"},
+            {"outer_before": "outer_before", "inner_else": "inner_else",
+             "outer_after": "outer_after"},
+        )
+
+    def test_non_name_annotations(self):
+        code = """
+            before: "before"
+            if {cond}:
+                a = "x"
+                a[0]: int
+            else:
+                a = object()
+                a.b: str
+            after: "after"
+        """
+        expected = {"before": "before", "after": "after"}
+        self.check_scopes(code, expected, expected)
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-06-17-04-27.gh-issue-130935.ss4lmS.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-06-17-04-27.gh-issue-130935.ss4lmS.rst
new file mode 100644
index 00000000000000..7870497986fb10
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-06-17-04-27.gh-issue-130935.ss4lmS.rst
@@ -0,0 +1,3 @@
+Annotations at the class and module level that are conditionally defined are
+now only reflected in ``__annotations__`` if the block they are in is
+executed. Patch by Jelle Zijlstra.
diff --git a/Python/codegen.c b/Python/codegen.c
index 44d23ca6d93014..8cc484e98d64af 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -2,7 +2,7 @@
  * This file implements the compiler's code generation stage, which
  * produces a sequence of pseudo-instructions from an AST.
  *
- * The primary entry point is _PyCodegen_Body() for modules, and
+ * The primary entry point is _PyCodegen_Module() for modules, and
  * _PyCodegen_Expression() for expressions.
  *
  * CAUTION: The VISIT_* macros abort the current function when they
@@ -204,8 +204,11 @@ static int codegen_subscript(compiler *, expr_ty);
 static int codegen_slice_two_parts(compiler *, expr_ty);
 static int codegen_slice(compiler *, expr_ty);
 
-static int codegen_with(compiler *, stmt_ty, int);
-static int codegen_async_with(compiler *, stmt_ty, int);
+static int codegen_body(compiler *, location, asdl_stmt_seq *, bool);
+static int codegen_with(compiler *, stmt_ty);
+static int codegen_async_with(compiler *, stmt_ty);
+static int codegen_with_inner(compiler *, stmt_ty, int);
+static int codegen_async_with_inner(compiler *, stmt_ty, int);
 static int codegen_async_for(compiler *, stmt_ty);
 static int codegen_call_simple_kw_helper(compiler *c,
                                          location loc,
@@ -681,12 +684,13 @@ codegen_setup_annotations_scope(compiler *c, location loc,
 }
 
 static int
-codegen_leave_annotations_scope(compiler *c, location loc,
-                                Py_ssize_t annotations_len)
+codegen_leave_annotations_scope(compiler *c, location loc)
 {
-    ADDOP_I(c, loc, BUILD_MAP, annotations_len);
     ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
     PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1);
+    if (co == NULL) {
+        return ERROR;
+    }
 
     // We want the parameter to __annotate__ to be named "format" in the
     // signature  shown by inspect.signature(), but we need to use a
@@ -696,16 +700,19 @@ codegen_leave_annotations_scope(compiler *c, location loc,
     // co->co_localsplusnames = ("format", *co->co_localsplusnames[1:])
     const Py_ssize_t size = PyObject_Size(co->co_localsplusnames);
     if (size == -1) {
+        Py_DECREF(co);
         return ERROR;
     }
     PyObject *new_names = PyTuple_New(size);
     if (new_names == NULL) {
+        Py_DECREF(co);
         return ERROR;
     }
     PyTuple_SET_ITEM(new_names, 0, Py_NewRef(&_Py_ID(format)));
     for (int i = 1; i < size; i++) {
         PyObject *item = PyTuple_GetItem(co->co_localsplusnames, i);
         if (item == NULL) {
+            Py_DECREF(co);
             Py_DECREF(new_names);
             return ERROR;
         }
@@ -715,23 +722,75 @@ codegen_leave_annotations_scope(compiler *c, location loc,
     Py_SETREF(co->co_localsplusnames, new_names);
 
     _PyCompile_ExitScope(c);
-    if (co == NULL) {
-        return ERROR;
-    }
     int ret = codegen_make_closure(c, loc, co, 0);
     Py_DECREF(co);
     RETURN_IF_ERROR(ret);
     return SUCCESS;
 }
 
+static int
+codegen_deferred_annotations_body(compiler *c, location loc,
+    PyObject *deferred_anno, PyObject *conditional_annotation_indices, int 
scope_type)
+{
+    Py_ssize_t annotations_len = PyList_GET_SIZE(deferred_anno);
+
+    assert(PyList_CheckExact(conditional_annotation_indices));
+    assert(annotations_len == PyList_Size(conditional_annotation_indices));
+
+    ADDOP_I(c, loc, BUILD_MAP, 0); // stack now contains <annos>
+
+    for (Py_ssize_t i = 0; i < annotations_len; i++) {
+        PyObject *ptr = PyList_GET_ITEM(deferred_anno, i);
+        stmt_ty st = (stmt_ty)PyLong_AsVoidPtr(ptr);
+        if (st == NULL) {
+            return ERROR;
+        }
+        PyObject *mangled = _PyCompile_Mangle(c, 
st->v.AnnAssign.target->v.Name.id);
+        if (!mangled) {
+            return ERROR;
+        }
+        PyObject *cond_index = PyList_GET_ITEM(conditional_annotation_indices, 
i);
+        assert(PyLong_CheckExact(cond_index));
+        long idx = PyLong_AS_LONG(cond_index);
+        NEW_JUMP_TARGET_LABEL(c, not_set);
+
+        if (idx != -1) {
+            ADDOP_LOAD_CONST(c, LOC(st), cond_index);
+            if (scope_type == COMPILE_SCOPE_CLASS) {
+                ADDOP_NAME(
+                    c, LOC(st), LOAD_DEREF, 
&_Py_ID(__conditional_annotations__), freevars);
+            }
+            else {
+                ADDOP_NAME(
+                    c, LOC(st), LOAD_GLOBAL, 
&_Py_ID(__conditional_annotations__), names);
+            }
+
+            ADDOP_I(c, LOC(st), CONTAINS_OP, 0);
+            ADDOP_JUMP(c, LOC(st), POP_JUMP_IF_FALSE, not_set);
+        }
+
+        VISIT(c, expr, st->v.AnnAssign.annotation);
+        ADDOP_I(c, LOC(st), COPY, 2);
+        ADDOP_LOAD_CONST_NEW(c, LOC(st), mangled);
+        // stack now contains <annos> <name> <annos> <value>
+        ADDOP(c, loc, STORE_SUBSCR);
+        // stack now contains <annos>
+
+        USE_LABEL(c, not_set);
+    }
+    return SUCCESS;
+}
+
 static int
 codegen_process_deferred_annotations(compiler *c, location loc)
 {
-    PyObject *deferred_anno = _PyCompile_DeferredAnnotations(c);
+    PyObject *deferred_anno = NULL;
+    PyObject *conditional_annotation_indices = NULL;
+    _PyCompile_DeferredAnnotations(c, &deferred_anno, 
&conditional_annotation_indices);
     if (deferred_anno == NULL) {
+        assert(conditional_annotation_indices == NULL);
         return SUCCESS;
     }
-    Py_INCREF(deferred_anno);
 
     // It's possible that ste_annotations_block is set but
     // u_deferred_annotations is not, because the former is still
@@ -741,35 +800,28 @@ 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) {
-        Py_DECREF(deferred_anno);
-        return ERROR;
+        goto error;
     }
-    Py_ssize_t annotations_len = PyList_Size(deferred_anno);
-    for (Py_ssize_t i = 0; i < annotations_len; i++) {
-        PyObject *ptr = PyList_GET_ITEM(deferred_anno, i);
-        stmt_ty st = (stmt_ty)PyLong_AsVoidPtr(ptr);
-        if (st == NULL) {
-            _PyCompile_ExitScope(c);
-            Py_DECREF(deferred_anno);
-            return ERROR;
-        }
-        PyObject *mangled = _PyCompile_Mangle(c, 
st->v.AnnAssign.target->v.Name.id);
-        if (!mangled) {
-            _PyCompile_ExitScope(c);
-            Py_DECREF(deferred_anno);
-            return ERROR;
-        }
-        ADDOP_LOAD_CONST_NEW(c, LOC(st), mangled);
-        VISIT(c, expr, st->v.AnnAssign.annotation);
+    if (codegen_deferred_annotations_body(c, loc, deferred_anno,
+                                          conditional_annotation_indices, 
scope_type) < 0) {
+        _PyCompile_ExitScope(c);
+        goto error;
     }
+
     Py_DECREF(deferred_anno);
+    Py_DECREF(conditional_annotation_indices);
 
-    RETURN_IF_ERROR(codegen_leave_annotations_scope(c, loc, annotations_len));
+    RETURN_IF_ERROR(codegen_leave_annotations_scope(c, loc));
     RETURN_IF_ERROR(codegen_nameop(c, loc, &_Py_ID(__annotate__), Store));
 
     return SUCCESS;
+error:
+    Py_XDECREF(deferred_anno);
+    Py_XDECREF(conditional_annotation_indices);
+    return ERROR;
 }
 
 /* Compile an expression */
@@ -784,7 +836,17 @@ _PyCodegen_Expression(compiler *c, expr_ty e)
    and for annotations. */
 
 int
-_PyCodegen_Body(compiler *c, location loc, asdl_stmt_seq *stmts, bool 
is_interactive)
+_PyCodegen_Module(compiler *c, location loc, asdl_stmt_seq *stmts, bool 
is_interactive)
+{
+    if (SYMTABLE_ENTRY(c)->ste_has_conditional_annotations) {
+        ADDOP_I(c, loc, BUILD_SET, 0);
+        ADDOP_N(c, loc, STORE_NAME, &_Py_ID(__conditional_annotations__), 
names);
+    }
+    return codegen_body(c, loc, stmts, is_interactive);
+}
+
+int
+codegen_body(compiler *c, location loc, asdl_stmt_seq *stmts, bool 
is_interactive)
 {
     /* If from __future__ import annotations is active,
      * every annotated class and module should have __annotations__.
@@ -1029,8 +1091,8 @@ codegen_annotations_in_scope(compiler *c, location loc,
 }
 
 static int
-codegen_annotations(compiler *c, location loc,
-                    arguments_ty args, expr_ty returns)
+codegen_function_annotations(compiler *c, location loc,
+                             arguments_ty args, expr_ty returns)
 {
     /* Push arg annotation names and values.
        The expressions are evaluated separately from the rest of the source 
code.
@@ -1050,9 +1112,8 @@ codegen_annotations(compiler *c, location loc,
         RETURN_IF_ERROR_IN_SCOPE(
             c, codegen_annotations_in_scope(c, loc, args, returns, 
&annotations_len)
         );
-        RETURN_IF_ERROR(
-            codegen_leave_annotations_scope(c, loc, annotations_len)
-        );
+        ADDOP_I(c, loc, BUILD_MAP, annotations_len);
+        RETURN_IF_ERROR(codegen_leave_annotations_scope(c, loc));
         return MAKE_FUNCTION_ANNOTATE;
     }
     else {
@@ -1378,7 +1439,7 @@ codegen_function(compiler *c, stmt_ty s, int is_async)
         }
     }
 
-    int annotations_flag = codegen_annotations(c, loc, args, returns);
+    int annotations_flag = codegen_function_annotations(c, loc, args, returns);
     if (annotations_flag < 0) {
         if (is_generic) {
             _PyCompile_ExitScope(c);
@@ -1471,8 +1532,12 @@ codegen_class_body(compiler *c, stmt_ty s, int 
firstlineno)
         // that by default.
         ADDOP_N_IN_SCOPE(c, loc, STORE_DEREF, &_Py_ID(__classdict__), 
cellvars);
     }
+    if (SYMTABLE_ENTRY(c)->ste_has_conditional_annotations) {
+        ADDOP_I(c, loc, BUILD_SET, 0);
+        ADDOP_N_IN_SCOPE(c, loc, STORE_DEREF, 
&_Py_ID(__conditional_annotations__), cellvars);
+    }
     /* compile the body proper */
-    RETURN_IF_ERROR_IN_SCOPE(c, _PyCodegen_Body(c, loc, s->v.ClassDef.body, 
false));
+    RETURN_IF_ERROR_IN_SCOPE(c, codegen_body(c, loc, s->v.ClassDef.body, 
false));
     PyObject *static_attributes = _PyCompile_StaticAttributesAsTuple(c);
     if (static_attributes == NULL) {
         _PyCompile_ExitScope(c);
@@ -2895,6 +2960,14 @@ codegen_stmt_expr(compiler *c, location loc, expr_ty 
value)
     return SUCCESS;
 }
 
+#define CODEGEN_COND_BLOCK(FUNC, C, S) \
+    do { \
+        _PyCompile_EnterConditionalBlock((C)); \
+        int result = FUNC((C), (S)); \
+        _PyCompile_LeaveConditionalBlock((C)); \
+        return result; \
+    } while(0)
+
 static int
 codegen_visit_stmt(compiler *c, stmt_ty s)
 {
@@ -2929,13 +3002,17 @@ codegen_visit_stmt(compiler *c, stmt_ty s)
     case AnnAssign_kind:
         return codegen_annassign(c, s);
     case For_kind:
-        return codegen_for(c, s);
+        CODEGEN_COND_BLOCK(codegen_for, c, s);
+        break;
     case While_kind:
-        return codegen_while(c, s);
+        CODEGEN_COND_BLOCK(codegen_while, c, s);
+        break;
     case If_kind:
-        return codegen_if(c, s);
+        CODEGEN_COND_BLOCK(codegen_if, c, s);
+        break;
     case Match_kind:
-        return codegen_match(c, s);
+        CODEGEN_COND_BLOCK(codegen_match, c, s);
+        break;
     case Raise_kind:
     {
         Py_ssize_t n = 0;
@@ -2951,9 +3028,11 @@ codegen_visit_stmt(compiler *c, stmt_ty s)
         break;
     }
     case Try_kind:
-        return codegen_try(c, s);
+        CODEGEN_COND_BLOCK(codegen_try, c, s);
+        break;
     case TryStar_kind:
-        return codegen_try_star(c, s);
+        CODEGEN_COND_BLOCK(codegen_try_star, c, s);
+        break;
     case Assert_kind:
         return codegen_assert(c, s);
     case Import_kind:
@@ -2981,13 +3060,16 @@ codegen_visit_stmt(compiler *c, stmt_ty s)
         return codegen_continue(c, LOC(s));
     }
     case With_kind:
-        return codegen_with(c, s, 0);
+        CODEGEN_COND_BLOCK(codegen_with, c, s);
+        break;
     case AsyncFunctionDef_kind:
         return codegen_function(c, s, 1);
     case AsyncWith_kind:
-        return codegen_async_with(c, s, 0);
+        CODEGEN_COND_BLOCK(codegen_async_with, c, s);
+        break;
     case AsyncFor_kind:
-        return codegen_async_for(c, s);
+        CODEGEN_COND_BLOCK(codegen_async_for, c, s);
+        break;
     }
 
     return SUCCESS;
@@ -4725,7 +4807,7 @@ codegen_with_except_finish(compiler *c, jump_target_label 
cleanup) {
            raise
  */
 static int
-codegen_async_with(compiler *c, stmt_ty s, int pos)
+codegen_async_with_inner(compiler *c, stmt_ty s, int pos)
 {
     location loc = LOC(s);
     withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);
@@ -4770,7 +4852,7 @@ codegen_async_with(compiler *c, stmt_ty s, int pos)
         VISIT_SEQ(c, stmt, s->v.AsyncWith.body);
     }
     else {
-        RETURN_IF_ERROR(codegen_async_with(c, s, pos));
+        RETURN_IF_ERROR(codegen_async_with_inner(c, s, pos));
     }
 
     _PyCompile_PopFBlock(c, COMPILE_FBLOCK_ASYNC_WITH, block);
@@ -4805,6 +4887,12 @@ codegen_async_with(compiler *c, stmt_ty s, int pos)
     return SUCCESS;
 }
 
+static int
+codegen_async_with(compiler *c, stmt_ty s)
+{
+    return codegen_async_with_inner(c, s, 0);
+}
+
 
 /*
    Implements the with statement from PEP 343.
@@ -4828,7 +4916,7 @@ codegen_async_with(compiler *c, stmt_ty s, int pos)
  */
 
 static int
-codegen_with(compiler *c, stmt_ty s, int pos)
+codegen_with_inner(compiler *c, stmt_ty s, int pos)
 {
     withitem_ty item = asdl_seq_GET(s->v.With.items, pos);
 
@@ -4869,7 +4957,7 @@ codegen_with(compiler *c, stmt_ty s, int pos)
         VISIT_SEQ(c, stmt, s->v.With.body);
     }
     else {
-        RETURN_IF_ERROR(codegen_with(c, s, pos));
+        RETURN_IF_ERROR(codegen_with_inner(c, s, pos));
     }
 
     ADDOP(c, NO_LOCATION, POP_BLOCK);
@@ -4896,6 +4984,12 @@ codegen_with(compiler *c, stmt_ty s, int pos)
     return SUCCESS;
 }
 
+static int
+codegen_with(compiler *c, stmt_ty s)
+{
+    return codegen_with_inner(c, s, 0);
+}
+
 static int
 codegen_visit_expr(compiler *c, expr_ty e)
 {
@@ -5224,7 +5318,18 @@ codegen_annassign(compiler *c, stmt_ty s)
                 ADDOP(c, loc, STORE_SUBSCR);
             }
             else {
-                RETURN_IF_ERROR(_PyCompile_AddDeferredAnnotaion(c, s));
+                PyObject *conditional_annotation_index = NULL;
+                RETURN_IF_ERROR(_PyCompile_AddDeferredAnnotation(
+                    c, s, &conditional_annotation_index));
+                if (conditional_annotation_index != NULL) {
+                    ADDOP_NAME(
+                        c, loc,
+                        SCOPE_TYPE(c) == COMPILE_SCOPE_CLASS ? LOAD_DEREF : 
LOAD_NAME,
+                        &_Py_ID(__conditional_annotations__), cellvars);
+                    ADDOP_LOAD_CONST_NEW(c, loc, conditional_annotation_index);
+                    ADDOP_I(c, loc, SET_ADD, 1);
+                    ADDOP(c, loc, POP_TOP);
+                }
             }
         }
         break;
diff --git a/Python/compile.c b/Python/compile.c
index 303d959c9d790c..430898b5966842 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -60,11 +60,14 @@ struct compiler_unit {
     PyObject *u_private;            /* for private name mangling */
     PyObject *u_static_attributes;  /* for class: attributes accessed via 
self.X */
     PyObject *u_deferred_annotations; /* AnnAssign nodes deferred to the end 
of compilation */
+    PyObject *u_conditional_annotation_indices;  /* indices of annotations 
that are conditionally executed (or -1 for unconditional annotations) */
+    long u_next_conditional_annotation_index;  /* index of the next 
conditional annotation */
 
     instr_sequence *u_instr_sequence; /* codegen output */
 
     int u_nfblocks;
     int u_in_inlined_comp;
+    int u_in_conditional_block;
 
     _PyCompile_FBlockInfo u_fblock[CO_MAXBLOCKS];
 
@@ -187,6 +190,7 @@ compiler_unit_free(struct compiler_unit *u)
     Py_CLEAR(u->u_private);
     Py_CLEAR(u->u_static_attributes);
     Py_CLEAR(u->u_deferred_annotations);
+    Py_CLEAR(u->u_conditional_annotation_indices);
     PyMem_Free(u);
 }
 
@@ -620,6 +624,16 @@ _PyCompile_EnterScope(compiler *c, identifier name, int 
scope_type,
             return ERROR;
         }
     }
+    if (u->u_ste->ste_has_conditional_annotations) {
+        /* Cook up an implicit __conditional__annotations__ cell */
+        Py_ssize_t res;
+        assert(u->u_scope_type == COMPILE_SCOPE_CLASS || u->u_scope_type == 
COMPILE_SCOPE_MODULE);
+        res = _PyCompile_DictAddObj(u->u_metadata.u_cellvars, 
&_Py_ID(__conditional_annotations__));
+        if (res < 0) {
+            compiler_unit_free(u);
+            return ERROR;
+        }
+    }
 
     u->u_metadata.u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, 
DEF_FREE_CLASS,
                                PyDict_GET_SIZE(u->u_metadata.u_cellvars));
@@ -649,6 +663,8 @@ _PyCompile_EnterScope(compiler *c, identifier name, int 
scope_type,
     }
 
     u->u_deferred_annotations = NULL;
+    u->u_conditional_annotation_indices = NULL;
+    u->u_next_conditional_annotation_index = 0;
     if (scope_type == COMPILE_SCOPE_CLASS) {
         u->u_static_attributes = PySet_New(0);
         if (!u->u_static_attributes) {
@@ -768,10 +784,13 @@ _PyCompile_TopFBlock(compiler *c)
     return &c->u->u_fblock[c->u->u_nfblocks - 1];
 }
 
-PyObject *
-_PyCompile_DeferredAnnotations(compiler *c)
+void
+_PyCompile_DeferredAnnotations(compiler *c,
+                               PyObject **deferred_annotations,
+                               PyObject **conditional_annotation_indices)
 {
-    return c->u->u_deferred_annotations;
+    *deferred_annotations = Py_XNewRef(c->u->u_deferred_annotations);
+    *conditional_annotation_indices = 
Py_XNewRef(c->u->u_conditional_annotation_indices);
 }
 
 static location
@@ -797,13 +816,13 @@ compiler_codegen(compiler *c, mod_ty mod)
     switch (mod->kind) {
     case Module_kind: {
         asdl_stmt_seq *stmts = mod->v.Module.body;
-        RETURN_IF_ERROR(_PyCodegen_Body(c, start_location(stmts), stmts, 
false));
+        RETURN_IF_ERROR(_PyCodegen_Module(c, start_location(stmts), stmts, 
false));
         break;
     }
     case Interactive_kind: {
         c->c_interactive = 1;
         asdl_stmt_seq *stmts = mod->v.Interactive.body;
-        RETURN_IF_ERROR(_PyCodegen_Body(c, start_location(stmts), stmts, 
true));
+        RETURN_IF_ERROR(_PyCodegen_Module(c, start_location(stmts), stmts, 
true));
         break;
     }
     case Expression_kind: {
@@ -838,7 +857,8 @@ _PyCompile_GetRefType(compiler *c, PyObject *name)
 {
     if (c->u->u_scope_type == COMPILE_SCOPE_CLASS &&
         (_PyUnicode_EqualToASCIIString(name, "__class__") ||
-         _PyUnicode_EqualToASCIIString(name, "__classdict__"))) {
+         _PyUnicode_EqualToASCIIString(name, "__classdict__") ||
+         _PyUnicode_EqualToASCIIString(name, "__conditional_annotations__"))) {
         return CELL;
     }
     PySTEntryObject *ste = c->u->u_ste;
@@ -1095,8 +1115,22 @@ _PyCompile_RevertInlinedComprehensionScopes(compiler *c, 
location loc,
     return SUCCESS;
 }
 
+void
+_PyCompile_EnterConditionalBlock(struct _PyCompiler *c)
+{
+    c->u->u_in_conditional_block++;
+}
+
+void
+_PyCompile_LeaveConditionalBlock(struct _PyCompiler *c)
+{
+    assert(c->u->u_in_conditional_block > 0);
+    c->u->u_in_conditional_block--;
+}
+
 int
-_PyCompile_AddDeferredAnnotaion(compiler *c, stmt_ty s)
+_PyCompile_AddDeferredAnnotation(compiler *c, stmt_ty s,
+                                 PyObject **conditional_annotation_index)
 {
     if (c->u->u_deferred_annotations == NULL) {
         c->u->u_deferred_annotations = PyList_New(0);
@@ -1104,6 +1138,12 @@ _PyCompile_AddDeferredAnnotaion(compiler *c, stmt_ty s)
             return ERROR;
         }
     }
+    if (c->u->u_conditional_annotation_indices == NULL) {
+        c->u->u_conditional_annotation_indices = PyList_New(0);
+        if (c->u->u_conditional_annotation_indices == NULL) {
+            return ERROR;
+        }
+    }
     PyObject *ptr = PyLong_FromVoidPtr((void *)s);
     if (ptr == NULL) {
         return ERROR;
@@ -1113,6 +1153,24 @@ _PyCompile_AddDeferredAnnotaion(compiler *c, stmt_ty s)
         return ERROR;
     }
     Py_DECREF(ptr);
+    PyObject *index;
+    if (c->u->u_in_conditional_block) {
+        index = PyLong_FromLong(c->u->u_next_conditional_annotation_index);
+        if (index == NULL) {
+            return ERROR;
+        }
+        *conditional_annotation_index = Py_NewRef(index);
+        c->u->u_next_conditional_annotation_index++;
+    }
+    else {
+        index = PyLong_FromLong(-1);
+        if (index == NULL) {
+            return ERROR;
+        }
+    }
+    int rc = PyList_Append(c->u->u_conditional_annotation_indices, index);
+    Py_DECREF(index);
+    RETURN_IF_ERROR(rc);
     return SUCCESS;
 }
 
diff --git a/Python/symtable.c b/Python/symtable.c
index b9c45de0a454d6..622cd528f3a481 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -139,6 +139,8 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty 
block,
     ste->ste_can_see_class_scope = 0;
     ste->ste_comp_iter_expr = 0;
     ste->ste_needs_classdict = 0;
+    ste->ste_has_conditional_annotations = 0;
+    ste->ste_in_conditional_block = 0;
     ste->ste_annotation_block = NULL;
 
     ste->ste_has_docstring = 0;
@@ -945,6 +947,12 @@ drop_class_free(PySTEntryObject *ste, PyObject *free)
         return 0;
     if (res)
         ste->ste_needs_classdict = 1;
+    res = PySet_Discard(free, &_Py_ID(__conditional_annotations__));
+    if (res < 0)
+        return 0;
+    if (res) {
+        ste->ste_has_conditional_annotations = 1;
+    }
     return 1;
 }
 
@@ -1198,6 +1206,8 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, 
PyObject *free,
             goto error;
         if (PySet_Add(newbound, &_Py_ID(__classdict__)) < 0)
             goto error;
+        if (PySet_Add(newbound, &_Py_ID(__conditional_annotations__)) < 0)
+            goto error;
     }
 
     /* Recursively call analyze_child_block() on each child block.
@@ -1729,6 +1739,13 @@ symtable_enter_type_param_block(struct symtable *st, 
identifier name,
         } \
     } while(0)
 
+#define ENTER_CONDITIONAL_BLOCK(ST) \
+    int in_conditional_block = (ST)->st_cur->ste_in_conditional_block; \
+    (ST)->st_cur->ste_in_conditional_block = 1;
+
+#define LEAVE_CONDITIONAL_BLOCK(ST) \
+    (ST)->st_cur->ste_in_conditional_block = in_conditional_block;
+
 #define ENTER_RECURSIVE() \
 if (Py_EnterRecursiveCall(" during compilation")) { \
     return 0; \
@@ -2012,30 +2029,42 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
         VISIT(st, expr, s->v.AugAssign.value);
         break;
     }
-    case For_kind:
+    case For_kind: {
         VISIT(st, expr, s->v.For.target);
         VISIT(st, expr, s->v.For.iter);
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, stmt, s->v.For.body);
         if (s->v.For.orelse)
             VISIT_SEQ(st, stmt, s->v.For.orelse);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
-    case While_kind:
+    }
+    case While_kind: {
         VISIT(st, expr, s->v.While.test);
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, stmt, s->v.While.body);
         if (s->v.While.orelse)
             VISIT_SEQ(st, stmt, s->v.While.orelse);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
-    case If_kind:
+    }
+    case If_kind: {
         /* XXX if 0: and lookup_yield() hacks */
         VISIT(st, expr, s->v.If.test);
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, stmt, s->v.If.body);
         if (s->v.If.orelse)
             VISIT_SEQ(st, stmt, s->v.If.orelse);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
-    case Match_kind:
+    }
+    case Match_kind: {
         VISIT(st, expr, s->v.Match.subject);
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, match_case, s->v.Match.cases);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
+    }
     case Raise_kind:
         if (s->v.Raise.exc) {
             VISIT(st, expr, s->v.Raise.exc);
@@ -2044,18 +2073,24 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
             }
         }
         break;
-    case Try_kind:
+    case Try_kind: {
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, stmt, s->v.Try.body);
         VISIT_SEQ(st, excepthandler, s->v.Try.handlers);
         VISIT_SEQ(st, stmt, s->v.Try.orelse);
         VISIT_SEQ(st, stmt, s->v.Try.finalbody);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
-    case TryStar_kind:
+    }
+    case TryStar_kind: {
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, stmt, s->v.TryStar.body);
         VISIT_SEQ(st, excepthandler, s->v.TryStar.handlers);
         VISIT_SEQ(st, stmt, s->v.TryStar.orelse);
         VISIT_SEQ(st, stmt, s->v.TryStar.finalbody);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
+    }
     case Assert_kind:
         VISIT(st, expr, s->v.Assert.test);
         if (s->v.Assert.msg)
@@ -2142,10 +2177,13 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
     case Continue_kind:
         /* nothing to do here */
         break;
-    case With_kind:
+    case With_kind: {
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, withitem, s->v.With.items);
         VISIT_SEQ(st, stmt, s->v.With.body);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
+    }
     case AsyncFunctionDef_kind: {
         if (!symtable_add_def(st, s->v.AsyncFunctionDef.name, DEF_LOCAL, 
LOCATION(s)))
             return 0;
@@ -2201,26 +2239,32 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
         }
         break;
     }
-    case AsyncWith_kind:
+    case AsyncWith_kind: {
         maybe_set_ste_coroutine_for_module(st, s);
         if (!symtable_raise_if_not_coroutine(st, 
ASYNC_WITH_OUTSIDE_ASYNC_FUNC, LOCATION(s))) {
             return 0;
         }
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, withitem, s->v.AsyncWith.items);
         VISIT_SEQ(st, stmt, s->v.AsyncWith.body);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
-    case AsyncFor_kind:
+    }
+    case AsyncFor_kind: {
         maybe_set_ste_coroutine_for_module(st, s);
         if (!symtable_raise_if_not_coroutine(st, ASYNC_FOR_OUTSIDE_ASYNC_FUNC, 
LOCATION(s))) {
             return 0;
         }
         VISIT(st, expr, s->v.AsyncFor.target);
         VISIT(st, expr, s->v.AsyncFor.iter);
+        ENTER_CONDITIONAL_BLOCK(st);
         VISIT_SEQ(st, stmt, s->v.AsyncFor.body);
         if (s->v.AsyncFor.orelse)
             VISIT_SEQ(st, stmt, s->v.AsyncFor.orelse);
+        LEAVE_CONDITIONAL_BLOCK(st);
         break;
     }
+    }
     LEAVE_RECURSIVE();
     return 1;
 }
@@ -2689,6 +2733,15 @@ symtable_visit_params(struct symtable *st, asdl_arg_seq 
*args)
 static int
 symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key)
 {
+    if ((st->st_cur->ste_type == ClassBlock || st->st_cur->ste_type == 
ModuleBlock)
+            && st->st_cur->ste_in_conditional_block
+            && !st->st_cur->ste_has_conditional_annotations)
+    {
+        st->st_cur->ste_has_conditional_annotations = 1;
+        if (!symtable_add_def(st, &_Py_ID(__conditional_annotations__), USE, 
LOCATION(annotation))) {
+            return 0;
+        }
+    }
     struct _symtable_entry *parent_ste = st->st_cur;
     if (parent_ste->ste_annotation_block == NULL) {
         _Py_block_ty current_type = parent_ste->ste_type;

_______________________________________________
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