bpo-38908: Fix issue when non runtime_protocol failed to raise TypeError (GH-26067)

(cherry picked from commit c40486a32d)

Co-authored-by: Yurii Karabas <1998uriyyo@gmail.com>
This commit is contained in:
Miss Islington (bot) 2021-05-12 09:09:04 -07:00 committed by GitHub
parent bd5dfd6c8c
commit a2d94a0a9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 2 deletions

View File

@ -1422,6 +1422,14 @@ class CustomProtocol(TestCase, Protocol):
class CustomContextManager(typing.ContextManager, Protocol): class CustomContextManager(typing.ContextManager, Protocol):
pass 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): class GenericTests(BaseTestCase):
def test_basics(self): def test_basics(self):

View File

@ -1343,14 +1343,14 @@ def _no_init(self, *args, **kwargs):
raise TypeError('Protocols cannot be instantiated') 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. """Allow instance and class checks for special stdlib modules.
The abc and functools modules indiscriminately call isinstance() and The abc and functools modules indiscriminately call isinstance() and
issubclass() on the whole MRO of a user class, which may contain protocols. issubclass() on the whole MRO of a user class, which may contain protocols.
""" """
try: 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(). except (AttributeError, ValueError): # For platforms without _getframe().
return True return True
@ -1370,6 +1370,14 @@ class _ProtocolMeta(ABCMeta):
def __instancecheck__(cls, instance): def __instancecheck__(cls, instance):
# We need this method for situations where attributes are # We need this method for situations where attributes are
# assigned in __init__. # 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 if ((not getattr(cls, '_is_protocol', False) or
_is_callable_members_only(cls)) and _is_callable_members_only(cls)) and
issubclass(instance.__class__, cls)): issubclass(instance.__class__, cls)):

View File

@ -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.