Skip to content

Commit 9264f0d

Browse files
author
Anselm Kruis
authored
Stackless issue python#196: pickling of coroutine_wrapper objects
Stackless can now pickle coroutine_wrapper objects.
1 parent 71b1e9c commit 9264f0d

File tree

6 files changed

+120
-1
lines changed

6 files changed

+120
-1
lines changed

Objects/genobject.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,6 +2158,55 @@ async_gen_athrow_new(PyAsyncGenObject *gen, PyObject *args)
21582158

21592159

21602160
#ifdef STACKLESS
2161+
PyObject *
2162+
slp_coro_wrapper_reduce(PyObject *o, PyTypeObject * wrapper_type)
2163+
{
2164+
PyCoroWrapper *cw;
2165+
PyObject *tup;
2166+
2167+
assert(o != NULL);
2168+
assert(Py_TYPE(o) == &_PyCoroWrapper_Type);
2169+
assert(wrapper_type != NULL);
2170+
2171+
cw = (PyCoroWrapper *)o;
2172+
tup = Py_BuildValue("(O()(O))",
2173+
wrapper_type,
2174+
cw->cw_coroutine);
2175+
return tup;
2176+
}
2177+
2178+
PyObject *
2179+
slp_coro_wrapper_new(PyCoroObject *co)
2180+
{
2181+
return coro_await(co);
2182+
}
2183+
2184+
PyObject *
2185+
slp_coro_wrapper_setstate(PyObject *self, PyObject *args)
2186+
{
2187+
PyCoroWrapper *cw;
2188+
PyObject *co;
2189+
2190+
assert(self != NULL);
2191+
cw = (PyCoroWrapper *)self;
2192+
2193+
assert(args != NULL);
2194+
if (!PyArg_ParseTuple(args, "O!:coro_wrapper_setstate",
2195+
&PyCoro_Type,
2196+
&co)) {
2197+
return NULL;
2198+
}
2199+
2200+
assert(PyCoro_CheckExact(co));
2201+
Py_INCREF(co);
2202+
Py_SETREF(cw->cw_coroutine, (PyCoroObject*)co);
2203+
2204+
Py_TYPE(self) = Py_TYPE(self)->tp_base;
2205+
Py_INCREF(self);
2206+
return self;
2207+
}
2208+
2209+
21612210
int
21622211
slp_async_gen_init_hooks(PyAsyncGenObject *o)
21632212
{

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://github.com/stackless-dev/stackless/issues/196
13+
Stackless can now pickle coroutine_wrapper objects.
14+
1215
- https://github.com/stackless-dev/stackless/issues/190
1316
Silently ignore attempts to close a running generator, coroutine or
1417
asynchronous generator. This avoids spurious error messages, if such an

Stackless/core/stackless_impl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,9 @@ int slp_resurrect_and_kill(PyObject *self,
741741
void(*killer)(PyObject *));
742742

743743
/* stackless pickling support */
744+
PyObject * slp_coro_wrapper_reduce(PyObject *o, PyTypeObject * wrapper_type);
745+
PyObject * slp_coro_wrapper_new(PyCoroObject *gen);
746+
PyObject * slp_coro_wrapper_setstate(PyObject *self, PyObject *args);
744747
int slp_async_gen_init_hooks(PyAsyncGenObject *o);
745748
PyObject * slp_async_gen_asend_reduce(PyObject *o, PyTypeObject * wrapper_type);
746749
PyObject * slp_async_gen_asend_new(PyAsyncGenObject *gen);

Stackless/pickling/prickelpit.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,12 +1520,14 @@ static int init_dictitemsviewtype(PyObject * mod)
15201520

15211521
static PyTypeObject wrap_PyGen_Type;
15221522
static PyTypeObject wrap_PyCoro_Type;
1523+
static PyTypeObject wrap__PyCoroWrapper_Type;
15231524
static PyTypeObject wrap_PyAsyncGen_Type;
15241525
static PyTypeObject wrap__PyAsyncGenASend_Type;
15251526
static PyTypeObject wrap__PyAsyncGenAThrow_Type;
15261527

15271528
/* Used to initialize a generator created by gen_new. */
15281529
static PyFrameObject *gen_exhausted_frame = NULL;
1530+
static PyCoroObject *gen_exhausted_coro = NULL;
15291531
static PyAsyncGenObject *gen_exhausted_asyncgen = NULL;
15301532

15311533
/* A helper for pickling the _PyErr_StackItem* members of generator-like and tasklet
@@ -1850,8 +1852,11 @@ static int init_generatortype(PyObject * mod)
18501852
/* A reference to frame is stolen by PyGen_New. */
18511853
Py_INCREF(gen_exhausted_frame);
18521854
gen_exhausted_asyncgen = (PyAsyncGenObject *)PyAsyncGen_New(gen_exhausted_frame, NULL, NULL);
1855+
/* A reference to frame is stolen by PyCoro_New. */
1856+
Py_INCREF(gen_exhausted_frame);
1857+
gen_exhausted_coro = (PyCoroObject *)PyCoro_New(gen_exhausted_frame, NULL, NULL);
18531858
}
1854-
if (gen_exhausted_asyncgen == NULL) {
1859+
if (gen_exhausted_asyncgen == NULL || gen_exhausted_coro == NULL) {
18551860
res = -1;
18561861
}
18571862

@@ -2194,6 +2199,41 @@ init_async_generator_athrow_type(PyObject * mod)
21942199
#undef initchain
21952200
#define initchain init_async_generator_athrow_type
21962201

2202+
static PyObject *
2203+
coro_wrapper_reduce(PyObject *o)
2204+
{
2205+
return slp_coro_wrapper_reduce(o, &wrap__PyCoroWrapper_Type);
2206+
}
2207+
2208+
static PyObject *
2209+
coro_wrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
2210+
{
2211+
PyObject * o;
2212+
if (is_wrong_type(type)) return NULL;
2213+
assert(type == &wrap__PyCoroWrapper_Type);
2214+
assert(gen_exhausted_coro != NULL);
2215+
o = slp_coro_wrapper_new(gen_exhausted_coro);
2216+
if (o != NULL)
2217+
Py_TYPE(o) = type;
2218+
return o;
2219+
}
2220+
2221+
static PyObject *
2222+
coro_wrapper_setstate(PyObject *self, PyObject *args)
2223+
{
2224+
if (is_wrong_type(Py_TYPE(self))) return NULL;
2225+
return slp_coro_wrapper_setstate(self, args);
2226+
}
2227+
2228+
MAKE_WRAPPERTYPE(_PyCoroWrapper_Type, coro_wrapper, "coroutine_wrapper", coro_wrapper_reduce,
2229+
coro_wrapper_new, coro_wrapper_setstate)
2230+
2231+
static int init_coroutine_wrapper_type(PyObject * mod)
2232+
{
2233+
return init_type(&wrap__PyCoroWrapper_Type, initchain, mod);
2234+
}
2235+
#undef initchain
2236+
#define initchain init_coroutine_wrapper_type
21972237

21982238
/******************************************************
21992239

Stackless/unittests/test_pickle.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,21 @@ def test_pickling_origin(self):
707707
self.assertTupleEqual(orig_origin, origin)
708708
self.assertRaises(StopIteration, c.send, None)
709709

710+
def test_coro_wrapper_pickling(self):
711+
c = self.c()
712+
cw = c.__await__()
713+
cw_type = type(cw)
714+
self.assertEqual(cw_type.__name__, "coroutine_wrapper")
715+
p = self.dumps(cw)
716+
self.assertEqual(c.send(None), 1)
717+
self.assertRaises(StopIteration, c.send, None)
718+
719+
cw = self.loads(p)
720+
self.assertIsInstance(cw, cw_type)
721+
self.assertEqual(cw.send(None), 1)
722+
self.assertRaises(StopIteration, cw.send, None)
723+
self.assertRaisesRegex(RuntimeError, "cannot reuse already awaited coroutine", cw.send, None)
724+
710725

711726
async_gen_finalize_counter = 0
712727

@@ -1011,6 +1026,14 @@ async def c():
10111026
self.assertRaises(StopIteration, obj.send, None)
10121027
self.assertRaises(StopIteration, c.send, None)
10131028

1029+
def test_coro_wrapper(self):
1030+
async def c():
1031+
return 1
1032+
obj = c().__await__()
1033+
cw = self._test(obj)
1034+
self.assertRaises(StopIteration, cw.send, None)
1035+
self.assertRaisesRegex(RuntimeError, "cannot reuse already awaited coroutine", obj.send, None)
1036+
10141037
def test_async_generator(self):
10151038
async def ag():
10161039
yield 100

Tools/c-globals/ignored-globals.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ Py_UnwindToken
500500
# justification: constant singleton
501501
gen_exhausted_frame
502502
gen_exhausted_asyncgen
503+
gen_exhausted_coro
503504

504505
# justification: constant
505506
_new_methoddef

0 commit comments

Comments
 (0)