From ec7824aede7064b028db68e0ef217249e5918e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:17:12 +0200 Subject: [PATCH 1/5] improve `partialmethod.__repr__` --- Lib/functools.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index a80e1a6c6a56ac..c073f48bf91987 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -373,15 +373,14 @@ def __init__(self, func, /, *args, **keywords): self.keywords = keywords def __repr__(self): + module = self.__class__.__module__ + typename = self.__class__.__qualname__ args = ", ".join(map(repr, self.args)) - keywords = ", ".join("{}={!r}".format(k, v) - for k, v in self.keywords.items()) - format_string = "{module}.{cls}({func}, {args}, {keywords})" - return format_string.format(module=self.__class__.__module__, - cls=self.__class__.__qualname__, - func=self.func, - args=args, - keywords=keywords) + keywords = ", ".join(f"{k}={v!r}" for k, v in self.keywords.items()) + arguments = ", ".join(filter(None, (args, keywords))) + if arguments: + return f"{module}.{typename}({self.func}, {arguments})" + return f"{module}.{typename}({self.func})" def _make_unbound_method(self): def _method(cls_or_self, /, *args, **keywords): From d5ea97dce48afa6f9da0375914427638ee9f2ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:17:19 +0200 Subject: [PATCH 2/5] add tests --- Lib/test/test_functools.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 26701ea8b4daf9..559213fef1313d 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -569,6 +569,14 @@ class B: method = functools.partialmethod(func=capture, a=1) def test_repr(self): + self.assertEqual(repr(vars(self.A)['nothing']), + 'functools.partialmethod({})'.format(capture)) + self.assertEqual(repr(vars(self.A)['positional']), + 'functools.partialmethod({}, 1)'.format(capture)) + self.assertEqual(repr(vars(self.A)['keywords']), + 'functools.partialmethod({}, a=2)'.format(capture)) + self.assertEqual(repr(vars(self.A)['spec_keywords']), + 'functools.partialmethod({}, self=1, func=2)'.format(capture)) self.assertEqual(repr(vars(self.A)['both']), 'functools.partialmethod({}, 3, b=4)'.format(capture)) From 087b8e460a1559444ad518dc653be4ab0970b667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:17:25 +0200 Subject: [PATCH 3/5] blurb --- .../next/Library/2024-06-26-10-13-40.gh-issue-121025.M-XXlV.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-06-26-10-13-40.gh-issue-121025.M-XXlV.rst diff --git a/Misc/NEWS.d/next/Library/2024-06-26-10-13-40.gh-issue-121025.M-XXlV.rst b/Misc/NEWS.d/next/Library/2024-06-26-10-13-40.gh-issue-121025.M-XXlV.rst new file mode 100644 index 00000000000000..38cad610396787 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-26-10-13-40.gh-issue-121025.M-XXlV.rst @@ -0,0 +1,2 @@ +Improve the :meth:`~object.__repr__` of :class:`functools.partialmethod`. +Patch by Bénédikt Tran. From fca145842c70b7d29165a0f0d28df1da3b35513a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:21:56 +0200 Subject: [PATCH 4/5] re-use `partial.__repr__` implementation --- Lib/functools.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index c073f48bf91987..3d0fd6671fb63e 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -373,14 +373,13 @@ def __init__(self, func, /, *args, **keywords): self.keywords = keywords def __repr__(self): - module = self.__class__.__module__ - typename = self.__class__.__qualname__ - args = ", ".join(map(repr, self.args)) - keywords = ", ".join(f"{k}={v!r}" for k, v in self.keywords.items()) - arguments = ", ".join(filter(None, (args, keywords))) - if arguments: - return f"{module}.{typename}({self.func}, {arguments})" - return f"{module}.{typename}({self.func})" + cls = type(self) + module = cls.__module__ + qualname = cls.__qualname__ + args = [repr(self.func)] + args.extend(map(repr, self.args)) + args.extend(f"{k}={v!r}" for k, v in self.keywords.items()) + return f"{module}.{qualname}({', '.join(args)})" def _make_unbound_method(self): def _method(cls_or_self, /, *args, **keywords): From 94b9d6972996e3f8c7a8fec607a117669cda69a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:25:37 +0200 Subject: [PATCH 5/5] fix regex --- Lib/test/test_asyncio/test_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 5b660de28d6fa0..5221d80ee9fbf6 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2362,7 +2362,7 @@ def test_handle_repr(self): h = asyncio.Handle(cb, (), self.loop) cb_regex = r'' - cb_regex = fr'functools.partialmethod\({cb_regex}, , \)\(\)' + cb_regex = fr'functools.partialmethod\({cb_regex}\)\(\)' regex = fr'^$' self.assertRegex(repr(h), regex)