From 8f5be78bce95deb338e2e1cf13a0a579b3b42dd2 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sun, 25 Feb 2024 23:55:19 +0300 Subject: gh-72249: Include the module name in the repr of partial object (GH-101910) Co-authored-by: Anilyka Barry --- Lib/functools.py | 8 ++++---- Lib/test/test_functools.py | 14 ++------------ .../2023-02-14-17-19-59.gh-issue-72249.fv35wU.rst | 2 ++ Modules/_functoolsmodule.c | 21 +++++++++++++++++++-- 4 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-14-17-19-59.gh-issue-72249.fv35wU.rst diff --git a/Lib/functools.py b/Lib/functools.py index 7045be5..601cb8e 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -303,13 +303,13 @@ class partial: @recursive_repr() def __repr__(self): - qualname = type(self).__qualname__ + cls = type(self) + qualname = cls.__qualname__ + module = cls.__module__ args = [repr(self.func)] args.extend(repr(x) for x in self.args) args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items()) - if type(self).__module__ == "functools": - return f"functools.{qualname}({', '.join(args)})" - return f"{qualname}({', '.join(args)})" + return f"{module}.{qualname}({', '.join(args)})" def __reduce__(self): return type(self), (self.func,), (self.func, self.args, diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 2c814d5..4eb3226 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -31,10 +31,6 @@ c_functools = import_helper.import_fresh_module('functools', decimal = import_helper.import_fresh_module('decimal', fresh=['_decimal']) -_partial_types = [py_functools.partial] -if c_functools: - _partial_types.append(c_functools.partial) - @contextlib.contextmanager def replaced_module(name, replacement): @@ -207,10 +203,7 @@ class TestPartial: kwargs = {'a': object(), 'b': object()} kwargs_reprs = ['a={a!r}, b={b!r}'.format_map(kwargs), 'b={b!r}, a={a!r}'.format_map(kwargs)] - if self.partial in _partial_types: - name = 'functools.partial' - else: - name = self.partial.__name__ + name = f"{self.partial.__module__}.{self.partial.__qualname__}" f = self.partial(capture) self.assertEqual(f'{name}({capture!r})', repr(f)) @@ -229,10 +222,7 @@ class TestPartial: for kwargs_repr in kwargs_reprs]) def test_recursive_repr(self): - if self.partial in _partial_types: - name = 'functools.partial' - else: - name = self.partial.__name__ + name = f"{self.partial.__module__}.{self.partial.__qualname__}" f = self.partial(capture) f.__setstate__((f, (), {}, {})) diff --git a/Misc/NEWS.d/next/Library/2023-02-14-17-19-59.gh-issue-72249.fv35wU.rst b/Misc/NEWS.d/next/Library/2023-02-14-17-19-59.gh-issue-72249.fv35wU.rst new file mode 100644 index 0000000..10cc5a4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-14-17-19-59.gh-issue-72249.fv35wU.rst @@ -0,0 +1,2 @@ +:func:`functools.partial`s of :func:`repr` has been improved to include the +:term:`module` name. Patched by Furkan Onder and Anilyka Barry. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 9ab8471..d2212d4 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -365,6 +365,8 @@ partial_repr(partialobject *pto) { PyObject *result = NULL; PyObject *arglist; + PyObject *mod; + PyObject *name; Py_ssize_t i, n; PyObject *key, *value; int status; @@ -399,13 +401,28 @@ partial_repr(partialobject *pto) if (arglist == NULL) goto done; } - result = PyUnicode_FromFormat("%s(%R%U)", Py_TYPE(pto)->tp_name, - pto->fn, arglist); + + mod = _PyType_GetModuleName(Py_TYPE(pto)); + if (mod == NULL) { + goto error; + } + name = PyType_GetQualName(Py_TYPE(pto)); + if (name == NULL) { + Py_DECREF(mod); + goto error; + } + result = PyUnicode_FromFormat("%S.%S(%R%U)", mod, name, pto->fn, arglist); + Py_DECREF(mod); + Py_DECREF(name); Py_DECREF(arglist); done: Py_ReprLeave((PyObject *)pto); return result; + error: + Py_DECREF(arglist); + Py_ReprLeave((PyObject *)pto); + return NULL; } /* Pickle strategy: -- cgit v0.12