gh-109118: Allow lambdas in annotation scopes in classes (#118019)

This commit is contained in:
Jelle Zijlstra 2024-04-22 12:50:26 -07:00 committed by GitHub
parent 4c7bfdff90
commit 85f727c5fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 53 additions and 13 deletions

View File

@ -241,6 +241,9 @@ Other Language Changes
ones if configured to do so. ones if configured to do so.
(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
contain lambdas. (Contributed by Jelle Zijlstra in :gh:`109118`.)
New Modules New Modules
=========== ===========

View File

@ -486,8 +486,6 @@ class C[T]:
{} {}
""" """
error_cases = [ error_cases = [
"type Alias1[T] = lambda: T",
"type Alias2 = lambda: T",
"type Alias3[T] = (T for _ in (1,))", "type Alias3[T] = (T for _ in (1,))",
"type Alias4 = (T for _ in (1,))", "type Alias4 = (T for _ in (1,))",
"type Alias5[T] = [T for _ in (1,)]", "type Alias5[T] = [T for _ in (1,)]",
@ -499,6 +497,54 @@ class C[T]:
r"Cannot use [a-z]+ in annotation scope within class scope"): r"Cannot use [a-z]+ in annotation scope within class scope"):
run_code(code.format(case)) run_code(code.format(case))
def test_lambda_in_alias_in_class(self):
code = """
T = "global"
class C:
T = "class"
type Alias = lambda: T
"""
C = run_code(code)["C"]
self.assertEqual(C.Alias.__value__(), "global")
def test_lambda_in_alias_in_generic_class(self):
code = """
class C[T]:
T = "class"
type Alias = lambda: T
"""
C = run_code(code)["C"]
self.assertIs(C.Alias.__value__(), C.__type_params__[0])
def test_lambda_in_generic_alias_in_class(self):
# A lambda nested in the alias cannot see the class scope, but can see
# a surrounding annotation scope.
code = """
T = U = "global"
class C:
T = "class"
U = "class"
type Alias[T] = lambda: (T, U)
"""
C = run_code(code)["C"]
T, U = C.Alias.__value__()
self.assertIs(T, C.Alias.__type_params__[0])
self.assertEqual(U, "global")
def test_lambda_in_generic_alias_in_generic_class(self):
# A lambda nested in the alias cannot see the class scope, but can see
# a surrounding annotation scope.
code = """
class C[T, U]:
T = "class"
U = "class"
type Alias[T] = lambda: (T, U)
"""
C = run_code(code)["C"]
T, U = C.Alias.__value__()
self.assertIs(T, C.Alias.__type_params__[0])
self.assertIs(U, C.__type_params__[1])
def make_base(arg): def make_base(arg):
class Base: class Base:

View File

@ -0,0 +1,2 @@
:ref:`annotation scope <annotation-scopes>` within class scopes can now
contain lambdas.

View File

@ -2140,17 +2140,6 @@ 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)