Skip to content

Commit 6468d62

Browse files
author
Anselm Kruis
committed
Issue python#112: Prepare Stackless 3.5
- Preserve the new generator attributes __qualname__ and __name__ on pickling https://bitbucket.org/stackless-dev/stackless/issues/112
1 parent 3e8fd78 commit 6468d62

File tree

3 files changed

+77
-4
lines changed

3 files changed

+77
-4
lines changed

Stackless/changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ What's New in Stackless 3.X.X?
99

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

12+
- https://bitbucket.org/stackless-dev/stackless/issues/112
13+
Adapt Stackless to Python 3.5.
14+
- PyGenObject got two additional fields
15+
1216
- https://bitbucket.org/stackless-dev/stackless/issues/111
1317
Restore the Python ABI function PyGen_New(). Previously Stackless named this
1418
function PyGenerator_New() and used a macro the redefine PyGen_New as

Stackless/pickling/prickelpit.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2240,10 +2240,12 @@ static PyObject *
22402240
gen_reduce(PyGenObject *gen)
22412241
{
22422242
PyObject *tup;
2243-
tup = Py_BuildValue("(O()(Oi))",
2243+
tup = Py_BuildValue("(O()(OiOO))",
22442244
&wrap_PyGen_Type,
22452245
gen->gi_frame,
2246-
gen->gi_running
2246+
gen->gi_running,
2247+
gen->gi_name,
2248+
gen->gi_qualname
22472249
);
22482250
return tup;
22492251
}
@@ -2270,12 +2272,22 @@ gen_setstate(PyObject *self, PyObject *args)
22702272
PyGenObject *gen = (PyGenObject *) self;
22712273
PyFrameObject *f;
22722274
int gi_running;
2275+
PyObject *name = NULL;
2276+
PyObject *qualname = NULL;
22732277

22742278
if (is_wrong_type(self->ob_type)) return NULL;
2275-
if (!PyArg_ParseTuple(args, "O!i:generator",
2276-
&PyFrame_Type, &f, &gi_running))
2279+
if (!PyArg_ParseTuple(args, "O!i|OO:generator",
2280+
&PyFrame_Type, &f, &gi_running, &name, &qualname))
22772281
return NULL;
22782282

2283+
if (name == NULL) {
2284+
name = f->f_code->co_name;
2285+
assert(name != NULL);
2286+
}
2287+
if (qualname == NULL) {
2288+
qualname = name;
2289+
}
2290+
22792291
if (!gi_running) {
22802292
if ((f = slp_ensure_new_frame(f)) != NULL) {
22812293
/* use a second one for late initialization */
@@ -2290,6 +2302,10 @@ gen_setstate(PyObject *self, PyObject *args)
22902302
Py_SETREF(gen->gi_frame, f);
22912303
Py_INCREF(f->f_code);
22922304
Py_SETREF(gen->gi_code, (PyObject *)f->f_code);
2305+
Py_INCREF(name);
2306+
Py_SETREF(gen->gi_name, name);
2307+
Py_INCREF(qualname);
2308+
Py_SETREF(gen->gi_qualname, qualname);
22932309
/* The frame the temporary generator references
22942310
will have GeneratorExit raised on it, when the
22952311
temporary generator is torn down. So clearing
@@ -2317,6 +2333,10 @@ gen_setstate(PyObject *self, PyObject *args)
23172333
Py_INCREF(f->f_code);
23182334
Py_SETREF(gen->gi_code, (PyObject *)f->f_code);
23192335
gen->gi_running = gi_running;
2336+
Py_INCREF(name);
2337+
Py_SETREF(gen->gi_name, name);
2338+
Py_INCREF(qualname);
2339+
Py_SETREF(gen->gi_qualname, qualname);
23202340
Py_TYPE(gen) = Py_TYPE(gen)->tp_base;
23212341
Py_INCREF(gen);
23222342
return (PyObject *)gen;

Stackless/unittests/test_generator.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import gc
33
import stackless
44
import types
5+
import pickle
6+
import copyreg
57

68
from support import StacklessTestCase
79

@@ -49,5 +51,52 @@ def test_wrap_generator_frame_code(self):
4951
self.assertIsNot(g0.gi_frame, g1.gi_frame)
5052
self.assertEqual(g0.__name__, "exhausted_generator")
5153

54+
55+
class TestGeneratorPickling(StacklessTestCase):
56+
def test_name(self):
57+
def g():
58+
yield 1
59+
gen_orig = g()
60+
p = pickle.dumps(gen_orig)
61+
gen_new = pickle.loads(p)
62+
63+
self.assertEqual(gen_new.__name__, gen_orig.__name__)
64+
65+
def test_qualname(self):
66+
def g():
67+
yield 1
68+
gen_orig = g()
69+
p = pickle.dumps(gen_orig)
70+
gen_new = pickle.loads(p)
71+
72+
self.assertEqual(gen_new.__qualname__, gen_orig.__qualname__)
73+
74+
def test_noname(self):
75+
# Ensure unpickling compatibility with Python versions < 3.5
76+
def g():
77+
yield 11
78+
gen_orig = g()
79+
80+
r = copyreg.dispatch_table[type(gen_orig)](gen_orig)
81+
82+
self.assertIsInstance(r, tuple)
83+
self.assertEqual(len(r), 3)
84+
self.assertEqual(len(r[2]), 4)
85+
86+
gen_new = r[0](*r[1])
87+
self.assertEqual(gen_new.__qualname__, "exhausted_generator")
88+
self.assertEqual(gen_new.__name__, "exhausted_generator")
89+
self.assertIs(type(gen_new), stackless._wrap.generator)
90+
91+
gen_new.__setstate__(r[2][:-2])
92+
93+
self.assertEqual(gen_new.__qualname__, "g")
94+
self.assertEqual(gen_new.__name__, "g")
95+
self.assertIs(type(gen_new), type(gen_orig))
96+
97+
v = gen_new.__next__()
98+
self.assertEqual(v, 11)
99+
100+
52101
if __name__ == '__main__':
53102
unittest.main()

0 commit comments

Comments
 (0)