diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2021-08-02 17:08:59 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-02 17:08:59 (GMT) |
commit | 2cc19a5463c804b2f39b94de896d55dcb57a364c (patch) | |
tree | a92e648481d76d40bcbd84e4e54c74e24dabd2e8 | |
parent | 9de590151d49f2988d3a5c4d30b9daf86616f6f3 (diff) | |
download | cpython-2cc19a5463c804b2f39b94de896d55dcb57a364c.zip cpython-2cc19a5463c804b2f39b94de896d55dcb57a364c.tar.gz cpython-2cc19a5463c804b2f39b94de896d55dcb57a364c.tar.bz2 |
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 043cd60abed09edddc7185bcf7d039771acc734d)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
-rw-r--r-- | Lib/test/test_typing.py | 36 | ||||
-rw-r--r-- | Lib/typing.py | 12 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2021-08-02-14-37-32.bpo-44806.wOW_Qn.rst | 2 |
3 files changed, 48 insertions, 2 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 3d9a347..6e557c4 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -875,6 +875,9 @@ class ProtocolTests(BaseTestCase): class C(P): pass self.assertIsInstance(C(), C) + with self.assertRaises(TypeError): + C(42) + T = TypeVar('T') class PG(Protocol[T]): pass @@ -889,6 +892,8 @@ class ProtocolTests(BaseTestCase): class CG(PG[T]): pass self.assertIsInstance(CG[int](), CG) + with self.assertRaises(TypeError): + CG[int](42) def test_cannot_instantiate_abstract(self): @runtime_checkable @@ -1316,6 +1321,37 @@ class ProtocolTests(BaseTestCase): 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): T = TypeVar('T') S = TypeVar('S') diff --git a/Lib/typing.py b/Lib/typing.py index e492bd2..6f884e1 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1379,8 +1379,7 @@ def _is_callable_members_only(cls): 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__'): try: @@ -1523,6 +1522,15 @@ class Protocol(Generic, metaclass=_ProtocolMeta): # We have nothing more to do for non-protocols... 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 # ... otherwise check consistency of bases, and prohibit instantiation. diff --git a/Misc/NEWS.d/next/Library/2021-08-02-14-37-32.bpo-44806.wOW_Qn.rst b/Misc/NEWS.d/next/Library/2021-08-02-14-37-32.bpo-44806.wOW_Qn.rst new file mode 100644 index 0000000..6d818c3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-08-02-14-37-32.bpo-44806.wOW_Qn.rst @@ -0,0 +1,2 @@ +Non-protocol subclasses of :class:`typing.Protocol` ignore now the +``__init__`` method inherited from protocol base classes. |