Skip to content

Commit 5ffdaf7

Browse files
AlexWaygoodcarljm
andauthored
gh-102433: Add tests for how classes with properties interact with isinstance() checks on typing.runtime_checkable protocols (#102449)
Co-authored-by: Carl Meyer <[email protected]>
1 parent 08b67fb commit 5ffdaf7

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

Lib/test/test_typing.py

+88
Original file line numberDiff line numberDiff line change
@@ -2530,6 +2530,94 @@ def meth(x): ...
25302530
with self.assertRaises(TypeError):
25312531
isinstance(C(), BadPG)
25322532

2533+
def test_protocols_isinstance_properties_and_descriptors(self):
2534+
class C:
2535+
@property
2536+
def attr(self):
2537+
return 42
2538+
2539+
class CustomDescriptor:
2540+
def __get__(self, obj, objtype=None):
2541+
return 42
2542+
2543+
class D:
2544+
attr = CustomDescriptor()
2545+
2546+
# Check that properties set on superclasses
2547+
# are still found by the isinstance() logic
2548+
class E(C): ...
2549+
class F(D): ...
2550+
2551+
class Empty: ...
2552+
2553+
T = TypeVar('T')
2554+
2555+
@runtime_checkable
2556+
class P(Protocol):
2557+
@property
2558+
def attr(self): ...
2559+
2560+
@runtime_checkable
2561+
class P1(Protocol):
2562+
attr: int
2563+
2564+
@runtime_checkable
2565+
class PG(Protocol[T]):
2566+
@property
2567+
def attr(self): ...
2568+
2569+
@runtime_checkable
2570+
class PG1(Protocol[T]):
2571+
attr: T
2572+
2573+
for protocol_class in P, P1, PG, PG1:
2574+
for klass in C, D, E, F:
2575+
with self.subTest(
2576+
klass=klass.__name__,
2577+
protocol_class=protocol_class.__name__
2578+
):
2579+
self.assertIsInstance(klass(), protocol_class)
2580+
2581+
with self.subTest(klass="Empty", protocol_class=protocol_class.__name__):
2582+
self.assertNotIsInstance(Empty(), protocol_class)
2583+
2584+
class BadP(Protocol):
2585+
@property
2586+
def attr(self): ...
2587+
2588+
class BadP1(Protocol):
2589+
attr: int
2590+
2591+
class BadPG(Protocol[T]):
2592+
@property
2593+
def attr(self): ...
2594+
2595+
class BadPG1(Protocol[T]):
2596+
attr: T
2597+
2598+
for obj in PG[T], PG[C], PG1[T], PG1[C], BadP, BadP1, BadPG, BadPG1:
2599+
for klass in C, D, E, F, Empty:
2600+
with self.subTest(klass=klass.__name__, obj=obj):
2601+
with self.assertRaises(TypeError):
2602+
isinstance(klass(), obj)
2603+
2604+
def test_protocols_isinstance_not_fooled_by_custom_dir(self):
2605+
@runtime_checkable
2606+
class HasX(Protocol):
2607+
x: int
2608+
2609+
class CustomDirWithX:
2610+
x = 10
2611+
def __dir__(self):
2612+
return []
2613+
2614+
class CustomDirWithoutX:
2615+
def __dir__(self):
2616+
return ["x"]
2617+
2618+
self.assertIsInstance(CustomDirWithX(), HasX)
2619+
self.assertNotIsInstance(CustomDirWithoutX(), HasX)
2620+
25332621
def test_protocols_isinstance_py36(self):
25342622
class APoint:
25352623
def __init__(self, x, y, label):

0 commit comments

Comments
 (0)