Skip to content

Commit 2810787

Browse files
miss-islingtonAlexWaygoodcarljm
authored
gh-102433: Add tests for how classes with properties interact with isinstance() checks on typing.runtime_checkable protocols (GH-102449)
(cherry picked from commit 5ffdaf7) Co-authored-by: Alex Waygood <[email protected]> Co-authored-by: Carl Meyer <[email protected]>
1 parent 7276ee0 commit 2810787

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
@@ -1142,6 +1142,94 @@ def meth(x): ...
11421142
with self.assertRaises(TypeError):
11431143
isinstance(C(), BadPG)
11441144

1145+
def test_protocols_isinstance_properties_and_descriptors(self):
1146+
class C:
1147+
@property
1148+
def attr(self):
1149+
return 42
1150+
1151+
class CustomDescriptor:
1152+
def __get__(self, obj, objtype=None):
1153+
return 42
1154+
1155+
class D:
1156+
attr = CustomDescriptor()
1157+
1158+
# Check that properties set on superclasses
1159+
# are still found by the isinstance() logic
1160+
class E(C): ...
1161+
class F(D): ...
1162+
1163+
class Empty: ...
1164+
1165+
T = TypeVar('T')
1166+
1167+
@runtime_checkable
1168+
class P(Protocol):
1169+
@property
1170+
def attr(self): ...
1171+
1172+
@runtime_checkable
1173+
class P1(Protocol):
1174+
attr: int
1175+
1176+
@runtime_checkable
1177+
class PG(Protocol[T]):
1178+
@property
1179+
def attr(self): ...
1180+
1181+
@runtime_checkable
1182+
class PG1(Protocol[T]):
1183+
attr: T
1184+
1185+
for protocol_class in P, P1, PG, PG1:
1186+
for klass in C, D, E, F:
1187+
with self.subTest(
1188+
klass=klass.__name__,
1189+
protocol_class=protocol_class.__name__
1190+
):
1191+
self.assertIsInstance(klass(), protocol_class)
1192+
1193+
with self.subTest(klass="Empty", protocol_class=protocol_class.__name__):
1194+
self.assertNotIsInstance(Empty(), protocol_class)
1195+
1196+
class BadP(Protocol):
1197+
@property
1198+
def attr(self): ...
1199+
1200+
class BadP1(Protocol):
1201+
attr: int
1202+
1203+
class BadPG(Protocol[T]):
1204+
@property
1205+
def attr(self): ...
1206+
1207+
class BadPG1(Protocol[T]):
1208+
attr: T
1209+
1210+
for obj in PG[T], PG[C], PG1[T], PG1[C], BadP, BadP1, BadPG, BadPG1:
1211+
for klass in C, D, E, F, Empty:
1212+
with self.subTest(klass=klass.__name__, obj=obj):
1213+
with self.assertRaises(TypeError):
1214+
isinstance(klass(), obj)
1215+
1216+
def test_protocols_isinstance_not_fooled_by_custom_dir(self):
1217+
@runtime_checkable
1218+
class HasX(Protocol):
1219+
x: int
1220+
1221+
class CustomDirWithX:
1222+
x = 10
1223+
def __dir__(self):
1224+
return []
1225+
1226+
class CustomDirWithoutX:
1227+
def __dir__(self):
1228+
return ["x"]
1229+
1230+
self.assertIsInstance(CustomDirWithX(), HasX)
1231+
self.assertNotIsInstance(CustomDirWithoutX(), HasX)
1232+
11451233
def test_protocols_isinstance_py36(self):
11461234
class APoint:
11471235
def __init__(self, x, y, label):

0 commit comments

Comments
 (0)