diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2021-07-16 13:25:57 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-16 13:25:57 (GMT) |
commit | 948e39a866ccf33b4e30668c3f88a95a65966159 (patch) | |
tree | 159e898a10bd6ca907e9318c37c6744736d4aff7 /Lib | |
parent | c3007ab3c6cb384203bac8aa64d89c4b42f671a1 (diff) | |
download | cpython-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.py | 24 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 41 |
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): |