[Python-checkins] bpo-44806: Fix __init__ in subclasses of protocols (GH-27545)

miss-islington webhook-mailer at python.org
Mon Aug 2 13:09:08 EDT 2021


https://github.com/python/cpython/commit/2cc19a5463c804b2f39b94de896d55dcb57a364c
commit: 2cc19a5463c804b2f39b94de896d55dcb57a364c
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2021-08-02T10:08:59-07:00
summary:

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 at gmail.com>

files:
A Misc/NEWS.d/next/Library/2021-08-02-14-37-32.bpo-44806.wOW_Qn.rst
M Lib/test/test_typing.py
M Lib/typing.py

diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 3d9a347dcb707..6e557c4783338 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -875,6 +875,9 @@ class P(Protocol): pass
         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 PG(Protocol[T]): pass
         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 @@ def __init__(self):
 
         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 e492bd2ee916b..6f884e1fe9860 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 @@ def _proto_hook(other):
 
         # 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 0000000000000..6d818c3fc5798
--- /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.



More information about the Python-checkins mailing list