1 import unittest
2
3 GLOBAL_VAR = None
4
5 class ESC[4;38;5;81mNamedExpressionInvalidTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
6
7 def test_named_expression_invalid_01(self):
8 code = """x := 0"""
9
10 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
11 exec(code, {}, {})
12
13 def test_named_expression_invalid_02(self):
14 code = """x = y := 0"""
15
16 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
17 exec(code, {}, {})
18
19 def test_named_expression_invalid_03(self):
20 code = """y := f(x)"""
21
22 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
23 exec(code, {}, {})
24
25 def test_named_expression_invalid_04(self):
26 code = """y0 = y1 := f(x)"""
27
28 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
29 exec(code, {}, {})
30
31 def test_named_expression_invalid_06(self):
32 code = """((a, b) := (1, 2))"""
33
34 with self.assertRaisesRegex(SyntaxError, "cannot use assignment expressions with tuple"):
35 exec(code, {}, {})
36
37 def test_named_expression_invalid_07(self):
38 code = """def spam(a = b := 42): pass"""
39
40 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
41 exec(code, {}, {})
42
43 def test_named_expression_invalid_08(self):
44 code = """def spam(a: b := 42 = 5): pass"""
45
46 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
47 exec(code, {}, {})
48
49 def test_named_expression_invalid_09(self):
50 code = """spam(a=b := 'c')"""
51
52 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
53 exec(code, {}, {})
54
55 def test_named_expression_invalid_10(self):
56 code = """spam(x = y := f(x))"""
57
58 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
59 exec(code, {}, {})
60
61 def test_named_expression_invalid_11(self):
62 code = """spam(a=1, b := 2)"""
63
64 with self.assertRaisesRegex(SyntaxError,
65 "positional argument follows keyword argument"):
66 exec(code, {}, {})
67
68 def test_named_expression_invalid_12(self):
69 code = """spam(a=1, (b := 2))"""
70
71 with self.assertRaisesRegex(SyntaxError,
72 "positional argument follows keyword argument"):
73 exec(code, {}, {})
74
75 def test_named_expression_invalid_13(self):
76 code = """spam(a=1, (b := 2))"""
77
78 with self.assertRaisesRegex(SyntaxError,
79 "positional argument follows keyword argument"):
80 exec(code, {}, {})
81
82 def test_named_expression_invalid_14(self):
83 code = """(x := lambda: y := 1)"""
84
85 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
86 exec(code, {}, {})
87
88 def test_named_expression_invalid_15(self):
89 code = """(lambda: x := 1)"""
90
91 with self.assertRaisesRegex(SyntaxError,
92 "cannot use assignment expressions with lambda"):
93 exec(code, {}, {})
94
95 def test_named_expression_invalid_16(self):
96 code = "[i + 1 for i in i := [1,2]]"
97
98 with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
99 exec(code, {}, {})
100
101 def test_named_expression_invalid_17(self):
102 code = "[i := 0, j := 1 for i, j in [(1, 2), (3, 4)]]"
103
104 with self.assertRaisesRegex(SyntaxError,
105 "did you forget parentheses around the comprehension target?"):
106 exec(code, {}, {})
107
108 def test_named_expression_invalid_in_class_body(self):
109 code = """class Foo():
110 [(42, 1 + ((( j := i )))) for i in range(5)]
111 """
112
113 with self.assertRaisesRegex(SyntaxError,
114 "assignment expression within a comprehension cannot be used in a class body"):
115 exec(code, {}, {})
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
180 def test_named_expression_invalid_rebinding_list_comprehension_iteration_variable(self):
181 cases = [
182 ("Local reuse", 'i', "[i := 0 for i in range(5)]"),
183 ("Nested reuse", 'j', "[[(j := 0) for i in range(5)] for j in range(5)]"),
184 ("Reuse inner loop target", 'j', "[(j := 0) for i in range(5) for j in range(5)]"),
185 ("Unpacking reuse", 'i', "[i := 0 for i, j in [(0, 1)]]"),
186 ("Reuse in loop condition", 'i', "[i+1 for i in range(5) if (i := 0)]"),
187 ("Unreachable reuse", 'i', "[False or (i:=0) for i in range(5)]"),
188 ("Unreachable nested reuse", 'i',
189 "[(i, j) for i in range(5) for j in range(5) if True or (i:=10)]"),
190 ]
191 for case, target, code in cases:
192 msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'"
193 with self.subTest(case=case):
194 with self.assertRaisesRegex(SyntaxError, msg):
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
200
201 def test_named_expression_invalid_rebinding_list_comprehension_inner_loop(self):
202 cases = [
203 ("Inner reuse", 'j', "[i for i in range(5) if (j := 0) for j in range(5)]"),
204 ("Inner unpacking reuse", 'j', "[i for i in range(5) if (j := 0) for j, k in [(0, 1)]]"),
205 ]
206 for case, target, code in cases:
207 msg = f"comprehension inner loop cannot rebind assignment expression target '{target}'"
208 with self.subTest(case=case):
209 with self.assertRaisesRegex(SyntaxError, msg):
210 exec(code, {}) # Module scope
211 with self.assertRaisesRegex(SyntaxError, msg):
212 exec(code, {}, {}) # Class scope
213 with self.assertRaisesRegex(SyntaxError, msg):
214 exec(f"lambda: {code}", {}) # Function scope
215
216 def test_named_expression_invalid_list_comprehension_iterable_expression(self):
217 cases = [
218 ("Top level", "[i for i in (i := range(5))]"),
219 ("Inside tuple", "[i for i in (2, 3, i := range(5))]"),
220 ("Inside list", "[i for i in [2, 3, i := range(5)]]"),
221 ("Different name", "[i for i in (j := range(5))]"),
222 ("Lambda expression", "[i for i in (lambda:(j := range(5)))()]"),
223 ("Inner loop", "[i for i in range(5) for j in (i := range(5))]"),
224 ("Nested comprehension", "[i for i in [j for j in (k := range(5))]]"),
225 ("Nested comprehension condition", "[i for i in [j for j in range(5) if (j := True)]]"),
226 ("Nested comprehension body", "[i for i in [(j := True) for j in range(5)]]"),
227 ]
228 msg = "assignment expression cannot be used in a comprehension iterable expression"
229 for case, code in cases:
230 with self.subTest(case=case):
231 with self.assertRaisesRegex(SyntaxError, msg):
232 exec(code, {}) # Module scope
233 with self.assertRaisesRegex(SyntaxError, msg):
234 exec(code, {}, {}) # Class scope
235 with self.assertRaisesRegex(SyntaxError, msg):
236 exec(f"lambda: {code}", {}) # Function scope
237
238 def test_named_expression_invalid_rebinding_set_comprehension_iteration_variable(self):
239 cases = [
240 ("Local reuse", 'i', "{i := 0 for i in range(5)}"),
241 ("Nested reuse", 'j', "{{(j := 0) for i in range(5)} for j in range(5)}"),
242 ("Reuse inner loop target", 'j', "{(j := 0) for i in range(5) for j in range(5)}"),
243 ("Unpacking reuse", 'i', "{i := 0 for i, j in {(0, 1)}}"),
244 ("Reuse in loop condition", 'i', "{i+1 for i in range(5) if (i := 0)}"),
245 ("Unreachable reuse", 'i', "{False or (i:=0) for i in range(5)}"),
246 ("Unreachable nested reuse", 'i',
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}"),
253 ]
254 for case, target, code in cases:
255 msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'"
256 with self.subTest(case=case):
257 with self.assertRaisesRegex(SyntaxError, msg):
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
263
264 def test_named_expression_invalid_rebinding_set_comprehension_inner_loop(self):
265 cases = [
266 ("Inner reuse", 'j', "{i for i in range(5) if (j := 0) for j in range(5)}"),
267 ("Inner unpacking reuse", 'j', "{i for i in range(5) if (j := 0) for j, k in {(0, 1)}}"),
268 ]
269 for case, target, code in cases:
270 msg = f"comprehension inner loop cannot rebind assignment expression target '{target}'"
271 with self.subTest(case=case):
272 with self.assertRaisesRegex(SyntaxError, msg):
273 exec(code, {}) # Module scope
274 with self.assertRaisesRegex(SyntaxError, msg):
275 exec(code, {}, {}) # Class scope
276 with self.assertRaisesRegex(SyntaxError, msg):
277 exec(f"lambda: {code}", {}) # Function scope
278
279 def test_named_expression_invalid_set_comprehension_iterable_expression(self):
280 cases = [
281 ("Top level", "{i for i in (i := range(5))}"),
282 ("Inside tuple", "{i for i in (2, 3, i := range(5))}"),
283 ("Inside list", "{i for i in {2, 3, i := range(5)}}"),
284 ("Different name", "{i for i in (j := range(5))}"),
285 ("Lambda expression", "{i for i in (lambda:(j := range(5)))()}"),
286 ("Inner loop", "{i for i in range(5) for j in (i := range(5))}"),
287 ("Nested comprehension", "{i for i in {j for j in (k := range(5))}}"),
288 ("Nested comprehension condition", "{i for i in {j for j in range(5) if (j := True)}}"),
289 ("Nested comprehension body", "{i for i in {(j := True) for j in range(5)}}"),
290 ]
291 msg = "assignment expression cannot be used in a comprehension iterable expression"
292 for case, code in cases:
293 with self.subTest(case=case):
294 with self.assertRaisesRegex(SyntaxError, msg):
295 exec(code, {}) # Module scope
296 with self.assertRaisesRegex(SyntaxError, msg):
297 exec(code, {}, {}) # Class scope
298 with self.assertRaisesRegex(SyntaxError, msg):
299 exec(f"lambda: {code}", {}) # Function scope
300
301
302 class ESC[4;38;5;81mNamedExpressionAssignmentTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
303
304 def test_named_expression_assignment_01(self):
305 (a := 10)
306
307 self.assertEqual(a, 10)
308
309 def test_named_expression_assignment_02(self):
310 a = 20
311 (a := a)
312
313 self.assertEqual(a, 20)
314
315 def test_named_expression_assignment_03(self):
316 (total := 1 + 2)
317
318 self.assertEqual(total, 3)
319
320 def test_named_expression_assignment_04(self):
321 (info := (1, 2, 3))
322
323 self.assertEqual(info, (1, 2, 3))
324
325 def test_named_expression_assignment_05(self):
326 (x := 1, 2)
327
328 self.assertEqual(x, 1)
329
330 def test_named_expression_assignment_06(self):
331 (z := (y := (x := 0)))
332
333 self.assertEqual(x, 0)
334 self.assertEqual(y, 0)
335 self.assertEqual(z, 0)
336
337 def test_named_expression_assignment_07(self):
338 (loc := (1, 2))
339
340 self.assertEqual(loc, (1, 2))
341
342 def test_named_expression_assignment_08(self):
343 if spam := "eggs":
344 self.assertEqual(spam, "eggs")
345 else: self.fail("variable was not assigned using named expression")
346
347 def test_named_expression_assignment_09(self):
348 if True and (spam := True):
349 self.assertTrue(spam)
350 else: self.fail("variable was not assigned using named expression")
351
352 def test_named_expression_assignment_10(self):
353 if (match := 10) == 10:
354 pass
355 else: self.fail("variable was not assigned using named expression")
356
357 def test_named_expression_assignment_11(self):
358 def spam(a):
359 return a
360 input_data = [1, 2, 3]
361 res = [(x, y, x/y) for x in input_data if (y := spam(x)) > 0]
362
363 self.assertEqual(res, [(1, 1, 1.0), (2, 2, 1.0), (3, 3, 1.0)])
364
365 def test_named_expression_assignment_12(self):
366 def spam(a):
367 return a
368 res = [[y := spam(x), x/y] for x in range(1, 5)]
369
370 self.assertEqual(res, [[1, 1.0], [2, 1.0], [3, 1.0], [4, 1.0]])
371
372 def test_named_expression_assignment_13(self):
373 length = len(lines := [1, 2])
374
375 self.assertEqual(length, 2)
376 self.assertEqual(lines, [1,2])
377
378 def test_named_expression_assignment_14(self):
379 """
380 Where all variables are positive integers, and a is at least as large
381 as the n'th root of x, this algorithm returns the floor of the n'th
382 root of x (and roughly doubling the number of accurate bits per
383 iteration):
384 """
385 a = 9
386 n = 2
387 x = 3
388
389 while a > (d := x // a**(n-1)):
390 a = ((n-1)*a + d) // n
391
392 self.assertEqual(a, 1)
393
394 def test_named_expression_assignment_15(self):
395 while a := False:
396 pass # This will not run
397
398 self.assertEqual(a, False)
399
400 def test_named_expression_assignment_16(self):
401 a, b = 1, 2
402 fib = {(c := a): (a := b) + (b := a + c) - b for __ in range(6)}
403 self.assertEqual(fib, {1: 2, 2: 3, 3: 5, 5: 8, 8: 13, 13: 21})
404
405 def test_named_expression_assignment_17(self):
406 a = [1]
407 element = a[b:=0]
408 self.assertEqual(b, 0)
409 self.assertEqual(element, a[0])
410
411 def test_named_expression_assignment_18(self):
412 class ESC[4;38;5;81mTwoDimensionalList:
413 def __init__(self, two_dimensional_list):
414 self.two_dimensional_list = two_dimensional_list
415
416 def __getitem__(self, index):
417 return self.two_dimensional_list[index[0]][index[1]]
418
419 a = TwoDimensionalList([[1], [2]])
420 element = a[b:=0, c:=0]
421 self.assertEqual(b, 0)
422 self.assertEqual(c, 0)
423 self.assertEqual(element, a.two_dimensional_list[b][c])
424
425
426
427 class ESC[4;38;5;81mNamedExpressionScopeTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
428
429 def test_named_expression_scope_01(self):
430 code = """def spam():
431 (a := 5)
432 print(a)"""
433
434 with self.assertRaisesRegex(NameError, "name 'a' is not defined"):
435 exec(code, {}, {})
436
437 def test_named_expression_scope_02(self):
438 total = 0
439 partial_sums = [total := total + v for v in range(5)]
440
441 self.assertEqual(partial_sums, [0, 1, 3, 6, 10])
442 self.assertEqual(total, 10)
443
444 def test_named_expression_scope_03(self):
445 containsOne = any((lastNum := num) == 1 for num in [1, 2, 3])
446
447 self.assertTrue(containsOne)
448 self.assertEqual(lastNum, 1)
449
450 def test_named_expression_scope_04(self):
451 def spam(a):
452 return a
453 res = [[y := spam(x), x/y] for x in range(1, 5)]
454
455 self.assertEqual(y, 4)
456
457 def test_named_expression_scope_05(self):
458 def spam(a):
459 return a
460 input_data = [1, 2, 3]
461 res = [(x, y, x/y) for x in input_data if (y := spam(x)) > 0]
462
463 self.assertEqual(res, [(1, 1, 1.0), (2, 2, 1.0), (3, 3, 1.0)])
464 self.assertEqual(y, 3)
465
466 def test_named_expression_scope_06(self):
467 res = [[spam := i for i in range(3)] for j in range(2)]
468
469 self.assertEqual(res, [[0, 1, 2], [0, 1, 2]])
470 self.assertEqual(spam, 2)
471
472 def test_named_expression_scope_07(self):
473 len(lines := [1, 2])
474
475 self.assertEqual(lines, [1, 2])
476
477 def test_named_expression_scope_08(self):
478 def spam(a):
479 return a
480
481 def eggs(b):
482 return b * 2
483
484 res = [spam(a := eggs(b := h)) for h in range(2)]
485
486 self.assertEqual(res, [0, 2])
487 self.assertEqual(a, 2)
488 self.assertEqual(b, 1)
489
490 def test_named_expression_scope_09(self):
491 def spam(a):
492 return a
493
494 def eggs(b):
495 return b * 2
496
497 res = [spam(a := eggs(a := h)) for h in range(2)]
498
499 self.assertEqual(res, [0, 2])
500 self.assertEqual(a, 2)
501
502 def test_named_expression_scope_10(self):
503 res = [b := [a := 1 for i in range(2)] for j in range(2)]
504
505 self.assertEqual(res, [[1, 1], [1, 1]])
506 self.assertEqual(a, 1)
507 self.assertEqual(b, [1, 1])
508
509 def test_named_expression_scope_11(self):
510 res = [j := i for i in range(5)]
511
512 self.assertEqual(res, [0, 1, 2, 3, 4])
513 self.assertEqual(j, 4)
514
515 def test_named_expression_scope_17(self):
516 b = 0
517 res = [b := i + b for i in range(5)]
518
519 self.assertEqual(res, [0, 1, 3, 6, 10])
520 self.assertEqual(b, 10)
521
522 def test_named_expression_scope_18(self):
523 def spam(a):
524 return a
525
526 res = spam(b := 2)
527
528 self.assertEqual(res, 2)
529 self.assertEqual(b, 2)
530
531 def test_named_expression_scope_19(self):
532 def spam(a):
533 return a
534
535 res = spam((b := 2))
536
537 self.assertEqual(res, 2)
538 self.assertEqual(b, 2)
539
540 def test_named_expression_scope_20(self):
541 def spam(a):
542 return a
543
544 res = spam(a=(b := 2))
545
546 self.assertEqual(res, 2)
547 self.assertEqual(b, 2)
548
549 def test_named_expression_scope_21(self):
550 def spam(a, b):
551 return a + b
552
553 res = spam(c := 2, b=1)
554
555 self.assertEqual(res, 3)
556 self.assertEqual(c, 2)
557
558 def test_named_expression_scope_22(self):
559 def spam(a, b):
560 return a + b
561
562 res = spam((c := 2), b=1)
563
564 self.assertEqual(res, 3)
565 self.assertEqual(c, 2)
566
567 def test_named_expression_scope_23(self):
568 def spam(a, b):
569 return a + b
570
571 res = spam(b=(c := 2), a=1)
572
573 self.assertEqual(res, 3)
574 self.assertEqual(c, 2)
575
576 def test_named_expression_scope_24(self):
577 a = 10
578 def spam():
579 nonlocal a
580 (a := 20)
581 spam()
582
583 self.assertEqual(a, 20)
584
585 def test_named_expression_scope_25(self):
586 ns = {}
587 code = """a = 10
588 def spam():
589 global a
590 (a := 20)
591 spam()"""
592
593 exec(code, ns, {})
594
595 self.assertEqual(ns["a"], 20)
596
597 def test_named_expression_variable_reuse_in_comprehensions(self):
598 # The compiler is expected to raise syntax error for comprehension
599 # iteration variables, but should be fine with rebinding of other
600 # names (e.g. globals, nonlocals, other assignment expressions)
601
602 # The cases are all defined to produce the same expected result
603 # Each comprehension is checked at both function scope and module scope
604 rebinding = "[x := i for i in range(3) if (x := i) or not x]"
605 filter_ref = "[x := i for i in range(3) if x or not x]"
606 body_ref = "[x for i in range(3) if (x := i) or not x]"
607 nested_ref = "[j for i in range(3) if x or not x for j in range(3) if (x := i)][:-3]"
608 cases = [
609 ("Rebind global", f"x = 1; result = {rebinding}"),
610 ("Rebind nonlocal", f"result, x = (lambda x=1: ({rebinding}, x))()"),
611 ("Filter global", f"x = 1; result = {filter_ref}"),
612 ("Filter nonlocal", f"result, x = (lambda x=1: ({filter_ref}, x))()"),
613 ("Body global", f"x = 1; result = {body_ref}"),
614 ("Body nonlocal", f"result, x = (lambda x=1: ({body_ref}, x))()"),
615 ("Nested global", f"x = 1; result = {nested_ref}"),
616 ("Nested nonlocal", f"result, x = (lambda x=1: ({nested_ref}, x))()"),
617 ]
618 for case, code in cases:
619 with self.subTest(case=case):
620 ns = {}
621 exec(code, ns)
622 self.assertEqual(ns["x"], 2)
623 self.assertEqual(ns["result"], [0, 1, 2])
624
625 def test_named_expression_global_scope(self):
626 sentinel = object()
627 global GLOBAL_VAR
628 def f():
629 global GLOBAL_VAR
630 [GLOBAL_VAR := sentinel for _ in range(1)]
631 self.assertEqual(GLOBAL_VAR, sentinel)
632 try:
633 f()
634 self.assertEqual(GLOBAL_VAR, sentinel)
635 finally:
636 GLOBAL_VAR = None
637
638 def test_named_expression_global_scope_no_global_keyword(self):
639 sentinel = object()
640 def f():
641 GLOBAL_VAR = None
642 [GLOBAL_VAR := sentinel for _ in range(1)]
643 self.assertEqual(GLOBAL_VAR, sentinel)
644 f()
645 self.assertEqual(GLOBAL_VAR, None)
646
647 def test_named_expression_nonlocal_scope(self):
648 sentinel = object()
649 def f():
650 nonlocal_var = None
651 def g():
652 nonlocal nonlocal_var
653 [nonlocal_var := sentinel for _ in range(1)]
654 g()
655 self.assertEqual(nonlocal_var, sentinel)
656 f()
657
658 def test_named_expression_nonlocal_scope_no_nonlocal_keyword(self):
659 sentinel = object()
660 def f():
661 nonlocal_var = None
662 def g():
663 [nonlocal_var := sentinel for _ in range(1)]
664 g()
665 self.assertEqual(nonlocal_var, None)
666 f()
667
668 def test_named_expression_scope_in_genexp(self):
669 a = 1
670 b = [1, 2, 3, 4]
671 genexp = (c := i + a for i in b)
672
673 self.assertNotIn("c", locals())
674 for idx, elem in enumerate(genexp):
675 self.assertEqual(elem, b[idx] + a)
676
677
678 if __name__ == "__main__":
679 unittest.main()