summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2021-07-16 13:25:57 (GMT)
committerGitHub <noreply@github.com>2021-07-16 13:25:57 (GMT)
commit948e39a866ccf33b4e30668c3f88a95a65966159 (patch)
tree159e898a10bd6ca907e9318c37c6744736d4aff7 /Lib
parentc3007ab3c6cb384203bac8aa64d89c4b42f671a1 (diff)
downloadcpython-948e39a866ccf33b4e30668c3f88a95a65966159.zip
cpython-948e39a866ccf33b4e30668c3f88a95a65966159.tar.gz
cpython-948e39a866ccf33b4e30668c3f88a95a65966159.tar.bz2
bpo-40897:Give priority to using the current class constructor in `inspect.signature` (GH-27177) (#27189)
Co-authored-by: Ɓukasz Langa <lukasz@langa.pl> (cherry picked from commit 6aab5f9bf303a8e4cd8377fabcdcb499e0541f9a) Co-authored-by: Weipeng Hong <hongweichen8888@sina.com>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/inspect.py24
-rw-r--r--Lib/test/test_inspect.py41
2 files changed, 57 insertions, 8 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 0273ffa..750fd45 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -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-
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 0ab6530..38d1618 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -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):