summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_named_expressions.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_named_expressions.py')
-rw-r--r--Lib/test/test_named_expressions.py120
1 files changed, 84 insertions, 36 deletions
diff --git a/Lib/test/test_named_expressions.py b/Lib/test/test_named_expressions.py
index f73e6fe..b1027ce 100644
--- a/Lib/test/test_named_expressions.py
+++ b/Lib/test/test_named_expressions.py
@@ -104,15 +104,69 @@ class NamedExpressionInvalidTest(unittest.TestCase):
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
exec(code, {}, {})
- def test_named_expression_invalid_18(self):
+ def test_named_expression_invalid_in_class_body(self):
code = """class Foo():
[(42, 1 + ((( j := i )))) for i in range(5)]
"""
- with self.assertRaisesRegex(TargetScopeError,
- "named expression within a comprehension cannot be used in a class body"):
+ with self.assertRaisesRegex(SyntaxError,
+ "assignment expression within a comprehension cannot be used in a class body"):
exec(code, {}, {})
+ def test_named_expression_invalid_rebinding_comprehension_iteration_variable(self):
+ cases = [
+ ("Local reuse", 'i', "[i := 0 for i in range(5)]"),
+ ("Nested reuse", 'j', "[[(j := 0) for i in range(5)] for j in range(5)]"),
+ ("Reuse inner loop target", 'j', "[(j := 0) for i in range(5) for j in range(5)]"),
+ ("Unpacking reuse", 'i', "[i := 0 for i, j in [(0, 1)]]"),
+ ("Reuse in loop condition", 'i', "[i+1 for i in range(5) if (i := 0)]"),
+ ("Unreachable reuse", 'i', "[False or (i:=0) for i in range(5)]"),
+ ("Unreachable nested reuse", 'i',
+ "[(i, j) for i in range(5) for j in range(5) if True or (i:=10)]"),
+ ]
+ for case, target, code in cases:
+ msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'"
+ with self.subTest(case=case):
+ with self.assertRaisesRegex(SyntaxError, msg):
+ exec(code, {}, {})
+
+ def test_named_expression_invalid_rebinding_comprehension_inner_loop(self):
+ cases = [
+ ("Inner reuse", 'j', "[i for i in range(5) if (j := 0) for j in range(5)]"),
+ ("Inner unpacking reuse", 'j', "[i for i in range(5) if (j := 0) for j, k in [(0, 1)]]"),
+ ]
+ for case, target, code in cases:
+ msg = f"comprehension inner loop cannot rebind assignment expression target '{target}'"
+ with self.subTest(case=case):
+ with self.assertRaisesRegex(SyntaxError, msg):
+ exec(code, {}) # Module scope
+ with self.assertRaisesRegex(SyntaxError, msg):
+ exec(code, {}, {}) # Class scope
+ with self.assertRaisesRegex(SyntaxError, msg):
+ exec(f"lambda: {code}", {}) # Function scope
+
+ def test_named_expression_invalid_comprehension_iterable_expression(self):
+ cases = [
+ ("Top level", "[i for i in (i := range(5))]"),
+ ("Inside tuple", "[i for i in (2, 3, i := range(5))]"),
+ ("Inside list", "[i for i in [2, 3, i := range(5)]]"),
+ ("Different name", "[i for i in (j := range(5))]"),
+ ("Lambda expression", "[i for i in (lambda:(j := range(5)))()]"),
+ ("Inner loop", "[i for i in range(5) for j in (i := range(5))]"),
+ ("Nested comprehension", "[i for i in [j for j in (k := range(5))]]"),
+ ("Nested comprehension condition", "[i for i in [j for j in range(5) if (j := True)]]"),
+ ("Nested comprehension body", "[i for i in [(j := True) for j in range(5)]]"),
+ ]
+ msg = "assignment expression cannot be used in a comprehension iterable expression"
+ for case, code in cases:
+ with self.subTest(case=case):
+ with self.assertRaisesRegex(SyntaxError, msg):
+ exec(code, {}) # Module scope
+ with self.assertRaisesRegex(SyntaxError, msg):
+ exec(code, {}, {}) # Class scope
+ with self.assertRaisesRegex(SyntaxError, msg):
+ exec(f"lambda: {code}", {}) # Function scope
+
class NamedExpressionAssignmentTest(unittest.TestCase):
@@ -306,39 +360,6 @@ print(a)"""
self.assertEqual(res, [0, 1, 2, 3, 4])
self.assertEqual(j, 4)
- def test_named_expression_scope_12(self):
- res = [i := i for i in range(5)]
-
- self.assertEqual(res, [0, 1, 2, 3, 4])
- self.assertEqual(i, 4)
-
- def test_named_expression_scope_13(self):
- res = [i := 0 for i, j in [(1, 2), (3, 4)]]
-
- self.assertEqual(res, [0, 0])
- self.assertEqual(i, 0)
-
- def test_named_expression_scope_14(self):
- res = [(i := 0, j := 1) for i, j in [(1, 2), (3, 4)]]
-
- self.assertEqual(res, [(0, 1), (0, 1)])
- self.assertEqual(i, 0)
- self.assertEqual(j, 1)
-
- def test_named_expression_scope_15(self):
- res = [(i := i, j := j) for i, j in [(1, 2), (3, 4)]]
-
- self.assertEqual(res, [(1, 2), (3, 4)])
- self.assertEqual(i, 3)
- self.assertEqual(j, 4)
-
- def test_named_expression_scope_16(self):
- res = [(i := j, j := i) for i, j in [(1, 2), (3, 4)]]
-
- self.assertEqual(res, [(2, 2), (4, 4)])
- self.assertEqual(i, 4)
- self.assertEqual(j, 4)
-
def test_named_expression_scope_17(self):
b = 0
res = [b := i + b for i in range(5)]
@@ -421,6 +442,33 @@ spam()"""
self.assertEqual(ns["a"], 20)
+ def test_named_expression_variable_reuse_in_comprehensions(self):
+ # The compiler is expected to raise syntax error for comprehension
+ # iteration variables, but should be fine with rebinding of other
+ # names (e.g. globals, nonlocals, other assignment expressions)
+
+ # The cases are all defined to produce the same expected result
+ # Each comprehension is checked at both function scope and module scope
+ rebinding = "[x := i for i in range(3) if (x := i) or not x]"
+ filter_ref = "[x := i for i in range(3) if x or not x]"
+ body_ref = "[x for i in range(3) if (x := i) or not x]"
+ nested_ref = "[j for i in range(3) if x or not x for j in range(3) if (x := i)][:-3]"
+ cases = [
+ ("Rebind global", f"x = 1; result = {rebinding}"),
+ ("Rebind nonlocal", f"result, x = (lambda x=1: ({rebinding}, x))()"),
+ ("Filter global", f"x = 1; result = {filter_ref}"),
+ ("Filter nonlocal", f"result, x = (lambda x=1: ({filter_ref}, x))()"),
+ ("Body global", f"x = 1; result = {body_ref}"),
+ ("Body nonlocal", f"result, x = (lambda x=1: ({body_ref}, x))()"),
+ ("Nested global", f"x = 1; result = {nested_ref}"),
+ ("Nested nonlocal", f"result, x = (lambda x=1: ({nested_ref}, x))()"),
+ ]
+ for case, code in cases:
+ with self.subTest(case=case):
+ ns = {}
+ exec(code, ns)
+ self.assertEqual(ns["x"], 2)
+ self.assertEqual(ns["result"], [0, 1, 2])
if __name__ == "__main__":
unittest.main()