mirror of https://github.com/python/cpython
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:
parent
08b67fb34f
commit
5ffdaf748d
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue