@@ -2760,6 +2760,120 @@ def test_patma_255(self):
2760
2760
self .assertEqual (y , 1 )
2761
2761
self .assertIs (z , x )
2762
2762
2763
+ def test_patma_256 (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 ):
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_257 (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 ):
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_258 (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 ):
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
+ g = 0
2870
+ match inst :
2871
+ case P (x , y ):
2872
+ self .assertEqual (x , 1 )
2873
+ self .assertEqual (y , 2 )
2874
+ g = 1
2875
+ self .assertEqual (g , 1 )
2876
+
2763
2877
2764
2878
class TestSyntaxErrors (unittest .TestCase ):
2765
2879
@@ -3198,6 +3312,35 @@ def test_class_pattern_not_type(self):
3198
3312
w = 0
3199
3313
self .assertIsNone (w )
3200
3314
3315
+ def test_regular_protocol (self ):
3316
+ from typing import Protocol
3317
+ class P (Protocol ): ...
3318
+ msg = (
3319
+ 'Instance and class checks can only be used '
3320
+ 'with @runtime_checkable protocols'
3321
+ )
3322
+ w = None
3323
+ with self .assertRaisesRegex (TypeError , msg ):
3324
+ match 1 :
3325
+ case P ():
3326
+ w = 0
3327
+ self .assertIsNone (w )
3328
+
3329
+ def test_positional_patterns_with_regular_protocol (self ):
3330
+ from typing import Protocol
3331
+ class P (Protocol ):
3332
+ x : int # no `__match_args__`
3333
+ y : int
3334
+ class A :
3335
+ x = 1
3336
+ y = 2
3337
+ w = None
3338
+ with self .assertRaises (TypeError ):
3339
+ match A ():
3340
+ case P (x , y ):
3341
+ w = 0
3342
+ self .assertIsNone (w )
3343
+
3201
3344
3202
3345
class TestValueErrors (unittest .TestCase ):
3203
3346
0 commit comments