[3.11] gh-105834: Backport new tests for typing.Protocol (#105835) (#105860)

This commit is contained in:
Alex Waygood 2023-06-16 17:39:12 +01:00 committed by GitHub
parent a9d4f51e7e
commit e590338893
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -4,6 +4,7 @@
from functools import lru_cache, wraps
import inspect
import itertools
import gc
import pickle
import re
import sys
@ -2643,6 +2644,104 @@ def x(self): ...
with self.assertRaises(TypeError):
issubclass(PG, PG[int])
only_classes_allowed = r"issubclass\(\) arg 1 must be a class"
with self.assertRaisesRegex(TypeError, only_classes_allowed):
issubclass(1, P)
with self.assertRaisesRegex(TypeError, only_classes_allowed):
issubclass(1, PG)
with self.assertRaisesRegex(TypeError, only_classes_allowed):
issubclass(1, BadP)
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()
with self.assertRaisesRegex(
TypeError,
"Protocols can only inherit from other protocols"
):
class Foo(collections.abc.Mapping, Protocol):
pass
self.assertNotIsInstance([], collections.abc.Mapping)
def test_protocols_issubclass_non_callable(self):
class C:
x = 1