Skip to content

Commit 179f960

Browse files
Issue #25455: Fixed a crash in repr of recursive functools.partial objects.
1 parent cbe6142 commit 179f960

3 files changed

Lines changed: 61 additions & 18 deletions

File tree

Lib/test/test_functools.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,24 @@ def test_repr(self):
217217
['{}({!r}, {}, {})'.format(name, capture, args_repr, kwargs_repr)
218218
for kwargs_repr in kwargs_reprs])
219219

220+
def test_recursive_repr(self):
221+
if self.partial is c_functools.partial:
222+
name = 'functools.partial'
223+
else:
224+
name = self.partial.__name__
225+
226+
f = self.partial(capture)
227+
f.__setstate__((f, (), {}, {}))
228+
self.assertEqual(repr(f), '%s(%s(...))' % (name, name))
229+
230+
f = self.partial(capture)
231+
f.__setstate__((capture, (f,), {}, {}))
232+
self.assertEqual(repr(f), '%s(%r, %s(...))' % (name, capture, name))
233+
234+
f = self.partial(capture)
235+
f.__setstate__((capture, (), {'a': f}, {}))
236+
self.assertEqual(repr(f), '%s(%r, a=%s(...))' % (name, capture, name))
237+
220238
def test_pickle(self):
221239
f = self.partial(signature, ['asdf'], bar=[True])
222240
f.attr = []
@@ -297,6 +315,25 @@ def test_setstate_subclasses(self):
297315
self.assertEqual(r, ((1, 2), {}))
298316
self.assertIs(type(r[0]), tuple)
299317

318+
def test_recursive_pickle(self):
319+
f = self.partial(capture)
320+
f.__setstate__((f, (), {}, {}))
321+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
322+
with self.assertRaises(RecursionError):
323+
pickle.dumps(f, proto)
324+
325+
f = self.partial(capture)
326+
f.__setstate__((capture, (f,), {}, {}))
327+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
328+
f_copy = pickle.loads(pickle.dumps(f, proto))
329+
self.assertIs(f_copy.args[0], f_copy)
330+
331+
f = self.partial(capture)
332+
f.__setstate__((capture, (), {'a': f}, {}))
333+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
334+
f_copy = pickle.loads(pickle.dumps(f, proto))
335+
self.assertIs(f_copy.keywords['a'], f_copy)
336+
300337
# Issue 6083: Reference counting bug
301338
def test_setstate_refcount(self):
302339
class BadSequence:

Misc/NEWS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ Core and Builtins
143143
Library
144144
-------
145145

146-
- Issue #25455: Fixed a crash in repr of ElementTree.Element with recursive tag.
146+
- Issue #25455: Fixed crashes in repr of recursive ElementTree.Element and
147+
functools.partial objects.
147148

148149
- Issue #26556: Update expat to 2.1.1, fixes CVE-2015-1283.
149150

Modules/_functoolsmodule.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -203,40 +203,45 @@ static PyGetSetDef partial_getsetlist[] = {
203203
static PyObject *
204204
partial_repr(partialobject *pto)
205205
{
206-
PyObject *result;
206+
PyObject *result = NULL;
207207
PyObject *arglist;
208-
PyObject *tmp;
209208
Py_ssize_t i, n;
210209
PyObject *key, *value;
210+
int status;
211211

212-
arglist = PyUnicode_FromString("");
213-
if (arglist == NULL) {
214-
return NULL;
212+
status = Py_ReprEnter((PyObject *)pto);
213+
if (status != 0) {
214+
if (status < 0)
215+
return NULL;
216+
return PyUnicode_FromFormat("%s(...)", Py_TYPE(pto)->tp_name);
215217
}
218+
219+
arglist = PyUnicode_FromString("");
220+
if (arglist == NULL)
221+
goto done;
216222
/* Pack positional arguments */
217223
assert (PyTuple_Check(pto->args));
218224
n = PyTuple_GET_SIZE(pto->args);
219225
for (i = 0; i < n; i++) {
220-
tmp = PyUnicode_FromFormat("%U, %R", arglist,
221-
PyTuple_GET_ITEM(pto->args, i));
222-
Py_DECREF(arglist);
223-
if (tmp == NULL)
224-
return NULL;
225-
arglist = tmp;
226+
Py_SETREF(arglist, PyUnicode_FromFormat("%U, %R", arglist,
227+
PyTuple_GET_ITEM(pto->args, i)));
228+
if (arglist == NULL)
229+
goto done;
226230
}
227231
/* Pack keyword arguments */
228232
assert (PyDict_Check(pto->kw));
229233
for (i = 0; PyDict_Next(pto->kw, &i, &key, &value);) {
230-
tmp = PyUnicode_FromFormat("%U, %U=%R", arglist,
231-
key, value);
232-
Py_DECREF(arglist);
233-
if (tmp == NULL)
234-
return NULL;
235-
arglist = tmp;
234+
Py_SETREF(arglist, PyUnicode_FromFormat("%U, %U=%R", arglist,
235+
key, value));
236+
if (arglist == NULL)
237+
goto done;
236238
}
237239
result = PyUnicode_FromFormat("%s(%R%U)", Py_TYPE(pto)->tp_name,
238240
pto->fn, arglist);
239241
Py_DECREF(arglist);
242+
243+
done:
244+
Py_ReprLeave((PyObject *)pto);
240245
return result;
241246
}
242247

0 commit comments

Comments
 (0)