mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
gh-109118: Disallow nested scopes within PEP 695 scopes within classes (#109196)
Fixes #109118. Fixes #109194. Co-authored-by: Carl Meyer <carl@oddbird.net>
This commit is contained in:
parent
2b1e2f1cd1
commit
b88d9e75f6
3 changed files with 118 additions and 0 deletions
|
@ -412,6 +412,99 @@ class TypeParamsAccessTest(unittest.TestCase):
|
||||||
func, = T.__bound__
|
func, = T.__bound__
|
||||||
self.assertEqual(func(), 1)
|
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]():
|
def global_generic_func[T]():
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Disallow nested scopes (lambdas, generator expressions, and comprehensions)
|
||||||
|
within PEP 695 annotation scopes that are nested within classes.
|
|
@ -2010,6 +2010,17 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
|
||||||
VISIT(st, expr, e->v.UnaryOp.operand);
|
VISIT(st, expr, e->v.UnaryOp.operand);
|
||||||
break;
|
break;
|
||||||
case Lambda_kind: {
|
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)
|
if (e->v.Lambda.args->defaults)
|
||||||
VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
|
VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
|
||||||
if (e->v.Lambda.args->kw_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,
|
identifier scope_name, asdl_comprehension_seq *generators,
|
||||||
expr_ty elt, expr_ty value)
|
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);
|
int is_generator = (e->kind == GeneratorExp_kind);
|
||||||
comprehension_ty outermost = ((comprehension_ty)
|
comprehension_ty outermost = ((comprehension_ty)
|
||||||
asdl_seq_GET(generators, 0));
|
asdl_seq_GET(generators, 0));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue