]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-109118: Allow lambdas in annotation scopes in classes (#118019)
authorJelle Zijlstra <jelle.zijlstra@gmail.com>
Mon, 22 Apr 2024 19:50:26 +0000 (12:50 -0700)
committerGitHub <noreply@github.com>
Mon, 22 Apr 2024 19:50:26 +0000 (12:50 -0700)
Doc/whatsnew/3.13.rst
Lib/test/test_type_params.py
Misc/NEWS.d/next/Core and Builtins/2024-04-17-17-52-32.gh-issue-109118.q9iPEI.rst [new file with mode: 0644]
Python/symtable.c

index c04dc924d1efa535b3c859504a1937196722b023..67d1956a19697e63e850b0490fe72af74c0a1194 100644 (file)
@@ -241,6 +241,9 @@ Other Language Changes
   ones if configured to do so.
   (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
 ===========
index 25ee188731f31fbe9762c5d7cd882e11c5225ec0..fbb80d9aac99421b00f7285877a046f8973172e5 100644 (file)
@@ -486,8 +486,6 @@ class TypeParamsAccessTest(unittest.TestCase):
                 {}
         """
         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,)]",
@@ -499,6 +497,54 @@ class TypeParamsAccessTest(unittest.TestCase):
                                             r"Cannot use [a-z]+ in annotation scope within class scope"):
                     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):
     class Base:
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-17-17-52-32.gh-issue-109118.q9iPEI.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-17-17-52-32.gh-issue-109118.q9iPEI.rst
new file mode 100644 (file)
index 0000000..1245400
--- /dev/null
@@ -0,0 +1,2 @@
+:ref:`annotation scope <annotation-scopes>` within class scopes can now
+contain lambdas.
index 36ccc0e73723d59d9686732b3a7a586bce4d3052..483ef1c3c465422840a3bd88a2bddf1ce03486c7 100644 (file)
@@ -2140,17 +2140,6 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
         VISIT(st, expr, e->v.UnaryOp.operand);
         break;
     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)
             VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
         if (e->v.Lambda.args->kw_defaults)