summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2025-03-05 11:10:05 (GMT)
committerGitHub <noreply@github.com>2025-03-05 11:10:05 (GMT)
commitf33d21e24fdb05da7512c2a203467c3ffd0e7713 (patch)
tree28eaa07a6a698026bd79eda50d7e6277ded06e86
parent67a942d4272145ccdbdf4ceff31318e176f71355 (diff)
downloadcpython-f33d21e24fdb05da7512c2a203467c3ffd0e7713.zip
cpython-f33d21e24fdb05da7512c2a203467c3ffd0e7713.tar.gz
cpython-f33d21e24fdb05da7512c2a203467c3ffd0e7713.tar.bz2
gh-127750: Improve repr of functools.singledispatchmethod (GH-130220)
-rw-r--r--Lib/functools.py22
-rw-r--r--Lib/test/test_functools.py61
-rw-r--r--Misc/NEWS.d/next/Library/2025-02-17-12-36-39.gh-issue-127750.ZC-hBq.rst2
3 files changed, 85 insertions, 0 deletions
diff --git a/Lib/functools.py b/Lib/functools.py
index 5e2579f..e0e45bc 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -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__',
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index ef85664..5e04b15 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -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
index 0000000..e438dbb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-02-17-12-36-39.gh-issue-127750.ZC-hBq.rst
@@ -0,0 +1,2 @@
+Improve repr of :class:`functools.singledispatchmethod` methods and
+descriptors.