[Python-checkins] [3.10] Reorganize the pattern matching suite (GH-26661) (GH-26787)

miss-islington webhook-mailer at python.org
Fri Jun 18 13:48:25 EDT 2021


https://github.com/python/cpython/commit/bba726710b33a4f52b4a15fb5d94ee402e38d552
commit: bba726710b33a4f52b4a15fb5d94ee402e38d552
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2021-06-18T10:48:07-07:00
summary:

[3.10] Reorganize the pattern matching suite (GH-26661) (GH-26787)



(cherry picked from commit c106cf31f816f719de0a83ff31b9f4d0bea3519b)


Co-authored-by: Brandt Bucher <brandt at python.org>

Automerge-Triggered-By: GH:brandtbucher

files:
M Lib/test/test_patma.py

diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py
index 084d0879f1764..91b1f7e2aa4c7 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
-
-
- at 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 @@ def http_error(status):
         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 @@ def whereis(point):
                 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 @@ def f(w):
     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 @@ def f():
                     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 @@ def f(x):
         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 @@ def f(x):
         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 @@ def f(x):
         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 @@ def f(x):
         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 @@ def f(x):
         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,275 +2356,77 @@ def f(x):
         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
-        """)
+    def test_patma_228(self):
+        match():
+            case():
+                x = 0
+        self.assertEqual(x, 0)
 
-    @no_perf
-    def test_patma_238(self):
-        self.assert_syntax_error("""
-        match ...:
-            case *a, b, *c, d, *e:
-                pass
-        """)
+    def test_patma_229(self):
+        x = 0
+        match(x):
+            case(x):
+                y = 0
+        self.assertEqual(x, 0)
+        self.assertEqual(y, 0)
 
-    @no_perf
-    def test_patma_239(self):
-        self.assert_syntax_error("""
-        match ...:
-            case a, *b, c, *d, e:
-                pass
-        """)
+    def test_patma_230(self):
+        x = 0
+        match x:
+            case False:
+                y = 0
+            case 0:
+                y = 1
+        self.assertEqual(x, 0)
+        self.assertEqual(y, 1)
 
-    @no_perf
-    def test_patma_240(self):
-        self.assert_syntax_error("""
-        match ...:
-            case 0+0:
-                pass
-        """)
+    def test_patma_231(self):
+        x = 1
+        match x:
+            case True:
+                y = 0
+            case 1:
+                y = 1
+        self.assertEqual(x, 1)
+        self.assertEqual(y, 1)
 
-    @no_perf
-    def test_patma_241(self):
-        self.assert_syntax_error("""
-        match ...:
-            case f"":
-                pass
-        """)
+    def test_patma_232(self):
+        class Eq:
+            def __eq__(self, other):
+                return True
+        x = eq = Eq()
+        y = None
+        match x:
+            case None:
+                y = 0
+        self.assertIs(x, eq)
+        self.assertEqual(y, None)
 
-    @no_perf
-    def test_patma_242(self):
-        self.assert_syntax_error("""
-        match ...:
-            case f"{x}":
-                pass
-        """)
+    def test_patma_233(self):
+        x = False
+        match x:
+            case False:
+                y = 0
+        self.assertIs(x, False)
+        self.assertEqual(y, 0)
 
-    @no_perf
-    def test_patma_243(self):
-        self.assert_syntax_error("""
-        match 42:
-            case x:
-                pass
-            case y:
-                pass
-        """)
+    def test_patma_234(self):
+        x = True
+        match x:
+            case True:
+                y = 0
+        self.assertIs(x, True)
+        self.assertEqual(y, 0)
 
-    @no_perf
-    def test_patma_244(self):
-        self.assert_syntax_error("""
-        match ...:
-            case {**_}:
-                pass
-        """)
+    def test_patma_235(self):
+        x = None
+        match x:
+            case None:
+                y = 0
+        self.assertIs(x, None)
+        self.assertEqual(y, 0)
 
-    @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):
-        match():
-            case():
-                x = 0
-        self.assertEqual(x, 0)
-
-    def test_patma_256(self):
-        x = 0
-        match(x):
-            case(x):
-                y = 0
-        self.assertEqual(x, 0)
-        self.assertEqual(y, 0)
-
-    def test_patma_257(self):
-        x = 0
-        match x:
-            case False:
-                y = 0
-            case 0:
-                y = 1
-        self.assertEqual(x, 0)
-        self.assertEqual(y, 1)
-
-    def test_patma_258(self):
-        x = 1
-        match x:
-            case True:
-                y = 0
-            case 1:
-                y = 1
-        self.assertEqual(x, 1)
-        self.assertEqual(y, 1)
-
-    def test_patma_259(self):
-        class Eq:
-            def __eq__(self, other):
-                return True
-        x = eq = Eq()
-        y = None
-        match x:
-            case None:
-                y = 0
-        self.assertIs(x, eq)
-        self.assertEqual(y, None)
-
-    def test_patma_260(self):
-        x = False
-        match x:
-            case False:
-                y = 0
-        self.assertIs(x, False)
-        self.assertEqual(y, 0)
-
-    def test_patma_261(self):
-        x = True
-        match x:
-            case True:
-                y = 0
-        self.assertIs(x, True)
-        self.assertEqual(y, 0)
-
-    def test_patma_262(self):
-        x = None
-        match x:
-            case None:
-                y = 0
-        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 @@ def test_patma_263(self):
         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 @@ def test_patma_264(self):
         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,87 +2465,7 @@ def test_patma_265(self):
         self.assertEqual(y, 0)
         self.assertEqual(z, (2, 3))
 
-    @no_perf
-    def test_patma_266(self):
-        self.assert_syntax_error("""
-        match ...:
-            case _ | _:
-                pass
-        """)
-
-    @no_perf
-    def test_patma_267(self):
-        self.assert_syntax_error("""
-        match ...:
-            case (_ as x) | [x]:
-                pass
-        """)
-
-
-    @no_perf
-    def test_patma_268(self):
-        self.assert_syntax_error("""
-        match ...:
-            case _ | _ if condition():
-                pass
-        """)
-
-
-    @no_perf
-    def test_patma_269(self):
-        self.assert_syntax_error("""
-        match ...:
-            case x | [_ as x] if x:
-                pass
-        """)
-
-    @no_perf
-    def test_patma_270(self):
-        self.assert_syntax_error("""
-        match ...:
-            case _:
-                pass
-            case None:
-                pass
-        """)
-
-    @no_perf
-    def test_patma_271(self):
-        self.assert_syntax_error("""
-        match ...:
-            case x:
-                pass
-            case [x] if x:
-                pass
-        """)
-
-    @no_perf
-    def test_patma_272(self):
-        self.assert_syntax_error("""
-        match ...:
-            case x:
-                pass
-            case _:
-                pass
-        """)
-
-    @no_perf
-    def test_patma_273(self):
-        self.assert_syntax_error("""
-        match ...:
-            case (None | _) | _:
-                pass
-        """)
-
-    @no_perf
-    def test_patma_274(self):
-        self.assert_syntax_error("""
-        match ...:
-            case _ | (True | False):
-                pass
-        """)
-
-    def test_patma_275(self):
+    def test_patma_239(self):
         x = collections.UserDict({0: 1, 2: 3})
         match x:
             case {2: 3}:
@@ -2784,7 +2473,7 @@ def test_patma_275(self):
         self.assertEqual(x, {0: 1, 2: 3})
         self.assertEqual(y, 0)
 
-    def test_patma_276(self):
+    def test_patma_240(self):
         x = collections.UserDict({0: 1, 2: 3})
         match x:
             case {2: 3, **z}:
@@ -2793,7 +2482,7 @@ def test_patma_276(self):
         self.assertEqual(y, 0)
         self.assertEqual(z, {0: 1})
 
-    def test_patma_277(self):
+    def test_patma_241(self):
         x = [[{0: 0}]]
         match x:
             case list([({-0-0j: int(real=0+0j, imag=0-0j) | (1) as z},)]):
@@ -2802,7 +2491,7 @@ def test_patma_277(self):
         self.assertEqual(y, 0)
         self.assertEqual(z, 0)
 
-    def test_patma_278(self):
+    def test_patma_242(self):
         x = range(3)
         match x:
             case [y, *_, z]:
@@ -2812,7 +2501,7 @@ def test_patma_278(self):
         self.assertEqual(y, 0)
         self.assertEqual(z, 2)
 
-    def test_patma_279(self):
+    def test_patma_243(self):
         x = range(3)
         match x:
             case [_, *_, y]:
@@ -2821,7 +2510,7 @@ def test_patma_279(self):
         self.assertEqual(y, 2)
         self.assertEqual(z, 0)
 
-    def test_patma_280(self):
+    def test_patma_244(self):
         x = range(3)
         match x:
             case [*_, y]:
@@ -2830,228 +2519,488 @@ def test_patma_280(self):
         self.assertEqual(y, 2)
         self.assertEqual(z, 0)
 
-    @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_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)
 
-    @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):
+    def test_patma_246(self):
+        def f(x):
             match x:
-                case Class(y, z):
+                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
-        self.assertIs(w, None)
-        self.assertIs(y, None)
-        self.assertIs(z, None)
+            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])
 
-    @no_perf
-    def test_patma_283(self):
+
+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 "a" | a:
+                pass
+        """)
+
+    def test_alternative_patterns_bind_different_names_1(self):
+        self.assert_syntax_error("""
+        match ...:
+            case [a, [b] | [c] | [d]]:
+                pass
+        """)
+
+
+    def test_attribute_name_repeated_in_class_pattern(self):
+        self.assert_syntax_error("""
+        match ...:
+            case Class(a=_, a=_):
+                pass
+        """)
+
+    def test_imaginary_number_required_in_complex_literal_0(self):
+        self.assert_syntax_error("""
+        match ...:
+            case 0+0:
+                pass
+        """)
+
+    def test_imaginary_number_required_in_complex_literal_1(self):
         self.assert_syntax_error("""
         match ...:
             case {0+0: _}:
                 pass
         """)
 
