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_invalid_rebinding_list_comprehension_iteration_variable(self):
118 cases = [
119 ("Local reuse", 'i', "[i := 0 for i in range(5)]"),
120 ("Nested reuse", 'j', "[[(j := 0) for i in range(5)] for j in range(5)]"),
121 ("Reuse inner loop target", 'j', "[(j := 0) for i in range(5) for j in range(5)]"),
122 ("Unpacking reuse", 'i', "[i := 0 for i, j in [(0, 1)]]"),
123 ("Reuse in loop condition", 'i', "[i+1 for i in range(5) if (i := 0)]"),
124 ("Unreachable reuse", 'i', "[False or (i:=0) for i in range(5)]"),
125 ("Unreachable nested reuse", 'i',
126 "[(i, j) for i in range(5) for j in range(5) if True or (i:=10)]"),
127 ]
128 for case, target, code in cases:
129 msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'"
130 with self.subTest(case=case):
131 with self.assertRaisesRegex(SyntaxError, msg):
132 exec(code, {}, {})
133
134 def test_named_expression_invalid_rebinding_list_comprehension_inner_loop(self):
135 cases = [
136 ("Inner reuse", 'j', "[i for i in range(5) if (j := 0) for j in range(5)]"),
137 ("Inner unpacking reuse", 'j', "[i for i in range(5) if (j := 0) for j, k in [(0, 1)]]"),
138 ]
139 for case, target, code in cases:
140 msg = f"comprehension inner loop cannot rebind assignment expression target '{target}'"
141 with self.subTest(case=case):
142 with self.assertRaisesRegex(SyntaxError, msg):
143 exec(code, {}) # Module scope
144 with self.assertRaisesRegex(SyntaxError, msg):
145 exec(code, {}, {}) # Class scope
146 with self.assertRaisesRegex(SyntaxError, msg):
147 exec(f"lambda: {code}", {}) # Function scope
148
149 def test_named_expression_invalid_list_comprehension_iterable_expression(self):
150 cases = [
151 ("Top level", "[i for i in (i := range(5))]"),
152 ("Inside tuple", "[i for i in (2, 3, i := range(5))]"),
153 ("Inside list", "[i for i in [2, 3, i := range(5)]]"),
154 ("Different name", "[i for i in (j := range(5))]"),
155 ("Lambda expression", "[i for i in (lambda:(j := range(5)))()]"),
156 ("Inner loop", "[i for i in range(5) for j in (i := range(5))]"),
157 ("Nested comprehension", "[i for i in [j for j in (k := range(5))]]"),
158 ("Nested comprehension condition", "[i for i in [j for j in range(5) if (j := True)]]"),
159 ("Nested comprehension body", "[i for i in [(j := True) for j in range(5)]]"),
160 ]
161 msg = "assignment expression cannot be used in a comprehension iterable expression"
162 for case, code in cases:
163 with self.subTest(case=case):
164 with self.assertRaisesRegex(SyntaxError, msg):
165 exec(code, {}) # Module scope
166 with self.assertRaisesRegex(SyntaxError, msg):
167 exec(code, {}, {}) # Class scope
168 with self.assertRaisesRegex(SyntaxError, msg):
169 exec(f"lambda: {code}", {}) # Function scope
170
171 def test_named_expression_invalid_rebinding_set_comprehension_iteration_variable(self):
172 cases = [
173 ("Local reuse", 'i', "{i := 0 for i in range(5)}"),
174 ("Nested reuse", 'j', "{{(j := 0) for i in range(5)} for j in range(5)}"),
175 ("Reuse inner loop target", 'j', "{(j := 0) for i in range(5) for j in range(5)}"),
176 ("Unpacking reuse", 'i', "{i := 0 for i, j in {(0, 1)}}"),
177 ("Reuse in loop condition", 'i', "{i+1 for i in range(5) if (i := 0)}"),
178 ("Unreachable reuse", 'i', "{False or (i:=0) for i in range(5)}"),
179 ("Unreachable nested reuse", 'i',
180 "{(i, j) for i in range(5) for j in range(5) if True or (i:=10)}"),
181 ]
182 for case, target, code in cases:
183 msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'"
184 with self.subTest(case=case):
185 with self.assertRaisesRegex(SyntaxError, msg):
186 exec(code, {}, {})
187
188 def test_named_expression_invalid_rebinding_set_comprehension_inner_loop(self):
189 cases = [
190 ("Inner reuse", 'j', "{i for i in range(5) if (j := 0) for j in range(5)}"),
191 ("Inner unpacking reuse", 'j', "{i for i in range(5) if (j := 0) for j, k in {(0, 1)}}"),
192 ]
193 for case, target, code in cases:
194 msg = f"comprehension inner loop cannot rebind assignment expression target '{target}'"
195 with self.subTest(case=case):
196 with self.assertRaisesRegex(SyntaxError, msg):
197 exec(code, {}) # Module scope
198 with self.assertRaisesRegex(SyntaxError, msg):
199 exec(code, {}, {}) # Class scope
200 with self.assertRaisesRegex(SyntaxError, msg):
201 exec(f"lambda: {code}", {}) # Function scope
202
203 def test_named_expression_invalid_set_comprehension_iterable_expression(self):
204 cases = [
205 ("Top level", "{i for i in (i := range(5))}"),
206 ("Inside tuple", "{i for i in (2, 3, i := range(5))}"),
207 ("Inside list", "{i for i in {2, 3, i := range(5)}}"),
208 ("Different name", "{i for i in (j := range(5))}"),
209 ("Lambda expression", "{i for i in (lambda:(j := range(5)))()}"),
210 ("Inner loop", "{i for i in range(5) for j in (i := range(5))}"),
211 ("Nested comprehension", "{i for i in {j for j in (k := range(5))}}"),
212 ("Nested comprehension condition", "{i for i in {j for j in range(5) if (j := True)}}"),
213 ("Nested comprehension body", "{i for i in {(j := True) for j in range(5)}}"),
214 ]
215 msg = "assignment expression cannot be used in a comprehension iterable expression"
216 for case, code in cases:
217 with self.subTest(case=case):
218 with self.assertRaisesRegex(SyntaxError, msg):
219 exec(code, {}) # Module scope
220 with self.assertRaisesRegex(SyntaxError, msg):
221 exec(code, {}, {}) # Class scope
222 with self.assertRaisesRegex(SyntaxError, msg):
223 exec(f"lambda: {code}", {}) # Function scope
224
225
226 class ESC[4;38;5;81mNamedExpressionAssignmentTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
227
228 def test_named_expression_assignment_01(self):
229 (a := 10)
230
231 self.assertEqual(a, 10)
232
233 def test_named_expression_assignment_02(self):
234 a = 20
235 (a := a)
236
237 self.assertEqual(a, 20)
238
239 def test_named_expression_assignment_03(self):
240 (total := 1 + 2)
241
242 self.assertEqual(total, 3)
243
244 def test_named_expression_assignment_04(self):
245 (info := (1, 2, 3))
246
247 self.assertEqual(info, (1, 2, 3))
248
249 def test_named_expression_assignment_05(self):
250 (x := 1, 2)
251
252 self.assertEqual(x, 1)
253
254 def test_named_expression_assignment_06(self):
255 (z := (y := (x := 0)))
256
257 self.assertEqual(x, 0)
258 self.assertEqual(y, 0)
259 self.assertEqual(z, 0)
260
261 def test_named_expression_assignment_07(self):
262 (loc := (1, 2))
263
264 self.assertEqual(loc, (1, 2))
265
266 def test_named_expression_assignment_08(self):
267 if spam := "eggs":
268 self.assertEqual(spam, "eggs")
269 else: self.fail("variable was not assigned using named expression")
270
271 def test_named_expression_assignment_09(self):
272 if True and (spam := True):
273 self.assertTrue(spam)
274 else: self.fail("variable was not assigned using named expression")
275
276 def test_named_expression_assignment_10(self):
277 if (match := 10) == 10:
278 pass
279 else: self.fail("variable was not assigned using named expression")
280
281 def test_named_expression_assignment_11(self):
282 def spam(a):
283 return a
284 input_data = [1, 2, 3]
285 res = [(x, y, x/y) for x in input_data if (y := spam(x)) > 0]
286
287 self.assertEqual(res, [(1, 1, 1.0), (2, 2, 1.0), (3, 3, 1.0)])
288
289 def test_named_expression_assignment_12(self):
290 def spam(a):
291 return a
292 res = [[y := spam(x), x/y] for x in range(1, 5)]
293
294 self.assertEqual(res, [[1, 1.0], [2, 1.0], [3, 1.0], [4, 1.0]])
295
296 def test_named_expression_assignment_13(self):
297 length = len(lines := [1, 2])
298
299 self.assertEqual(length, 2)
300 self.assertEqual(lines, [1,2])
301
302 def test_named_expression_assignment_14(self):
303 """
304 Where all variables are positive integers, and a is at least as large
305 as the n'th root of x, this algorithm returns the floor of the n'th
306 root of x (and roughly doubling the number of accurate bits per
307 iteration):
308 """
309 a = 9
310 n = 2
311 x = 3
312
313 while a > (d := x // a**(n-1)):
314 a = ((n-1)*a + d) // n
315
316 self.assertEqual(a, 1)
317
318 def test_named_expression_assignment_15(self):
319 while a := False:
320 pass # This will not run
321
322 self.assertEqual(a, False)
323
324 def test_named_expression_assignment_16(self):
325 a, b = 1, 2
326 fib = {(c := a): (a := b) + (b := a + c) - b for __ in range(6)}
327 self.assertEqual(fib, {1: 2, 2: 3, 3: 5, 5: 8, 8: 13, 13: 21})
328
329 def test_named_expression_assignment_17(self):
330 a = [1]
331 element = a[b:=0]
332 self.assertEqual(b, 0)
333 self.assertEqual(element, a[0])
334
335 def test_named_expression_assignment_18(self):
336 class ESC[4;38;5;81mTwoDimensionalList:
337 def __init__(self, two_dimensional_list):
338 self.two_dimensional_list = two_dimensional_list
339
340 def __getitem__(self, index):
341 return self.two_dimensional_list[index[0]][index[1]]
342
343 a = TwoDimensionalList([[1], [2]])
344 element = a[b:=0, c:=0]
345 self.assertEqual(b, 0)
346 self.assertEqual(c, 0)
347 self.assertEqual(element, a.two_dimensional_list[b][c])
348
349
350
351 class ESC[4;38;5;81mNamedExpressionScopeTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
352
353 def test_named_expression_scope_01(self):
354 code = """def spam():
355 (a := 5)
356 print(a)"""
357
358 with self.assertRaisesRegex(NameError, "name 'a' is not defined"):
359 exec(code, {}, {})
360
361 def test_named_expression_scope_02(self):
362 total = 0
363 partial_sums = [total := total + v for v in range(5)]
364
365 self.assertEqual(partial_sums, [0, 1, 3, 6, 10])
366 self.assertEqual(total, 10)
367
368 def test_named_expression_scope_03(self):
369 containsOne = any((lastNum := num) == 1 for num in [1, 2, 3])
370
371 self.assertTrue(containsOne)
372 self.assertEqual(lastNum, 1)
373
374 def test_named_expression_scope_04(self):
375 def spam(a):
376 return a
377 res = [[y := spam(x), x/y] for x in range(1, 5)]
378
379 self.assertEqual(y, 4)
380
381 def test_named_expression_scope_05(self):
382 def spam(a):
383 return a
384 input_data = [1, 2, 3]
385 res = [(x, y, x/y) for x in input_data if (y := spam(x)) > 0]
386
387 self.assertEqual(res, [(1, 1, 1.0), (2, 2, 1.0), (3, 3, 1.0)])
388 self.assertEqual(y, 3)
389
390 def test_named_expression_scope_06(self):
391 res = [[spam := i for i in range(3)] for j in range(2)]
392
393 self.assertEqual(res, [[0, 1, 2], [0, 1, 2]])
394 self.assertEqual(spam, 2)
395
396 def test_named_expression_scope_07(self):
397 len(lines := [1, 2])
398
399 self.assertEqual(lines, [1, 2])
400
401 def test_named_expression_scope_08(self):
402 def spam(a):
403 return a
404
405 def eggs(b):
406 return b * 2
407
408 res = [spam(a := eggs(b := h)) for h in range(2)]
409
410 self.assertEqual(res, [0, 2])
411 self.assertEqual(a, 2)
412 self.assertEqual(b, 1)
413
414 def test_named_expression_scope_09(self):
415 def spam(a):
416 return a
417
418 def eggs(b):
419 return b * 2
420
421 res = [spam(a := eggs(a := h)) for h in range(2)]
422
423 self.assertEqual(res, [0, 2])
424 self.assertEqual(a, 2)
425
426 def test_named_expression_scope_10(self):
427 res = [b := [a := 1 for i in range(2)] for j in range(2)]
428
429 self.assertEqual(res, [[1, 1], [1, 1]])
430 self.assertEqual(a, 1)
431 self.assertEqual(b, [1, 1])
432
433 def test_named_expression_scope_11(self):
434 res = [j := i for i in range(5)]
435
436 self.assertEqual(res, [0, 1, 2, 3, 4])
437 self.assertEqual(j, 4)
438
439 def test_named_expression_scope_17(self):
440 b = 0
441 res = [b := i + b for i in range(5)]
442
443 self.assertEqual(res, [0, 1, 3, 6, 10])
444 self.assertEqual(b, 10)
445
446 def test_named_expression_scope_18(self):
447 def spam(a):
448 return a
449
450 res = spam(b := 2)
451
452 self.assertEqual(res, 2)
453 self.assertEqual(b, 2)
454
455 def test_named_expression_scope_19(self):
456 def spam(a):
457 return a
458
459 res = spam((b := 2))
460
461 self.assertEqual(res, 2)
462 self.assertEqual(b, 2)
463
464 def test_named_expression_scope_20(self):
465 def spam(a):
466 return a
467
468 res = spam(a=(b := 2))
469
470 self.assertEqual(res, 2)
471 self.assertEqual(b, 2)
472
473 def test_named_expression_scope_21(self):
474 def spam(a, b):
475 return a + b
476
477 res = spam(c := 2, b=1)
478
479 self.assertEqual(res, 3)
480 self.assertEqual(c, 2)
481
482 def test_named_expression_scope_22(self):
483 def spam(a, b):
484 return a + b
485
486 res = spam((c := 2), b=1)
487
488 self.assertEqual(res, 3)
489 self.assertEqual(c, 2)
490
491 def test_named_expression_scope_23(self):
492 def spam(a, b):
493 return a + b
494
495 res = spam(b=(c := 2), a=1)
496
497 self.assertEqual(res, 3)
498 self.assertEqual(c, 2)
499
500 def test_named_expression_scope_24(self):
501 a = 10
502 def spam():
503 nonlocal a
504 (a := 20)
505 spam()
506
507 self.assertEqual(a, 20)
508
509 def test_named_expression_scope_25(self):
510 ns = {}
511 code = """a = 10
512 def spam():
513 global a
514 (a := 20)
515 spam()"""
516
517 exec(code, ns, {})
518
519 self.assertEqual(ns["a"], 20)
520
521 def test_named_expression_variable_reuse_in_comprehensions(self):
522 # The compiler is expected to raise syntax error for comprehension
523 # iteration variables, but should be fine with rebinding of other
524 # names (e.g. globals, nonlocals, other assignment expressions)
525
526 # The cases are all defined to produce the same expected result
527 # Each comprehension is checked at both function scope and module scope
528 rebinding = "[x := i for i in range(3) if (x := i) or not x]"
529 filter_ref = "[x := i for i in range(3) if x or not x]"
530 body_ref = "[x for i in range(3) if (x := i) or not x]"
531 nested_ref = "[j for i in range(3) if x or not x for j in range(3) if (x := i)][:-3]"
532 cases = [
533 ("Rebind global", f"x = 1; result = {rebinding}"),
534 ("Rebind nonlocal", f"result, x = (lambda x=1: ({rebinding}, x))()"),
535 ("Filter global", f"x = 1; result = {filter_ref}"),
536 ("Filter nonlocal", f"result, x = (lambda x=1: ({filter_ref}, x))()"),
537 ("Body global", f"x = 1; result = {body_ref}"),
538 ("Body nonlocal", f"result, x = (lambda x=1: ({body_ref}, x))()"),
539 ("Nested global", f"x = 1; result = {nested_ref}"),
540 ("Nested nonlocal", f"result, x = (lambda x=1: ({nested_ref}, x))()"),
541 ]
542 for case, code in cases:
543 with self.subTest(case=case):
544 ns = {}
545 exec(code, ns)
546 self.assertEqual(ns["x"], 2)
547 self.assertEqual(ns["result"], [0, 1, 2])
548
549 def test_named_expression_global_scope(self):
550 sentinel = object()
551 global GLOBAL_VAR
552 def f():
553 global GLOBAL_VAR
554 [GLOBAL_VAR := sentinel for _ in range(1)]
555 self.assertEqual(GLOBAL_VAR, sentinel)
556 try:
557 f()
558 self.assertEqual(GLOBAL_VAR, sentinel)
559 finally:
560 GLOBAL_VAR = None
561
562 def test_named_expression_global_scope_no_global_keyword(self):
563 sentinel = object()
564 def f():
565 GLOBAL_VAR = None
566 [GLOBAL_VAR := sentinel for _ in range(1)]
567 self.assertEqual(GLOBAL_VAR, sentinel)
568 f()
569 self.assertEqual(GLOBAL_VAR, None)
570
571 def test_named_expression_nonlocal_scope(self):
572 sentinel = object()
573 def f():
574 nonlocal_var = None
575 def g():
576 nonlocal nonlocal_var
577 [nonlocal_var := sentinel for _ in range(1)]
578 g()
579 self.assertEqual(nonlocal_var, sentinel)
580 f()
581
582 def test_named_expression_nonlocal_scope_no_nonlocal_keyword(self):
583 sentinel = object()
584 def f():
585 nonlocal_var = None
586 def g():
587 [nonlocal_var := sentinel for _ in range(1)]
588 g()
589 self.assertEqual(nonlocal_var, None)
590 f()
591
592 def test_named_expression_scope_in_genexp(self):
593 a = 1
594 b = [1, 2, 3, 4]
595 genexp = (c := i + a for i in b)
596
597 self.assertNotIn("c", locals())
598 for idx, elem in enumerate(genexp):
599 self.assertEqual(elem, b[idx] + a)
600
601
602 if __name__ == "__main__":
603 unittest.main()