https://github.com/python/cpython/commit/e2c038f5be2df39a7b06364dec0836a25c423b67
commit: e2c038f5be2df39a7b06364dec0836a25c423b67
branch: main
author: Brian Schubert <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2025-09-05T20:44:50Z
summary:

gh-138010: Fix `__init_subclass__` forwarding by `warnings.deprecated` (#138210)

files:
A Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst
M Lib/_py_warnings.py
M Lib/test/test_warnings/__init__.py

diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py
index 5070caea6bb054..2e7113a637a2ac 100644
--- a/Lib/_py_warnings.py
+++ b/Lib/_py_warnings.py
@@ -765,27 +765,27 @@ def __new__(cls, /, *args, **kwargs):
 
             arg.__new__ = staticmethod(__new__)
 
-            original_init_subclass = arg.__init_subclass__
-            # We need slightly different behavior if __init_subclass__
-            # is a bound method (likely if it was implemented in Python)
-            if isinstance(original_init_subclass, MethodType):
-                original_init_subclass = original_init_subclass.__func__
+            if "__init_subclass__" in arg.__dict__:
+                # __init_subclass__ is directly present on the decorated class.
+                # Synthesize a wrapper that calls this method directly.
+                original_init_subclass = arg.__init_subclass__
+                # We need slightly different behavior if __init_subclass__
+                # is a bound method (likely if it was implemented in Python).
+                # Otherwise, it likely means it's a builtin such as
+                # object's implementation of __init_subclass__.
+                if isinstance(original_init_subclass, MethodType):
+                    original_init_subclass = original_init_subclass.__func__
 
                 @functools.wraps(original_init_subclass)
                 def __init_subclass__(*args, **kwargs):
                     _wm.warn(msg, category=category, stacklevel=stacklevel + 1)
                     return original_init_subclass(*args, **kwargs)
-
-                arg.__init_subclass__ = classmethod(__init_subclass__)
-            # Or otherwise, which likely means it's a builtin such as
-            # object's implementation of __init_subclass__.
             else:
-                @functools.wraps(original_init_subclass)
-                def __init_subclass__(*args, **kwargs):
+                def __init_subclass__(cls, *args, **kwargs):
                     _wm.warn(msg, category=category, stacklevel=stacklevel + 1)
-                    return original_init_subclass(*args, **kwargs)
+                    return super(arg, cls).__init_subclass__(*args, **kwargs)
 
-                arg.__init_subclass__ = __init_subclass__
+            arg.__init_subclass__ = classmethod(__init_subclass__)
 
             arg.__deprecated__ = __new__.__deprecated__ = msg
             __init_subclass__.__deprecated__ = msg
diff --git a/Lib/test/test_warnings/__init__.py 
b/Lib/test/test_warnings/__init__.py
index 694cfc97064c30..256713365286c2 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -1863,6 +1863,25 @@ class D(C, x=3):
 
         self.assertEqual(D.inited, 3)
 
+    def test_existing_init_subclass_in_sibling_base(self):
+        @deprecated("A will go away soon")
+        class A:
+            pass
+        class B:
+            def __init_subclass__(cls, x):
+                super().__init_subclass__()
+                cls.inited = x
+
+        with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"):
+            class C(A, B, x=42):
+                pass
+        self.assertEqual(C.inited, 42)
+
+        with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"):
+            class D(B, A, x=42):
+                pass
+        self.assertEqual(D.inited, 42)
+
     def test_init_subclass_has_correct_cls(self):
         init_subclass_saw = None
 
diff --git 
a/Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst 
b/Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst
new file mode 100644
index 00000000000000..117f3ad6c6761b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-27-17-05-36.gh-issue-138010.ZZJmPL.rst
@@ -0,0 +1,4 @@
+Fix an issue where defining a class with an :func:`@warnings.deprecated
+<warnings.deprecated>`-decorated base class may not invoke the correct
+:meth:`~object.__init_subclass__` method in cases involving multiple
+inheritance. Patch by Brian Schubert.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to