summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-09-12 14:31:26 (GMT)
committerGitHub <noreply@github.com>2023-09-12 14:31:26 (GMT)
commit8208657f3d6398817f7dd6e5160ea037840e562f (patch)
treee9c8c4fcf68ad8d1a093f012ca18bfdcb9d3c1b6
parent778d094126f8975a9823ed7da1a630e29bb75bf0 (diff)
downloadcpython-8208657f3d6398817f7dd6e5160ea037840e562f.zip
cpython-8208657f3d6398817f7dd6e5160ea037840e562f.tar.gz
cpython-8208657f3d6398817f7dd6e5160ea037840e562f.tar.bz2
[3.12] gh-109118: Disallow nested scopes within PEP 695 scopes within classes (GH-109196) (#109297)
gh-109118: Disallow nested scopes within PEP 695 scopes within classes (GH-109196) Fixes GH-109118. Fixes GH-109194. (cherry picked from commit b88d9e75f68f102aca45fa62e2b0e2e2ff46d810) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Carl Meyer <carl@oddbird.net>
-rw-r--r--Lib/test/test_type_params.py93
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-09-09-12-49-46.gh-issue-109118.gx0X4h.rst2
-rw-r--r--Python/symtable.c23
3 files changed, 118 insertions, 0 deletions
diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py
index f93d088..b1848ae 100644
--- a/Lib/test/test_type_params.py
+++ b/Lib/test/test_type_params.py
@@ -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
index 0000000..87069c8
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-09-12-49-46.gh-issue-109118.gx0X4h.rst
@@ -0,0 +1,2 @@
+Disallow nested scopes (lambdas, generator expressions, and comprehensions)
+within PEP 695 annotation scopes that are nested within classes.
diff --git a/Python/symtable.c b/Python/symtable.c
index 115882d..691698e 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -1969,6 +1969,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)
@@ -2418,6 +2429,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));