]> granicus.if.org Git - python/commitdiff
bpo-34574: Prevent OrderedDict iterators from exhaustion during pickling. (GH-9051)
authorSergey Fedoseev <fedoseev.sergey@gmail.com>
Sat, 20 Oct 2018 05:20:39 +0000 (10:20 +0500)
committerSerhiy Storchaka <storchaka@gmail.com>
Sat, 20 Oct 2018 05:20:39 +0000 (08:20 +0300)
Lib/test/test_ordered_dict.py
Misc/NEWS.d/next/Library/2018-09-04-09-32-54.bpo-34574.X4RwYI.rst [new file with mode: 0644]
Objects/odictobject.c

index a296f6055b1a1265bf583dc397b1cfd8aa5c233f..b1d7f86a6760b3b4016edf0bd05d49545afe26cf 100644 (file)
@@ -732,6 +732,23 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
                 del od['c']
         self.assertEqual(list(od), list('bdeaf'))
 
+    def test_iterators_pickling(self):
+        OrderedDict = self.OrderedDict
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        od = OrderedDict(pairs)
+
+        for method_name in ('keys', 'values', 'items'):
+            meth = getattr(od, method_name)
+            expected = list(meth())[1:]
+            for i in range(pickle.HIGHEST_PROTOCOL + 1):
+                with self.subTest(method_name=method_name, protocol=i):
+                    it = iter(meth())
+                    next(it)
+                    p = pickle.dumps(it, i)
+                    unpickled = pickle.loads(p)
+                    self.assertEqual(list(unpickled), expected)
+                    self.assertEqual(list(it), expected)
+
 
 class PurePythonOrderedDictSubclassTests(PurePythonOrderedDictTests):
 
diff --git a/Misc/NEWS.d/next/Library/2018-09-04-09-32-54.bpo-34574.X4RwYI.rst b/Misc/NEWS.d/next/Library/2018-09-04-09-32-54.bpo-34574.X4RwYI.rst
new file mode 100644 (file)
index 0000000..de718ad
--- /dev/null
@@ -0,0 +1,2 @@
+OrderedDict iterators are not exhausted during pickling anymore. Patch by
+Sergey Fedoseev.
index 47e77b6046b24ff6ef7cb5b85ff512f79782d9e1..67c3674ee09528bde42b54e770e06ea8d0fe99ec 100644 (file)
@@ -1805,38 +1805,19 @@ PyDoc_STRVAR(reduce_doc, "Return state information for pickling");
 static PyObject *
 odictiter_reduce(odictiterobject *di)
 {
-    PyObject *list, *iter;
-
-    list = PyList_New(0);
-    if (!list)
-        return NULL;
+    /* copy the iterator state */
+    odictiterobject tmp = *di;
+    Py_XINCREF(tmp.di_odict);
+    Py_XINCREF(tmp.di_current);
 
     /* iterate the temporary into a list */
-    for(;;) {
-        PyObject *element = odictiter_iternext(di);
-        if (element) {
-            if (PyList_Append(list, element)) {
-                Py_DECREF(element);
-                Py_DECREF(list);
-                return NULL;
-            }
-            Py_DECREF(element);
-        }
-        else {
-            /* done iterating? */
-            break;
-        }
-    }
-    if (PyErr_Occurred()) {
-        Py_DECREF(list);
-        return NULL;
-    }
-    iter = _PyObject_GetBuiltin("iter");
-    if (iter == NULL) {
-        Py_DECREF(list);
+    PyObject *list = PySequence_List((PyObject*)&tmp);
+    Py_XDECREF(tmp.di_odict);
+    Py_XDECREF(tmp.di_current);
+    if (list == NULL) {
         return NULL;
     }
-    return Py_BuildValue("N(N)", iter, list);
+    return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list);
 }
 
 static PyMethodDef odictiter_methods[] = {