diff options
author | Brandt Bucher <brandt@python.org> | 2021-06-18 17:18:14 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-18 17:18:14 (GMT) |
commit | c106cf31f816f719de0a83ff31b9f4d0bea3519b (patch) | |
tree | 553712969a997745d84d34bc81eee07ba3309914 | |
parent | 0982ded179f280176868c1c4eccf77bf70687816 (diff) | |
download | cpython-c106cf31f816f719de0a83ff31b9f4d0bea3519b.zip cpython-c106cf31f816f719de0a83ff31b9f4d0bea3519b.tar.gz cpython-c106cf31f816f719de0a83ff31b9f4d0bea3519b.tar.bz2 |
Reorganize the pattern matching suite (GH-26661)
-rw-r--r-- | Lib/test/test_patma.py | 1045 |
1 files changed, 497 insertions, 548 deletions
diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index 084d087..91b1f7e 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -1,23 +1,9 @@ import array import collections -import contextlib import dataclasses import enum import inspect import unittest -import warnings - - -def no_perf(f): - f.no_perf = None - return f - - -@dataclasses.dataclass -class MyClass: - x: int - y: str - __match_args__ = ("x", "y") @dataclasses.dataclass @@ -26,11 +12,58 @@ class Point: y: int -class TestPatma(unittest.TestCase): +class TestCompiler(unittest.TestCase): - def assert_syntax_error(self, code: str): - with self.assertRaises(SyntaxError): - compile(inspect.cleandoc(code), "<test>", "exec") + def test_refleaks(self): + # Hunting for leaks using -R doesn't catch leaks in the compiler itself, + # just the code under test. This test ensures that if there are leaks in + # the pattern compiler, those runs will fail: + with open(__file__) as file: + compile(file.read(), __file__, "exec") + + +class TestInheritance(unittest.TestCase): + + def test_multiple_inheritance(self): + class C: + pass + class S1(collections.UserList, collections.abc.Mapping): + pass + class S2(C, collections.UserList, collections.abc.Mapping): + pass + class S3(list, C, collections.abc.Mapping): + pass + class S4(collections.UserList, dict, C): + pass + class M1(collections.UserDict, collections.abc.Sequence): + pass + class M2(C, collections.UserDict, collections.abc.Sequence): + pass + class M3(collections.UserDict, C, list): + pass + class M4(dict, collections.abc.Sequence, C): + pass + def f(x): + match x: + case []: + return "seq" + case {}: + return "map" + def g(x): + match x: + case {}: + return "map" + case []: + return "seq" + for Seq in (S1, S2, S3, S4): + self.assertEqual(f(Seq()), "seq") + self.assertEqual(g(Seq()), "seq") + for Map in (M1, M2, M3, M4): + self.assertEqual(f(Map()), "map") + self.assertEqual(g(Map()), "map") + + +class TestPatma(unittest.TestCase): def test_patma_000(self): match 0: @@ -1701,7 +1734,6 @@ class TestPatma(unittest.TestCase): self.assertIs(http_error("400"), None) self.assertIs(http_error(401 | 403 | 404), None) # 407 - @no_perf def test_patma_176(self): def whereis(point): match point: @@ -1714,13 +1746,12 @@ class TestPatma(unittest.TestCase): case (x, y): return f"X={x}, Y={y}" case _: - raise ValueError("Not a point") + return "Not a point" self.assertEqual(whereis((0, 0)), "Origin") self.assertEqual(whereis((0, -1.0)), "Y=-1.0") self.assertEqual(whereis(("X", 0)), "X=X") self.assertEqual(whereis((None, 1j)), "X=None, Y=1j") - with self.assertRaises(ValueError): - whereis(42) + self.assertEqual(whereis(42), "Not a point") def test_patma_177(self): def whereis(point): @@ -2179,11 +2210,11 @@ class TestPatma(unittest.TestCase): def test_patma_212(self): def f(w): match w: - case MyClass(int(xx), y="hello"): + case Point(int(xx), y="hello"): out = locals() del out["w"] return out - self.assertEqual(f(MyClass(42, "hello")), {"xx": 42}) + self.assertEqual(f(Point(42, "hello")), {"xx": 42}) def test_patma_213(self): def f(w): @@ -2225,99 +2256,35 @@ class TestPatma(unittest.TestCase): return locals() self.assertEqual(set(f()), {"abc"}) - @no_perf def test_patma_218(self): - self.assert_syntax_error(""" - match ...: - case "a" | a: - pass - """) - - @no_perf - def test_patma_219(self): - self.assert_syntax_error(""" - match ...: - case a | "a": - pass - """) - - def test_patma_220(self): def f(): match ..., ...: case a, b: return locals() self.assertEqual(set(f()), {"a", "b"}) - @no_perf - def test_patma_221(self): - self.assert_syntax_error(""" - match ...: - case a, a: - pass - """) - - def test_patma_222(self): + def test_patma_219(self): def f(): match {"k": ..., "l": ...}: case {"k": a, "l": b}: return locals() self.assertEqual(set(f()), {"a", "b"}) - @no_perf - def test_patma_223(self): - self.assert_syntax_error(""" - match ...: - case {"k": a, "l": a}: - pass - """) - - def test_patma_224(self): + def test_patma_220(self): def f(): - match MyClass(..., ...): - case MyClass(x, y=y): + match Point(..., ...): + case Point(x, y=y): return locals() self.assertEqual(set(f()), {"x", "y"}) - @no_perf - def test_patma_225(self): - self.assert_syntax_error(""" - match ...: - case MyClass(x, x): - pass - """) - - @no_perf - def test_patma_226(self): - self.assert_syntax_error(""" - match ...: - case MyClass(x=x, y=x): - pass - """) - - @no_perf - def test_patma_227(self): - self.assert_syntax_error(""" - match ...: - case MyClass(x, y=x): - pass - """) - - def test_patma_228(self): + def test_patma_221(self): def f(): match ...: case b as a: return locals() self.assertEqual(set(f()), {"a", "b"}) - @no_perf - def test_patma_229(self): - self.assert_syntax_error(""" - match ...: - case a as a: - pass - """) - - def test_patma_230(self): + def test_patma_222(self): def f(x): match x: case _: @@ -2327,7 +2294,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(f(2), 0) self.assertEqual(f(3), 0) - def test_patma_231(self): + def test_patma_223(self): def f(x): match x: case 0: @@ -2337,7 +2304,7 @@ class TestPatma(unittest.TestCase): self.assertIs(f(2), None) self.assertIs(f(3), None) - def test_patma_232(self): + def test_patma_224(self): def f(x): match x: case 0: @@ -2349,7 +2316,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(f(2), 1) self.assertEqual(f(3), 1) - def test_patma_233(self): + def test_patma_225(self): def f(x): match x: case 0: @@ -2361,7 +2328,7 @@ class TestPatma(unittest.TestCase): self.assertIs(f(2), None) self.assertIs(f(3), None) - def test_patma_234(self): + def test_patma_226(self): def f(x): match x: case 0: @@ -2375,7 +2342,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(f(2), 2) self.assertEqual(f(3), 2) - def test_patma_235(self): + def test_patma_227(self): def f(x): match x: case 0: @@ -2389,211 +2356,13 @@ class TestPatma(unittest.TestCase): self.assertEqual(f(2), 2) self.assertIs(f(3), None) - @no_perf - def test_patma_236(self): - self.assert_syntax_error(""" - match ...: - case {**rest, "key": value}: - pass - """) - - @no_perf - def test_patma_237(self): - self.assert_syntax_error(""" - match ...: - case {"first": first, **rest, "last": last}: - pass - """) - - @no_perf - def test_patma_238(self): - self.assert_syntax_error(""" - match ...: - case *a, b, *c, d, *e: - pass - """) - - @no_perf - def test_patma_239(self): - self.assert_syntax_error(""" - match ...: - case a, *b, c, *d, e: - pass - """) - - @no_perf - def test_patma_240(self): - self.assert_syntax_error(""" - match ...: - case 0+0: - pass - """) - - @no_perf - def test_patma_241(self): - self.assert_syntax_error(""" - match ...: - case f"": - pass - """) - - @no_perf - def test_patma_242(self): - self.assert_syntax_error(""" - match ...: - case f"{x}": - pass - """) - - @no_perf - def test_patma_243(self): - self.assert_syntax_error(""" - match 42: - case x: - pass - case y: - pass - """) - - @no_perf - def test_patma_244(self): - self.assert_syntax_error(""" - match ...: - case {**_}: - pass - """) - - @no_perf - def test_patma_245(self): - self.assert_syntax_error(""" - match ...: - case 42 as _: - pass - """) - - @no_perf - def test_patma_246(self): - class Class: - __match_args__ = None - x = Class() - y = z = None - with self.assertRaises(TypeError): - match x: - case Class(y): - z = 0 - self.assertIs(y, None) - self.assertIs(z, None) - - @no_perf - def test_patma_247(self): - class Class: - __match_args__ = "XYZ" - x = Class() - y = z = None - with self.assertRaises(TypeError): - match x: - case Class(y): - z = 0 - self.assertIs(y, None) - self.assertIs(z, None) - - @no_perf - def test_patma_248(self): - class Class: - __match_args__ = (None,) - x = Class() - y = z = None - with self.assertRaises(TypeError): - match x: - case Class(y): - z = 0 - self.assertIs(y, None) - self.assertIs(z, None) - - @no_perf - def test_patma_249(self): - class Class: - __match_args__ = () - x = Class() - y = z = None - with self.assertRaises(TypeError): - match x: - case Class(y): - z = 0 - self.assertIs(y, None) - self.assertIs(z, None) - - @no_perf - def test_patma_250(self): - self.assert_syntax_error(""" - match ...: - case Class(a=_, a=_): - pass - """) - - @no_perf - def test_patma_251(self): - x = {"a": 0, "b": 1} - w = y = z = None - with self.assertRaises(ValueError): - match x: - case {"a": y, "a": z}: - w = 0 - self.assertIs(w, None) - self.assertIs(y, None) - self.assertIs(z, None) - - @no_perf - def test_patma_252(self): - class Keys: - KEY = "a" - x = {"a": 0, "b": 1} - w = y = z = None - with self.assertRaises(ValueError): - match x: - case {Keys.KEY: y, "a": z}: - w = 0 - self.assertIs(w, None) - self.assertIs(y, None) - self.assertIs(z, None) - - @no_perf - def test_patma_253(self): - class Class: - __match_args__ = ("a", "a") - a = None - x = Class() - w = y = z = None - with self.assertRaises(TypeError): - match x: - case Class(y, z): - w = 0 - self.assertIs(w, None) - self.assertIs(y, None) - self.assertIs(z, None) - - @no_perf - def test_patma_254(self): - class Class: - __match_args__ = ("a",) - a = None - x = Class() - w = y = z = None - with self.assertRaises(TypeError): - match x: - case Class(y, a=z): - w = 0 - self.assertIs(w, None) - self.assertIs(y, None) - self.assertIs(z, None) - - def test_patma_255(self): + def test_patma_228(self): match(): case(): x = 0 self.assertEqual(x, 0) - def test_patma_256(self): + def test_patma_229(self): x = 0 match(x): case(x): @@ -2601,7 +2370,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(x, 0) self.assertEqual(y, 0) - def test_patma_257(self): + def test_patma_230(self): x = 0 match x: case False: @@ -2611,7 +2380,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(x, 0) self.assertEqual(y, 1) - def test_patma_258(self): + def test_patma_231(self): x = 1 match x: case True: @@ -2621,7 +2390,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(x, 1) self.assertEqual(y, 1) - def test_patma_259(self): + def test_patma_232(self): class Eq: def __eq__(self, other): return True @@ -2633,7 +2402,7 @@ class TestPatma(unittest.TestCase): self.assertIs(x, eq) self.assertEqual(y, None) - def test_patma_260(self): + def test_patma_233(self): x = False match x: case False: @@ -2641,7 +2410,7 @@ class TestPatma(unittest.TestCase): self.assertIs(x, False) self.assertEqual(y, 0) - def test_patma_261(self): + def test_patma_234(self): x = True match x: case True: @@ -2649,7 +2418,7 @@ class TestPatma(unittest.TestCase): self.assertIs(x, True) self.assertEqual(y, 0) - def test_patma_262(self): + def test_patma_235(self): x = None match x: case None: @@ -2657,7 +2426,7 @@ class TestPatma(unittest.TestCase): self.assertIs(x, None) self.assertEqual(y, 0) - def test_patma_263(self): + def test_patma_236(self): x = 0 match x: case (0 as w) as z: @@ -2667,7 +2436,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(y, 0) self.assertEqual(z, 0) - def test_patma_264(self): + def test_patma_237(self): x = 0 match x: case (0 as w) as z: @@ -2677,7 +2446,7 @@ class TestPatma(unittest.TestCase): self.assertEqual(y, 0) self.assertEqual(z, 0) - def test_patma_265(self): + def test_patma_238(self): x = ((0, 1), (2, 3)) match x: case ((a as b, c as d) as e) as w, ((f as g, h) as i) as z: @@ -2696,362 +2465,542 @@ class TestPatma(unittest.TestCase): self.assertEqual(y, 0) self.assertEqual(z, (2, 3)) - @no_perf - def test_patma_266(self): + def test_patma_239(self): + x = collections.UserDict({0: 1, 2: 3}) + match x: + case {2: 3}: + y = 0 + self.assertEqual(x, {0: 1, 2: 3}) + self.assertEqual(y, 0) + + def test_patma_240(self): + x = collections.UserDict({0: 1, 2: 3}) + match x: + case {2: 3, **z}: + y = 0 + self.assertEqual(x, {0: 1, 2: 3}) + self.assertEqual(y, 0) + self.assertEqual(z, {0: 1}) + + def test_patma_241(self): + x = [[{0: 0}]] + match x: + case list([({-0-0j: int(real=0+0j, imag=0-0j) | (1) as z},)]): + y = 0 + self.assertEqual(x, [[{0: 0}]]) + self.assertEqual(y, 0) + self.assertEqual(z, 0) + + def test_patma_242(self): + x = range(3) + match x: + case [y, *_, z]: + w = 0 + self.assertEqual(w, 0) + self.assertEqual(x, range(3)) + self.assertEqual(y, 0) + self.assertEqual(z, 2) + + def test_patma_243(self): + x = range(3) + match x: + case [_, *_, y]: + z = 0 + self.assertEqual(x, range(3)) + self.assertEqual(y, 2) + self.assertEqual(z, 0) + + def test_patma_244(self): + x = range(3) + match x: + case [*_, y]: + z = 0 + self.assertEqual(x, range(3)) + self.assertEqual(y, 2) + self.assertEqual(z, 0) + + def test_patma_245(self): + x = {"y": 1} + match x: + case {"y": (0 as y) | (1 as y)}: + z = 0 + self.assertEqual(x, {"y": 1}) + self.assertEqual(y, 1) + self.assertEqual(z, 0) + + def test_patma_246(self): + def f(x): + match x: + case ((a, b, c, d, e, f, g, h, i, 9) | + (h, g, i, a, b, d, e, c, f, 10) | + (g, b, a, c, d, -5, e, h, i, f) | + (-1, d, f, b, g, e, i, a, h, c)): + w = 0 + out = locals() + del out["x"] + return out + alts = [ + dict(a=0, b=1, c=2, d=3, e=4, f=5, g=6, h=7, i=8, w=0), + dict(h=1, g=2, i=3, a=4, b=5, d=6, e=7, c=8, f=9, w=0), + dict(g=0, b=-1, a=-2, c=-3, d=-4, e=-6, h=-7, i=-8, f=-9, w=0), + dict(d=-2, f=-3, b=-4, g=-5, e=-6, i=-7, a=-8, h=-9, c=-10, w=0), + dict(), + ] + self.assertEqual(f(range(10)), alts[0]) + self.assertEqual(f(range(1, 11)), alts[1]) + self.assertEqual(f(range(0, -10, -1)), alts[2]) + self.assertEqual(f(range(-1, -11, -1)), alts[3]) + self.assertEqual(f(range(10, 20)), alts[4]) + + def test_patma_247(self): + def f(x): + match x: + case [y, (a, b, c, d, e, f, g, h, i, 9) | + (h, g, i, a, b, d, e, c, f, 10) | + (g, b, a, c, d, -5, e, h, i, f) | + (-1, d, f, b, g, e, i, a, h, c), z]: + w = 0 + out = locals() + del out["x"] + return out + alts = [ + dict(a=0, b=1, c=2, d=3, e=4, f=5, g=6, h=7, i=8, w=0, y=False, z=True), + dict(h=1, g=2, i=3, a=4, b=5, d=6, e=7, c=8, f=9, w=0, y=False, z=True), + dict(g=0, b=-1, a=-2, c=-3, d=-4, e=-6, h=-7, i=-8, f=-9, w=0, y=False, z=True), + dict(d=-2, f=-3, b=-4, g=-5, e=-6, i=-7, a=-8, h=-9, c=-10, w=0, y=False, z=True), + dict(), + ] + self.assertEqual(f((False, range(10), True)), alts[0]) + self.assertEqual(f((False, range(1, 11), True)), alts[1]) + self.assertEqual(f((False, range(0, -10, -1), True)), alts[2]) + self.assertEqual(f((False, range(-1, -11, -1), True)), alts[3]) + self.assertEqual(f((False, range(10, 20), True)), alts[4]) + + +class TestSyntaxErrors(unittest.TestCase): + + def assert_syntax_error(self, code: str): + with self.assertRaises(SyntaxError): + compile(inspect.cleandoc(code), "<test>", "exec") + + def test_alternative_patterns_bind_different_names_0(self): self.assert_syntax_error(""" match ...: - case _ | _: + case "a" | a: pass """) - @no_perf - def test_patma_267(self): + def test_alternative_patterns_bind_different_names_1(self): self.assert_syntax_error(""" match ...: - case (_ as x) | [x]: + case [a, [b] | [c] | [d]]: pass """) - @no_perf - def test_patma_268(self): + def test_attribute_name_repeated_in_class_pattern(self): self.assert_syntax_error(""" match ...: - case _ | _ if condition(): + case Class(a=_, a=_): pass """) + def test_imaginary_number_required_in_complex_literal_0(self): + self.assert_syntax_error(""" + match ...: + case 0+0: + pass + """) - @no_perf - def test_patma_269(self): + def test_imaginary_number_required_in_complex_literal_1(self): self.assert_syntax_error(""" match ...: - case x | [_ as x] if x: + case {0+0: _}: pass """) - @no_perf - def test_patma_270(self): + def test_invalid_syntax_0(self): self.assert_syntax_error(""" match ...: - case _: + case {**rest, "key": value}: pass - case None: + """) + + def test_invalid_syntax_1(self): + self.assert_syntax_error(""" + match ...: + case {"first": first, **rest, "last": last}: pass """) - @no_perf - def test_patma_271(self): + def test_invalid_syntax_2(self): self.assert_syntax_error(""" match ...: - case x: + case {**_}: pass - case [x] if x: + """) + + def test_invalid_syntax_3(self): + self.assert_syntax_error(""" + match ...: + case 42 as _: pass """) - @no_perf - def test_patma_272(self): + def test_mapping_pattern_keys_may_only_match_literals_and_attribute_lookups(self): self.assert_syntax_error(""" match ...: - case x: + case {f"": _}: pass - case _: + """) + + def test_multiple_assignments_to_name_in_pattern_0(self): + self.assert_syntax_error(""" + match ...: + case a, a: pass """) - @no_perf - def test_patma_273(self): + def test_multiple_assignments_to_name_in_pattern_1(self): self.assert_syntax_error(""" match ...: - case (None | _) | _: + case {"k": a, "l": a}: pass """) - @no_perf - def test_patma_274(self): + def test_multiple_assignments_to_name_in_pattern_2(self): self.assert_syntax_error(""" match ...: - case _ | (True | False): + case MyClass(x, x): pass """) - def test_patma_275(self): - x = collections.UserDict({0: 1, 2: 3}) - match x: - case {2: 3}: - y = 0 - self.assertEqual(x, {0: 1, 2: 3}) - self.assertEqual(y, 0) + def test_multiple_assignments_to_name_in_pattern_3(self): + self.assert_syntax_error(""" + match ...: + case MyClass(x=x, y=x): + pass + """) - def test_patma_276(self): - x = collections.UserDict({0: 1, 2: 3}) - match x: - case {2: 3, **z}: - y = 0 - self.assertEqual(x, {0: 1, 2: 3}) - self.assertEqual(y, 0) - self.assertEqual(z, {0: 1}) + def test_multiple_assignments_to_name_in_pattern_4(self): + self.assert_syntax_error(""" + match ...: + case MyClass(x, y=x): + pass + """) - def test_patma_277(self): - x = [[{0: 0}]] - match x: - case list([({-0-0j: int(real=0+0j, imag=0-0j) | (1) as z},)]): - y = 0 - self.assertEqual(x, [[{0: 0}]]) - self.assertEqual(y, 0) - self.assertEqual(z, 0) + def test_multiple_assignments_to_name_in_pattern_5(self): + self.assert_syntax_error(""" + match ...: + case a as a: + pass + """) - def test_patma_278(self): - x = range(3) - match x: - case [y, *_, z]: - w = 0 - self.assertEqual(w, 0) - self.assertEqual(x, range(3)) - self.assertEqual(y, 0) - self.assertEqual(z, 2) + def test_multiple_starred_names_in_sequence_pattern_0(self): + self.assert_syntax_error(""" + match ...: + case *a, b, *c, d, *e: + pass + """) - def test_patma_279(self): - x = range(3) - match x: - case [_, *_, y]: - z = 0 - self.assertEqual(x, range(3)) - self.assertEqual(y, 2) - self.assertEqual(z, 0) + def test_multiple_starred_names_in_sequence_pattern_1(self): + self.assert_syntax_error(""" + match ...: + case a, *b, c, *d, e: + pass + """) - def test_patma_280(self): - x = range(3) - match x: - case [*_, y]: - z = 0 - self.assertEqual(x, range(3)) - self.assertEqual(y, 2) - self.assertEqual(z, 0) + def test_name_capture_makes_remaining_patterns_unreachable_0(self): + self.assert_syntax_error(""" + match ...: + case a | "a": + pass + """) - @no_perf - def test_patma_281(self): - x = range(10) - y = None - with self.assertRaises(TypeError): - match x: - case range(10): - y = 0 - self.assertEqual(x, range(10)) - self.assertIs(y, None) + def test_name_capture_makes_remaining_patterns_unreachable_1(self): + self.assert_syntax_error(""" + match 42: + case x: + pass + case y: + pass + """) - @no_perf - def test_patma_282(self): - class Class: - __match_args__ = ["spam", "eggs"] - spam = 0 - eggs = 1 - x = Class() - w = y = z = None - with self.assertRaises(TypeError): - match x: - case Class(y, z): - w = 0 - self.assertIs(w, None) - self.assertIs(y, None) - self.assertIs(z, None) + def test_name_capture_makes_remaining_patterns_unreachable_2(self): + self.assert_syntax_error(""" + match ...: + case x | [_ as x] if x: + pass + """) - @no_perf - def test_patma_283(self): + def test_name_capture_makes_remaining_patterns_unreachable_3(self): self.assert_syntax_error(""" match ...: - case {0+0: _}: + case x: + pass + case [x] if x: pass """) - @no_perf - def test_patma_284(self): + def test_name_capture_makes_remaining_patterns_unreachable_4(self): self.assert_syntax_error(""" match ...: - case {f"": _}: + case x: + pass + case _: + pass + """) + + def test_patterns_may_only_match_literals_and_attribute_lookups_0(self): + self.assert_syntax_error(""" + match ...: + case f"": + pass + """) + + def test_patterns_may_only_match_literals_and_attribute_lookups_1(self): + self.assert_syntax_error(""" + match ...: + case f"{x}": pass """) - @no_perf - def test_patma_285(self): + def test_real_number_required_in_complex_literal_0(self): self.assert_syntax_error(""" match ...: case 0j+0: pass """) - @no_perf - def test_patma_286(self): + def test_real_number_required_in_complex_literal_1(self): self.assert_syntax_error(""" match ...: case 0j+0j: pass """) - @no_perf - def test_patma_287(self): + def test_real_number_required_in_complex_literal_2(self): self.assert_syntax_error(""" match ...: case {0j+0: _}: pass """) - @no_perf - def test_patma_288(self): + def test_real_number_required_in_complex_literal_3(self): self.assert_syntax_error(""" match ...: case {0j+0j: _}: pass """) - def test_patma_289(self): - x = {"y": 1} - match x: - case {"y": (0 as y) | (1 as y)}: - z = 0 - self.assertEqual(x, {"y": 1}) - self.assertEqual(y, 1) - self.assertEqual(z, 0) + def test_wildcard_makes_remaining_patterns_unreachable_0(self): + self.assert_syntax_error(""" + match ...: + case _ | _: + pass + """) - @no_perf - def test_patma_290(self): + def test_wildcard_makes_remaining_patterns_unreachable_1(self): self.assert_syntax_error(""" match ...: - case [a, [b] | [c] | [d]]: + case (_ as x) | [x]: pass """) - @no_perf - def test_patma_291(self): - # Hunting for leaks using -R doesn't catch leaks in the compiler itself, - # just the code under test. This test ensures that if there are leaks in - # the pattern compiler, those runs will fail: - with open(__file__) as file: - compile(file.read(), __file__, "exec") + def test_wildcard_makes_remaining_patterns_unreachable_2(self): + self.assert_syntax_error(""" + match ...: + case _ | _ if condition(): + pass + """) - def test_patma_292(self): - def f(x): + def test_wildcard_makes_remaining_patterns_unreachable_3(self): + self.assert_syntax_error(""" + match ...: + case _: + pass + case None: + pass + """) + + def test_wildcard_makes_remaining_patterns_unreachable_4(self): + self.assert_syntax_error(""" + match ...: + case (None | _) | _: + pass + """) + + def test_wildcard_makes_remaining_patterns_unreachable_5(self): + self.assert_syntax_error(""" + match ...: + case _ | (True | False): + pass + """) + + +class TestTypeErrors(unittest.TestCase): + + def test_accepts_positional_subpatterns_0(self): + class Class: + __match_args__ = () + x = Class() + y = z = None + with self.assertRaises(TypeError): match x: - case ((a, b, c, d, e, f, g, h, i, 9) | - (h, g, i, a, b, d, e, c, f, 10) | - (g, b, a, c, d, -5, e, h, i, f) | - (-1, d, f, b, g, e, i, a, h, c)): + case Class(y): + z = 0 + self.assertIs(y, None) + self.assertIs(z, None) + + def test_accepts_positional_subpatterns_1(self): + x = range(10) + y = None + with self.assertRaises(TypeError): + match x: + case range(10): + y = 0 + self.assertEqual(x, range(10)) + self.assertIs(y, None) + + def test_got_multiple_subpatterns_for_attribute_0(self): + class Class: + __match_args__ = ("a", "a") + a = None + x = Class() + w = y = z = None + with self.assertRaises(TypeError): + match x: + case Class(y, z): w = 0 - out = locals() - del out["x"] - return out - alts = [ - dict(a=0, b=1, c=2, d=3, e=4, f=5, g=6, h=7, i=8, w=0), - dict(h=1, g=2, i=3, a=4, b=5, d=6, e=7, c=8, f=9, w=0), - dict(g=0, b=-1, a=-2, c=-3, d=-4, e=-6, h=-7, i=-8, f=-9, w=0), - dict(d=-2, f=-3, b=-4, g=-5, e=-6, i=-7, a=-8, h=-9, c=-10, w=0), - dict(), - ] - self.assertEqual(f(range(10)), alts[0]) - self.assertEqual(f(range(1, 11)), alts[1]) - self.assertEqual(f(range(0, -10, -1)), alts[2]) - self.assertEqual(f(range(-1, -11, -1)), alts[3]) - self.assertEqual(f(range(10, 20)), alts[4]) + self.assertIs(w, None) + self.assertIs(y, None) + self.assertIs(z, None) - def test_patma_293(self): - def f(x): + def test_got_multiple_subpatterns_for_attribute_1(self): + class Class: + __match_args__ = ("a",) + a = None + x = Class() + w = y = z = None + with self.assertRaises(TypeError): match x: - case [y, (a, b, c, d, e, f, g, h, i, 9) | - (h, g, i, a, b, d, e, c, f, 10) | - (g, b, a, c, d, -5, e, h, i, f) | - (-1, d, f, b, g, e, i, a, h, c), z]: + case Class(y, a=z): w = 0 - out = locals() - del out["x"] - return out - alts = [ - dict(a=0, b=1, c=2, d=3, e=4, f=5, g=6, h=7, i=8, w=0, y=False, z=True), - dict(h=1, g=2, i=3, a=4, b=5, d=6, e=7, c=8, f=9, w=0, y=False, z=True), - dict(g=0, b=-1, a=-2, c=-3, d=-4, e=-6, h=-7, i=-8, f=-9, w=0, y=False, z=True), - dict(d=-2, f=-3, b=-4, g=-5, e=-6, i=-7, a=-8, h=-9, c=-10, w=0, y=False, z=True), - dict(), - ] - self.assertEqual(f((False, range(10), True)), alts[0]) - self.assertEqual(f((False, range(1, 11), True)), alts[1]) - self.assertEqual(f((False, range(0, -10, -1), True)), alts[2]) - self.assertEqual(f((False, range(-1, -11, -1), True)), alts[3]) - self.assertEqual(f((False, range(10, 20), True)), alts[4]) + self.assertIs(w, None) + self.assertIs(y, None) + self.assertIs(z, None) + + def test_match_args_elements_must_be_strings(self): + class Class: + __match_args__ = (None,) + x = Class() + y = z = None + with self.assertRaises(TypeError): + match x: + case Class(y): + z = 0 + self.assertIs(y, None) + self.assertIs(z, None) + def test_match_args_must_be_a_tuple_0(self): + class Class: + __match_args__ = None + x = Class() + y = z = None + with self.assertRaises(TypeError): + match x: + case Class(y): + z = 0 + self.assertIs(y, None) + self.assertIs(z, None) -class TestInheritance(unittest.TestCase): + def test_match_args_must_be_a_tuple_1(self): + class Class: + __match_args__ = "XYZ" + x = Class() + y = z = None + with self.assertRaises(TypeError): + match x: + case Class(y): + z = 0 + self.assertIs(y, None) + self.assertIs(z, None) - def test_multiple_inheritance(self): - class C: - pass - class S1(collections.UserList, collections.abc.Mapping): - pass - class S2(C, collections.UserList, collections.abc.Mapping): - pass - class S3(list, C, collections.abc.Mapping): - pass - class S4(collections.UserList, dict, C): - pass - class M1(collections.UserDict, collections.abc.Sequence): - pass - class M2(C, collections.UserDict, collections.abc.Sequence): - pass - class M3(collections.UserDict, C, list): - pass - class M4(dict, collections.abc.Sequence, C): - pass - def f(x): + def test_match_args_must_be_a_tuple_2(self): + class Class: + __match_args__ = ["spam", "eggs"] + spam = 0 + eggs = 1 + x = Class() + w = y = z = None + with self.assertRaises(TypeError): match x: - case []: - return "seq" - case {}: - return "map" - def g(x): + case Class(y, z): + w = 0 + self.assertIs(w, None) + self.assertIs(y, None) + self.assertIs(z, None) + + +class TestValueErrors(unittest.TestCase): + + def test_mapping_pattern_checks_duplicate_key_0(self): + x = {"a": 0, "b": 1} + w = y = z = None + with self.assertRaises(ValueError): match x: - case {}: - return "map" - case []: - return "seq" - for Seq in (S1, S2, S3, S4): - self.assertEqual(f(Seq()), "seq") - self.assertEqual(g(Seq()), "seq") - for Map in (M1, M2, M3, M4): - self.assertEqual(f(Map()), "map") - self.assertEqual(g(Map()), "map") + case {"a": y, "a": z}: + w = 0 + self.assertIs(w, None) + self.assertIs(y, None) + self.assertIs(z, None) + + def test_mapping_pattern_checks_duplicate_key_1(self): + class Keys: + KEY = "a" + x = {"a": 0, "b": 1} + w = y = z = None + with self.assertRaises(ValueError): + match x: + case {Keys.KEY: y, "a": z}: + w = 0 + self.assertIs(w, None) + self.assertIs(y, None) + self.assertIs(z, None) + +if __name__ == "__main__": + """ + # From inside environment using this Python, with pyperf installed: + sudo $(which pyperf) system tune && \ + $(which python) -m test.test_patma --rigorous; \ + sudo $(which pyperf) system reset + """ + import pyperf -class PerfPatma(TestPatma): - def assertEqual(*_, **__): - pass + class PerfPatma(TestPatma): - def assertIs(*_, **__): - pass + def assertEqual(*_, **__): + pass - def assertRaises(*_, **__): - assert False, "this test should be decorated with @no_perf!" + def assertIs(*_, **__): + pass - def assertWarns(*_, **__): - assert False, "this test should be decorated with @no_perf!" + def assertRaises(*_, **__): + assert False, "this test should be a method of a different class!" - def run_perf(self): - attrs = vars(TestPatma).items() - tests = [ - attr for name, attr in attrs - if name.startswith("test_") and not hasattr(attr, "no_perf") - ] - for _ in range(1 << 8): + def run_perf(self, count): + tests = [] + for attr in vars(TestPatma): + if attr.startswith("test_"): + tests.append(getattr(self, attr)) + tests *= count + start = pyperf.perf_counter() for test in tests: - test(self) - - @staticmethod - def setUpClass(): - raise unittest.SkipTest("performance testing") + test() + return pyperf.perf_counter() - start -""" -# From inside venv pointing to this Python, with pyperf installed: -sudo $(which python) -m pyperf system tune && \ - $(which python) -m pyperf timeit --rigorous --setup "from test.test_patma import PerfPatma; p = PerfPatma()" "p.run_perf()"; \ -sudo $(which python) -m pyperf system reset -""" + runner = pyperf.Runner() + runner.bench_time_func("patma", PerfPatma().run_perf) |