diff options
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/exception_hierarchy.txt | 1 | ||||
-rw-r--r-- | Lib/test/test_named_expressions.py | 415 | ||||
-rw-r--r-- | Lib/test/test_parser.py | 46 | ||||
-rw-r--r-- | Lib/test/test_tokenize.py | 1 |
4 files changed, 458 insertions, 5 deletions
diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt index 763a6c899b..15f4491cf2 100644 --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -42,6 +42,7 @@ BaseException | +-- NotImplementedError | +-- RecursionError +-- SyntaxError + | +-- TargetScopeError | +-- IndentationError | +-- TabError +-- SystemError diff --git a/Lib/test/test_named_expressions.py b/Lib/test/test_named_expressions.py new file mode 100644 index 0000000000..e49fd7de20 --- /dev/null +++ b/Lib/test/test_named_expressions.py @@ -0,0 +1,415 @@ +import os +import unittest + + +class NamedExpressionInvalidTest(unittest.TestCase): + + def test_named_expression_invalid_01(self): + code = """x := 0""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_02(self): + code = """x = y := 0""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_03(self): + code = """y := f(x)""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_04(self): + code = """y0 = y1 := f(x)""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_06(self): + code = """((a, b) := (1, 2))""" + + with self.assertRaisesRegex(SyntaxError, "cannot use named assignment with tuple"): + exec(code, {}, {}) + + def test_named_expression_invalid_07(self): + code = """def spam(a = b := 42): pass""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_08(self): + code = """def spam(a: b := 42 = 5): pass""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_09(self): + code = """spam(a=b := 'c')""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_10(self): + code = """spam(x = y := f(x))""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_11(self): + code = """spam(a=1, b := 2)""" + + with self.assertRaisesRegex(SyntaxError, + "positional argument follows keyword argument"): + exec(code, {}, {}) + + def test_named_expression_invalid_12(self): + code = """spam(a=1, (b := 2))""" + + with self.assertRaisesRegex(SyntaxError, + "positional argument follows keyword argument"): + exec(code, {}, {}) + + def test_named_expression_invalid_13(self): + code = """spam(a=1, (b := 2))""" + + with self.assertRaisesRegex(SyntaxError, + "positional argument follows keyword argument"): + exec(code, {}, {}) + + def test_named_expression_invalid_14(self): + code = """(x := lambda: y := 1)""" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_15(self): + code = """(lambda: x := 1)""" + + with self.assertRaisesRegex(SyntaxError, + "cannot use named assignment with lambda"): + exec(code, {}, {}) + + def test_named_expression_invalid_16(self): + code = "[i + 1 for i in i := [1,2]]" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_17(self): + code = "[i := 0, j := 1 for i, j in [(1, 2), (3, 4)]]" + + with self.assertRaisesRegex(SyntaxError, "invalid syntax"): + exec(code, {}, {}) + + def test_named_expression_invalid_18(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"): + exec(code, {}, {}) + + +class NamedExpressionAssignmentTest(unittest.TestCase): + + def test_named_expression_assignment_01(self): + (a := 10) + + self.assertEqual(a, 10) + + def test_named_expression_assignment_02(self): + a = 20 + (a := a) + + self.assertEqual(a, 20) + + def test_named_expression_assignment_03(self): + (total := 1 + 2) + + self.assertEqual(total, 3) + + def test_named_expression_assignment_04(self): + (info := (1, 2, 3)) + + self.assertEqual(info, (1, 2, 3)) + + def test_named_expression_assignment_05(self): + (x := 1, 2) + + self.assertEqual(x, 1) + + def test_named_expression_assignment_06(self): + (z := (y := (x := 0))) + + self.assertEqual(x, 0) + self.assertEqual(y, 0) + self.assertEqual(z, 0) + + def test_named_expression_assignment_07(self): + (loc := (1, 2)) + + self.assertEqual(loc, (1, 2)) + + def test_named_expression_assignment_08(self): + if spam := "eggs": + self.assertEqual(spam, "eggs") + else: self.fail("variable was not assigned using named expression") + + def test_named_expression_assignment_09(self): + if True and (spam := True): + self.assertTrue(spam) + else: self.fail("variable was not assigned using named expression") + + def test_named_expression_assignment_10(self): + if (match := 10) is 10: + pass + else: self.fail("variable was not assigned using named expression") + + def test_named_expression_assignment_11(self): + def spam(a): + return a + input_data = [1, 2, 3] + res = [(x, y, x/y) for x in input_data if (y := spam(x)) > 0] + + self.assertEqual(res, [(1, 1, 1.0), (2, 2, 1.0), (3, 3, 1.0)]) + + def test_named_expression_assignment_12(self): + def spam(a): + return a + res = [[y := spam(x), x/y] for x in range(1, 5)] + + self.assertEqual(res, [[1, 1.0], [2, 1.0], [3, 1.0], [4, 1.0]]) + + def test_named_expression_assignment_13(self): + length = len(lines := [1, 2]) + + self.assertEqual(length, 2) + self.assertEqual(lines, [1,2]) + + def test_named_expression_assignment_14(self): + """ + Where all variables are positive integers, and a is at least as large + as the n'th root of x, this algorithm returns the floor of the n'th + root of x (and roughly doubling the number of accurate bits per + iteration):: + """ + a = 9 + n = 2 + x = 3 + + while a > (d := x // a**(n-1)): + a = ((n-1)*a + d) // n + + self.assertEqual(a, 1) + + +class NamedExpressionScopeTest(unittest.TestCase): + + def test_named_expression_scope_01(self): + code = """def spam(): + (a := 5) +print(a)""" + + with self.assertRaisesRegex(NameError, "name 'a' is not defined"): + exec(code, {}, {}) + + def test_named_expression_scope_02(self): + total = 0 + partial_sums = [total := total + v for v in range(5)] + + self.assertEqual(partial_sums, [0, 1, 3, 6, 10]) + self.assertEqual(total, 10) + + def test_named_expression_scope_03(self): + containsOne = any((lastNum := num) == 1 for num in [1, 2, 3]) + + self.assertTrue(containsOne) + self.assertEqual(lastNum, 1) + + def test_named_expression_scope_04(self): + def spam(a): + return a + res = [[y := spam(x), x/y] for x in range(1, 5)] + + self.assertEqual(y, 4) + + def test_named_expression_scope_05(self): + def spam(a): + return a + input_data = [1, 2, 3] + res = [(x, y, x/y) for x in input_data if (y := spam(x)) > 0] + + self.assertEqual(res, [(1, 1, 1.0), (2, 2, 1.0), (3, 3, 1.0)]) + self.assertEqual(y, 3) + + def test_named_expression_scope_06(self): + res = [[spam := i for i in range(3)] for j in range(2)] + + self.assertEqual(res, [[0, 1, 2], [0, 1, 2]]) + self.assertEqual(spam, 2) + + def test_named_expression_scope_07(self): + len(lines := [1, 2]) + + self.assertEqual(lines, [1, 2]) + + def test_named_expression_scope_08(self): + def spam(a): + return a + + def eggs(b): + return b * 2 + + res = [spam(a := eggs(b := h)) for h in range(2)] + + self.assertEqual(res, [0, 2]) + self.assertEqual(a, 2) + self.assertEqual(b, 1) + + def test_named_expression_scope_09(self): + def spam(a): + return a + + def eggs(b): + return b * 2 + + res = [spam(a := eggs(a := h)) for h in range(2)] + + self.assertEqual(res, [0, 2]) + self.assertEqual(a, 2) + + def test_named_expression_scope_10(self): + res = [b := [a := 1 for i in range(2)] for j in range(2)] + + self.assertEqual(res, [[1, 1], [1, 1]]) + self.assertEqual(a, 1) + self.assertEqual(b, [1, 1]) + + def test_named_expression_scope_11(self): + res = [j := i for i in range(5)] + + 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)] + + self.assertEqual(res, [0, 1, 3, 6, 10]) + self.assertEqual(b, 10) + + def test_named_expression_scope_18(self): + def spam(a): + return a + + res = spam(b := 2) + + self.assertEqual(res, 2) + self.assertEqual(b, 2) + + def test_named_expression_scope_19(self): + def spam(a): + return a + + res = spam((b := 2)) + + self.assertEqual(res, 2) + self.assertEqual(b, 2) + + def test_named_expression_scope_20(self): + def spam(a): + return a + + res = spam(a=(b := 2)) + + self.assertEqual(res, 2) + self.assertEqual(b, 2) + + def test_named_expression_scope_21(self): + def spam(a, b): + return a + b + + res = spam(c := 2, b=1) + + self.assertEqual(res, 3) + self.assertEqual(c, 2) + + def test_named_expression_scope_22(self): + def spam(a, b): + return a + b + + res = spam((c := 2), b=1) + + self.assertEqual(res, 3) + self.assertEqual(c, 2) + + def test_named_expression_scope_23(self): + def spam(a, b): + return a + b + + res = spam(b=(c := 2), a=1) + + self.assertEqual(res, 3) + self.assertEqual(c, 2) + + def test_named_expression_scope_24(self): + a = 10 + def spam(): + nonlocal a + (a := 20) + spam() + + self.assertEqual(a, 20) + + def test_named_expression_scope_25(self): + ns = {} + code = """a = 10 +def spam(): + global a + (a := 20) +spam()""" + + exec(code, ns, {}) + + self.assertEqual(ns["a"], 20) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index 9b58bb93c8..ac3899baed 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -115,6 +115,7 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase): self.check_expr("foo * bar") self.check_expr("foo / bar") self.check_expr("foo // bar") + self.check_expr("(foo := 1)") self.check_expr("lambda: 0") self.check_expr("lambda x: 0") self.check_expr("lambda *y: 0") @@ -421,6 +422,40 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase): self.check_expr('{x**2:x[3] for x in seq if condition(x)}') self.check_expr('{x:x for x in seq1 for y in seq2 if condition(x, y)}') + def test_named_expressions(self): + self.check_suite("(a := 1)") + self.check_suite("(a := a)") + self.check_suite("if (match := pattern.search(data)) is None: pass") + self.check_suite("[y := f(x), y**2, y**3]") + self.check_suite("filtered_data = [y for x in data if (y := f(x)) is None]") + self.check_suite("(y := f(x))") + self.check_suite("y0 = (y1 := f(x))") + self.check_suite("foo(x=(y := f(x)))") + self.check_suite("def foo(answer=(p := 42)): pass") + self.check_suite("def foo(answer: (p := 42) = 5): pass") + self.check_suite("lambda: (x := 1)") + self.check_suite("(x := lambda: 1)") + self.check_suite("(x := lambda: (y := 1))") # not in PEP + self.check_suite("lambda line: (m := re.match(pattern, line)) and m.group(1)") + self.check_suite("x = (y := 0)") + self.check_suite("(z:=(y:=(x:=0)))") + self.check_suite("(info := (name, phone, *rest))") + self.check_suite("(x:=1,2)") + self.check_suite("(total := total + tax)") + self.check_suite("len(lines := f.readlines())") + self.check_suite("foo(x := 3, cat='vector')") + self.check_suite("foo(cat=(category := 'vector'))") + self.check_suite("if any(len(longline := l) >= 100 for l in lines): print(longline)") + self.check_suite( + "if env_base := os.environ.get('PYTHONUSERBASE', None): return env_base" + ) + self.check_suite( + "if self._is_special and (ans := self._check_nans(context=context)): return ans" + ) + self.check_suite("foo(b := 2, a=1)") + self.check_suite("foo(b := 2, a=1)") + self.check_suite("foo((b := 2), a=1)") + self.check_suite("foo(c=(b := 2), a=1)") # # Second, we take *invalid* trees and make sure we get ParserError @@ -694,16 +729,16 @@ class IllegalSyntaxTestCase(unittest.TestCase): def test_illegal_encoding(self): # Illegal encoding declaration tree = \ - (340, + (341, (257, (0, ''))) self.check_bad_tree(tree, "missed encoding") tree = \ - (340, + (341, (257, (0, '')), b'iso-8859-1') self.check_bad_tree(tree, "non-string encoding") tree = \ - (340, + (341, (257, (0, '')), '\udcff') with self.assertRaises(UnicodeEncodeError): @@ -776,8 +811,9 @@ class ParserStackLimitTestCase(unittest.TestCase): return "["*level+"]"*level def test_deeply_nested_list(self): - # XXX used to be 99 levels in 2.x - e = self._nested_expression(93) + # This has fluctuated between 99 levels in 2.x, down to 93 levels in + # 3.7.X and back up to 99 in 3.8.X. Related to MAXSTACK size in Parser.h + e = self._nested_expression(99) st = parser.expr(e) st.compile() diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 04a12542c6..4c90092893 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1429,6 +1429,7 @@ class TestTokenize(TestCase): self.assertExactTypeEqual('**=', token.DOUBLESTAREQUAL) self.assertExactTypeEqual('//', token.DOUBLESLASH) self.assertExactTypeEqual('//=', token.DOUBLESLASHEQUAL) + self.assertExactTypeEqual(':=', token.COLONEQUAL) self.assertExactTypeEqual('...', token.ELLIPSIS) self.assertExactTypeEqual('->', token.RARROW) self.assertExactTypeEqual('@', token.AT) |