Skip to content

Commit 88136bb

Browse files
[3.9] bpo-38908: Fix issue when non runtime_protocol does not raise TypeError (GH-26067) (GH-26075)
(cherry picked from commit c40486a) Co-authored-by: Yurii Karabas 1998uriyyo@gmail.com Automerge-Triggered-By: GH:gvanrossum
1 parent 1be9396 commit 88136bb

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

Lib/test/test_typing.py

+8
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,14 @@ class CustomProtocol(TestCase, Protocol):
14221422
class CustomContextManager(typing.ContextManager, Protocol):
14231423
pass
14241424

1425+
def test_non_runtime_protocol_isinstance_check(self):
1426+
class P(Protocol):
1427+
x: int
1428+
1429+
with self.assertRaisesRegex(TypeError, "@runtime_checkable"):
1430+
isinstance(1, P)
1431+
1432+
14251433
class GenericTests(BaseTestCase):
14261434

14271435
def test_basics(self):

Lib/typing.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -1079,14 +1079,14 @@ def _no_init(self, *args, **kwargs):
10791079
raise TypeError('Protocols cannot be instantiated')
10801080

10811081

1082-
def _allow_reckless_class_cheks():
1082+
def _allow_reckless_class_checks(depth=3):
10831083
"""Allow instance and class checks for special stdlib modules.
10841084
10851085
The abc and functools modules indiscriminately call isinstance() and
10861086
issubclass() on the whole MRO of a user class, which may contain protocols.
10871087
"""
10881088
try:
1089-
return sys._getframe(3).f_globals['__name__'] in ['abc', 'functools']
1089+
return sys._getframe(depth).f_globals['__name__'] in ['abc', 'functools']
10901090
except (AttributeError, ValueError): # For platforms without _getframe().
10911091
return True
10921092

@@ -1106,6 +1106,14 @@ class _ProtocolMeta(ABCMeta):
11061106
def __instancecheck__(cls, instance):
11071107
# We need this method for situations where attributes are
11081108
# assigned in __init__.
1109+
if (
1110+
getattr(cls, '_is_protocol', False) and
1111+
not getattr(cls, '_is_runtime_protocol', False) and
1112+
not _allow_reckless_class_checks(depth=2)
1113+
):
1114+
raise TypeError("Instance and class checks can only be used with"
1115+
" @runtime_checkable protocols")
1116+
11091117
if ((not getattr(cls, '_is_protocol', False) or
11101118
_is_callable_members_only(cls)) and
11111119
issubclass(instance.__class__, cls)):
@@ -1168,12 +1176,12 @@ def _proto_hook(other):
11681176

11691177
# First, perform various sanity checks.
11701178
if not getattr(cls, '_is_runtime_protocol', False):
1171-
if _allow_reckless_class_cheks():
1179+
if _allow_reckless_class_checks():
11721180
return NotImplemented
11731181
raise TypeError("Instance and class checks can only be used with"
11741182
" @runtime_checkable protocols")
11751183
if not _is_callable_members_only(cls):
1176-
if _allow_reckless_class_cheks():
1184+
if _allow_reckless_class_checks():
11771185
return NotImplemented
11781186
raise TypeError("Protocols with non-method members"
11791187
" don't support issubclass()")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix issue where :mod:`typing` protocols without the ``@runtime_checkable``
2+
decorator did not raise a ``TypeError`` when used with ``issubclass`` and
3+
``isinstance``. Now, subclassses of ``typing.Protocol`` will raise a
4+
``TypeError`` when used with with those checks.
5+
Patch provided by Yurii Karabas.

0 commit comments

Comments
 (0)