Skip to content

Commit f33d21e

Browse files
gh-127750: Improve repr of functools.singledispatchmethod (GH-130220)
1 parent 67a942d commit f33d21e

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

Lib/functools.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,15 @@ def __get__(self, obj, cls=None):
10331033
def __isabstractmethod__(self):
10341034
return getattr(self.func, '__isabstractmethod__', False)
10351035

1036+
def __repr__(self):
1037+
try:
1038+
name = self.func.__qualname__
1039+
except AttributeError:
1040+
try:
1041+
name = self.func.__name__
1042+
except AttributeError:
1043+
name = '?'
1044+
return f'<single dispatch method descriptor {name}>'
10361045

10371046
class _singledispatchmethod_get:
10381047
def __init__(self, unbound, obj, cls):
@@ -1052,6 +1061,19 @@ def __init__(self, unbound, obj, cls):
10521061
except AttributeError:
10531062
pass
10541063

1064+
def __repr__(self):
1065+
try:
1066+
name = self.__qualname__
1067+
except AttributeError:
1068+
try:
1069+
name = self.__name__
1070+
except AttributeError:
1071+
name = '?'
1072+
if self._obj is not None:
1073+
return f'<bound single dispatch method {name} of {self._obj!r}>'
1074+
else:
1075+
return f'<single dispatch method {name}>'
1076+
10551077
def __call__(self, /, *args, **kwargs):
10561078
if not args:
10571079
funcname = getattr(self._unbound.func, '__name__',

Lib/test/test_functools.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,67 @@ def static_func(arg: int) -> str:
29342934
self.assertEqual(A.static_func.__name__, 'static_func')
29352935
self.assertEqual(A().static_func.__name__, 'static_func')
29362936

2937+
def test_method_repr(self):
2938+
class Callable:
2939+
def __call__(self, *args):
2940+
pass
2941+
2942+
class CallableWithName:
2943+
__name__ = 'NOQUALNAME'
2944+
def __call__(self, *args):
2945+
pass
2946+
2947+
class A:
2948+
@functools.singledispatchmethod
2949+
def func(self, arg):
2950+
pass
2951+
@functools.singledispatchmethod
2952+
@classmethod
2953+
def cls_func(cls, arg):
2954+
pass
2955+
@functools.singledispatchmethod
2956+
@staticmethod
2957+
def static_func(arg):
2958+
pass
2959+
# No __qualname__, only __name__
2960+
no_qualname = functools.singledispatchmethod(CallableWithName())
2961+
# No __qualname__, no __name__
2962+
no_name = functools.singledispatchmethod(Callable())
2963+
2964+
self.assertEqual(repr(A.__dict__['func']),
2965+
f'<single dispatch method descriptor {A.__qualname__}.func>')
2966+
self.assertEqual(repr(A.__dict__['cls_func']),
2967+
f'<single dispatch method descriptor {A.__qualname__}.cls_func>')
2968+
self.assertEqual(repr(A.__dict__['static_func']),
2969+
f'<single dispatch method descriptor {A.__qualname__}.static_func>')
2970+
self.assertEqual(repr(A.__dict__['no_qualname']),
2971+
f'<single dispatch method descriptor NOQUALNAME>')
2972+
self.assertEqual(repr(A.__dict__['no_name']),
2973+
f'<single dispatch method descriptor ?>')
2974+
2975+
self.assertEqual(repr(A.func),
2976+
f'<single dispatch method {A.__qualname__}.func>')
2977+
self.assertEqual(repr(A.cls_func),
2978+
f'<single dispatch method {A.__qualname__}.cls_func>')
2979+
self.assertEqual(repr(A.static_func),
2980+
f'<single dispatch method {A.__qualname__}.static_func>')
2981+
self.assertEqual(repr(A.no_qualname),
2982+
f'<single dispatch method NOQUALNAME>')
2983+
self.assertEqual(repr(A.no_name),
2984+
f'<single dispatch method ?>')
2985+
2986+
a = A()
2987+
self.assertEqual(repr(a.func),
2988+
f'<bound single dispatch method {A.__qualname__}.func of {a!r}>')
2989+
self.assertEqual(repr(a.cls_func),
2990+
f'<bound single dispatch method {A.__qualname__}.cls_func of {a!r}>')
2991+
self.assertEqual(repr(a.static_func),
2992+
f'<bound single dispatch method {A.__qualname__}.static_func of {a!r}>')
2993+
self.assertEqual(repr(a.no_qualname),
2994+
f'<bound single dispatch method NOQUALNAME of {a!r}>')
2995+
self.assertEqual(repr(a.no_name),
2996+
f'<bound single dispatch method ? of {a!r}>')
2997+
29372998
def test_double_wrapped_methods(self):
29382999
def classmethod_friendly_decorator(func):
29393000
wrapped = func.__func__
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve repr of :class:`functools.singledispatchmethod` methods and
2+
descriptors.

0 commit comments

Comments
 (0)