@@ -1142,6 +1142,94 @@ def meth(x): ...
1142
1142
with self .assertRaises (TypeError ):
1143
1143
isinstance (C (), BadPG )
1144
1144
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
+
1145
1233
def test_protocols_isinstance_py36 (self ):
1146
1234
class APoint :
1147
1235
def __init__ (self , x , y , label ):
0 commit comments