Skip to content

Commit c615848

Browse files
author
Anselm Kruis
committed
merge 3.5-slp (Stackless python#130, fix exhausted generator pickling)
2 parents 4a3ec52 + 3269354 commit c615848

File tree

3 files changed

+68
-5
lines changed

3 files changed

+68
-5
lines changed

Stackless/changelog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ What's New in Stackless 3.X.X?
3030
- https://github.com/stackless-dev/stackless/issues/135
3131
Fix small memory leaks for types with Stackless extensions.
3232

33+
- https://bitbucket.org/stackless-dev/stackless/issues/130
34+
Pickling exhausted generators no longer crashes.
35+
3336
- https://bitbucket.org/stackless-dev/stackless/issues/129
3437
C-API: Calling PyTasklet_New( NULL, ...) no longer crashes.
3538

Stackless/pickling/prickelpit.c

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,8 +1706,15 @@ static PyObject *
17061706
gen_reduce(PyGenObject *gen)
17071707
{
17081708
PyObject *tup;
1709-
PyObject *frame_reducer;
1710-
frame_reducer = slp_reduce_frame(gen->gi_frame);
1709+
PyObject *frame_reducer = (PyObject *)gen->gi_frame;
1710+
if (frame_reducer == NULL) {
1711+
/* Pickle NULL as None. See gen_setstate() for the corresponding
1712+
* unpickling code. */
1713+
Py_INCREF(Py_None);
1714+
frame_reducer = Py_None;
1715+
} else {
1716+
frame_reducer = slp_reduce_frame(gen->gi_frame);
1717+
}
17111718
if (frame_reducer == NULL)
17121719
return NULL;
17131720
tup = Py_BuildValue("(O()(OiOO))",
@@ -1742,6 +1749,7 @@ gen_setstate(PyObject *self, PyObject *args)
17421749
{
17431750
PyGenObject *gen = (PyGenObject *) self;
17441751
PyFrameObject *f;
1752+
PyObject *obj;
17451753
int gi_running;
17461754
PyObject *name = NULL;
17471755
PyObject *qualname = NULL;
@@ -1751,12 +1759,50 @@ gen_setstate(PyObject *self, PyObject *args)
17511759
if ((args = unwrap_frame_arg(args)) == NULL) /* now args is a counted ref! */
17521760
return NULL;
17531761

1754-
if (!PyArg_ParseTuple(args, "O!i|OO:generator",
1755-
&PyFrame_Type, &f, &gi_running, &name, &qualname)) {
1762+
if (!PyArg_ParseTuple(args, "Oi|OO:generator",
1763+
&obj, &gi_running, &name, &qualname)) {
17561764
Py_DECREF(args);
17571765
return NULL;
17581766
}
1759-
Py_DECREF(args);
1767+
1768+
if (obj == Py_None) {
1769+
/* No frame, generator is exhausted */
1770+
Py_CLEAR(gen->gi_frame);
1771+
1772+
/* Even if gi_frame is NULL, gi_code is still valid. Therefore
1773+
* I set it to the code of the exhausted frame singleton.
1774+
*/
1775+
assert(gen_exhausted_frame != NULL);
1776+
assert(PyFrame_Check(gen_exhausted_frame));
1777+
obj = (PyObject *)gen_exhausted_frame->f_code;
1778+
Py_INCREF(obj);
1779+
Py_SETREF(gen->gi_code, obj);
1780+
1781+
gen->gi_running = gi_running;
1782+
if (name == NULL) {
1783+
name = gen_exhausted_frame->f_code->co_name;
1784+
assert(name != NULL);
1785+
}
1786+
if (qualname == NULL) {
1787+
qualname = name;
1788+
}
1789+
Py_INCREF(name);
1790+
Py_SETREF(gen->gi_name, name);
1791+
Py_INCREF(qualname);
1792+
Py_SETREF(gen->gi_qualname, qualname);
1793+
Py_TYPE(gen) = Py_TYPE(gen)->tp_base;
1794+
Py_INCREF(gen);
1795+
Py_DECREF(args); /* holds the obj ref */
1796+
return (PyObject *)gen;
1797+
}
1798+
if (!PyFrame_Check(obj)) {
1799+
PyErr_SetString(PyExc_TypeError,
1800+
"invalid frame object for gen_setstate");
1801+
Py_DECREF(args); /* holds the obj ref */
1802+
return NULL;
1803+
}
1804+
f = (PyFrameObject *)obj;
1805+
obj = NULL;
17601806

17611807
if (name == NULL) {
17621808
name = f->f_code->co_name;
@@ -1774,6 +1820,7 @@ gen_setstate(PyObject *self, PyObject *args)
17741820
if ((tmpgen = (PyGenObject *)
17751821
PyGen_NewWithQualName(f, NULL, NULL)) == NULL) {
17761822
Py_DECREF(f);
1823+
Py_DECREF(args); /* holds the frame and name refs */
17771824
return NULL;
17781825
}
17791826
Py_INCREF(f);
@@ -1796,6 +1843,7 @@ gen_setstate(PyObject *self, PyObject *args)
17961843
}
17971844
else
17981845
gen = NULL;
1846+
Py_DECREF(args); /* holds the frame and name refs */
17991847
return (PyObject *) gen;
18001848
}
18011849

@@ -1817,6 +1865,7 @@ gen_setstate(PyObject *self, PyObject *args)
18171865
Py_SETREF(gen->gi_qualname, qualname);
18181866
Py_TYPE(gen) = Py_TYPE(gen)->tp_base;
18191867
Py_INCREF(gen);
1868+
Py_DECREF(args); /* holds the frame and name refs */
18201869
return (PyObject *)gen;
18211870
}
18221871

Stackless/unittests/test_generator.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ def g():
102102
v = gen_new.__next__()
103103
self.assertEqual(v, 11)
104104

105+
def test_exhausted(self):
106+
def g():
107+
yield 1
108+
gen_orig = g()
109+
self.assertEqual(gen_orig.__next__(), 1)
110+
self.assertRaises(StopIteration, gen_orig.__next__)
111+
p = pickle.dumps(gen_orig)
112+
self.assertRaises(StopIteration, gen_orig.__next__)
113+
gen_new = pickle.loads(p)
114+
self.assertRaises(StopIteration, gen_new.__next__)
115+
105116

106117
if __name__ == '__main__':
107118
unittest.main()

0 commit comments

Comments
 (0)