From 12273ef1b7dfc7e59996f4d4f09fc7c75283e004 Mon Sep 17 00:00:00 2001 From: Stefan Behnel <stefan_ml@behnel.de> Date: Mon, 25 May 2015 21:53:56 +0200 Subject: [PATCH] rewrite __Pyx_ReturnWithStopIteration() to fix returning tuples and also fix it on the other side in __Pyx_PyGen_FetchStopIterationValue() --- Cython/Utility/Coroutine.c | 65 ++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c index f029cda46..44f216943 100644 --- a/Cython/Utility/Coroutine.c +++ b/Cython/Utility/Coroutine.c @@ -336,6 +336,23 @@ static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue) { if (!ev) { Py_INCREF(Py_None); ev = Py_None; + } else if (PyTuple_Check(ev)) { + // however, if it's a tuple, it is interpreted as separate constructor arguments (surprise!) + if (PyTuple_GET_SIZE(ev) >= 1) { + PyObject *value; +#if CYTHON_COMPILING_IN_CPYTHON + value = PySequence_ITEM(ev, 0); +#else + value = PyTuple_GET_ITEM(ev, 0); + Py_INCREF(value); +#endif + Py_DECREF(ev); + ev = value; + } else { + Py_INCREF(Py_None); + Py_DECREF(ev); + ev = Py_None; + } } Py_XDECREF(tb); Py_DECREF(et); @@ -1153,31 +1170,51 @@ static int __pyx_Generator_init(void) { /////////////// ReturnWithStopIteration.proto /////////////// -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030500B1 -// CPython 3.3 <= x < 3.5b1 crash in yield-from when the StopIteration is not instantiated #define __Pyx_ReturnWithStopIteration(value) \ if (value == Py_None) PyErr_SetNone(PyExc_StopIteration); else __Pyx__ReturnWithStopIteration(value) static void __Pyx__ReturnWithStopIteration(PyObject* value); /*proto*/ -#else -#define __Pyx_ReturnWithStopIteration(value) PyErr_SetObject(PyExc_StopIteration, value) -#endif /////////////// ReturnWithStopIteration /////////////// +//@requires: Exceptions.c::PyErrFetchRestore +//@substitute: naming + +// 1) Instantiating an exception just to pass back a value is costly. +// 2) CPython 3.3 <= x < 3.5b1 crash in yield-from when the StopIteration is not instantiated. +// 3) Passing a tuple as value into PyErr_SetObject() passes its items on as arguments. +// 4) If there is currently an exception being handled, we need to chain it. -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030500B1 static void __Pyx__ReturnWithStopIteration(PyObject* value) { PyObject *exc, *args; - args = PyTuple_New(1); - if (!args) return; - Py_INCREF(value); - PyTuple_SET_ITEM(args, 0, value); +#if CYTHON_COMPILING_IN_CPYTHON + if ((PY_VERSION_HEX >= 0x03030000 && PY_VERSION_HEX < 0x030500B1) || PyTuple_Check(value)) { + args = PyTuple_New(1); + if (unlikely(!args)) return; + Py_INCREF(value); + PyTuple_SET_ITEM(args, 0, value); + exc = PyType_Type.tp_call(PyExc_StopIteration, args, NULL); + Py_DECREF(args); + if (!exc) return; + } else { + // it's safe to avoid instantiating the exception + Py_INCREF(value); + exc = value; + } + if (!PyThreadState_GET()->exc_type) { + // no chaining needed => avoid the overhead in PyErr_SetObject() + Py_INCREF(PyExc_StopIteration); + __Pyx_ErrRestore(PyExc_StopIteration, exc, NULL); + return; + } +#else + args = PyTuple_Pack(1, value); + if (unlikely(!args)) return; exc = PyObject_Call(PyExc_StopIteration, args, NULL); Py_DECREF(args); - if (!exc) return; - Py_INCREF(PyExc_StopIteration); - PyErr_Restore(PyExc_StopIteration, exc, NULL); -} + if (unlikely(!exc)) return; #endif + PyErr_SetObject(PyExc_StopIteration, exc); + Py_DECREF(exc); +} //////////////////// PatchModuleWithCoroutine.proto //////////////////// -- 2.30.9