Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-38908: Fix issue when non runtime_protocol does not raise TypeError #26067

Merged
merged 4 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,14 @@ class CustomProtocol(TestCase, Protocol):
class CustomContextManager(typing.ContextManager, Protocol):
pass

def test_non_runtime_protocol_isinstance_check(self):
class P(Protocol):
x: int

with self.assertRaisesRegex(TypeError, "@runtime_checkable"):
isinstance(1, P)


class GenericTests(BaseTestCase):

def test_basics(self):
Expand Down
12 changes: 10 additions & 2 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1343,14 +1343,14 @@ def _no_init(self, *args, **kwargs):
raise TypeError('Protocols cannot be instantiated')


def _allow_reckless_class_checks():
def _allow_reckless_class_checks(depth=3):
"""Allow instance and class checks for special stdlib modules.

The abc and functools modules indiscriminately call isinstance() and
issubclass() on the whole MRO of a user class, which may contain protocols.
"""
try:
return sys._getframe(3).f_globals['__name__'] in ['abc', 'functools']
return sys._getframe(depth).f_globals['__name__'] in ['abc', 'functools']
except (AttributeError, ValueError): # For platforms without _getframe().
return True

Expand All @@ -1370,6 +1370,14 @@ class _ProtocolMeta(ABCMeta):
def __instancecheck__(cls, instance):
uriyyo marked this conversation as resolved.
Show resolved Hide resolved
# We need this method for situations where attributes are
# assigned in __init__.
if (
getattr(cls, '_is_protocol', False) and
not getattr(cls, '_is_runtime_protocol', False) and
not _allow_reckless_class_checks(depth=2)
):
raise TypeError("Instance and class checks can only be used with"
" @runtime_checkable protocols")

if ((not getattr(cls, '_is_protocol', False) or
_is_callable_members_only(cls)) and
issubclass(instance.__class__, cls)):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fix issue where :mod:`typing` protocols without the ``@runtime_checkable``
decorator did not raise a ``TypeError`` when used with ``issubclass`` and
``isinstance``. Now, subclassses of ``typing.Protocol`` will raise a
``TypeError`` when used with with those checks.
Patch provided by Yurii Karabas.