]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44807: Allow Protocol classes to define __init__ (GH-31628)
authorAdrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>
Mon, 11 Apr 2022 14:51:25 +0000 (09:51 -0500)
committerGitHub <noreply@github.com>
Mon, 11 Apr 2022 14:51:25 +0000 (07:51 -0700)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2022-03-02-04-25-58.bpo-44807.gHNC9J.rst [new file with mode: 0644]

index e09f8aa3fb84967f2f8cfd3b85f5181b9d6d60ec..b884f7b2cced35b446b3e3a6c96fcaeb7ec58197 100644 (file)
@@ -1598,6 +1598,32 @@ class ProtocolTests(BaseTestCase):
         with self.assertRaises(TypeError):
             CG[int](42)
 
+    def test_protocol_defining_init_does_not_get_overridden(self):
+        # check that P.__init__ doesn't get clobbered
+        # see https://bugs.python.org/issue44807
+
+        class P(Protocol):
+            x: int
+            def __init__(self, x: int) -> None:
+                self.x = x
+        class C: pass
+
+        c = C()
+        P.__init__(c, 1)
+        self.assertEqual(c.x, 1)
+
+    def test_concrete_class_inheriting_init_from_protocol(self):
+        class P(Protocol):
+            x: int
+            def __init__(self, x: int) -> None:
+                self.x = x
+
+        class C(P): pass
+
+        c = C(1)
+        self.assertIsInstance(c, C)
+        self.assertEqual(c.x, 1)
+
     def test_cannot_instantiate_abstract(self):
         @runtime_checkable
         class P(Protocol):
index 26c6b8c278b734093b03b8c5d2810035d3ae8723..ec8cbbd8b20a38292208972dd05fbcc53457ba02 100644 (file)
@@ -1997,7 +1997,8 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
                     issubclass(base, Generic) and base._is_protocol):
                 raise TypeError('Protocols can only inherit from other'
                                 ' protocols, got %r' % base)
-        cls.__init__ = _no_init_or_replace_init
+        if cls.__init__ is Protocol.__init__:
+            cls.__init__ = _no_init_or_replace_init
 
 
 class _AnnotatedAlias(_GenericAlias, _root=True):
diff --git a/Misc/NEWS.d/next/Library/2022-03-02-04-25-58.bpo-44807.gHNC9J.rst b/Misc/NEWS.d/next/Library/2022-03-02-04-25-58.bpo-44807.gHNC9J.rst
new file mode 100644 (file)
index 0000000..4757d34
--- /dev/null
@@ -0,0 +1 @@
+:class:`typing.Protocol` no longer silently replaces :meth:`__init__` methods defined on subclasses. Patch by Adrian Garcia Badaracco.