@@ -2525,6 +2525,94 @@ def meth(x): ...
2525
2525
with self .assertRaises (TypeError ):
2526
2526
isinstance (C (), BadPG )
2527
2527
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
+
2528
2616
def test_protocols_isinstance_py36 (self ):
2529
2617
class APoint :
2530
2618
def __init__ (self , x , y , label ):
0 commit comments