@@ -2643,6 +2643,104 @@ def x(self): ...
26432643 with self .assertRaises (TypeError ):
26442644 issubclass (PG , PG [int ])
26452645
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+
26462744 def test_protocols_issubclass_non_callable (self ):
26472745 class C :
26482746 x = 1
0 commit comments