@@ -1907,6 +1907,63 @@ class D(PNonCall): ...
1907
1907
with self .assertRaises (TypeError ):
1908
1908
issubclass (D , PNonCall )
1909
1909
1910
+ def test_no_weird_caching_with_issubclass_after_isinstance (self ):
1911
+ @runtime_checkable
1912
+ class Spam (Protocol ):
1913
+ x : int
1914
+
1915
+ class Eggs :
1916
+ def __init__ (self ) -> None :
1917
+ self .x = 42
1918
+
1919
+ self .assertIsInstance (Eggs (), Spam )
1920
+
1921
+ # gh-104555: If we didn't override ABCMeta.__subclasscheck__ in _ProtocolMeta,
1922
+ # TypeError wouldn't be raised here,
1923
+ # as the cached result of the isinstance() check immediately above
1924
+ # would mean the issubclass() call would short-circuit
1925
+ # before we got to the "raise TypeError" line
1926
+ with self .assertRaises (TypeError ):
1927
+ issubclass (Eggs , Spam )
1928
+
1929
+ def test_no_weird_caching_with_issubclass_after_isinstance_2 (self ):
1930
+ @runtime_checkable
1931
+ class Spam (Protocol ):
1932
+ x : int
1933
+
1934
+ class Eggs : ...
1935
+
1936
+ self .assertNotIsInstance (Eggs (), Spam )
1937
+
1938
+ # gh-104555: If we didn't override ABCMeta.__subclasscheck__ in _ProtocolMeta,
1939
+ # TypeError wouldn't be raised here,
1940
+ # as the cached result of the isinstance() check immediately above
1941
+ # would mean the issubclass() call would short-circuit
1942
+ # before we got to the "raise TypeError" line
1943
+ with self .assertRaises (TypeError ):
1944
+ issubclass (Eggs , Spam )
1945
+
1946
+ def test_no_weird_caching_with_issubclass_after_isinstance_3 (self ):
1947
+ @runtime_checkable
1948
+ class Spam (Protocol ):
1949
+ x : int
1950
+
1951
+ class Eggs :
1952
+ def __getattr__ (self , attr ):
1953
+ if attr == "x" :
1954
+ return 42
1955
+ raise AttributeError (attr )
1956
+
1957
+ self .assertNotIsInstance (Eggs (), Spam )
1958
+
1959
+ # gh-104555: If we didn't override ABCMeta.__subclasscheck__ in _ProtocolMeta,
1960
+ # TypeError wouldn't be raised here,
1961
+ # as the cached result of the isinstance() check immediately above
1962
+ # would mean the issubclass() call would short-circuit
1963
+ # before we got to the "raise TypeError" line
1964
+ with self .assertRaises (TypeError ):
1965
+ issubclass (Eggs , Spam )
1966
+
1910
1967
def test_protocols_isinstance (self ):
1911
1968
T = TypeVar ('T' )
1912
1969
@runtime_checkable
@@ -2235,17 +2292,31 @@ def meth(self): pass
2235
2292
class NonP (P ):
2236
2293
x = 1
2237
2294
class NonPR (PR ): pass
2238
- class C :
2295
+ class C ( metaclass = abc . ABCMeta ) :
2239
2296
x = 1
2240
- class D :
2241
- def meth (self ): pass
2297
+ class D ( metaclass = abc . ABCMeta ): # noqa: B024
2298
+ def meth (self ): pass # noqa: B027
2242
2299
self .assertNotIsInstance (C (), NonP )
2243
2300
self .assertNotIsInstance (D (), NonPR )
2244
2301
self .assertNotIsSubclass (C , NonP )
2245
2302
self .assertNotIsSubclass (D , NonPR )
2246
2303
self .assertIsInstance (NonPR (), PR )
2247
2304
self .assertIsSubclass (NonPR , PR )
2248
2305
2306
+ self .assertNotIn ("__protocol_attrs__" , vars (NonP ))
2307
+ self .assertNotIn ("__protocol_attrs__" , vars (NonPR ))
2308
+ self .assertNotIn ("__callable_proto_members_only__" , vars (NonP ))
2309
+ self .assertNotIn ("__callable_proto_members_only__" , vars (NonPR ))
2310
+
2311
+ acceptable_extra_attrs = {
2312
+ '_is_protocol' , '_is_runtime_protocol' , '__parameters__' ,
2313
+ '__init__' , '__annotations__' , '__subclasshook__' ,
2314
+ }
2315
+ self .assertLessEqual (vars (NonP ).keys (), vars (C ).keys () | acceptable_extra_attrs )
2316
+ self .assertLessEqual (
2317
+ vars (NonPR ).keys (), vars (D ).keys () | acceptable_extra_attrs
2318
+ )
2319
+
2249
2320
def test_custom_subclasshook (self ):
2250
2321
class P (Protocol ):
2251
2322
x = 1
@@ -2325,6 +2396,48 @@ def bar(self, x: str) -> str:
2325
2396
with self .assertRaises (TypeError ):
2326
2397
PR [int , ClassVar ]
2327
2398
2399
+ if sys .version_info >= (3 , 12 ):
2400
+ exec (textwrap .dedent (
2401
+ """
2402
+ def test_pep695_generic_protocol_callable_members(self):
2403
+ @runtime_checkable
2404
+ class Foo[T](Protocol):
2405
+ def meth(self, x: T) -> None: ...
2406
+
2407
+ class Bar[T]:
2408
+ def meth(self, x: T) -> None: ...
2409
+
2410
+ self.assertIsInstance(Bar(), Foo)
2411
+ self.assertIsSubclass(Bar, Foo)
2412
+
2413
+ @runtime_checkable
2414
+ class SupportsTrunc[T](Protocol):
2415
+ def __trunc__(self) -> T: ...
2416
+
2417
+ self.assertIsInstance(0.0, SupportsTrunc)
2418
+ self.assertIsSubclass(float, SupportsTrunc)
2419
+
2420
+ def test_no_weird_caching_with_issubclass_after_isinstance_pep695(self):
2421
+ @runtime_checkable
2422
+ class Spam[T](Protocol):
2423
+ x: T
2424
+
2425
+ class Eggs[T]:
2426
+ def __init__(self, x: T) -> None:
2427
+ self.x = x
2428
+
2429
+ self.assertIsInstance(Eggs(42), Spam)
2430
+
2431
+ # gh-104555: If we didn't override ABCMeta.__subclasscheck__ in _ProtocolMeta,
2432
+ # TypeError wouldn't be raised here,
2433
+ # as the cached result of the isinstance() check immediately above
2434
+ # would mean the issubclass() call would short-circuit
2435
+ # before we got to the "raise TypeError" line
2436
+ with self.assertRaises(TypeError):
2437
+ issubclass(Eggs, Spam)
2438
+ """
2439
+ ))
2440
+
2328
2441
def test_init_called (self ):
2329
2442
T = TypeVar ('T' )
2330
2443
class P (Protocol [T ]): pass
0 commit comments