gh-102433: Add tests for how classes with properties interact with `isinstance()` checks on `typing.runtime_checkable` protocols (#102449)

Co-authored-by: Carl Meyer <carl@oddbird.net>
This commit is contained in:
Alex Waygood 2023-03-11 01:20:20 +00:00 committed by GitHub
parent 08b67fb34f
commit 5ffdaf748d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 88 additions and 0 deletions

View File

@ -2530,6 +2530,94 @@ class ProtocolTests(BaseTestCase):
with self.assertRaises(TypeError):
isinstance(C(), BadPG)
def test_protocols_isinstance_properties_and_descriptors(self):
class C:
@property
def attr(self):
return 42
class CustomDescriptor:
def __get__(self, obj, objtype=None):
return 42
class D:
attr = CustomDescriptor()
# Check that properties set on superclasses
# are still found by the isinstance() logic
class E(C): ...
class F(D): ...
class Empty: ...
T = TypeVar('T')
@runtime_checkable
class P(Protocol):
@property
def attr(self): ...
@runtime_checkable
class P1(Protocol):
attr: int
@runtime_checkable
class PG(Protocol[T]):
@property
def attr(self): ...
@runtime_checkable
class PG1(Protocol[T]):
attr: T
for protocol_class in P, P1, PG, PG1:
for klass in C, D, E, F:
with self.subTest(
klass=klass.__name__,
protocol_class=protocol_class.__name__
):
self.assertIsInstance(klass(), protocol_class)
with self.subTest(klass="Empty", protocol_class=protocol_class.__name__):
self.assertNotIsInstance(Empty(), protocol_class)
class BadP(Protocol):
@property
def attr(self): ...
class BadP1(Protocol):
attr: int
class BadPG(Protocol[T]):
@property
def attr(self): ...
class BadPG1(Protocol[T]):
attr: T
for obj in PG[T], PG[C], PG1[T], PG1[C], BadP, BadP1, BadPG, BadPG1:
for klass in C, D, E, F, Empty:
with self.subTest(klass=klass.__name__, obj=obj):
with self.assertRaises(TypeError):
isinstance(klass(), obj)
def test_protocols_isinstance_not_fooled_by_custom_dir(self):
@runtime_checkable
class HasX(Protocol):
x: int
class CustomDirWithX:
x = 10
def __dir__(self):
return []
class CustomDirWithoutX:
def __dir__(self):
return ["x"]
self.assertIsInstance(CustomDirWithX(), HasX)
self.assertNotIsInstance(CustomDirWithoutX(), HasX)
def test_protocols_isinstance_py36(self):
class APoint:
def __init__(self, x, y, label):