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 66fe76d

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

File tree

Expand file treeCollapse file tree

2 files changed

+71
-2
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+71
-2
lines changed

‎Lib/inspect.py

Copy file name to clipboardExpand all lines: Lib/inspect.py
+33-2Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,25 @@ def _signature_get_user_defined_method(cls, method_name):
19611961
# callables, this check won't be necessary
19621962
return meth
19631963

1964+
def _signature_get_user_defined_method_with_descriptor(cls, method_name):
1965+
"""Private helper. Checks if ``cls`` has an attribute
1966+
named ``method_name`` and returns it with descriptor only if it is a
1967+
pure python function.
1968+
"""
1969+
try:
1970+
meth = getattr(cls, method_name)
1971+
except AttributeError:
1972+
return None, None
1973+
1974+
if isinstance(meth, _NonUserDefinedCallables):
1975+
# Once '__signature__' will be added to 'C'-level
1976+
# callables, this check won't be necessary
1977+
return None, None
1978+
1979+
descriptior = getattr_static(cls, method_name, default=None)
1980+
1981+
return meth, descriptior
1982+
19641983

19651984
def _signature_get_partial(wrapped_sig, partial, extra_args=()):
19661985
"""Private helper to calculate how 'wrapped_sig' signature will
@@ -2604,13 +2623,25 @@ def _signature_from_callable(obj, *,
26042623
# We also check that the 'obj' is not an instance of
26052624
# types.WrapperDescriptorType or types.MethodWrapperType to avoid
26062625
# infinite recursion (and even potential segfault)
2607-
call = _signature_get_user_defined_method(type(obj), '__call__')
2626+
call, descriptior = _signature_get_user_defined_method_with_descriptor(type(obj), '__call__')
2627+
26082628
if call is not None:
2629+
if descriptior is not None:
2630+
is_staticmethod = isinstance(descriptior, staticmethod)
2631+
is_classmethod = isinstance(descriptior, classmethod)
2632+
else:
2633+
is_staticmethod = False
2634+
is_classmethod = False
2635+
2636+
26092637
try:
2610-
sig = _get_signature_of(call)
2638+
sig = _get_signature_of(call, skip_bound_arg=not is_staticmethod)
26112639
except ValueError as ex:
26122640
msg = 'no signature found for {!r}'.format(obj)
26132641
raise ValueError(msg) from ex
2642+
else:
2643+
if is_staticmethod or is_classmethod:
2644+
return sig
26142645

26152646
if sig is not None:
26162647
# 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.