[Python-checkins] gh-105834: Add tests for calling `issubclass()` between two protocols (#105835)
AlexWaygood
webhook-mailer at python.org
Fri Jun 16 11:47:59 EDT 2023
https://github.com/python/cpython/commit/70c075c194d3739ae10ce76265f05fa82ed46487
commit: 70c075c194d3739ae10ce76265f05fa82ed46487
branch: main
author: Alex Waygood <Alex.Waygood at Gmail.com>
committer: AlexWaygood <Alex.Waygood at Gmail.com>
date: 2023-06-16T15:47:55Z
summary:
gh-105834: Add tests for calling `issubclass()` between two protocols (#105835)
Some parts of the implementation of `typing.Protocol` had poor test coverage
files:
M Lib/test/test_typing.py
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 3eb0fcad69e5e..ad67568770970 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -2759,6 +2759,80 @@ def x(self): ...
with self.assertRaisesRegex(TypeError, only_classes_allowed):
issubclass(1, BadPG)
+ def test_implicit_issubclass_between_two_protocols(self):
+ @runtime_checkable
+ class CallableMembersProto(Protocol):
+ def meth(self): ...
+
+ # All the below protocols should be considered "subclasses"
+ # of CallableMembersProto at runtime,
+ # even though none of them explicitly subclass CallableMembersProto
+
+ class IdenticalProto(Protocol):
+ def meth(self): ...
+
+ class SupersetProto(Protocol):
+ def meth(self): ...
+ def meth2(self): ...
+
+ class NonCallableMembersProto(Protocol):
+ meth: Callable[[], None]
+
+ class NonCallableMembersSupersetProto(Protocol):
+ meth: Callable[[], None]
+ meth2: Callable[[str, int], bool]
+
+ class MixedMembersProto1(Protocol):
+ meth: Callable[[], None]
+ def meth2(self): ...
+
+ class MixedMembersProto2(Protocol):
+ def meth(self): ...
+ meth2: Callable[[str, int], bool]
+
+ for proto in (
+ IdenticalProto, SupersetProto, NonCallableMembersProto,
+ NonCallableMembersSupersetProto, MixedMembersProto1, MixedMembersProto2
+ ):
+ with self.subTest(proto=proto.__name__):
+ self.assertIsSubclass(proto, CallableMembersProto)
+
+ # These two shouldn't be considered subclasses of CallableMembersProto, however,
+ # since they don't have the `meth` protocol member
+
+ class EmptyProtocol(Protocol): ...
+ class UnrelatedProtocol(Protocol):
+ def wut(self): ...
+
+ self.assertNotIsSubclass(EmptyProtocol, CallableMembersProto)
+ self.assertNotIsSubclass(UnrelatedProtocol, CallableMembersProto)
+
+ # These aren't protocols at all (despite having annotations),
+ # so they should only be considered subclasses of CallableMembersProto
+ # if they *actually have an attribute* matching the `meth` member
+ # (just having an annotation is insufficient)
+
+ class AnnotatedButNotAProtocol:
+ meth: Callable[[], None]
+
+ class NotAProtocolButAnImplicitSubclass:
+ def meth(self): pass
+
+ class NotAProtocolButAnImplicitSubclass2:
+ meth: Callable[[], None]
+ def meth(self): pass
+
+ class NotAProtocolButAnImplicitSubclass3:
+ meth: Callable[[], None]
+ meth2: Callable[[int, str], bool]
+ def meth(self): pass
+ def meth(self, x, y): return True
+
+ self.assertNotIsSubclass(AnnotatedButNotAProtocol, CallableMembersProto)
+ self.assertIsSubclass(NotAProtocolButAnImplicitSubclass, CallableMembersProto)
+ self.assertIsSubclass(NotAProtocolButAnImplicitSubclass2, CallableMembersProto)
+ self.assertIsSubclass(NotAProtocolButAnImplicitSubclass3, CallableMembersProto)
+
def test_isinstance_checks_not_at_whim_of_gc(self):
self.addCleanup(gc.enable)
gc.disable()
More information about the Python-checkins
mailing list