From: Serhiy Storchaka Date: Thu, 20 Feb 2025 09:08:49 +0000 (+0200) Subject: gh-127750: Restore inspect and pydoc support of singledispatchmethod (GH-130309) X-Git-Tag: v3.14.0a6~345 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=10b32054ad6bce821e3b40101d4414d025be6e36;p=thirdparty%2FPython%2Fcpython.git 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. --- diff --git a/Lib/functools.py b/Lib/functools.py index 92be41dcf8e9..70c59b109d97 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -1075,6 +1075,10 @@ class _singledispatchmethod_get: raise AttributeError return getattr(self._unbound.func, name) + @property + def __wrapped__(self): + return self._unbound.func + @property def register(self): return self._unbound.register diff --git a/Lib/inspect.py b/Lib/inspect.py index 124293727ca8..f143c89674b7 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -447,7 +447,8 @@ def isroutine(object): or isfunction(object) or ismethod(object) or ismethoddescriptor(object) - or ismethodwrapper(object)) + or ismethodwrapper(object) + or isinstance(object, functools._singledispatchmethod_get)) def isabstract(object): """Return true if the object is an abstract base class (ABC).""" diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index a67c7b2280df..b272631ae72c 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -3309,6 +3309,58 @@ class TestSingleDispatch(unittest.TestCase): support.gc_collect() self.assertIsNone(r()) + def test_signatures(self): + @functools.singledispatch + def func(item, arg: int) -> str: + return str(item) + @func.register + def _(item: int, arg: bytes) -> str: + return str(item) + + self.assertEqual(str(Signature.from_callable(func)), + '(item, arg: int) -> str') + + def test_method_signatures(self): + class A: + def m(self, item, arg: int) -> str: + return str(item) + @classmethod + def cm(cls, item, arg: int) -> str: + return str(item) + @functools.singledispatchmethod + def func(self, item, arg: int) -> str: + return str(item) + @func.register + def _(self, item, arg: bytes) -> str: + return str(item) + + @functools.singledispatchmethod + @classmethod + def cls_func(cls, item, arg: int) -> str: + return str(arg) + @func.register + @classmethod + def _(cls, item, arg: bytes) -> str: + return str(item) + + @functools.singledispatchmethod + @staticmethod + def static_func(item, arg: int) -> str: + return str(arg) + @func.register + @staticmethod + def _(item, arg: bytes) -> str: + return str(item) + + self.assertEqual(str(Signature.from_callable(A.func)), + '(self, item, arg: int) -> str') + self.assertEqual(str(Signature.from_callable(A().func)), + '(self, item, arg: int) -> str') + self.assertEqual(str(Signature.from_callable(A.cls_func)), + '(cls, item, arg: int) -> str') + self.assertEqual(str(Signature.from_callable(A.static_func)), + '(item, arg: int) -> str') + class CachedCostItem: _cost = 1 diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 06785e275f6b..6a562108c3a3 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -415,6 +415,27 @@ class TestPredicates(IsTestBase): # partial self.assertTrue(inspect.isroutine(functools.partial(mod.spam))) + def test_isroutine_singledispatch(self): + self.assertTrue(inspect.isroutine(functools.singledispatch(mod.spam))) + + class A: + @functools.singledispatchmethod + def method(self, arg): + pass + @functools.singledispatchmethod + @classmethod + def class_method(cls, arg): + pass + @functools.singledispatchmethod + @staticmethod + def static_method(arg): + pass + + self.assertTrue(inspect.isroutine(A.method)) + self.assertTrue(inspect.isroutine(A().method)) + self.assertTrue(inspect.isroutine(A.static_method)) + self.assertTrue(inspect.isroutine(A.class_method)) + def test_isclass(self): self.istest(inspect.isclass, 'mod.StupidGit') self.assertTrue(inspect.isclass(list))