@@ -2643,6 +2643,104 @@ def x(self): ...
2643
2643
with self .assertRaises (TypeError ):
2644
2644
issubclass (PG , PG [int ])
2645
2645
2646
+ only_classes_allowed = r"issubclass\(\) arg 1 must be a class"
2647
+
2648
+ with self .assertRaisesRegex (TypeError , only_classes_allowed ):
2649
+ issubclass (1 , P )
2650
+ with self .assertRaisesRegex (TypeError , only_classes_allowed ):
2651
+ issubclass (1 , PG )
2652
+ with self .assertRaisesRegex (TypeError , only_classes_allowed ):
2653
+ issubclass (1 , BadP )
2654
+ with self .assertRaisesRegex (TypeError , only_classes_allowed ):
2655
+ issubclass (1 , BadPG )
2656
+
2657
+ def test_implicit_issubclass_between_two_protocols (self ):
2658
+ @runtime_checkable
2659
+ class CallableMembersProto (Protocol ):
2660
+ def meth (self ): ...
2661
+
2662
+ # All the below protocols should be considered "subclasses"
2663
+ # of CallableMembersProto at runtime,
2664
+ # even though none of them explicitly subclass CallableMembersProto
2665
+
2666
+ class IdenticalProto (Protocol ):
2667
+ def meth (self ): ...
2668
+
2669
+ class SupersetProto (Protocol ):
2670
+ def meth (self ): ...
2671
+ def meth2 (self ): ...
2672
+
2673
+ class NonCallableMembersProto (Protocol ):
2674
+ meth : Callable [[], None ]
2675
+
2676
+ class NonCallableMembersSupersetProto (Protocol ):
2677
+ meth : Callable [[], None ]
2678
+ meth2 : Callable [[str , int ], bool ]
2679
+
2680
+ class MixedMembersProto1 (Protocol ):
2681
+ meth : Callable [[], None ]
2682
+ def meth2 (self ): ...
2683
+
2684
+ class MixedMembersProto2 (Protocol ):
2685
+ def meth (self ): ...
2686
+ meth2 : Callable [[str , int ], bool ]
2687
+
2688
+ for proto in (
2689
+ IdenticalProto , SupersetProto , NonCallableMembersProto ,
2690
+ NonCallableMembersSupersetProto , MixedMembersProto1 , MixedMembersProto2
2691
+ ):
2692
+ with self .subTest (proto = proto .__name__ ):
2693
+ self .assertIsSubclass (proto , CallableMembersProto )
2694
+
2695
+ # These two shouldn't be considered subclasses of CallableMembersProto, however,
2696
+ # since they don't have the `meth` protocol member
2697
+
2698
+ class EmptyProtocol (Protocol ): ...
2699
+ class UnrelatedProtocol (Protocol ):
2700
+ def wut (self ): ...
2701
+
2702
+ self .assertNotIsSubclass (EmptyProtocol , CallableMembersProto )
2703
+ self .assertNotIsSubclass (UnrelatedProtocol , CallableMembersProto )
2704
+
2705
+ # These aren't protocols at all (despite having annotations),
2706
+ # so they should only be considered subclasses of CallableMembersProto
2707
+ # if they *actually have an attribute* matching the `meth` member
2708
+ # (just having an annotation is insufficient)
2709
+
2710
+ class AnnotatedButNotAProtocol :
2711
+ meth : Callable [[], None ]
2712
+
2713
+ class NotAProtocolButAnImplicitSubclass :
2714
+ def meth (self ): pass
2715
+
2716
+ class NotAProtocolButAnImplicitSubclass2 :
2717
+ meth : Callable [[], None ]
2718
+ def meth (self ): pass
2719
+
2720
+ class NotAProtocolButAnImplicitSubclass3 :
2721
+ meth : Callable [[], None ]
2722
+ meth2 : Callable [[int , str ], bool ]
2723
+ def meth (self ): pass
2724
+ def meth (self , x , y ): return True
2725
+
2726
+ self .assertNotIsSubclass (AnnotatedButNotAProtocol , CallableMembersProto )
2727
+ self .assertIsSubclass (NotAProtocolButAnImplicitSubclass , CallableMembersProto )
2728
+ self .assertIsSubclass (NotAProtocolButAnImplicitSubclass2 , CallableMembersProto )
2729
+ self .assertIsSubclass (NotAProtocolButAnImplicitSubclass3 , CallableMembersProto )
2730
+
2731
+ def test_isinstance_checks_not_at_whim_of_gc (self ):
2732
+ self .addCleanup (gc .enable )
2733
+ gc .disable ()
2734
+
2735
+ with self .assertRaisesRegex (
2736
+ TypeError ,
2737
+ "Protocols can only inherit from other protocols"
2738
+ ):
2739
+ class Foo (collections .abc .Mapping , Protocol ):
2740
+ pass
2741
+
2742
+ self .assertNotIsInstance ([], collections .abc .Mapping )
2743
+
2646
2744
def test_protocols_issubclass_non_callable (self ):
2647
2745
class C :
2648
2746
x = 1
0 commit comments