@@ -114,6 +114,69 @@ def test_named_expression_invalid_in_class_body(self):
114
114
"assignment expression within a comprehension cannot be used in a class body" ):
115
115
exec (code , {}, {})
116
116
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
+
117
180
def test_named_expression_invalid_rebinding_list_comprehension_iteration_variable (self ):
118
181
cases = [
119
182
("Local reuse" , 'i' , "[i := 0 for i in range(5)]" ),
@@ -129,7 +192,11 @@ def test_named_expression_invalid_rebinding_list_comprehension_iteration_variabl
129
192
msg = f"assignment expression cannot rebind comprehension iteration variable '{ target } '"
130
193
with self .subTest (case = case ):
131
194
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
133
200
134
201
def test_named_expression_invalid_rebinding_list_comprehension_inner_loop (self ):
135
202
cases = [
@@ -178,12 +245,21 @@ def test_named_expression_invalid_rebinding_set_comprehension_iteration_variable
178
245
("Unreachable reuse" , 'i' , "{False or (i:=0) for i in range(5)}" ),
179
246
("Unreachable nested reuse" , 'i' ,
180
247
"{(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}" ),
181
253
]
182
254
for case , target , code in cases :
183
255
msg = f"assignment expression cannot rebind comprehension iteration variable '{ target } '"
184
256
with self .subTest (case = case ):
185
257
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
187
263
188
264
def test_named_expression_invalid_rebinding_set_comprehension_inner_loop (self ):
189
265
cases = [
0 commit comments