[Python-checkins] cpython (merge 3.5 -> default): Issue #25455: Fixed a crash in repr of recursive functools.partial objects.

serhiy.storchaka python-checkins at python.org
Sun Jun 12 04:57:43 EDT 2016


https://hg.python.org/cpython/rev/86959c696ab7
changeset:   101933:86959c696ab7
parent:      101930:be40dd774041
parent:      101932:17e78918f608
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Sun Jun 12 11:51:26 2016 +0300
summary:
  Issue #25455: Fixed a crash in repr of recursive functools.partial objects.

files:
  Lib/test/test_functools.py |  37 ++++++++++++++++++++++++
  Misc/NEWS                  |   3 +-
  Modules/_functoolsmodule.c |  39 ++++++++++++++-----------
  3 files changed, 61 insertions(+), 18 deletions(-)


diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -217,6 +217,24 @@
                       ['{}({!r}, {}, {})'.format(name, capture, args_repr, kwargs_repr)
                        for kwargs_repr in kwargs_reprs])
 
+    def test_recursive_repr(self):
+        if self.partial is c_functools.partial:
+            name = 'functools.partial'
+        else:
+            name = self.partial.__name__
+
+        f = self.partial(capture)
+        f.__setstate__((f, (), {}, {}))
+        self.assertEqual(repr(f), '%s(%s(...))' % (name, name))
+
+        f = self.partial(capture)
+        f.__setstate__((capture, (f,), {}, {}))
+        self.assertEqual(repr(f), '%s(%r, %s(...))' % (name, capture, name))
+
+        f = self.partial(capture)
+        f.__setstate__((capture, (), {'a': f}, {}))
+        self.assertEqual(repr(f), '%s(%r, a=%s(...))' % (name, capture, name))
+
     def test_pickle(self):
         f = self.partial(signature, ['asdf'], bar=[True])
         f.attr = []
@@ -297,6 +315,25 @@
         self.assertEqual(r, ((1, 2), {}))
         self.assertIs(type(r[0]), tuple)
 
+    def test_recursive_pickle(self):
+        f = self.partial(capture)
+        f.__setstate__((f, (), {}, {}))
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            with self.assertRaises(RecursionError):
+                pickle.dumps(f, proto)
+
+        f = self.partial(capture)
+        f.__setstate__((capture, (f,), {}, {}))
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            f_copy = pickle.loads(pickle.dumps(f, proto))
+            self.assertIs(f_copy.args[0], f_copy)
+
+        f = self.partial(capture)
+        f.__setstate__((capture, (), {'a': f}, {}))
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            f_copy = pickle.loads(pickle.dumps(f, proto))
+            self.assertIs(f_copy.keywords['a'], f_copy)
+
     # Issue 6083: Reference counting bug
     def test_setstate_refcount(self):
         class BadSequence:
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -44,7 +44,8 @@
 Library
 -------
 
-- Issue #25455: Fixed a crash in repr of ElementTree.Element with recursive tag.
+- Issue #25455: Fixed crashes in repr of recursive ElementTree.Element and
+  functools.partial objects.
 
 - Issue #27294: Improved repr for Tkinter event objects.
 
diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c
--- a/Modules/_functoolsmodule.c
+++ b/Modules/_functoolsmodule.c
@@ -203,40 +203,45 @@
 static PyObject *
 partial_repr(partialobject *pto)
 {
-    PyObject *result;
+    PyObject *result = NULL;
     PyObject *arglist;
-    PyObject *tmp;
     Py_ssize_t i, n;
     PyObject *key, *value;
+    int status;
+
+    status = Py_ReprEnter((PyObject *)pto);
+    if (status != 0) {
+        if (status < 0)
+            return NULL;
+        return PyUnicode_FromFormat("%s(...)", Py_TYPE(pto)->tp_name);
+    }
 
     arglist = PyUnicode_FromString("");
-    if (arglist == NULL) {
-        return NULL;
-    }
+    if (arglist == NULL)
+        goto done;
     /* 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;
+        Py_SETREF(arglist, PyUnicode_FromFormat("%U, %R", arglist,
+                                        PyTuple_GET_ITEM(pto->args, i)));
+        if (arglist == NULL)
+            goto done;
     }
     /* Pack keyword arguments */
     assert (PyDict_Check(pto->kw));
     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;
+        Py_SETREF(arglist, PyUnicode_FromFormat("%U, %U=%R", arglist,
+                                                key, value));
+        if (arglist == NULL)
+            goto done;
     }
     result = PyUnicode_FromFormat("%s(%R%U)", Py_TYPE(pto)->tp_name,
                                   pto->fn, arglist);
     Py_DECREF(arglist);
+
+ done:
+    Py_ReprLeave((PyObject *)pto);
     return result;
 }
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list