diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2025-03-05 11:10:05 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-05 11:10:05 (GMT) |
commit | f33d21e24fdb05da7512c2a203467c3ffd0e7713 (patch) | |
tree | 28eaa07a6a698026bd79eda50d7e6277ded06e86 | |
parent | 67a942d4272145ccdbdf4ceff31318e176f71355 (diff) | |
download | cpython-f33d21e24fdb05da7512c2a203467c3ffd0e7713.zip cpython-f33d21e24fdb05da7512c2a203467c3ffd0e7713.tar.gz cpython-f33d21e24fdb05da7512c2a203467c3ffd0e7713.tar.bz2 |
gh-127750: Improve repr of functools.singledispatchmethod (GH-130220)
-rw-r--r-- | Lib/functools.py | 22 | ||||
-rw-r--r-- | Lib/test/test_functools.py | 61 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2025-02-17-12-36-39.gh-issue-127750.ZC-hBq.rst | 2 |
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. |