Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e5b2de3

Browse filesBrowse files
committed
Fix gh-101293
1 parent ca066bd commit e5b2de3
Copy full SHA for e5b2de3

File tree

Expand file treeCollapse file tree

2 files changed

+65
-8
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+65
-8
lines changed

‎Lib/inspect.py

Copy file name to clipboardExpand all lines: Lib/inspect.py
+27-8Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,7 +1946,7 @@ def getcoroutinelocals(coroutine):
19461946
types.BuiltinFunctionType)
19471947

19481948

1949-
def _signature_get_user_defined_method(cls, method_name):
1949+
def _signature_get_user_defined_method(cls, method_name, *, return_with_descriptor=False):
19501950
"""Private helper. Checks if ``cls`` has an attribute
19511951
named ``method_name`` and returns it only if it is a
19521952
pure python function.
@@ -1955,11 +1955,18 @@ def _signature_get_user_defined_method(cls, method_name):
19551955
meth = getattr(cls, method_name)
19561956
except AttributeError:
19571957
return
1958-
else:
1959-
if not isinstance(meth, _NonUserDefinedCallables):
1960-
# Once '__signature__' will be added to 'C'-level
1961-
# callables, this check won't be necessary
1962-
return meth
1958+
1959+
if isinstance(meth, _NonUserDefinedCallables):
1960+
# Once '__signature__' will be added to 'C'-level
1961+
# callables, this check won't be necessary
1962+
return None
1963+
1964+
if not return_with_descriptor:
1965+
return meth
1966+
1967+
postentional_descriptior = getattr_static(cls, method_name, default=None)
1968+
1969+
return meth, postentional_descriptior if meth is not postentional_descriptior else None
19631970

19641971

19651972
def _signature_get_partial(wrapped_sig, partial, extra_args=()):
@@ -2604,13 +2611,25 @@ def _signature_from_callable(obj, *,
26042611
# We also check that the 'obj' is not an instance of
26052612
# types.WrapperDescriptorType or types.MethodWrapperType to avoid
26062613
# infinite recursion (and even potential segfault)
2607-
call = _signature_get_user_defined_method(type(obj), '__call__')
2614+
call, descriptior = _signature_get_user_defined_method(type(obj), '__call__', return_with_descriptor=True)
2615+
26082616
if call is not None:
2617+
if descriptior is not None:
2618+
is_staticmethod = isinstance(descriptior, staticmethod)
2619+
is_classmethod = isinstance(descriptior, classmethod)
2620+
else:
2621+
is_staticmethod = False
2622+
is_classmethod = False
2623+
2624+
26092625
try:
2610-
sig = _get_signature_of(call)
2626+
sig = _get_signature_of(call, skip_bound_arg=not is_staticmethod)
26112627
except ValueError as ex:
26122628
msg = 'no signature found for {!r}'.format(obj)
26132629
raise ValueError(msg) from ex
2630+
else:
2631+
if is_staticmethod or is_classmethod:
2632+
return sig
26142633

26152634
if sig is not None:
26162635
# For classes and objects we skip the first parameter of their

‎Lib/test/test_inspect.py

Copy file name to clipboardExpand all lines: Lib/test/test_inspect.py
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3286,6 +3286,44 @@ class Wrapped:
32863286
with self.assertRaisesRegex(ValueError, 'wrapper loop'):
32873287
self.signature(Wrapped)
32883288

3289+
def test_signature_on_class_callable_objects(self):
3290+
class Foo:
3291+
@classmethod
3292+
def __call__(cls, a):
3293+
pass
3294+
3295+
self.assertEqual(self.signature(Foo()),
3296+
((('a', ..., ..., "positional_or_keyword"),),
3297+
...))
3298+
3299+
class Bar:
3300+
@classmethod
3301+
def __call__(cls):
3302+
pass
3303+
3304+
self.assertEqual(self.signature(Bar()),
3305+
((()),
3306+
...))
3307+
3308+
def test_signature_on_static_callable_objects(self):
3309+
class Foo:
3310+
@staticmethod
3311+
def __call__(a):
3312+
pass
3313+
3314+
self.assertEqual(self.signature(Foo()),
3315+
((('a', ..., ..., "positional_or_keyword"),),
3316+
...))
3317+
3318+
class Bar:
3319+
@staticmethod
3320+
def __call__():
3321+
pass
3322+
3323+
self.assertEqual(self.signature(Bar()),
3324+
((()),
3325+
...))
3326+
32893327
def test_signature_on_lambdas(self):
32903328
self.assertEqual(self.signature((lambda a=10: a)),
32913329
((('a', 10, ..., "positional_or_keyword"),),

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.