]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-127750: Improve repr of functools.singledispatchmethod (GH-130220)
authorSerhiy Storchaka <storchaka@gmail.com>
Wed, 5 Mar 2025 11:10:05 +0000 (13:10 +0200)
committerGitHub <noreply@github.com>
Wed, 5 Mar 2025 11:10:05 +0000 (13:10 +0200)
Lib/functools.py
Lib/test/test_functools.py
Misc/NEWS.d/next/Library/2025-02-17-12-36-39.gh-issue-127750.ZC-hBq.rst [new file with mode: 0644]

index 5e2579f6d8eb0f8504ef1619b4a11bd072f3760a..e0e45bc336c1efb4441fe415654c342a5c99dfc0 100644 (file)
@@ -1033,6 +1033,15 @@ class singledispatchmethod:
     def __isabstractmethod__(self):
         return getattr(self.func, '__isabstractmethod__', False)
 
+    def __repr__(self):
+        try:
+            name = self.func.__qualname__
+        except AttributeError:
+            try:
+                name = self.func.__name__
+            except AttributeError:
+                name = '?'
+        return f'<single dispatch method descriptor {name}>'
 
 class _singledispatchmethod_get:
     def __init__(self, unbound, obj, cls):
@@ -1052,6 +1061,19 @@ class _singledispatchmethod_get:
         except AttributeError:
             pass
 
+    def __repr__(self):
+        try:
+            name = self.__qualname__
+        except AttributeError:
+            try:
+                name = self.__name__
+            except AttributeError:
+                name = '?'
+        if self._obj is not None:
+            return f'<bound single dispatch method {name} of {self._obj!r}>'
+        else:
+            return f'<single dispatch method {name}>'
+
     def __call__(self, /, *args, **kwargs):
         if not args:
             funcname = getattr(self._unbound.func, '__name__',
index ef85664cb78f64d776730e23427e3d0bbcad5f71..5e04b15e014ea26babb36a1f91c7841f51e4bfce 100644 (file)
@@ -2934,6 +2934,67 @@ class TestSingleDispatch(unittest.TestCase):
         self.assertEqual(A.static_func.__name__, 'static_func')
         self.assertEqual(A().static_func.__name__, 'static_func')
 
+    def test_method_repr(self):
+        class Callable:
+            def __call__(self, *args):
+                pass
+
+        class CallableWithName:
+            __name__ = 'NOQUALNAME'
+            def __call__(self, *args):
+                pass
+
+        class A:
+            @functools.singledispatchmethod
+            def func(self, arg):
+                pass
+            @functools.singledispatchmethod
+            @classmethod
+            def cls_func(cls, arg):
+                pass
+            @functools.singledispatchmethod
+            @staticmethod
+            def static_func(arg):
+                pass
+            # No __qualname__, only __name__
+            no_qualname = functools.singledispatchmethod(CallableWithName())
+            # No __qualname__, no __name__
+            no_name = functools.singledispatchmethod(Callable())
+
+        self.assertEqual(repr(A.__dict__['func']),
+            f'<single dispatch method descriptor {A.__qualname__}.func>')
+        self.assertEqual(repr(A.__dict__['cls_func']),
+            f'<single dispatch method descriptor {A.__qualname__}.cls_func>')
+        self.assertEqual(repr(A.__dict__['static_func']),
+            f'<single dispatch method descriptor {A.__qualname__}.static_func>')
+        self.assertEqual(repr(A.__dict__['no_qualname']),
+            f'<single dispatch method descriptor NOQUALNAME>')
+        self.assertEqual(repr(A.__dict__['no_name']),
+            f'<single dispatch method descriptor ?>')
+
+        self.assertEqual(repr(A.func),
+            f'<single dispatch method {A.__qualname__}.func>')
+        self.assertEqual(repr(A.cls_func),
+            f'<single dispatch method {A.__qualname__}.cls_func>')
+        self.assertEqual(repr(A.static_func),
+            f'<single dispatch method {A.__qualname__}.static_func>')
+        self.assertEqual(repr(A.no_qualname),
+            f'<single dispatch method NOQUALNAME>')
+        self.assertEqual(repr(A.no_name),
+            f'<single dispatch method ?>')
+
+        a = A()
+        self.assertEqual(repr(a.func),
+            f'<bound single dispatch method {A.__qualname__}.func of {a!r}>')
+        self.assertEqual(repr(a.cls_func),
+            f'<bound single dispatch method {A.__qualname__}.cls_func of {a!r}>')
+        self.assertEqual(repr(a.static_func),
+            f'<bound single dispatch method {A.__qualname__}.static_func of {a!r}>')
+        self.assertEqual(repr(a.no_qualname),
+            f'<bound single dispatch method NOQUALNAME of {a!r}>')
+        self.assertEqual(repr(a.no_name),
+            f'<bound single dispatch method ? of {a!r}>')
+
     def test_double_wrapped_methods(self):
         def classmethod_friendly_decorator(func):
             wrapped = func.__func__
diff --git a/Misc/NEWS.d/next/Library/2025-02-17-12-36-39.gh-issue-127750.ZC-hBq.rst b/Misc/NEWS.d/next/Library/2025-02-17-12-36-39.gh-issue-127750.ZC-hBq.rst
new file mode 100644 (file)
index 0000000..e438dbb
--- /dev/null
@@ -0,0 +1,2 @@
+Improve repr of :class:`functools.singledispatchmethod` methods and
+descriptors.