8000 merge 3.4-slp (Stackless #130, fix exhausted generator pickling) · stackless-dev/stackless@3269354 · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 3269354

Browse files
author
Anselm Kruis
committed
merge 3.4-slp (Stackless #130, fix exhausted generator pickling)
2 parents cbd6e7a + 895f2c6 commit 3269354

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
@@ -9,6 +9,9 @@ What's New in Stackless 3.X.X?
99

1010
*Release date: 20XX-XX-XX*
1111

12+
- https://bitbucket.org/stackless-dev/stackless/issues/130
13+
Pickling exhausted generators no longer crashes.
14+
1215
- https://bitbucket.org/stackless-dev/stackless/issues/129
1316
C-API: Calling PyTasklet_New( NULL, ...) no longer crashes.
1417

Stackless/pickling/prickelpit.c

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,8 +1700,15 @@ static PyObject *
17001700
gen_reduce(PyGenObject *gen)
17011701
{
17021702
PyObject *tup;
1703-
PyObject *frame_reducer;
1704-
frame_reducer = slp_reduce_frame(gen->gi_frame);
1703+
PyObject *frame_reducer = (PyObject *)gen->gi_frame;
1704+
if (frame_reducer == NULL) {
1705+
/* Pickle NULL as None. See gen_setstate() for the corresponding
1706+
* unpickling code. */
1707+
Py_INCREF(Py_None);
1708+
frame_reducer = Py_None;
1709+
} else {
1710+
frame_reducer = slp_reduce_frame(gen->gi_frame);
1711+
}
17051712
if (frame_reducer == NULL)
17061713
return NULL;
17071714
tup = Py_BuildValue("(O()(OiOO))",
@@ -1736,6 +1743,7 @@ gen_setstate(PyObject *self, PyObject *args)
17361743
{
17371744
PyGenObject *gen = (PyGenObject *) self;
17381745
PyFrameObject *f;
1746+
PyObject *obj;
17391747
int gi_running;
17401748
PyObject *name = NULL;
17411749
PyObject *qualname = NULL;
@@ -1745,12 +1753,50 @@ gen_setstate(PyObject *self, PyObject *args)
17451753
if ((args = unwrap_frame_arg(args)) == NULL) /* now args is a counted ref! */
17461754
return NULL;
17471755

1748-
if (!PyArg_ParseTuple(args, "O!i|OO:generator",
1749-
&PyFrame_Type, &f, &gi_running, &name, &qualname)) {
1756+
if (!PyArg_ParseTuple(args, "Oi|OO:generator",
1757+
&obj, &gi_running, &name, &qualname)) {
17501758
Py_DECREF(args);
17511759
return NULL;
17521760
}
1753-
Py_DECREF(args);
1761+
1762+
if (obj == Py_None) {
1763+
/* No frame, generator is exhausted */
1764+
Py_CLEAR(gen->gi_frame);
1765+
1766+
/* Even if gi_frame is NULL, gi_code is still valid. Therefore
1767+
* I set it to the code of the exhausted frame singleton.
1768+
*/
1769+
assert(gen_exhausted_frame != NULL);
1770+
assert(PyFrame_Check(gen_exhausted_frame));
1771+
obj = (PyObject *)gen_exhausted_frame->f_code;
1772+
Py_INCREF(obj);
1773+
Py_SETREF(gen->gi_code, obj);
1774+
1775+
gen->gi_running = gi_running;
1776+
if (name == NULL) {
1777+
name = gen_exhausted_frame->f_code->co_name;
1778+
assert(name != NULL);
1779+
}
1780+
if (qualname == NULL) {
1781+
qualname = name;
1782+
}
1783+
Py_INCREF(name);
1784+
Py_SETREF(gen->gi_name, name);
1785+
Py_INCREF(qualname);
1786+
Py_SETREF(gen->gi_qualname, qualname);
1787+
Py_TYPE(gen) = Py_TYPE(gen)->tp_base;
1788+
Py_INCREF(gen);
< 8000 /td>
1789+
Py_DECREF(args); /* holds the obj ref */
1790+
return (PyObject *)gen;
1791+
}
1792+
if (!PyFrame_Check(obj)) {
1793+
PyErr_SetString(PyExc_TypeError,
1794+
"invalid frame object for gen_setstate");
1795+
Py_DECREF(args); /* holds the obj ref */
1796+
return NULL;
1797+
}
1798+
f = (PyFrameObject *)obj;
1799+
obj = NULL;
17541800

17551801
if (name == NULL) {
17561802
name = f->f_code->co_name;
@@ -1768,6 +1814,7 @@ gen_setstate(PyObject *self, PyObject *args)
17681814
if ((tmpgen = (PyGenObject *)
17691815
PyGen_NewWithQualName(f, NULL, NULL)) == NULL) {
17701816
Py_DECREF(f);
1817+
Py_DECREF(args); /* holds the frame and name refs */
17711818
return NULL;
17721819
}
17731820
Py_INCREF(f);
@@ -1790,6 +1837,7 @@ gen_setstate(PyObject *self, PyObject *args)
17901837
}
17911838
else
17921839
gen = NULL;
1840+
Py_DECREF(args); /* holds the frame and name refs */
17931841
return (PyObject *) gen;
17941842
}
17951843

@@ -1811,6 +1859,7 @@ gen_setstate(PyObject *self, PyObject *args)
18111859
Py_SETREF(gen->gi_qualname, qualname);
18121860
Py_TYPE(gen) = Py_TYPE(gen)->tp_base;
18131861
Py_INCREF(gen);
1862+
Py_DECREF(args); /* holds the frame and name refs */
18141863
return (PyObject *)gen;
18151864
}
18161865

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)
0