@@ -2760,6 +2760,132 @@ def test_patma_255(self):
2760
2760
self .assertEqual (y , 1 )
2761
2761
self .assertIs (z , x )
2762
2762
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
+
2763
2889
2764
2890
class TestSyntaxErrors (unittest .TestCase ):
2765
2891
@@ -3198,6 +3324,35 @@ def test_class_pattern_not_type(self):
3198
3324
w = 0
3199
3325
self .assertIsNone (w )
3200
3326
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
+
3201
3356
3202
3357
class TestValueErrors (unittest .TestCase ):
3203
3358
0 commit comments