Skip to content

Commit 85f727c

Browse files
gh-109118: Allow lambdas in annotation scopes in classes (#118019)
1 parent 4c7bfdf commit 85f727c

File tree

4 files changed

+53
-13
lines changed

4 files changed

+53
-13
lines changed

Doc/whatsnew/3.13.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ Other Language Changes
241241
ones if configured to do so.
242242
(Contributed by Pedro Sousa Lacerda in :gh:`66449`.)
243243

244+
* :ref:`annotation scope <annotation-scopes>` within class scopes can now
245+
contain lambdas. (Contributed by Jelle Zijlstra in :gh:`109118`.)
246+
244247

245248
New Modules
246249
===========

Lib/test/test_type_params.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,8 +486,6 @@ class C[T]:
486486
{}
487487
"""
488488
error_cases = [
489-
"type Alias1[T] = lambda: T",
490-
"type Alias2 = lambda: T",
491489
"type Alias3[T] = (T for _ in (1,))",
492490
"type Alias4 = (T for _ in (1,))",
493491
"type Alias5[T] = [T for _ in (1,)]",
@@ -499,6 +497,54 @@ class C[T]:
499497
r"Cannot use [a-z]+ in annotation scope within class scope"):
500498
run_code(code.format(case))
501499

500+
def test_lambda_in_alias_in_class(self):
501+
code = """
502+
T = "global"
503+
class C:
504+
T = "class"
505+
type Alias = lambda: T
506+
"""
507+
C = run_code(code)["C"]
508+
self.assertEqual(C.Alias.__value__(), "global")
509+
510+
def test_lambda_in_alias_in_generic_class(self):
511+
code = """
512+
class C[T]:
513+
T = "class"
514+
type Alias = lambda: T
515+
"""
516+
C = run_code(code)["C"]
517+
self.assertIs(C.Alias.__value__(), C.__type_params__[0])
518+
519+
def test_lambda_in_generic_alias_in_class(self):
520+
# A lambda nested in the alias cannot see the class scope, but can see
521+
# a surrounding annotation scope.
522+
code = """
523+
T = U = "global"
524+
class C:
525+
T = "class"
526+
U = "class"
527+
type Alias[T] = lambda: (T, U)
528+
"""
529+
C = run_code(code)["C"]
530+
T, U = C.Alias.__value__()
531+
self.assertIs(T, C.Alias.__type_params__[0])
532+
self.assertEqual(U, "global")
533+
534+
def test_lambda_in_generic_alias_in_generic_class(self):
535+
# A lambda nested in the alias cannot see the class scope, but can see
536+
# a surrounding annotation scope.
537+
code = """
538+
class C[T, U]:
539+
T = "class"
540+
U = "class"
541+
type Alias[T] = lambda: (T, U)
542+
"""
543+
C = run_code(code)["C"]
544+
T, U = C.Alias.__value__()
545+
self.assertIs(T, C.Alias.__type_params__[0])
546+
self.assertIs(U, C.__type_params__[1])
547+
502548

503549
def make_base(arg):
504550
class Base:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:ref:`annotation scope <annotation-scopes>` within class scopes can now
2+
contain lambdas.

Python/symtable.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,17 +2140,6 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
21402140
VISIT(st, expr, e->v.UnaryOp.operand);
21412141
break;
21422142
case Lambda_kind: {
2143-
if (st->st_cur->ste_can_see_class_scope) {
2144-
// gh-109118
2145-
PyErr_Format(PyExc_SyntaxError,
2146-
"Cannot use lambda in annotation scope within class scope");
2147-
PyErr_RangedSyntaxLocationObject(st->st_filename,
2148-
e->lineno,
2149-
e->col_offset + 1,
2150-
e->end_lineno,
2151-
e->end_col_offset + 1);
2152-
VISIT_QUIT(st, 0);
2153-
}
21542143
if (e->v.Lambda.args->defaults)
21552144
VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
21562145
if (e->v.Lambda.args->kw_defaults)

0 commit comments

Comments
 (0)