Skip to content

Commit 3373985

Browse files
sobolevnGlyphack
authored andcommitted
pythongh-110686: Test pattern matching with runtime_checkable protocols (python#110687)
1 parent 9213ee1 commit 3373985

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed

Lib/test/test_patma.py

+155
Original file line numberDiff line numberDiff line change
@@ -2760,6 +2760,132 @@ def test_patma_255(self):
27602760
self.assertEqual(y, 1)
27612761
self.assertIs(z, x)
27622762

2763+
def test_patma_runtime_checkable_protocol(self):
2764+
# Runtime-checkable protocol
2765+
from typing import Protocol, runtime_checkable
2766+
2767+
@runtime_checkable
2768+
class P(Protocol):
2769+
x: int
2770+
y: int
2771+
2772+
class A:
2773+
def __init__(self, x: int, y: int):
2774+
self.x = x
2775+
self.y = y
2776+
2777+
class B(A): ...
2778+
2779+
for cls in (A, B):
2780+
with self.subTest(cls=cls.__name__):
2781+
inst = cls(1, 2)
2782+
w = 0
2783+
match inst:
2784+
case P() as p:
2785+
self.assertIsInstance(p, cls)
2786+
self.assertEqual(p.x, 1)
2787+
self.assertEqual(p.y, 2)
2788+
w = 1
2789+
self.assertEqual(w, 1)
2790+
2791+
q = 0
2792+
match inst:
2793+
case P(x=x, y=y):
2794+
self.assertEqual(x, 1)
2795+
self.assertEqual(y, 2)
2796+
q = 1
2797+
self.assertEqual(q, 1)
2798+
2799+
2800+
def test_patma_generic_protocol(self):
2801+
# Runtime-checkable generic protocol
2802+
from typing import Generic, TypeVar, Protocol, runtime_checkable
2803+
2804+
T = TypeVar('T') # not using PEP695 to be able to backport changes
2805+
2806+
@runtime_checkable
2807+
class P(Protocol[T]):
2808+
a: T
2809+
b: T
2810+
2811+
class A:
2812+
def __init__(self, x: int, y: int):
2813+
self.x = x
2814+
self.y = y
2815+
2816+
class G(Generic[T]):
2817+
def __init__(self, x: T, y: T):
2818+
self.x = x
2819+
self.y = y
2820+
2821+
for cls in (A, G):
2822+
with self.subTest(cls=cls.__name__):
2823+
inst = cls(1, 2)
2824+
w = 0
2825+
match inst:
2826+
case P():
2827+
w = 1
2828+
self.assertEqual(w, 0)
2829+
2830+
def test_patma_protocol_with_match_args(self):
2831+
# Runtime-checkable protocol with `__match_args__`
2832+
from typing import Protocol, runtime_checkable
2833+
2834+
# Used to fail before
2835+
# https://github.com/python/cpython/issues/110682
2836+
@runtime_checkable
2837+
class P(Protocol):
2838+
__match_args__ = ('x', 'y')
2839+
x: int
2840+
y: int
2841+
2842+
class A:
2843+
def __init__(self, x: int, y: int):
2844+
self.x = x
2845+
self.y = y
2846+
2847+
class B(A): ...
2848+
2849+
for cls in (A, B):
2850+
with self.subTest(cls=cls.__name__):
2851+
inst = cls(1, 2)
2852+
w = 0
2853+
match inst:
2854+
case P() as p:
2855+
self.assertIsInstance(p, cls)
2856+
self.assertEqual(p.x, 1)
2857+
self.assertEqual(p.y, 2)
2858+
w = 1
2859+
self.assertEqual(w, 1)
2860+
2861+
q = 0
2862+
match inst:
2863+
case P(x=x, y=y):
2864+
self.assertEqual(x, 1)
2865+
self.assertEqual(y, 2)
2866+
q = 1
2867+
self.assertEqual(q, 1)
2868+
2869+
j = 0
2870+
match inst:
2871+
case P(x=1, y=2):
2872+
j = 1
2873+
self.assertEqual(j, 1)
2874+
2875+
g = 0
2876+
match inst:
2877+
case P(x, y):
2878+
self.assertEqual(x, 1)
2879+
self.assertEqual(y, 2)
2880+
g = 1
2881+
self.assertEqual(g, 1)
2882+
2883+
h = 0
2884+
match inst:
2885+
case P(1, 2):
2886+
h = 1
2887+
self.assertEqual(h, 1)
2888+
27632889

27642890
class TestSyntaxErrors(unittest.TestCase):
27652891

@@ -3198,6 +3324,35 @@ def test_class_pattern_not_type(self):
31983324
w = 0
31993325
self.assertIsNone(w)
32003326

3327+
def test_regular_protocol(self):
3328+
from typing import Protocol
3329+
class P(Protocol): ...
3330+
msg = (
3331+
'Instance and class checks can only be used '
3332+
'with @runtime_checkable protocols'
3333+
)
3334+
w = None
3335+
with self.assertRaisesRegex(TypeError, msg):
3336+
match 1:
3337+
case P():
3338+
w = 0
3339+
self.assertIsNone(w)
3340+
3341+
def test_positional_patterns_with_regular_protocol(self):
3342+
from typing import Protocol
3343+
class P(Protocol):
3344+
x: int # no `__match_args__`
3345+
y: int
3346+
class A:
3347+
x = 1
3348+
y = 2
3349+
w = None
3350+
with self.assertRaises(TypeError):
3351+
match A():
3352+
case P(x, y):
3353+
w = 0
3354+
self.assertIsNone(w)
3355+
32013356

32023357
class TestValueErrors(unittest.TestCase):
32033358

0 commit comments

Comments
 (0)