mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
bpo-40897:Give priority to using the current class constructor in inspect.signature
(#27177)
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
8ce3008585
commit
6aab5f9bf3
3 changed files with 59 additions and 8 deletions
|
@ -2444,15 +2444,23 @@ def _signature_from_callable(obj, *,
|
||||||
if call is not None:
|
if call is not None:
|
||||||
sig = _get_signature_of(call)
|
sig = _get_signature_of(call)
|
||||||
else:
|
else:
|
||||||
# Now we check if the 'obj' class has a '__new__' method
|
factory_method = None
|
||||||
new = _signature_get_user_defined_method(obj, '__new__')
|
new = _signature_get_user_defined_method(obj, '__new__')
|
||||||
if new is not None:
|
init = _signature_get_user_defined_method(obj, '__init__')
|
||||||
sig = _get_signature_of(new)
|
# Now we check if the 'obj' class has an own '__new__' method
|
||||||
else:
|
if '__new__' in obj.__dict__:
|
||||||
# Finally, we should have at least __init__ implemented
|
factory_method = new
|
||||||
init = _signature_get_user_defined_method(obj, '__init__')
|
# or an own '__init__' method
|
||||||
if init is not None:
|
elif '__init__' in obj.__dict__:
|
||||||
sig = _get_signature_of(init)
|
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:
|
if sig is None:
|
||||||
# At this point we know, that `obj` is a class, with no user-
|
# At this point we know, that `obj` is a class, with no user-
|
||||||
|
|
|
@ -3063,6 +3063,47 @@ class TestSignatureObject(unittest.TestCase):
|
||||||
('bar', 2, ..., "keyword_only")),
|
('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,
|
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
||||||
"Signature information for builtins requires docstrings")
|
"Signature information for builtins requires docstrings")
|
||||||
def test_signature_on_class_without_init(self):
|
def test_signature_on_class_without_init(self):
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Give priority to using the current class constructor in
|
||||||
|
:func:`inspect.signature`. Patch by Weipeng Hong.
|
Loading…
Add table
Add a link
Reference in a new issue