]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-127750: Restore inspect and pydoc support of singledispatchmethod (GH-130309)
authorSerhiy Storchaka <storchaka@gmail.com>
Thu, 20 Feb 2025 09:08:49 +0000 (11:08 +0200)
committerGitHub <noreply@github.com>
Thu, 20 Feb 2025 09:08:49 +0000 (11:08 +0200)
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.

Lib/functools.py
Lib/inspect.py
Lib/test/test_functools.py
Lib/test/test_inspect/test_inspect.py

index 92be41dcf8e9e3d7faf04cbc048f85320bdbd5aa..70c59b109d9703d5861f592fc21de55c05e2beb9 100644 (file)
@@ -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
index 124293727ca84a8c8e03739a6dac01cd110ed873..f143c89674b7b2ec69018dc168a4b372f1c17aab 100644 (file)
@@ -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)."""
index a67c7b2280df9e8a8ca9419dd60e6f8ab877a415..b272631ae72c9dd2d4ad3915f242fec5025646d0 100644 (file)
@@ -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
index 06785e275f6b118e957bd688cb364460f69bd08a..6a562108c3a34b4d1830c12e215627558ce2b4fa 100644 (file)
@@ -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))