mirror of
https://github.com/python/cpython.git
synced 2025-09-17 14:16:02 +00:00
bpo-44806: Fix __init__ in subclasses of protocols (GH-27545)
Non-protocol subclasses of protocol ignore now the __init__ method
inherited from protocol base classes.
(cherry picked from commit 043cd60abe
)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
9de590151d
commit
2cc19a5463
3 changed files with 48 additions and 2 deletions
|
@ -875,6 +875,9 @@ class ProtocolTests(BaseTestCase):
|
||||||
class C(P): pass
|
class C(P): pass
|
||||||
|
|
||||||
self.assertIsInstance(C(), C)
|
self.assertIsInstance(C(), C)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C(42)
|
||||||
|
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
|
||||||
class PG(Protocol[T]): pass
|
class PG(Protocol[T]): pass
|
||||||
|
@ -889,6 +892,8 @@ class ProtocolTests(BaseTestCase):
|
||||||
class CG(PG[T]): pass
|
class CG(PG[T]): pass
|
||||||
|
|
||||||
self.assertIsInstance(CG[int](), CG)
|
self.assertIsInstance(CG[int](), CG)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
CG[int](42)
|
||||||
|
|
||||||
def test_cannot_instantiate_abstract(self):
|
def test_cannot_instantiate_abstract(self):
|
||||||
@runtime_checkable
|
@runtime_checkable
|
||||||
|
@ -1316,6 +1321,37 @@ class ProtocolTests(BaseTestCase):
|
||||||
|
|
||||||
self.assertEqual(C[int]().test, 'OK')
|
self.assertEqual(C[int]().test, 'OK')
|
||||||
|
|
||||||
|
class B:
|
||||||
|
def __init__(self):
|
||||||
|
self.test = 'OK'
|
||||||
|
|
||||||
|
class D1(B, P[T]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(D1[int]().test, 'OK')
|
||||||
|
|
||||||
|
class D2(P[T], B):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(D2[int]().test, 'OK')
|
||||||
|
|
||||||
|
def test_new_called(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
class P(Protocol[T]): pass
|
||||||
|
|
||||||
|
class C(P[T]):
|
||||||
|
def __new__(cls, *args):
|
||||||
|
self = super().__new__(cls, *args)
|
||||||
|
self.test = 'OK'
|
||||||
|
return self
|
||||||
|
|
||||||
|
self.assertEqual(C[int]().test, 'OK')
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C[int](42)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
C[int](a=42)
|
||||||
|
|
||||||
def test_protocols_bad_subscripts(self):
|
def test_protocols_bad_subscripts(self):
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
S = TypeVar('S')
|
S = TypeVar('S')
|
||||||
|
|
|
@ -1379,7 +1379,6 @@ def _is_callable_members_only(cls):
|
||||||
|
|
||||||
|
|
||||||
def _no_init(self, *args, **kwargs):
|
def _no_init(self, *args, **kwargs):
|
||||||
if type(self)._is_protocol:
|
|
||||||
raise TypeError('Protocols cannot be instantiated')
|
raise TypeError('Protocols cannot be instantiated')
|
||||||
|
|
||||||
def _caller(depth=1, default='__main__'):
|
def _caller(depth=1, default='__main__'):
|
||||||
|
@ -1523,6 +1522,15 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
|
||||||
|
|
||||||
# We have nothing more to do for non-protocols...
|
# We have nothing more to do for non-protocols...
|
||||||
if not cls._is_protocol:
|
if not cls._is_protocol:
|
||||||
|
if cls.__init__ == _no_init:
|
||||||
|
for base in cls.__mro__:
|
||||||
|
init = base.__dict__.get('__init__', _no_init)
|
||||||
|
if init != _no_init:
|
||||||
|
cls.__init__ = init
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# should not happen
|
||||||
|
cls.__init__ = object.__init__
|
||||||
return
|
return
|
||||||
|
|
||||||
# ... otherwise check consistency of bases, and prohibit instantiation.
|
# ... otherwise check consistency of bases, and prohibit instantiation.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Non-protocol subclasses of :class:`typing.Protocol` ignore now the
|
||||||
|
``__init__`` method inherited from protocol base classes.
|
Loading…
Add table
Add a link
Reference in a new issue