Skip to content

Commit e590338

Browse files
authored
[3.11] gh-105834: Backport new tests for typing.Protocol (#105835) (#105860)
1 parent a9d4f51 commit e590338

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

Lib/test/test_typing.py

+99
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from functools import lru_cache, wraps
55
import inspect
66
import itertools
7+
import gc
78
import pickle
89
import re
910
import sys
@@ -2643,6 +2644,104 @@ def x(self): ...
26432644
with self.assertRaises(TypeError):
26442645
issubclass(PG, PG[int])
26452646

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

0 commit comments

Comments
 (0)