Index: Lib/test/test_functools.py =================================================================== --- Lib/test/test_functools.py (revision 86529) +++ Lib/test/test_functools.py (working copy) @@ -146,6 +146,31 @@ 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(k + '=' + repr(v) for k, v in kwargs.items()) + name = 'functools.partial' if ( + self.thetype is functools.partial + ) else 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 +188,9 @@ 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 Index: Modules/_functoolsmodule.c =================================================================== --- Modules/_functoolsmodule.c (revision 86529) +++ Modules/_functoolsmodule.c (working copy) @@ -196,6 +196,84 @@ {NULL} /* Sentinel */ }; +static PyObject * +partial_repr(partialobject *pto) +{ + PyObject *s; + PyObject *tmp1; + PyObject *tmp2; + PyObject *it; + PyObject *arg; + + s = PyUnicode_FromFormat("%s(%R", Py_TYPE(pto)->tp_name, pto->fn); + if (s == NULL) { + return NULL; + } + + it = PyObject_GetIter(pto->args); + if (it == NULL) { + Py_DECREF(s); + return NULL; + } + + while ((arg = PyIter_Next(it))) { + tmp1 = PyUnicode_FromFormat(", %R", arg); + Py_DECREF(arg); + if (tmp1 == NULL) { + Py_DECREF(it); + Py_DECREF(s); + return NULL; + } + + tmp2 = PyUnicode_Concat(s, tmp1); + Py_DECREF(s); + Py_DECREF(tmp1); + s = tmp2; + if (s == NULL) { + Py_DECREF(it); + return NULL; + } + } + Py_DECREF(it); + if (PyErr_Occurred()) { + Py_DECREF(s); + return NULL; + } + + if (pto->kw != NULL) { + PyObject *k; + PyObject *v; + Py_ssize_t pos = 0; + + while (PyDict_Next(pto->kw, &pos, &k, &v)) { + tmp1 = PyUnicode_FromFormat(", %U=%R", k, v); + if (tmp1 == NULL) { + Py_DECREF(s); + return NULL; + } + + tmp2 = PyUnicode_Concat(s, tmp1); + Py_DECREF(s); + Py_DECREF(tmp1); + s = tmp2; + if (s == NULL) { + return NULL; + } + } + } + + tmp1 = PyUnicode_FromString(")"); + if (tmp1 == NULL) { + Py_DECREF(s); + return NULL; + } + + tmp2 = PyUnicode_Concat(s, tmp1); + Py_DECREF(s); + Py_DECREF(tmp1); + return tmp2; +} + /* 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 +332,7 @@ 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 */