@@ -1584,14 +1584,31 @@ def meth(x): ...
1584
1584
@runtime_checkable
1585
1585
class PG (Protocol [T ]):
1586
1586
def meth (x ): ...
1587
+ @runtime_checkable
1588
+ class WeirdProto (Protocol ):
1589
+ meth = str .maketrans
1590
+ @runtime_checkable
1591
+ class WeirdProto2 (Protocol ):
1592
+ meth = lambda * args , ** kwargs : None # noqa: E731
1593
+ class CustomCallable :
1594
+ def __call__ (self , * args , ** kwargs ):
1595
+ pass
1596
+ @runtime_checkable
1597
+ class WeirderProto (Protocol ):
1598
+ meth = CustomCallable ()
1587
1599
class BadP (Protocol ):
1588
1600
def meth (x ): ...
1589
1601
class BadPG (Protocol [T ]):
1590
1602
def meth (x ): ...
1591
1603
class C :
1592
1604
def meth (x ): ...
1593
- self .assertIsInstance (C (), P )
1594
- self .assertIsInstance (C (), PG )
1605
+ class C2 :
1606
+ def __init__ (self ):
1607
+ self .meth = lambda : None
1608
+ for klass in C , C2 :
1609
+ for proto in P , PG , WeirdProto , WeirdProto2 , WeirderProto :
1610
+ with self .subTest (klass = klass .__name__ , proto = proto .__name__ ):
1611
+ self .assertIsInstance (klass (), proto )
1595
1612
with self .assertRaises (TypeError ):
1596
1613
isinstance (C (), PG [T ])
1597
1614
with self .assertRaises (TypeError ):
@@ -1601,6 +1618,94 @@ def meth(x): ...
1601
1618
with self .assertRaises (TypeError ):
1602
1619
isinstance (C (), BadPG )
1603
1620
1621
+ def test_protocols_isinstance_properties_and_descriptors (self ):
1622
+ class C :
1623
+ @property
1624
+ def attr (self ):
1625
+ return 42
1626
+
1627
+ class CustomDescriptor :
1628
+ def __get__ (self , obj , objtype = None ):
1629
+ return 42
1630
+
1631
+ class D :
1632
+ attr = CustomDescriptor ()
1633
+
1634
+ # Check that properties set on superclasses
1635
+ # are still found by the isinstance() logic
1636
+ class E (C ): ...
1637
+ class F (D ): ...
1638
+
1639
+ class Empty : ...
1640
+
1641
+ T = TypeVar ('T' )
1642
+
1643
+ @runtime_checkable
1644
+ class P (Protocol ):
1645
+ @property
1646
+ def attr (self ): ...
1647
+
1648
+ @runtime_checkable
1649
+ class P1 (Protocol ):
1650
+ attr : int
1651
+
1652
+ @runtime_checkable
1653
+ class PG (Protocol [T ]):
1654
+ @property
1655
+ def attr (self ): ...
1656
+
1657
+ @runtime_checkable
1658
+ class PG1 (Protocol [T ]):
1659
+ attr : T
1660
+
1661
+ for protocol_class in P , P1 , PG , PG1 :
1662
+ for klass in C , D , E , F :
1663
+ with self .subTest (
1664
+ klass = klass .__name__ ,
1665
+ protocol_class = protocol_class .__name__
1666
+ ):
1667
+ self .assertIsInstance (klass (), protocol_class )
1668
+
1669
+ with self .subTest (klass = "Empty" , protocol_class = protocol_class .__name__ ):
1670
+ self .assertNotIsInstance (Empty (), protocol_class )
1671
+
1672
+ class BadP (Protocol ):
1673
+ @property
1674
+ def attr (self ): ...
1675
+
1676
+ class BadP1 (Protocol ):
1677
+ attr : int
1678
+
1679
+ class BadPG (Protocol [T ]):
1680
+ @property
1681
+ def attr (self ): ...
1682
+
1683
+ class BadPG1 (Protocol [T ]):
1684
+ attr : T
1685
+
1686
+ for obj in PG [T ], PG [C ], PG1 [T ], PG1 [C ], BadP , BadP1 , BadPG , BadPG1 :
1687
+ for klass in C , D , E , F , Empty :
1688
+ with self .subTest (klass = klass .__name__ , obj = obj ):
1689
+ with self .assertRaises (TypeError ):
1690
+ isinstance (klass (), obj )
1691
+
1692
+ def test_protocols_isinstance_not_fooled_by_custom_dir (self ):
1693
+ @runtime_checkable
1694
+ class HasX (Protocol ):
1695
+ x : int
1696
+
1697
+ class CustomDirWithX :
1698
+ x = 10
1699
+ def __dir__ (self ):
1700
+ return []
1701
+
1702
+ class CustomDirWithoutX :
1703
+ def __dir__ (self ):
1704
+ return ["x" ]
1705
+
1706
+ self .assertIsInstance (CustomDirWithX (), HasX )
1707
+ self .assertNotIsInstance (CustomDirWithoutX (), HasX )
1708
+
1604
1709
def test_protocols_isinstance_py36 (self ):
1605
1710
class APoint :
1606
1711
def __init__ (self , x , y , label ):
@@ -1646,6 +1751,20 @@ def __init__(self, x):
1646
1751
self .assertIsInstance (C (1 ), P )
1647
1752
self .assertIsInstance (C (1 ), PG )
1648
1753
1754
+ def test_protocols_isinstance_monkeypatching (self ):
1755
+ @runtime_checkable
1756
+ class HasX (Protocol ):
1757
+ x : int
1758
+
1759
+ class Foo : ...
1760
+
1761
+ f = Foo ()
1762
+ self .assertNotIsInstance (f , HasX )
1763
+ f .x = 42
1764
+ self .assertIsInstance (f , HasX )
1765
+ del f .x
1766
+ self .assertNotIsInstance (f , HasX )
1767
+
1649
1768
def test_protocols_support_register (self ):
1650
1769
@runtime_checkable
1651
1770
class P (Protocol ):
0 commit comments