mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +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 C[T]:
 | 
				
			||||||
                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 C[T]:
 | 
				
			||||||
                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 C[T]:
 | 
				
			||||||
                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