diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index e5921a758f3..f41a144a141 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -146,6 +146,32 @@ class TestPartial(unittest.TestCase): join = self.thetype(''.join) self.assertEqual(join(data), '0123456789') + def test_repr(self): + args = (object(), object()) + args_repr = ', '.join(repr(a) for a in args) + kwargs = {'a': object(), 'b': object()} + kwargs_repr = ', '.join("%s=%r" % (k, v) for k, v in kwargs.items()) + if self.thetype is functools.partial: + name = 'functools.partial' + else: + name = self.thetype.__name__ + + f = self.thetype(capture) + self.assertEqual('{}({!r})'.format(name, capture), + repr(f)) + + f = self.thetype(capture, *args) + self.assertEqual('{}({!r}, {})'.format(name, capture, args_repr), + repr(f)) + + f = self.thetype(capture, **kwargs) + self.assertEqual('{}({!r}, {})'.format(name, capture, kwargs_repr), + repr(f)) + + f = self.thetype(capture, *args, **kwargs) + self.assertEqual('{}({!r}, {}, {})'.format(name, capture, args_repr, kwargs_repr), + repr(f)) + def test_pickle(self): f = self.thetype(signature, 'asdf', bar=True) f.add_something_to__dict__ = True @@ -163,6 +189,9 @@ class TestPythonPartial(TestPartial): thetype = PythonPartial + # the python version hasn't a nice repr + def test_repr(self): pass + # the python version isn't picklable def test_pickle(self): pass diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 71f0e8aab93..b322f7dbdb0 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -12,6 +12,7 @@ from test.support import run_unittest from test import inspect_fodder as mod from test import inspect_fodder2 as mod2 +from test import inspect_fodder3 as mod3 # C module for test_findsource_binary import unicodedata @@ -388,6 +389,12 @@ class TestBuggyCases(GetSourceBase): self.assertEqual(inspect.findsource(co), (lines,0)) self.assertEqual(inspect.getsource(co), lines[0]) +class TestNoEOF(GetSourceBase): + fodderFile = mod3 + + def test_class(self): + self.assertSourceEqual(mod3.X, 1, 2) + # Helper for testing classify_class_attrs. def attrs_wo_objs(cls): return [t[:3] for t in inspect.classify_class_attrs(cls)] diff --git a/Misc/NEWS b/Misc/NEWS index 41273943b8a..7d77b20a328 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -46,6 +46,9 @@ Core and Builtins Library ------- +- Issue #4113: Added custom ``__repr__`` method to ``functools.partial``. + Original patch by Daniel Urban. + - Issue #10273: Rename `assertRegexpMatches` and `assertRaisesRegexp` to `assertRegex` and `assertRaisesRegex`. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 34373539336..bb2f37b05e3 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -196,6 +196,48 @@ static PyGetSetDef partial_getsetlist[] = { {NULL} /* Sentinel */ }; +static PyObject * +partial_repr(partialobject *pto) +{ + PyObject *result; + PyObject *arglist; + PyObject *tmp; + Py_ssize_t i, n; + + arglist = PyUnicode_FromString(""); + if (arglist == NULL) { + return NULL; + } + /* Pack positional arguments */ + assert (PyTuple_Check(pto->args)); + n = PyTuple_GET_SIZE(pto->args); + for (i = 0; i < n; i++) { + tmp = PyUnicode_FromFormat("%U, %R", arglist, + PyTuple_GET_ITEM(pto->args, i)); + Py_DECREF(arglist); + if (tmp == NULL) + return NULL; + arglist = tmp; + } + /* Pack keyword arguments */ + assert (pto->kw == Py_None || PyDict_Check(pto->kw)); + if (pto->kw != Py_None) { + PyObject *key, *value; + for (i = 0; PyDict_Next(pto->kw, &i, &key, &value);) { + tmp = PyUnicode_FromFormat("%U, %U=%R", arglist, + key, value); + Py_DECREF(arglist); + if (tmp == NULL) + return NULL; + arglist = tmp; + } + } + result = PyUnicode_FromFormat("%s(%R%U)", Py_TYPE(pto)->tp_name, + pto->fn, arglist); + Py_DECREF(arglist); + return result; +} + /* Pickle strategy: __reduce__ by itself doesn't support getting kwargs in the unpickle operation so we define a __setstate__ that replaces all the information @@ -254,7 +296,7 @@ static PyTypeObject partial_type = { 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ - 0, /* tp_repr */ + (reprfunc)partial_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */