8000 bpo-40897:Give priority to using the current class constructor in `in… · python/cpython@948e39a · GitHub
[go: up one dir, main page]

Skip to content

Commit 948e39a

Browse files
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 6aab5f9) Co-authored-by: Weipeng Hong <hongweichen8888@sina.com>
1 parent c3007ab commit 948e39a

File tree

3 files changed

+59
-8
lines changed

3 files changed

+59
-8
lines changed

Lib/inspect.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,15 +2471,23 @@ def _signature_from_callable(obj, *,
24712471
if call is not None:
24722472
sig = _get_signature_of(call)
24732473
else:
2474-
# Now we check if the 'obj' class has a '__new__' method
2474+
factory_method = None
24752475
new = _signature_get_user_defined_method(obj, '__new__')
2476-
if new is not None:
2477-
sig = _get_signature_of(new)
2478-
else:
2479-
# Finally, we should have at least __init__ implemented
2480-
init = _signature_get_user_defined_method(obj, '__init__')
2481-
if init is not None:
2482-
sig = _get_signature_of(init)
2476+
init = _signature_get_user_defined_method(obj, '__init__')
2477+
# Now we check if the 'obj' class has an own '__new__' method
2478+
if '__new__' in obj.__dict__:
2479+
factory_method = new
2480+
# or an own '__init__' method
2481+
elif '__init__' in obj.__dict__:
2482+
factory_method = init
2483+
# If not, we take inherited '__new__' or '__init__', if present
2484+
elif new is not None:
2485+
factory_method = new
2486+
elif init is not None:
2487+
factory_method = init
2488+
2489+
if factory_method is not None:
2490+
sig = _get_signature_of(factory_method)
24832491

24842492
if sig is None:
24852493
# At this point we know, that `obj` is a class, with no user-

Lib/test/test_inspect.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3063,6 +3063,47 @@ def __init__(self, b):
30633063
('bar', 2, ..., "keyword_only")),
30643064
...))
30653065

3066+
def test_signature_on_subclass(self):
3067+
class A:
3068+
def __new__(cls, a=1, *args, **kwargs):
3069+
return object.__new__(cls)
3070+
class B(A):
3071+
def __init__(self, b):
3072+
pass
3073+
class C(A):
3074+
def __new__(cls, a=1, b=2, *args, **kwargs):
3075+
return object.__new__(cls)
3076+
class D(A):
3077+
pass
3078+
3079+
self.assertEqual(self.signature(B),
3080+
((('b', ..., ..., "positional_or_keyword"),),
3081+
...))
3082+
self.assertEqual(self.signature(C),
3083+
((('a', 1, ..., 'positional_or_keyword'),
3084+
('b', 2, ..., 'positional_or_keyword'),
3085+
('args', ..., ..., 'var_positional'),
3086+
('kwargs', ..., ..., 'var_keyword')),
3087+
...))
3088+
self.assertEqual(self.signature(D),
3089+
((('a', 1, ..., 'positional_or_keyword'),
3090+
('args', ..., ..., 'var_positional'),
3091+
('kwargs', ..., ..., 'var_keyword')),
3092+
...))
3093+
3094+
def test_signature_on_generic_subclass(self):
3095+
from typing import Generic, TypeVar
3096+
3097+
T = TypeVar('T')
3098+
3099+
class A(Generic[T]):
3100+
def __init__(self, *, a: int) -> None:
3101+
pass
3102+
3103+
self.assertEqual(self.signature(A),
3104+
((('a', ..., int, 'keyword_only'),),
3105+
None))
3106+
30663107
@unittest.skipIf(MISSING_C_DOCSTRINGS,
30673108
"Signature information for builtins requires docstrings")
30683109
def test_signature_on_class_without_init(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Give priority to using the current class constructor in
2+
:func:`inspect.signature`. Patch by Weipeng Hong.

0 commit comments

Comments
 (0)
0