Skip to content

Commit d141fdd

Browse files
author
Anselm Kruis
committed
merge 3.4-slp (Stackless python#107)
2 parents f7d91c7 + 712763a commit d141fdd

File tree

10 files changed

+147
-40
lines changed

10 files changed

+147
-40
lines changed

Lib/stackless.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,53 @@ def transmogrify():
7373
this function creates a subclass of the ModuleType with properties.
7474
Stackless has historically had module properties, something very unusual in Python.
7575
We need to do that by replacing the current module object as it is being created
76+
77+
Additionally this function performs a few initialisations.
7678
"""
79+
from copyreg import pickle
80+
for name in dir(_wrap):
81+
cls = getattr(_wrap, name, None)
82+
if isinstance(cls, type) and cls.__name__ != "frame":
83+
pickle(cls.__bases__[0], cls.__reduce__)
84+
85+
try:
86+
# in case of reload(stackless)
87+
reduce_frame = _wrap.reduce_frame
88+
except AttributeError:
89+
from weakref import WeakValueDictionary
90+
91+
wrap_set_reduce_frame = _wrap.set_reduce_frame
92+
93+
def set_reduce_frame(func):
94+
wrap_set_reduce_frame(func)
95+
_wrap.reduce_frame = func
96+
97+
_wrap.set_reduce_frame = set_reduce_frame
98+
99+
wrap_frame__reduce = _wrap.frame.__reduce__
100+
cache = WeakValueDictionary()
101+
102+
class _Frame_Wrapper(object):
103+
"""Wrapper for frames to be pickled"""
104+
__slots__ = ('__weakref__', 'frame')
105+
106+
@classmethod
107+
def reduce_frame(cls, frame):
108+
oid = id(frame)
109+
try:
110+
return cache[oid]
111+
except KeyError:
112+
cache[oid] = reducer = cls(frame)
113+
return reducer
114+
115+
def __init__(self, frame):
116+
self.frame = frame
117+
118+
def __reduce__(self):
119+
return wrap_frame__reduce(self.frame)
120+
reduce_frame = _Frame_Wrapper.reduce_frame
121+
_wrap.set_reduce_frame(reduce_frame)
122+
77123

78124
class StacklessModuleType(types.ModuleType):
79125

Stackless/core/stackless_impl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,8 @@ PyObject * slp_restore_exception(PyFrameObject *f, int exc, PyObject *retval);
388388
PyObject * slp_restore_tracing(PyFrameObject *f, int exc, PyObject *retval);
389389
/* other eval_frame functions from Objects/typeobject.c */
390390
PyObject * slp_tp_init_callback(PyFrameObject *f, int exc, PyObject *retval);
391+
/* functions related to pickling */
392+
PyObject * slp_reduce_frame(PyFrameObject * frame);
391393

392394
/* rebirth of software stack avoidance */
393395

Stackless/module/taskletobject.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,14 @@ tasklet_reduce(PyTaskletObject * t)
442442
goto err_exit;
443443
}
444444
if (append_frame) {
445-
if (PyList_Append(lis, (PyObject *) f)) goto err_exit;
445+
int ret;
446+
PyObject * frame_reducer = slp_reduce_frame(f);
447+
if (frame_reducer == NULL)
448+
goto err_exit;
449+
ret = PyList_Append(lis, frame_reducer);
450+
Py_DECREF(frame_reducer);
451+
if (ret)
452+
goto err_exit;
446453
}
447454
f = f->f_back;
448455
}
@@ -1486,8 +1493,8 @@ tasklet_get_frame(PyTaskletObject *task)
14861493
{
14871494
PyObject *ret = (PyObject*) PyTasklet_GetFrame(task);
14881495
if (ret)
1489-
return ret;
1490-
Py_RETURN_NONE;
1496+
return ret;
1497+
Py_RETURN_NONE;
14911498
}
14921499

14931500
PyObject *

Stackless/pickling/prickelpit.c

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,36 @@ static struct _typeobject wrap_##type = { \
179179
};
180180

181181
static PyObject *types_mod = NULL;
182-
static PyObject *pickle_reg = NULL;
182+
static PyObject *reduce_frame_func = NULL;
183+
184+
PyDoc_STRVAR(set_reduce_frame__doc__,
185+
"set_reduce_frame(func) -- set the function used to reduce frames during pickling.\n"
186+
"The function takes a frame as its sole argument and must return a pickleable object.\n");
187+
188+
static PyObject *
189+
set_reduce_frame(PyObject *self, PyObject *func)
190+
{
191+
if (func == Py_None) {
192+
Py_CLEAR(reduce_frame_func);
193+
} else {
194+
if (!PyCallable_Check(func)) {
195+
TYPE_ERROR("func must be callable", NULL);
196+
}
197+
Py_INCREF(func);
198+
Py_XSETREF(reduce_frame_func, func);
199+
}
200+
Py_RETURN_NONE;
201+
}
202+
203+
PyObject *
204+
slp_reduce_frame(PyFrameObject * frame) {
205+
if (!PyFrame_Check(frame) || reduce_frame_func == NULL) {
206+
Py_INCREF(frame);
207+
return (PyObject *)frame;
208+
}
209+
return PyObject_CallFunctionObjArgs(reduce_frame_func, (PyObject *)frame, NULL);
210+
}
211+
183212

184213
static struct PyMethodDef _new_methoddef[] = {
185214
{"__new__", (PyCFunction)_new_wrapper, METH_VARARGS | METH_KEYWORDS,
@@ -192,8 +221,7 @@ static int init_type(PyTypeObject *t, int (*initchain)(void))
192221
{
193222
PyMethodDescrObject *reduce;
194223
PyWrapperDescrObject *init;
195-
PyObject *retval = NULL, *func;
196-
int ret = 0;
224+
PyObject *func;
197225
const char *name = strrchr(t->tp_name, '.')+1;
198226

199227
/* we patch the type to use *our* name, which makes no difference */
@@ -216,15 +244,9 @@ static int init_type(PyTypeObject *t, int (*initchain)(void))
216244
func = PyCFunction_New(_new_methoddef, (PyObject *)t);
217245
if (func == NULL || PyDict_SetItemString(t->tp_dict, "__new__", func))
218246
return -1;
219-
/* register with copy_reg */
220-
if (pickle_reg != NULL &&
221-
(retval = PyObject_CallFunction(pickle_reg, "OO",
222-
t->tp_base, reduce)) == NULL)
223-
ret = -1;
224-
Py_XDECREF(retval);
225-
if (ret == 0 && initchain != NULL)
226-
ret = initchain();
227-
return ret;
247+
if (initchain != NULL)
248+
return initchain();
249+
return 0;
228250
}
229251

230252
/* root of init function chain */
@@ -1164,13 +1186,19 @@ static PyObject *
11641186
tb_reduce(tracebackobject * tb)
11651187
{
11661188
PyObject *tup = NULL;
1167-
char *fmt = "(O()(OiiO))";
1189+
PyObject *frame_reducer;
1190+
const char *fmt = "(O()(OiiO))";
11681191

11691192
if (tb->tb_next == NULL)
11701193
fmt = "(O()(Oii))";
1194+
frame_reducer = slp_reduce_frame(tb->tb_frame);
1195+
if (frame_reducer == NULL)
1196+
return NULL;
1197+
11711198
tup = Py_BuildValue(fmt,
11721199
&wrap_PyTraceBack_Type,
1173-
tb->tb_frame, tb->tb_lasti, tb->tb_lineno, tb->tb_next);
1200+
frame_reducer, tb->tb_lasti, tb->tb_lineno, tb->tb_next);
1201+
Py_DECREF(frame_reducer);
11741202
return tup;
11751203
}
11761204

@@ -2250,13 +2278,18 @@ static PyObject *
22502278
gen_reduce(PyGenObject *gen)
22512279
{
22522280
PyObject *tup;
2281+
PyObject *frame_reducer;
2282+
frame_reducer = slp_reduce_frame(gen->gi_frame);
2283+
if (frame_reducer == NULL)
2284+
return NULL;
22532285
tup = Py_BuildValue("(O()(OiOO))",
22542286
&wrap_PyGen_Type,
2255-
gen->gi_frame,
2287+
frame_reducer,
22562288
gen->gi_running,
22572289
gen->gi_name,
22582290
gen->gi_qualname
22592291
);
2292+
Py_DECREF(frame_reducer);
22602293
return tup;
22612294
}
22622295

@@ -2486,22 +2519,29 @@ static int
24862519
_wrapmodule_traverse(PyObject *self, visitproc visit, void *arg)
24872520
{
24882521
Py_VISIT(gen_exhausted_frame);
2522+
Py_VISIT(reduce_frame_func);
24892523
return 0;
24902524
}
24912525

24922526
static int
24932527
_wrapmodule_clear(PyObject *self)
24942528
{
24952529
Py_CLEAR(gen_exhausted_frame);
2530+
Py_CLEAR(reduce_frame_func);
24962531
return 0;
24972532
}
24982533

2534+
static PyMethodDef _wrapmodule_methods[] = {
2535+
{"set_reduce_frame", set_reduce_frame, METH_O, set_reduce_frame__doc__},
2536+
{NULL, NULL} /* sentinel */
2537+
};
2538+
24992539
static struct PyModuleDef _wrapmodule = {
25002540
PyModuleDef_HEAD_INIT,
25012541
"_stackless._wrap",
25022542
NULL,
25032543
-1,
2504-
NULL,
2544+
_wrapmodule_methods,
25052545
NULL,
25062546
_wrapmodule_traverse,
25072547
_wrapmodule_clear,
@@ -2511,29 +2551,15 @@ static struct PyModuleDef _wrapmodule = {
25112551
PyObject*
25122552
init_prickelpit(void)
25132553
{
2514-
PyObject *copy_reg, *tmp;
2554+
PyObject *tmp;
25152555

25162556
types_mod = PyModule_Create(&_wrapmodule);
25172557
if (types_mod == NULL)
25182558
return NULL;
2519-
copy_reg = PyImport_ImportModule("copyreg");
2520-
if (copy_reg == NULL) {
2521-
Py_CLEAR(types_mod);
2522-
return NULL;
2523-
}
2524-
2525-
pickle_reg = PyObject_GetAttrString(copy_reg, "pickle");
2526-
Py_DECREF(copy_reg);
2527-
if (pickle_reg == NULL) {
2528-
Py_CLEAR(types_mod);
2529-
return NULL;
2530-
}
25312559
if (initchain()) {
2532-
Py_CLEAR(pickle_reg);
25332560
Py_CLEAR(types_mod);
25342561
return NULL;
25352562
}
2536-
Py_CLEAR(pickle_reg);
25372563
tmp = types_mod;
25382564
types_mod = NULL;
25392565
return tmp;

Stackless/unittests/support.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,14 @@ def helper():
747747
return result
748748

749749

750+
def get_reduce_frame():
751+
"""counterpart to stackless._wrap.set_reduce_frame()
752+
753+
Only for testing!
754+
"""
755+
return getattr(stackless._wrap, "reduce_frame", None)
756+
757+
750758
def test_main():
751759
"""Main function for the CPython :mod:`test.regrtest` test driver.
752760

Stackless/unittests/test_defects.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
from stackless import _test_nostacklesscall as apply_not_stackless
1616
from support import test_main # @UnusedImport
17-
from support import StacklessTestCase, captured_stderr, require_one_thread
17+
from support import (StacklessTestCase, captured_stderr, require_one_thread,
18+
get_reduce_frame)
1819

1920

2021
"""
@@ -236,6 +237,7 @@ def testCrasher(self):
236237
frameType = type(frame)
237238
while frame and frame.f_back:
238239
frame = frame.f_back
240+
frame = get_reduce_frame()(frame)
239241
p = pickle.dumps(frame, -1)
240242
frame = None
241243
frame = pickle.loads(p)

Stackless/unittests/test_generator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ def g():
9090
self.assertEqual(gen_new.__name__, "exhausted_generator")
9191
self.assertIs(type(gen_new), stackless._wrap.generator)
9292

93-
gen_new.__setstate__(r[2][:-2])
93+
# build the pre 3.5 argument tuple for __setstate__
94+
r = r[2][:-2]
95+
r = (r[0].frame,) + r[1:]
96+
gen_new.__setstate__(r)
9497

9598
self.assertEqual(gen_new.__qualname__, "g")
9699
self.assertEqual(gen_new.__name__, "g")

Stackless/unittests/test_pickle.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from stackless import schedule, tasklet, stackless
88

99
from support import test_main # @UnusedImport
10-
from support import StacklessTestCase, StacklessPickleTestCase
10+
from support import StacklessTestCase, StacklessPickleTestCase, get_reduce_frame
1111

1212

1313
# because test runner instances in the testsuite contain copies of the old stdin/stdout thingies,
@@ -494,6 +494,14 @@ def testFunctionModulePreservation(self):
494494

495495

496496
class TestFramePickling(StacklessTestCase):
497+
def test_get_set_reduce_frame(self):
498+
# test setting / getting the reduce frame function
499+
rf = get_reduce_frame()
500+
self.assertTrue(callable(rf))
501+
stackless._wrap.set_reduce_frame(None)
502+
self.assertIsNone(get_reduce_frame())
503+
stackless._wrap.set_reduce_frame(rf)
504+
self.assertIs(get_reduce_frame(), rf)
497505

498506
def testLocalplus(self):
499507
result = []
@@ -607,6 +615,7 @@ def d():
607615
p = self.dumps(tb)
608616
tb2 = self.loads(p)
609617
# basics
618+
innerframes_orig = inspect.getinnerframes(tb)
610619
self.assertIs(type(tb), type(tb2))
611620
self.assertIsNot(tb, tb2)
612621
innerframes = inspect.getinnerframes(tb2)

Stackless/unittests/test_thread.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ def to_current_thread(self, task):
235235
frameList[i] = newFrame
236236
# rebind the task
237237
task = reducedTask[0](*reducedTask[1])
238+
for i in range(len(reducedTask[2][3])):
239+
if not isinstance(reducedTask[2][3][i], stackless.cframe):
240+
reducedTask[2][3][i] = reducedTask[2][3][i].frame
238241
task.__setstate__(reducedTask[2])
239242
return task
240243

Stackless/unittests/test_tstate.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from stackless import *
77

88
from support import test_main # @UnusedImport
9-
from support import StacklessTestCase
9+
from support import StacklessTestCase, get_reduce_frame
1010

1111
# import os
1212
# def debug():
@@ -332,9 +332,10 @@ def testReduceOfTracingState(self):
332332
return
333333

334334
# test if the tracing cframe is present / not present
335-
self.assertListEqual(reduced_tasklet1[2][3], [frame])
335+
reduce_frame = get_reduce_frame()
336+
self.assertListEqual(reduced_tasklet1[2][3], [reduce_frame(frame)])
336337
self.assertEquals(len(reduced_tasklet2[2][3]), 2)
337-
self.assertIs(reduced_tasklet2[2][3][0], frame)
338+
self.assertIs(reduced_tasklet2[2][3][0], reduce_frame(frame))
338339
self.assertIsInstance(reduced_tasklet2[2][3][1], stackless.cframe)
339340

340341
cf = reduced_tasklet2[2][3][1]

0 commit comments

Comments
 (0)