mirror of https://github.com/python/cpython
gh-104935: typing: Fix interactions between `@runtime_checkable` and `Generic` (#104939)
--------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
77d7ec5aa9
commit
2b7027d0b2
|
@ -2472,6 +2472,48 @@ class ProtocolTests(BaseTestCase):
|
||||||
self.assertNotIsSubclass(types.FunctionType, P)
|
self.assertNotIsSubclass(types.FunctionType, P)
|
||||||
self.assertNotIsInstance(f, P)
|
self.assertNotIsInstance(f, P)
|
||||||
|
|
||||||
|
def test_runtime_checkable_generic_non_protocol(self):
|
||||||
|
# Make sure this doesn't raise AttributeError
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
"@runtime_checkable can be only applied to protocol classes",
|
||||||
|
):
|
||||||
|
@runtime_checkable
|
||||||
|
class Foo[T]: ...
|
||||||
|
|
||||||
|
def test_runtime_checkable_generic(self):
|
||||||
|
@runtime_checkable
|
||||||
|
class Foo[T](Protocol):
|
||||||
|
def meth(self) -> T: ...
|
||||||
|
|
||||||
|
class Impl:
|
||||||
|
def meth(self) -> int: ...
|
||||||
|
|
||||||
|
self.assertIsSubclass(Impl, Foo)
|
||||||
|
|
||||||
|
class NotImpl:
|
||||||
|
def method(self) -> int: ...
|
||||||
|
|
||||||
|
self.assertNotIsSubclass(NotImpl, Foo)
|
||||||
|
|
||||||
|
def test_pep695_generics_can_be_runtime_checkable(self):
|
||||||
|
@runtime_checkable
|
||||||
|
class HasX(Protocol):
|
||||||
|
x: int
|
||||||
|
|
||||||
|
class Bar[T]:
|
||||||
|
x: T
|
||||||
|
def __init__(self, x):
|
||||||
|
self.x = x
|
||||||
|
|
||||||
|
class Capybara[T]:
|
||||||
|
y: str
|
||||||
|
def __init__(self, y):
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
self.assertIsInstance(Bar(1), HasX)
|
||||||
|
self.assertNotIsInstance(Capybara('a'), HasX)
|
||||||
|
|
||||||
def test_everything_implements_empty_protocol(self):
|
def test_everything_implements_empty_protocol(self):
|
||||||
@runtime_checkable
|
@runtime_checkable
|
||||||
class Empty(Protocol):
|
class Empty(Protocol):
|
||||||
|
|
|
@ -1894,7 +1894,7 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
|
||||||
annotations = getattr(base, '__annotations__', {})
|
annotations = getattr(base, '__annotations__', {})
|
||||||
if (isinstance(annotations, collections.abc.Mapping) and
|
if (isinstance(annotations, collections.abc.Mapping) and
|
||||||
attr in annotations and
|
attr in annotations and
|
||||||
issubclass(other, Generic) and other._is_protocol):
|
issubclass(other, Generic) and getattr(other, '_is_protocol', False)):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
@ -1912,7 +1912,7 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
|
||||||
if not (base in (object, Generic) or
|
if not (base in (object, Generic) or
|
||||||
base.__module__ in _PROTO_ALLOWLIST and
|
base.__module__ in _PROTO_ALLOWLIST and
|
||||||
base.__name__ in _PROTO_ALLOWLIST[base.__module__] or
|
base.__name__ in _PROTO_ALLOWLIST[base.__module__] or
|
||||||
issubclass(base, Generic) and base._is_protocol):
|
issubclass(base, Generic) and getattr(base, '_is_protocol', False)):
|
||||||
raise TypeError('Protocols can only inherit from other'
|
raise TypeError('Protocols can only inherit from other'
|
||||||
' protocols, got %r' % base)
|
' protocols, got %r' % base)
|
||||||
if cls.__init__ is Protocol.__init__:
|
if cls.__init__ is Protocol.__init__:
|
||||||
|
@ -2059,7 +2059,7 @@ def runtime_checkable(cls):
|
||||||
Warning: this will check only the presence of the required methods,
|
Warning: this will check only the presence of the required methods,
|
||||||
not their type signatures!
|
not their type signatures!
|
||||||
"""
|
"""
|
||||||
if not issubclass(cls, Generic) or not cls._is_protocol:
|
if not issubclass(cls, Generic) or not getattr(cls, '_is_protocol', False):
|
||||||
raise TypeError('@runtime_checkable can be only applied to protocol classes,'
|
raise TypeError('@runtime_checkable can be only applied to protocol classes,'
|
||||||
' got %r' % cls)
|
' got %r' % cls)
|
||||||
cls._is_runtime_protocol = True
|
cls._is_runtime_protocol = True
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix bugs with the interaction between :func:`typing.runtime_checkable` and
|
||||||
|
:class:`typing.Generic` that were introduced by the :pep:`695`
|
||||||
|
implementation. Patch by Jelle Zijlstra.
|
Loading…
Reference in New Issue