Skip to content

Commit

Permalink
merge 3.4-slp (Stackless python#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
Anselm Kruis committed Apr 6, 2017
2 parents f7d91c7 + 712763a commit d141fdd
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 40 deletions.
46 changes: 46 additions & 0 deletions Lib/stackless.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,53 @@ def transmogrify():
this function creates a subclass of the ModuleType with properties.
Stackless has historically had module properties, something very unusual in Python.
We need to do that by replacing the current module object as it is being created
Additionally this function performs a few initialisations.
"""
from copyreg import pickle
for name in dir(_wrap):
cls = getattr(_wrap, name, None)
if isinstance(cls, type) and cls.__name__ != "frame":
pickle(cls.__bases__[0], cls.__reduce__)

try:
# in case of reload(stackless)
reduce_frame = _wrap.reduce_frame
except AttributeError:
from weakref import WeakValueDictionary

wrap_set_reduce_frame = _wrap.set_reduce_frame

def set_reduce_frame(func):
wrap_set_reduce_frame(func)
_wrap.reduce_frame = func

_wrap.set_reduce_frame = set_reduce_frame

wrap_frame__reduce = _wrap.frame.__reduce__
cache = WeakValueDictionary()

class _Frame_Wrapper(object):
"""Wrapper for frames to be pickled"""
__slots__ = ('__weakref__', 'frame')

@classmethod
def reduce_frame(cls, frame):
oid = id(frame)
try:
return cache[oid]
except KeyError:
cache[oid] = reducer = cls(frame)
return reducer

def __init__(self, frame):
self.frame = frame

def __reduce__(self):
return wrap_frame__reduce(self.frame)
reduce_frame = _Frame_Wrapper.reduce_frame
_wrap.set_reduce_frame(reduce_frame)


class StacklessModuleType(types.ModuleType):

Expand Down
2 changes: 2 additions & 0 deletions Stackless/core/stackless_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ PyObject * slp_restore_exception(PyFrameObject *f, int exc, PyObject *retval);
PyObject * slp_restore_tracing(PyFrameObject *f, int exc, PyObject *retval);
/* other eval_frame functions from Objects/typeobject.c */
PyObject * slp_tp_init_callback(PyFrameObject *f, int exc, PyObject *retval);
/* functions related to pickling */
PyObject * slp_reduce_frame(PyFrameObject * frame);

/* rebirth of software stack avoidance */

Expand Down
13 changes: 10 additions & 3 deletions Stackless/module/taskletobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,14 @@ tasklet_reduce(PyTaskletObject * t)
goto err_exit;
}
if (append_frame) {
if (PyList_Append(lis, (PyObject *) f)) goto err_exit;
int ret;
PyObject * frame_reducer = slp_reduce_frame(f);
if (frame_reducer == NULL)
goto err_exit;
ret = PyList_Append(lis, frame_reducer);
Py_DECREF(frame_reducer);
if (ret)
goto err_exit;
}
f = f->f_back;
}
Expand Down Expand Up @@ -1486,8 +1493,8 @@ tasklet_get_frame(PyTaskletObject *task)
{
PyObject *ret = (PyObject*) PyTasklet_GetFrame(task);
if (ret)
return ret;
Py_RETURN_NONE;
return ret;
Py_RETURN_NONE;
}

PyObject *
Expand Down
88 changes: 57 additions & 31 deletions Stackless/pickling/prickelpit.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,36 @@ static struct _typeobject wrap_##type = { \
};

static PyObject *types_mod = NULL;
static PyObject *pickle_reg = NULL;
static PyObject *reduce_frame_func = NULL;

PyDoc_STRVAR(set_reduce_frame__doc__,
"set_reduce_frame(func) -- set the function used to reduce frames during pickling.\n"
"The function takes a frame as its sole argument and must return a pickleable object.\n");

static PyObject *
set_reduce_frame(PyObject *self, PyObject *func)
{
if (func == Py_None) {
Py_CLEAR(reduce_frame_func);
} else {
if (!PyCallable_Check(func)) {
TYPE_ERROR("func must be callable", NULL);
}
Py_INCREF(func);
Py_XSETREF(reduce_frame_func, func);
}
Py_RETURN_NONE;
}

PyObject *
slp_reduce_frame(PyFrameObject * frame) {
if (!PyFrame_Check(frame) || reduce_frame_func == NULL) {
Py_INCREF(frame);
return (PyObject *)frame;
}
return PyObject_CallFunctionObjArgs(reduce_frame_func, (PyObject *)frame, NULL);
}


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

/* we patch the type to use *our* name, which makes no difference */
Expand All @@ -216,15 +244,9 @@ static int init_type(PyTypeObject *t, int (*initchain)(void))
func = PyCFunction_New(_new_methoddef, (PyObject *)t);
if (func == NULL || PyDict_SetItemString(t->tp_dict, "__new__", func))
return -1;
/* register with copy_reg */
if (pickle_reg != NULL &&
(retval = PyObject_CallFunction(pickle_reg, "OO",
t->tp_base, reduce)) == NULL)
ret = -1;
Py_XDECREF(retval);
if (ret == 0 && initchain != NULL)
ret = initchain();
return ret;
if (initchain != NULL)
return initchain();
return 0;
}

/* root of init function chain */
Expand Down Expand Up @@ -1164,13 +1186,19 @@ static PyObject *
tb_reduce(tracebackobject * tb)
{
PyObject *tup = NULL;
char *fmt = "(O()(OiiO))";
PyObject *frame_reducer;
const char *fmt = "(O()(OiiO))";

if (tb->tb_next == NULL)
fmt = "(O()(Oii))";
frame_reducer = slp_reduce_frame(tb->tb_frame);
if (frame_reducer == NULL)
return NULL;

tup = Py_BuildValue(fmt,
&wrap_PyTraceBack_Type,
tb->tb_frame, tb->tb_lasti, tb->tb_lineno, tb->tb_next);
frame_reducer, tb->tb_lasti, tb->tb_lineno, tb->tb_next);
Py_DECREF(frame_reducer);
return tup;
}

Expand Down Expand Up @@ -2250,13 +2278,18 @@ static PyObject *
gen_reduce(PyGenObject *gen)
{
PyObject *tup;
PyObject *frame_reducer;
frame_reducer = slp_reduce_frame(gen->gi_frame);
if (frame_reducer == NULL)
return NULL;
tup = Py_BuildValue("(O()(OiOO))",
&wrap_PyGen_Type,
gen->gi_frame,
frame_reducer,
gen->gi_running,
gen->gi_name,
gen->gi_qualname
);
Py_DECREF(frame_reducer);
return tup;
}

Expand Down Expand Up @@ -2486,22 +2519,29 @@ static int
_wrapmodule_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(gen_exhausted_frame);
Py_VISIT(reduce_frame_func);
return 0;
}

static int
_wrapmodule_clear(PyObject *self)
{
Py_CLEAR(gen_exhausted_frame);
Py_CLEAR(reduce_frame_func);
return 0;
}

static PyMethodDef _wrapmodule_methods[] = {
{"set_reduce_frame", set_reduce_frame, METH_O, set_reduce_frame__doc__},
{NULL, NULL} /* sentinel */
};

static struct PyModuleDef _wrapmodule = {
PyModuleDef_HEAD_INIT,
"_stackless._wrap",
NULL,
-1,
NULL,
_wrapmodule_methods,
NULL,
_wrapmodule_traverse,
_wrapmodule_clear,
Expand All @@ -2511,29 +2551,15 @@ static struct PyModuleDef _wrapmodule = {
PyObject*
init_prickelpit(void)
{
PyObject *copy_reg, *tmp;
PyObject *tmp;

types_mod = PyModule_Create(&_wrapmodule);
if (types_mod == NULL)
return NULL;
copy_reg = PyImport_ImportModule("copyreg");
if (copy_reg == NULL) {
Py_CLEAR(types_mod);
return NULL;
}

pickle_reg = PyObject_GetAttrString(copy_reg, "pickle");
Py_DECREF(copy_reg);
if (pickle_reg == NULL) {
Py_CLEAR(types_mod);
return NULL;
}
if (initchain()) {
Py_CLEAR(pickle_reg);
Py_CLEAR(types_mod);
return NULL;
}
Py_CLEAR(pickle_reg);
tmp = types_mod;
types_mod = NULL;
return tmp;
Expand Down
8 changes: 8 additions & 0 deletions Stackless/unittests/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,14 @@ def helper():
return result


def get_reduce_frame():
"""counterpart to stackless._wrap.set_reduce_frame()
Only for testing!
"""
return getattr(stackless._wrap, "reduce_frame", None)


def test_main():
"""Main function for the CPython :mod:`test.regrtest` test driver.
Expand Down
4 changes: 3 additions & 1 deletion Stackless/unittests/test_defects.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

from stackless import _test_nostacklesscall as apply_not_stackless
from support import test_main # @UnusedImport
from support import StacklessTestCase, captured_stderr, require_one_thread
from support import (StacklessTestCase, captured_stderr, require_one_thread,
get_reduce_frame)


"""
Expand Down Expand Up @@ -236,6 +237,7 @@ def testCrasher(self):
frameType = type(frame)
while frame and frame.f_back:
frame = frame.f_back
frame = get_reduce_frame()(frame)
p = pickle.dumps(frame, -1)
frame = None
frame = pickle.loads(p)
Expand Down
5 changes: 4 additions & 1 deletion Stackless/unittests/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ def g():
self.assertEqual(gen_new.__name__, "exhausted_generator")
self.assertIs(type(gen_new), stackless._wrap.generator)

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

self.assertEqual(gen_new.__qualname__, "g")
self.assertEqual(gen_new.__name__, "g")
Expand Down
11 changes: 10 additions & 1 deletion Stackless/unittests/test_pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from stackless import schedule, tasklet, stackless

from support import test_main # @UnusedImport
from support import StacklessTestCase, StacklessPickleTestCase
from support import StacklessTestCase, StacklessPickleTestCase, get_reduce_frame


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


class TestFramePickling(StacklessTestCase):
def test_get_set_reduce_frame(self):
# test setting / getting the reduce frame function
rf = get_reduce_frame()
self.assertTrue(callable(rf))
stackless._wrap.set_reduce_frame(None)
self.assertIsNone(get_reduce_frame())
stackless._wrap.set_reduce_frame(rf)
self.assertIs(get_reduce_frame(), rf)

def testLocalplus(self):
result = []
Expand Down Expand Up @@ -607,6 +615,7 @@ def d():
p = self.dumps(tb)
tb2 = self.loads(p)
# basics
innerframes_orig = inspect.getinnerframes(tb)
self.assertIs(type(tb), type(tb2))
self.assertIsNot(tb, tb2)
innerframes = inspect.getinnerframes(tb2)
Expand Down
3 changes: 3 additions & 0 deletions Stackless/unittests/test_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ def to_current_thread(self, task):
frameList[i] = newFrame
# rebind the task
task = reducedTask[0](*reducedTask[1])
for i in range(len(reducedTask[2][3])):
if not isinstance(reducedTask[2][3][i], stackless.cframe):
reducedTask[2][3][i] = reducedTask[2][3][i].frame
task.__setstate__(reducedTask[2])
return task

Expand Down
7 changes: 4 additions & 3 deletions Stackless/unittests/test_tstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from stackless import *

from support import test_main # @UnusedImport
from support import StacklessTestCase
from support import StacklessTestCase, get_reduce_frame

# import os
# def debug():
Expand Down Expand Up @@ -332,9 +332,10 @@ def testReduceOfTracingState(self):
return

# test if the tracing cframe is present / not present
self.assertListEqual(reduced_tasklet1[2][3], [frame])
reduce_frame = get_reduce_frame()
self.assertListEqual(reduced_tasklet1[2][3], [reduce_frame(frame)])
self.assertEquals(len(reduced_tasklet2[2][3]), 2)
self.assertIs(reduced_tasklet2[2][3][0], frame)
self.assertIs(reduced_tasklet2[2][3][0], reduce_frame(frame))
self.assertIsInstance(reduced_tasklet2[2][3][1], stackless.cframe)

cf = reduced_tasklet2[2][3][1]
Expand Down

0 comments on commit d141fdd

Please sign in to comment.