-    @no_perf
-    def test_patma_284(self):
+    def test_invalid_syntax_0(self):
+        self.assert_syntax_error("""
+        match ...:
+            case {**rest, "key": value}:
+                pass
+        """)
+
+    def test_invalid_syntax_1(self):
+        self.assert_syntax_error("""
+        match ...:
+            case {"first": first, **rest, "last": last}:
+                pass
+        """)
+
+    def test_invalid_syntax_2(self):
+        self.assert_syntax_error("""
+        match ...:
+            case {**_}:
+                pass
+        """)
+
+    def test_invalid_syntax_3(self):
+        self.assert_syntax_error("""
+        match ...:
+            case 42 as _:
+                pass
+        """)
+
+    def test_mapping_pattern_keys_may_only_match_literals_and_attribute_lookups(self):
         self.assert_syntax_error("""
         match ...:
             case {f"": _}:
                 pass
         """)
 
-    @no_perf
-    def test_patma_285(self):
+    def test_multiple_assignments_to_name_in_pattern_0(self):
+        self.assert_syntax_error("""
+        match ...:
+            case a, a:
+                pass
+        """)
+
+    def test_multiple_assignments_to_name_in_pattern_1(self):
+        self.assert_syntax_error("""
+        match ...:
+            case {"k": a, "l": a}:
+                pass
+        """)
+
+    def test_multiple_assignments_to_name_in_pattern_2(self):
+        self.assert_syntax_error("""
+        match ...:
+            case MyClass(x, x):
+                pass
+        """)
+
+    def test_multiple_assignments_to_name_in_pattern_3(self):
+        self.assert_syntax_error("""
+        match ...:
+            case MyClass(x=x, y=x):
+                pass
+        """)
+
+    def test_multiple_assignments_to_name_in_pattern_4(self):
+        self.assert_syntax_error("""
+        match ...:
+            case MyClass(x, y=x):
+                pass
+        """)
+
+    def test_multiple_assignments_to_name_in_pattern_5(self):
+        self.assert_syntax_error("""
+        match ...:
+            case a as a:
+                pass
+        """)
+
+    def test_multiple_starred_names_in_sequence_pattern_0(self):
+        self.assert_syntax_error("""
+        match ...:
+            case *a, b, *c, d, *e:
+                pass
+        """)
+
+    def test_multiple_starred_names_in_sequence_pattern_1(self):
+        self.assert_syntax_error("""
+        match ...:
+            case a, *b, c, *d, e:
+                pass
+        """)
+
+    def test_name_capture_makes_remaining_patterns_unreachable_0(self):
+        self.assert_syntax_error("""
+        match ...:
+            case a | "a":
+                pass
+        """)
+
+    def test_name_capture_makes_remaining_patterns_unreachable_1(self):
+        self.assert_syntax_error("""
+        match 42:
+            case x:
+                pass
+            case y:
+                pass
+        """)
+
+    def test_name_capture_makes_remaining_patterns_unreachable_2(self):
+        self.assert_syntax_error("""
+        match ...:
+            case x | [_ as x] if x:
+                pass
+        """)
+
+    def test_name_capture_makes_remaining_patterns_unreachable_3(self):
+        self.assert_syntax_error("""
+        match ...:
+            case x:
+                pass
+            case [x] if x:
+                pass
+        """)
+
+    def test_name_capture_makes_remaining_patterns_unreachable_4(self):
+        self.assert_syntax_error("""
+        match ...:
+            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
+        """)
+
+    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)
 
