From f94f26a073ee5c9987ccab6acb87ad453c6ec625 Mon Sep 17 00:00:00 2001 From: da-woods Date: Mon, 27 Sep 2021 09:58:20 +0100 Subject: Make __Pyx_CoroutineAwaitType non-pickleable (GH-4381) This is explicitly tested for: https://github.com/cython/cython/blob/aea4e6b84b38223c540266f8c57093ee2039f284/tests/run/test_coroutines_pep492.pyx#L2400 It turns out some earlier versions of Python assume that C-API classes without a dict or slot are pickleable by the class name. Currently it isn't pickleable because the class name lookup is failing but this change makes it more robust. See https://github.com/cython/cython/pull/4376 --- Cython/Utility/Coroutine.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c index 1332c9824..e530a078d 100644 --- a/Cython/Utility/Coroutine.c +++ b/Cython/Utility/Coroutine.c @@ -1501,6 +1501,22 @@ static PyObject *__Pyx_CoroutineAwait_no_new(PyTypeObject *type, PyObject *args, } #endif +// In earlier versions of Python an object with no __dict__ and not __slots__ is assumed +// to be pickleable by default. Coroutine-wrappers have significant state so shouldn't be. +// Therefore provide a default implementation. +// Something similar applies to heaptypes (i.e. with type_specs) with protocols 0 and 1 +// even in more recent versions. +// We are applying this to all Python versions (hence the commented out version guard) +// to make the behaviour explicit. +// #if PY_VERSION_HEX < 0x03060000 || CYTHON_USE_TYPE_SPECS +static PyObject *__Pyx_CoroutineAwait_reduce_ex(__pyx_CoroutineAwaitObject *self, PyObject *arg) { + CYTHON_UNUSED_VAR(arg); + PyErr_Format(PyExc_TypeError, "cannot pickle '%.200s' object", + Py_TYPE(self)->tp_name); + return NULL; +} +// #endif + static PyMethodDef __pyx_CoroutineAwait_methods[] = { {"send", (PyCFunction) __Pyx_CoroutineAwait_Send, METH_O, (char*) PyDoc_STR("send(arg) -> send 'arg' into coroutine,\nreturn next yielded value or raise StopIteration.")}, @@ -1508,6 +1524,11 @@ static PyMethodDef __pyx_CoroutineAwait_methods[] = { (char*) PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in coroutine,\nreturn next yielded value or raise StopIteration.")}, {"close", (PyCFunction) __Pyx_CoroutineAwait_Close, METH_NOARGS, (char*) PyDoc_STR("close() -> raise GeneratorExit inside coroutine.")}, +// only needed with type-specs or version<3.6, but included in all versions for clarity +// #if PY_VERSION_HEX < 0x03060000 || CYTHON_USE_TYPE_SPECS + {"__reduce_ex__", (PyCFunction) __Pyx_CoroutineAwait_reduce_ex, METH_O, 0}, + {"__reduce__", (PyCFunction) __Pyx_CoroutineAwait_reduce_ex, METH_NOARGS, 0}, +// #endif {0, 0, 0, 0} }; -- cgit v1.2.1