@@ -114,6 +114,69 @@ def test_named_expression_invalid_in_class_body(self):
114114 "assignment expression within a comprehension cannot be used in a class body" ):
115115 exec (code , {}, {})
116116
117+ def test_named_expression_valid_rebinding_iteration_variable (self ):
118+ # This test covers that we can reassign variables
119+ # that are not directly assigned in the
120+ # iterable part of a comprehension.
121+ cases = [
122+ # Regression tests from https://github.com/python/cpython/issues/87447
123+ ("Complex expression: c" ,
124+ "{0}(c := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
125+ ("Complex expression: d" ,
126+ "{0}(d := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
127+ ("Complex expression: e" ,
128+ "{0}(e := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
129+ ("Complex expression: f" ,
130+ "{0}(f := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
131+ ("Complex expression: g" ,
132+ "{0}(g := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
133+ ("Complex expression: h" ,
134+ "{0}(h := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
135+ ("Complex expression: i" ,
136+ "{0}(i := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
137+ ("Complex expression: j" ,
138+ "{0}(j := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
139+ ]
140+ for test_case , code in cases :
141+ for lpar , rpar in [('(' , ')' ), ('[' , ']' ), ('{' , '}' )]:
142+ code = code .format (lpar , rpar )
143+ with self .subTest (case = test_case , lpar = lpar , rpar = rpar ):
144+ # Names used in snippets are not defined,
145+ # but we are fine with it: just must not be a SyntaxError.
146+ # Names used in snippets are not defined,
147+ # but we are fine with it: just must not be a SyntaxError.
148+ with self .assertRaises (NameError ):
149+ exec (code , {}) # Module scope
150+ with self .assertRaises (NameError ):
151+ exec (code , {}, {}) # Class scope
152+ exec (f"lambda: { code } " , {}) # Function scope
153+
154+ def test_named_expression_invalid_rebinding_iteration_variable (self ):
155+ # This test covers that we cannot reassign variables
156+ # that are directly assigned in the iterable part of a comprehension.
157+ cases = [
158+ # Regression tests from https://github.com/python/cpython/issues/87447
159+ ("Complex expression: a" , "a" ,
160+ "{0}(a := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
161+ ("Complex expression: b" , "b" ,
162+ "{0}(b := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}" ),
163+ ]
164+ for test_case , target , code in cases :
165+ msg = f"assignment expression cannot rebind comprehension iteration variable '{ target } '"
166+ for lpar , rpar in [('(' , ')' ), ('[' , ']' ), ('{' , '}' )]:
167+ code = code .format (lpar , rpar )
168+ with self .subTest (case = test_case , lpar = lpar , rpar = rpar ):
169+ # Names used in snippets are not defined,
170+ # but we are fine with it: just must not be a SyntaxError.
171+ # Names used in snippets are not defined,
172+ # but we are fine with it: just must not be a SyntaxError.
173+ with self .assertRaisesRegex (SyntaxError , msg ):
174+ exec (code , {}) # Module scope
175+ with self .assertRaisesRegex (SyntaxError , msg ):
176+ exec (code , {}, {}) # Class scope
177+ with self .assertRaisesRegex (SyntaxError , msg ):
178+ exec (f"lambda: { code } " , {}) # Function scope
179+
117180 def test_named_expression_invalid_rebinding_list_comprehension_iteration_variable (self ):
118181 cases = [
119182 ("Local reuse" , 'i' , "[i := 0 for i in range(5)]" ),
@@ -129,7 +192,11 @@ def test_named_expression_invalid_rebinding_list_comprehension_iteration_variabl
129192 msg = f"assignment expression cannot rebind comprehension iteration variable '{ target } '"
130193 with self .subTest (case = case ):
131194 with self .assertRaisesRegex (SyntaxError , msg ):
132- exec (code , {}, {})
195+ exec (code , {}) # Module scope
196+ with self .assertRaisesRegex (SyntaxError , msg ):
197+ exec (code , {}, {}) # Class scope
198+ with self .assertRaisesRegex (SyntaxError , msg ):
199+ exec (f"lambda: { code } " , {}) # Function scope
133200
134201 def test_named_expression_invalid_rebinding_list_comprehension_inner_loop (self ):
135202 cases = [
@@ -178,12 +245,21 @@ def test_named_expression_invalid_rebinding_set_comprehension_iteration_variable
178245 ("Unreachable reuse" , 'i' , "{False or (i:=0) for i in range(5)}" ),
179246 ("Unreachable nested reuse" , 'i' ,
180247 "{(i, j) for i in range(5) for j in range(5) if True or (i:=10)}" ),
248+ # Regression tests from https://github.com/python/cpython/issues/87447
249+ ("Complex expression: a" , "a" ,
250+ "{(a := 1) for a, (*b, c[d+e::f(g)], h.i) in j}" ),
251+ ("Complex expression: b" , "b" ,
252+ "{(b := 1) for a, (*b, c[d+e::f(g)], h.i) in j}" ),
181253 ]
182254 for case , target , code in cases :
183255 msg = f"assignment expression cannot rebind comprehension iteration variable '{ target } '"
184256 with self .subTest (case = case ):
185257 with self .assertRaisesRegex (SyntaxError , msg ):
186- exec (code , {}, {})
258+ exec (code , {}) # Module scope
259+ with self .assertRaisesRegex (SyntaxError , msg ):
260+ exec (code , {}, {}) # Class scope
261+ with self .assertRaisesRegex (SyntaxError , msg ):
262+ exec (f"lambda: { code } " , {}) # Function scope
187263
188264 def test_named_expression_invalid_rebinding_set_comprehension_inner_loop (self ):
189265 cases = [
0 commit comments