]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-40897:Give priority to using the current class constructor in `inspect.signature...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 16 Jul 2021 13:25:57 +0000 (06:25 -0700)
committerGitHub <noreply@github.com>
Fri, 16 Jul 2021 13:25:57 +0000 (15:25 +0200)
Co-authored-by: Ɓukasz Langa <lukasz@langa.pl>
(cherry picked from commit 6aab5f9bf303a8e4cd8377fabcdcb499e0541f9a)

Co-authored-by: Weipeng Hong <hongweichen8888@sina.com>
Lib/inspect.py
Lib/test/test_inspect.py
Misc/NEWS.d/next/Library/2021-07-16-13-40-31.bpo-40897.aveAre.rst [new file with mode: 0644]

index 0273ffa2ba4d1b20d40c27ba3e9c5f0ea6be4fcc..750fd4515339b3326fa1b52820fcbd65b7a6b604 100644 (file)
@@ -2471,15 +2471,23 @@ def _signature_from_callable(obj, *,
         if call is not None:
             sig = _get_signature_of(call)
         else:
-            # Now we check if the 'obj' class has a '__new__' method
+            factory_method = None
             new = _signature_get_user_defined_method(obj, '__new__')
-            if new is not None:
-                sig = _get_signature_of(new)
-            else:
-                # Finally, we should have at least __init__ implemented
-                init = _signature_get_user_defined_method(obj, '__init__')
-                if init is not None:
-                    sig = _get_signature_of(init)
+            init = _signature_get_user_defined_method(obj, '__init__')
+            # Now we check if the 'obj' class has an own '__new__' method
+            if '__new__' in obj.__dict__:
+                factory_method = new
+            # or an own '__init__' method
+            elif '__init__' in obj.__dict__:
+                factory_method = init
+            # If not, we take inherited '__new__' or '__init__', if present
+            elif new is not None:
+                factory_method = new
+            elif init is not None:
+                factory_method = init
+
+            if factory_method is not None:
+                sig = _get_signature_of(factory_method)
 
         if sig is None:
             # At this point we know, that `obj` is a class, with no user-
index 0ab65307cdd0954178a69dd35fbe66bbeb4e67b3..38d161898fd08acebdd97693faf46b8e2cf15972 100644 (file)
@@ -3063,6 +3063,47 @@ class TestSignatureObject(unittest.TestCase):
                            ('bar', 2, ..., "keyword_only")),
                           ...))
 
+    def test_signature_on_subclass(self):
+        class A:
+            def __new__(cls, a=1, *args, **kwargs):
+                return object.__new__(cls)
+        class B(A):
+            def __init__(self, b):
+                pass
+        class C(A):
+            def __new__(cls, a=1, b=2, *args, **kwargs):
+                return object.__new__(cls)
+        class D(A):
+            pass
+
+        self.assertEqual(self.signature(B),
+                         ((('b', ..., ..., "positional_or_keyword"),),
+                          ...))
+        self.assertEqual(self.signature(C),
+                         ((('a', 1, ..., 'positional_or_keyword'),
+                           ('b', 2, ..., 'positional_or_keyword'),
+                           ('args', ..., ..., 'var_positional'),
+                           ('kwargs', ..., ..., 'var_keyword')),
+                          ...))
+        self.assertEqual(self.signature(D),
+                         ((('a', 1, ..., 'positional_or_keyword'),
+                           ('args', ..., ..., 'var_positional'),
+                           ('kwargs', ..., ..., 'var_keyword')),
+                          ...))
+
+    def test_signature_on_generic_subclass(self):
+        from typing import Generic, TypeVar
+
+        T = TypeVar('T')
+
+        class A(Generic[T]):
+            def __init__(self, *, a: int) -> None:
+                pass
+
+        self.assertEqual(self.signature(A),
+                         ((('a', ..., int, 'keyword_only'),),
+                          None))
+
     @unittest.skipIf(MISSING_C_DOCSTRINGS,
                      "Signature information for builtins requires docstrings")
     def test_signature_on_class_without_init(self):
diff --git a/Misc/NEWS.d/next/Library/2021-07-16-13-40-31.bpo-40897.aveAre.rst b/Misc/NEWS.d/next/Library/2021-07-16-13-40-31.bpo-40897.aveAre.rst
new file mode 100644 (file)
index 0000000..04f1465
--- /dev/null
@@ -0,0 +1,2 @@
+Give priority to using the current class constructor in
+:func:`inspect.signature`. Patch by Weipeng Hong.