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