Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2601138

Browse files
committedMay 16, 2023
pythongh-104555: Sidestep the ABCMeta.__instancecheck__ cache in typing._ProtocolMeta.__instancecheck__
1 parent 1163782 commit 2601138

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed
 

‎Lib/test/test_typing.py

+36
Original file line numberDiff line numberDiff line change
@@ -2654,6 +2654,42 @@ class D(PNonCall): ...
26542654
with self.assertRaises(TypeError):
26552655
issubclass(D, PNonCall)
26562656

2657+
def test_no_weird_caching_with_issubclass_after_isinstance(self):
2658+
@runtime_checkable
2659+
class Spam(Protocol[T]):
2660+
x: T
2661+
2662+
class Eggs(Generic[T]):
2663+
def __init__(self, x: T) -> None:
2664+
self.x = x
2665+
2666+
# gh-104555: ABCMeta might cache the result of this isinstance check
2667+
# if we called super().__instancecheck__ in the wrong place
2668+
# in _ProtocolMeta.__instancheck__...
2669+
self.assertIsInstance(Eggs(42), Spam)
2670+
2671+
# ...and if it did, then TypeError wouldn't be raised here!
2672+
with self.assertRaises(TypeError):
2673+
issubclass(Eggs, Spam)
2674+
2675+
def test_no_weird_caching_with_issubclass_after_isinstance_pep695(self):
2676+
@runtime_checkable
2677+
class Spam[T](Protocol):
2678+
x: T
2679+
2680+
class Eggs[T]:
2681+
def __init__(self, x: T) -> None:
2682+
self.x = x
2683+
2684+
# gh-104555: ABCMeta might cache the result of this isinstance check
2685+
# if we called super().__instancecheck__ in the wrong place
2686+
# in _ProtocolMeta.__instancheck__...
2687+
self.assertIsInstance(Eggs(42), Spam)
2688+
2689+
# ...and if it did, then TypeError wouldn't be raised here!
2690+
with self.assertRaises(TypeError):
2691+
issubclass(Eggs, Spam)
2692+
26572693
def test_protocols_isinstance(self):
26582694
T = TypeVar('T')
26592695

‎Lib/typing.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1798,7 +1798,10 @@ def __instancecheck__(cls, instance):
17981798
raise TypeError("Instance and class checks can only be used with"
17991799
" @runtime_checkable protocols")
18001800

1801-
if super().__instancecheck__(instance):
1801+
# gh-104555: Don't call super().__instancecheck__ here,
1802+
# ABCMeta.__instancecheck__ would erroneously use it to populate the cache,
1803+
# which would cause incorrect results for *issubclass()* calls
1804+
if type.__instancecheck__(cls, instance):
18021805
return True
18031806

18041807
if is_protocol_cls:
@@ -1813,7 +1816,7 @@ def __instancecheck__(cls, instance):
18131816
else:
18141817
return True
18151818

1816-
return False
1819+
return super().__instancecheck__(instance)
18171820

18181821

18191822
class Protocol(Generic, metaclass=_ProtocolMeta):

0 commit comments

Comments
 (0)
Please sign in to comment.