(root)/
Python-3.12.0/
Lib/
test/
test_named_expressions.py
       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()