8000 [3.12] gh-109118: Disallow nested scopes within PEP 695 scopes within… · python/cpython@8208657 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8208657

Browse files
miss-islingtonJelleZijlstracarljm
authored
[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 b88d9e7) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Carl Meyer <carl@oddbird.net>
1 parent 778d094 commit 8208657

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

Lib/test/test_type_params.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,99 @@ def test_comprehension_02(self):
412412
func, = T.__bound__
413413
self.assertEqual(func(), 1)
414414

415+
def test_gen_exp_in_nested_class(self):
416+
code = """
417+
from test.test_type_params import make_base
418+
419+
class C[T]:
420+
T = "class"
421+
class Inner(make_base(T for _ in (1,)), make_base(T)):
422+
pass
423+
"""
424+
C = run_code(code)["C"]
425+
T, = C.__type_params__
426+
base1, base2 = C.Inner.__bases__
427+
self.assertEqual(list(base1.__arg__), [T])
428+
self.assertEqual(base2.__arg__, "class")
429+
430+
def test_gen_exp_in_nested_generic_class(self):
431+
code = """
432+
from test.test_type_params import make_base
433+
434+
class C[T]:
435+
T = "class"
436+
class Inner[U](make_base(T for _ in (1,)), make_base(T)):
437+
pass
438+
"""
439+
with self.assertRaisesRegex(SyntaxError,
440+
"Cannot use comprehension in annotation scope within class scope"):
441+
run_code(code)
442+
443+
def test_listcomp_in_nested_class(self):
444+
code = """
445+
from test.test_type_params import make_base
446+
447+
class C[T]:
448+
T = "class"
449+
class Inner(make_base([T for _ in (1,)]), make_base(T)):
450+
pass
451+
"""
452+
C = run_code(code)["C"]
453+
T, = C.__type_params__
454+
base1, base2 = C.Inner.__bases__
455+
self.assertEqual(base1.__arg__, [T])
456+
self.assertEqual(base2.__arg__, "class")
457+
458+
def test_listcomp_in_nested_generic_class(self):
459+
code = """
460+
from test.test_type_params import make_base
461+
462+
class C[T]:
463+
T = "class"
464+
class Inner[U](make_base([T for _ in (1,)]), make_base(T)):
465+
pass
466+
"""
467+
with self.assertRaisesRegex(SyntaxError,
468+
"Cannot use comprehension in annotation scope within class scope"):
469+
run_code(code)
470+
471+
def test_gen_exp_in_generic_method(self):
472+
code = """
473+
class C[T]:
474+
T = "class"
475+
def meth[U](x: (T for _ in (1,)), y: T):
476+
pass
477+
"""
478+
with self.assertRaisesRegex(SyntaxError,
479+
"Cannot use comprehension in annotation scope within class scope"):
480+
run_code(code)
481+
482+
def test_nested_scope_in_generic_alias(self):
483+
code = """
484+
class C[T]:
485+
T = "class"
486+
{}
487+
"""
488+
error_cases = [
489+
"type Alias1[T] = lambda: T",
490+
"type Alias2 = lambda: T",
491+
"type Alias3[T] = (T for _ in (1,))",
492+
"type Alias4 = (T for _ in (1,))",
493+
"type Alias5[T] = [T for _ in (1,)]",
494+
"type Alias6 = [T for _ in (1,)]",
495+
]
496+
for case in error_cases:
497+
with self.subTest(case=case):
498+
with self.assertRaisesRegex(SyntaxError,
499+
r"Cannot use [a-z]+ in annotation scope within class scope"):
500+
run_code(code.format(case))
501+
502+
503+
def make_base(arg):
504+
class Base:
505+
__arg__ = arg
506+
return Base
507+
415508

416509
def global_generic_func[T]():
417510
pass
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Disallow nested scopes (lambdas, generator expressions, and comprehensions)
2+
within PEP 695 annotation scopes that are nested within classes.

Python/symtable.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,17 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
19691969
VISIT(st, expr, e->v.UnaryOp.operand);
19701970
break;
19711971
case Lambda_kind: {
1972+
if (st->st_cur->ste_can_see_class_scope) {
1973+
// gh-109118
1974+
PyErr_Format(PyExc_SyntaxError,
1975+
"Cannot use lambda in annotation scope within class scope");
1976+
PyErr_RangedSyntaxLocationObject(st->st_filename,
1977+
e->lineno,
1978+
e->col_offset + 1,
1979+
e->end_lineno,
1980+
e->end_col_offset + 1);
1981+
VISIT_QUIT(st, 0);
1982+
}
19721983
if (e->v.Lambda.args->defaults)
19731984
VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
19741985
if (e->v.Lambda.args->kw_defaults)
@@ -2418,6 +2429,18 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
24182429
identifier scope_name, asdl_comprehension_seq *generators,
24192430
expr_ty elt, expr_ty value)
24202431
{
2432+
if (st->st_cur->ste_can_see_class_scope) {
2433+
// gh-109118
2434+
PyErr_Format(PyExc_SyntaxError,
2435+
"Cannot use comprehension in annotation scope within class scope");
2436+
PyErr_RangedSyntaxLocationObject(st->st_filename,
2437+
e->lineno,
2438+
e->col_offset + 1,
2439+
e->end_lineno,
2440+
e->end_col_offset + 1);
2441+
VISIT_QUIT(st, 0);
2442+
}
2443+
24212444
int is_generator = (e->kind == GeneratorExp_kind);
24222445
comprehension_ty outermost = ((comprehension_ty)
24232446
asdl_seq_GET(generators, 0));

0 commit comments

Comments
 (0)
0