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

Skip to content

Commit df7c629

Browse files
bpo-40897:Give priority to using the current class constructor in inspect.signature (GH-27177) (GH-27209)
Co-authored-by: Łukasz Langa <lukasz@langa.pl> (cherry picked from commit 6aab5f9) Co-authored-by: Weipeng Hong <hongweichen8888@sina.com>
1 parent ed2db9b commit df7c629

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
@@ -2341,15 +2341,23 @@ def _signature_from_callable(obj, *,
23412341
if call is not None:
23422342
sig = _get_signature_of(call)
23432343
else:
2344-
# Now we check if the 'obj' class has a '__new__' method
2344+
factory_method = None
23452345
new = _signature_get_user_defined_method(obj, '__new__')
2346-
if new is not None:
2347-
sig = _get_signature_of(new)
2348-
else:
2349-
# Finally, we should have at least __init__ implemented
2350-
init = _signature_get_user_defined_method(obj, '__init__')
2351-
if init is not None:
2352-
sig = _get_signature_of(init)
2346+
init = _signature_get_user_defined_method(obj, '__init__')
2347+
# Now we check if the 'obj' class has an own '__new__' method
2348+
if '__new__' in obj.__dict__:
2349+
factory_method = new
2350+
# or an own '__init__' method
2351+
elif '__init__' in obj.__dict__:
2352+
factory_method = init
2353+
# If not, we take inherited '__new__' or '__init__', if present
2354+
elif new is not None:
2355+
factory_method = new
2356+
elif init is not None:
2357+
factory_method = init
2358+
2359+
if factory_method is not None:
2360+
sig = _get_signature_of(factory_method)
23532361

23542362
if sig is None:
23552363
# 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
@@ -2958,6 +2958,47 @@ def __init__(self, b):
29582958
('bar', 2, ..., "keyword_only")),
29592959
...))
29602960

2961+
def test_signature_on_subclass(self):
2962+
class A:
2963+
def __new__(cls, a=1, *args, **kwargs):
2964+
return object.__new__(cls)
2965+
class B(A):
2966+
def __init__(self, b):
2967+
pass
2968+
class C(A):
2969+
def __new__(cls, a=1, b=2, *args, **kwargs):
2970+
return object.__new__(cls)
2971+
class D(A):
2972+
pass
2973+
2974+
self.assertEqual(self.signature(B),
2975+
((('b', ..., ..., "positional_or_keyword"),),
2976+
...))
2977+
self.assertEqual(self.signature(C),
2978+
((('a', 1, ..., 'positional_or_keyword'),
2979+
('b', 2, ..., 'positional_or_keyword'),
2980+
('args', ..., ..., 'var_positional'),
2981+
('kwargs', ..., ..., 'var_keyword')),
2982+
...))
2983+
self.assertEqual(self.signature(D),
2984+
((('a', 1, ..., 'positional_or_keyword'),
2985+
('args', ..., ..., 'var_positional'),
2986+
('kwargs', ..., ..., 'var_keyword')),
2987+
...))
2988+
2989+
def test_signature_on_generic_subclass(self):
2990+
from typing import Generic, TypeVar
2991+
2992+
T = TypeVar('T')
2993+
2994+
class A(Generic[T]):
2995+
def __init__(self, *, a: int) -> None:
2996+
pass
2997+
2998+
self.assertEqual(self.signature(A),
2999+
((('a', ..., int, 'keyword_only'),),
3000+
None))
3001+
29613002
@unittest.skipIf(MISSING_C_DOCSTRINGS,
29623003
"Signature information for builtins requires docstrings")
29633004
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