Skip to content

Commit 10b3205

Browse files
gh-127750: Restore inspect and pydoc support of singledispatchmethod (GH-130309)
The code is still flawed, because it does not recognize class and static methods, and the first argument is not removed from the signature of bound methods, but at least it does not worse than in 3.13 and older.
1 parent ed831b9 commit 10b3205

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

Lib/functools.py

+4
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,10 @@ def __getattr__(self, name):
10751075
raise AttributeError
10761076
return getattr(self._unbound.func, name)
10771077

1078+
@property
1079+
def __wrapped__(self):
1080+
return self._unbound.func
1081+
10781082
@property
10791083
def register(self):
10801084
return self._unbound.register

Lib/inspect.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,8 @@ def isroutine(object):
447447
or isfunction(object)
448448
or ismethod(object)
449449
or ismethoddescriptor(object)
450-
or ismethodwrapper(object))
450+
or ismethodwrapper(object)
451+
or isinstance(object, functools._singledispatchmethod_get))
451452

452453
def isabstract(object):
453454
"""Return true if the object is an abstract base class (ABC)."""

Lib/test/test_functools.py

+52
Original file line numberDiff line numberDiff line change
@@ -3309,6 +3309,58 @@ def t(self, arg):
33093309
support.gc_collect()
33103310
self.assertIsNone(r())
33113311

3312+
def test_signatures(self):
3313+
@functools.singledispatch
3314+
def func(item, arg: int) -> str:
3315+
return str(item)
3316+
@func.register
3317+
def _(item: int, arg: bytes) -> str:
3318+
return str(item)
3319+
3320+
self.assertEqual(str(Signature.from_callable(func)),
3321+
'(item, arg: int) -> str')
3322+
3323+
def test_method_signatures(self):
3324+
class A:
3325+
def m(self, item, arg: int) -> str:
3326+
return str(item)
3327+
@classmethod
3328+
def cm(cls, item, arg: int) -> str:
3329+
return str(item)
3330+
@functools.singledispatchmethod
3331+
def func(self, item, arg: int) -> str:
3332+
return str(item)
3333+
@func.register
3334+
def _(self, item, arg: bytes) -> str:
3335+
return str(item)
3336+
3337+
@functools.singledispatchmethod
3338+
@classmethod
3339+
def cls_func(cls, item, arg: int) -> str:
3340+
return str(arg)
3341+
@func.register
3342+
@classmethod
3343+
def _(cls, item, arg: bytes) -> str:
3344+
return str(item)
3345+
3346+
@functools.singledispatchmethod
3347+
@staticmethod
3348+
def static_func(item, arg: int) -> str:
3349+
return str(arg)
3350+
@func.register
3351+
@staticmethod
3352+
def _(item, arg: bytes) -> str:
3353+
return str(item)
3354+
3355+
self.assertEqual(str(Signature.from_callable(A.func)),
3356+
'(self, item, arg: int) -> str')
3357+
self.assertEqual(str(Signature.from_callable(A().func)),
3358+
'(self, item, arg: int) -> str')
3359+
self.assertEqual(str(Signature.from_callable(A.cls_func)),
3360+
'(cls, item, arg: int) -> str')
3361+
self.assertEqual(str(Signature.from_callable(A.static_func)),
3362+
'(item, arg: int) -> str')
3363+
33123364

33133365
class CachedCostItem:
33143366
_cost = 1

Lib/test/test_inspect/test_inspect.py

+21
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,27 @@ def test_isroutine(self):
415415
# partial
416416
self.assertTrue(inspect.isroutine(functools.partial(mod.spam)))
417417

418+
def test_isroutine_singledispatch(self):
419+
self.assertTrue(inspect.isroutine(functools.singledispatch(mod.spam)))
420+
421+
class A:
422+
@functools.singledispatchmethod
423+
def method(self, arg):
424+
pass
425+
@functools.singledispatchmethod
426+
@classmethod
427+
def class_method(cls, arg):
428+
pass
429+
@functools.singledispatchmethod
430+
@staticmethod
431+
def static_method(arg):
432+
pass
433+
434+
self.assertTrue(inspect.isroutine(A.method))
435+
self.assertTrue(inspect.isroutine(A().method))
436+
self.assertTrue(inspect.isroutine(A.static_method))
437+
self.assertTrue(inspect.isroutine(A.class_method))
438+
418439
def test_isclass(self):
419440
self.istest(inspect.isclass, 'mod.StupidGit')
420441
self.assertTrue(inspect.isclass(list))

0 commit comments

Comments
 (0)