-class TestInheritance(unittest.TestCase):
+    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)
 
-    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_1(self):
+        class Class:
+            __match_args__ = "XYZ"
+        x = Class()
+        y = z = None
+        with self.assertRaises(TypeError):
             match x:
-                case []:
-                    return "seq"
-                case {}:
-                    return "map"
-        def g(x):
+                case Class(y):
+                    z = 0
+        self.assertIs(y, None)
+        self.assertIs(z, None)
+
+    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 "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 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 {"a": y, "a": z}:
+                    w = 0
+        self.assertIs(w, None)
+        self.assertIs(y, None)
+        self.assertIs(z, None)
 
-class PerfPatma(TestPatma):
+    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)
 
-    def assertEqual(*_, **__):
-        pass
 
-    def assertIs(*_, **__):
-        pass
+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
 
-    def assertRaises(*_, **__):
-        assert False, "this test should be decorated with @no_perf!"
 
-    def assertWarns(*_, **__):
-        assert False, "this test should be decorated with @no_perf!"
+    class PerfPatma(TestPatma):
 
-    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):
-            for test in tests:
-                test(self)
+        def assertEqual(*_, **__):
+            pass
+
+        def assertIs(*_, **__):
+            pass
+
+        def assertRaises(*_, **__):
+            assert False, "this test should be a method of a different class!"
 
-    @staticmethod
-    def setUpClass():
-        raise unittest.SkipTest("performance testing")
+        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()
+            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)



More information about the Python-checkins mailing list