@@ -2530,6 +2530,94 @@ def meth(x): ...
2530
2530
with self .assertRaises (TypeError ):
2531
2531
isinstance (C (), BadPG )
2532
2532
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
+
2533
2621
def test_protocols_isinstance_py36 (self ):
2534
2622
class APoint :
2535
2623
def __init__ (self , x , y , label ):
0 commit comments