mirror of
https://github.com/python/cpython.git
synced 2025-08-27 12:16:04 +00:00
gh-109118: Make comprehensions work within annotation scopes, but without inlining (#118160)
Co-authored-by: Carl Meyer <carl@oddbird.net>
This commit is contained in:
parent
51aefc5bf9
commit
2326d6c868
4 changed files with 39 additions and 34 deletions
|
@ -276,7 +276,9 @@ Other Language Changes
|
||||||
(Contributed by Pedro Sousa Lacerda in :gh:`66449`.)
|
(Contributed by Pedro Sousa Lacerda in :gh:`66449`.)
|
||||||
|
|
||||||
* :ref:`annotation scope <annotation-scopes>` within class scopes can now
|
* :ref:`annotation scope <annotation-scopes>` within class scopes can now
|
||||||
contain lambdas. (Contributed by Jelle Zijlstra in :gh:`109118`.)
|
contain lambdas and comprehensions. Comprehensions that are located within
|
||||||
|
class scopes are not inlined into their parent scope. (Contributed by
|
||||||
|
Jelle Zijlstra in :gh:`109118` and :gh:`118160`.)
|
||||||
|
|
||||||
|
|
||||||
New Modules
|
New Modules
|
||||||
|
|
|
@ -436,9 +436,11 @@ class TypeParamsAccessTest(unittest.TestCase):
|
||||||
class Inner[U](make_base(T for _ in (1,)), make_base(T)):
|
class Inner[U](make_base(T for _ in (1,)), make_base(T)):
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
with self.assertRaisesRegex(SyntaxError,
|
ns = run_code(code)
|
||||||
"Cannot use comprehension in annotation scope within class scope"):
|
inner = ns["C"].Inner
|
||||||
run_code(code)
|
base1, base2, _ = inner.__bases__
|
||||||
|
self.assertEqual(list(base1.__arg__), [ns["C"].__type_params__[0]])
|
||||||
|
self.assertEqual(base2.__arg__, "class")
|
||||||
|
|
||||||
def test_listcomp_in_nested_class(self):
|
def test_listcomp_in_nested_class(self):
|
||||||
code = """
|
code = """
|
||||||
|
@ -464,9 +466,11 @@ class TypeParamsAccessTest(unittest.TestCase):
|
||||||
class Inner[U](make_base([T for _ in (1,)]), make_base(T)):
|
class Inner[U](make_base([T for _ in (1,)]), make_base(T)):
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
with self.assertRaisesRegex(SyntaxError,
|
ns = run_code(code)
|
||||||
"Cannot use comprehension in annotation scope within class scope"):
|
inner = ns["C"].Inner
|
||||||
run_code(code)
|
base1, base2, _ = inner.__bases__
|
||||||
|
self.assertEqual(base1.__arg__, [ns["C"].__type_params__[0]])
|
||||||
|
self.assertEqual(base2.__arg__, "class")
|
||||||
|
|
||||||
def test_gen_exp_in_generic_method(self):
|
def test_gen_exp_in_generic_method(self):
|
||||||
code = """
|
code = """
|
||||||
|
@ -475,27 +479,33 @@ class TypeParamsAccessTest(unittest.TestCase):
|
||||||
def meth[U](x: (T for _ in (1,)), y: T):
|
def meth[U](x: (T for _ in (1,)), y: T):
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
with self.assertRaisesRegex(SyntaxError,
|
ns = run_code(code)
|
||||||
"Cannot use comprehension in annotation scope within class scope"):
|
meth = ns["C"].meth
|
||||||
run_code(code)
|
self.assertEqual(list(meth.__annotations__["x"]), [ns["C"].__type_params__[0]])
|
||||||
|
self.assertEqual(meth.__annotations__["y"], "class")
|
||||||
|
|
||||||
def test_nested_scope_in_generic_alias(self):
|
def test_nested_scope_in_generic_alias(self):
|
||||||
code = """
|
code = """
|
||||||
class C[T]:
|
T = "global"
|
||||||
|
class C:
|
||||||
T = "class"
|
T = "class"
|
||||||
{}
|
{}
|
||||||
"""
|
"""
|
||||||
error_cases = [
|
cases = [
|
||||||
"type Alias3[T] = (T for _ in (1,))",
|
"type Alias[T] = (T for _ in (1,))",
|
||||||
"type Alias4 = (T for _ in (1,))",
|
"type Alias = (T for _ in (1,))",
|
||||||
"type Alias5[T] = [T for _ in (1,)]",
|
"type Alias[T] = [T for _ in (1,)]",
|
||||||
"type Alias6 = [T for _ in (1,)]",
|
"type Alias = [T for _ in (1,)]",
|
||||||
]
|
]
|
||||||
for case in error_cases:
|
for case in cases:
|
||||||
with self.subTest(case=case):
|
with self.subTest(case=case):
|
||||||
with self.assertRaisesRegex(SyntaxError,
|
ns = run_code(code.format(case))
|
||||||
r"Cannot use [a-z]+ in annotation scope within class scope"):
|
alias = ns["C"].Alias
|
||||||
run_code(code.format(case))
|
value = list(alias.__value__)[0]
|
||||||
|
if alias.__type_params__:
|
||||||
|
self.assertIs(value, alias.__type_params__[0])
|
||||||
|
else:
|
||||||
|
self.assertEqual(value, "global")
|
||||||
|
|
||||||
def test_lambda_in_alias_in_class(self):
|
def test_lambda_in_alias_in_class(self):
|
||||||
code = """
|
code = """
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
:ref:`Annotation scopes <annotation-scopes>` within classes can now contain
|
||||||
|
comprehensions. However, such comprehensions are not inlined into their
|
||||||
|
parent scope at runtime. Patch by Jelle Zijlstra.
|
|
@ -1154,10 +1154,12 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we inline all non-generator-expression comprehensions
|
// we inline all non-generator-expression comprehensions,
|
||||||
|
// except those in annotation scopes that are nested in classes
|
||||||
int inline_comp =
|
int inline_comp =
|
||||||
entry->ste_comprehension &&
|
entry->ste_comprehension &&
|
||||||
!entry->ste_generator;
|
!entry->ste_generator &&
|
||||||
|
!ste->ste_can_see_class_scope;
|
||||||
|
|
||||||
if (!analyze_child_block(entry, newbound, newfree, newglobal,
|
if (!analyze_child_block(entry, newbound, newfree, newglobal,
|
||||||
type_params, new_class_entry, &child_free))
|
type_params, new_class_entry, &child_free))
|
||||||
|
@ -2589,18 +2591,6 @@ 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