]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-109118: Disallow nested scopes within PEP 695 scopes within classes (#109196)
authorJelle Zijlstra <jelle.zijlstra@gmail.com>
Tue, 12 Sep 2023 00:11:06 +0000 (17:11 -0700)
committerGitHub <noreply@github.com>
Tue, 12 Sep 2023 00:11:06 +0000 (17:11 -0700)
Fixes #109118. Fixes #109194.

Co-authored-by: Carl Meyer <carl@oddbird.net>
Lib/test/test_type_params.py
Misc/NEWS.d/next/Core and Builtins/2023-09-09-12-49-46.gh-issue-109118.gx0X4h.rst [new file with mode: 0644]
Python/symtable.c

index f93d088ea758a9b813fab6f2305213ca55885e0d..b1848aee4753a1ff7f67c9ac4078b1bfd9cab2c4 100644 (file)
@@ -412,6 +412,99 @@ class TypeParamsAccessTest(unittest.TestCase):
         func, = T.__bound__
         self.assertEqual(func(), 1)
 
+    def test_gen_exp_in_nested_class(self):
+        code = """
+            from test.test_type_params import make_base
+
+            class C[T]:
+                T = "class"
+                class Inner(make_base(T for _ in (1,)), make_base(T)):
+                    pass
+        """
+        C = run_code(code)["C"]
+        T, = C.__type_params__
+        base1, base2 = C.Inner.__bases__
+        self.assertEqual(list(base1.__arg__), [T])
+        self.assertEqual(base2.__arg__, "class")
+
+    def test_gen_exp_in_nested_generic_class(self):
+        code = """
+            from test.test_type_params import make_base
+
+            class C[T]:
+                T = "class"
+                class Inner[U](make_base(T for _ in (1,)), make_base(T)):
+                    pass
+        """
+        with self.assertRaisesRegex(SyntaxError,
+                                    "Cannot use comprehension in annotation scope within class scope"):
+            run_code(code)
+
+    def test_listcomp_in_nested_class(self):
+        code = """
+            from test.test_type_params import make_base
+
+            class C[T]:
+                T = "class"
+                class Inner(make_base([T for _ in (1,)]), make_base(T)):
+                    pass
+        """
+        C = run_code(code)["C"]
+        T, = C.__type_params__
+        base1, base2 = C.Inner.__bases__
+        self.assertEqual(base1.__arg__, [T])
+        self.assertEqual(base2.__arg__, "class")
+
+    def test_listcomp_in_nested_generic_class(self):
+        code = """
+            from test.test_type_params import make_base
+
+            class C[T]:
+                T = "class"
+                class Inner[U](make_base([T for _ in (1,)]), make_base(T)):
+                    pass
+        """
+        with self.assertRaisesRegex(SyntaxError,
+                                    "Cannot use comprehension in annotation scope within class scope"):
+            run_code(code)
+
+    def test_gen_exp_in_generic_method(self):
+        code = """
+            class C[T]:
+                T = "class"
+                def meth[U](x: (T for _ in (1,)), y: T):
+                    pass
+        """
+        with self.assertRaisesRegex(SyntaxError,
+                                    "Cannot use comprehension in annotation scope within class scope"):
+            run_code(code)
+
+    def test_nested_scope_in_generic_alias(self):
+        code = """
+            class C[T]:
+                T = "class"
+                {}
+        """
+        error_cases = [
+            "type Alias1[T] = lambda: T",
+            "type Alias2 = lambda: T",
+            "type Alias3[T] = (T for _ in (1,))",
+            "type Alias4 = (T for _ in (1,))",
+            "type Alias5[T] = [T for _ in (1,)]",
+            "type Alias6 = [T for _ in (1,)]",
+        ]
+        for case in error_cases:
+            with self.subTest(case=case):
+                with self.assertRaisesRegex(SyntaxError,
+                                            r"Cannot use [a-z]+ in annotation scope within class scope"):
+                    run_code(code.format(case))
+
+
+def make_base(arg):
+    class Base:
+        __arg__ = arg
+    return Base
+
 
 def global_generic_func[T]():
     pass
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-09-12-49-46.gh-issue-109118.gx0X4h.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-09-12-49-46.gh-issue-109118.gx0X4h.rst
new file mode 100644 (file)
index 0000000..87069c8
--- /dev/null
@@ -0,0 +1,2 @@
+Disallow nested scopes (lambdas, generator expressions, and comprehensions)
+within PEP 695 annotation scopes that are nested within classes.
index 6c28b49a0655c635d5e27207d6fc5bbc359bfc12..d737c09203d31bde9d89d594718f4a551db02b81 100644 (file)
@@ -2010,6 +2010,17 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
         VISIT(st, expr, e->v.UnaryOp.operand);
         break;
     case Lambda_kind: {
+        if (st->st_cur->ste_can_see_class_scope) {
+            // gh-109118
+            PyErr_Format(PyExc_SyntaxError,
+                         "Cannot use lambda in annotation scope within class scope");
+            PyErr_RangedSyntaxLocationObject(st->st_filename,
+                                              e->lineno,
+                                              e->col_offset + 1,
+                                              e->end_lineno,
+                                              e->end_col_offset + 1);
+            VISIT_QUIT(st, 0);
+        }
         if (e->v.Lambda.args->defaults)
             VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
         if (e->v.Lambda.args->kw_defaults)
@@ -2459,6 +2470,18 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
                               identifier scope_name, asdl_comprehension_seq *generators,
                               expr_ty elt, expr_ty value)
 {
+    if (st->st_cur->ste_can_see_class_scope) {
+        // gh-109118
+        PyErr_Format(PyExc_SyntaxError,
+                     "Cannot use comprehension in annotation scope within class scope");
+        PyErr_RangedSyntaxLocationObject(st->st_filename,
+                                         e->lineno,
+                                         e->col_offset + 1,
+                                         e->end_lineno,
+                                         e->end_col_offset + 1);
+        VISIT_QUIT(st, 0);
+    }
+
     int is_generator = (e->kind == GeneratorExp_kind);
     comprehension_ty outermost = ((comprehension_ty)
                                     asdl_seq_GET(generators, 0));