From: Vitja Makarov Date: Fri, 10 Dec 2010 20:55:45 +0000 (+0300) Subject: Generators: add throw() support X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=b1d43e4619ffbbe95e7cd17c000dd617858fa0d2;p=cython.git Generators: add throw() support --- diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c7e0adf8..7f61bf70 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -8309,6 +8309,8 @@ proto=""" static PyObject *__CyGenerator_Next(PyObject *self); static PyObject *__CyGenerator_Send(PyObject *self, PyObject *value); static PyObject *__CyGenerator_Close(PyObject *self); +static PyObject *__CyGenerator_Throw(PyObject *gen, PyObject *args, CYTHON_UNUSED PyObject *kwds); + typedef PyObject *(*__cygenerator_body_t)(PyObject *, PyObject *); """, impl=""" @@ -8380,4 +8382,66 @@ static PyObject *__CyGenerator_Close(PyObject *self) } return NULL; } + +static PyObject *__CyGenerator_Throw(PyObject *self, PyObject *args, CYTHON_UNUSED PyObject *kwds) +{ + struct __CyGenerator *generator = (struct __CyGenerator *) self; + PyObject *typ; + PyObject *tb = NULL; + PyObject *val = NULL; + + if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) + return NULL; + + /* First, check the traceback argument, replacing None with + NULL. */ + if (tb == Py_None) + tb = NULL; + else if (tb != NULL && !PyTraceBack_Check(tb)) { + PyErr_SetString(PyExc_TypeError, + "throw() third argument must be a traceback object"); + return NULL; + } + + Py_INCREF(typ); + Py_XINCREF(val); + Py_XINCREF(tb); + + if (PyExceptionClass_Check(typ)) { + PyErr_NormalizeException(&typ, &val, &tb); + } + + else if (PyExceptionInstance_Check(typ)) { + /* Raising an instance. The value should be a dummy. */ + if (val && val != Py_None) { + PyErr_SetString(PyExc_TypeError, + "instance exception may not have a separate value"); + goto failed_throw; + } + else { + /* Normalize to raise , */ + Py_XDECREF(val); + val = typ; + typ = PyExceptionInstance_Class(typ); + Py_INCREF(typ); + } + } + else { + /* Not something you can raise. throw() fails. */ + PyErr_Format(PyExc_TypeError, + "exceptions must be classes, or instances, not %s", + typ->ob_type->tp_name); + goto failed_throw; + } + + PyErr_Restore(typ, val, tb); + return __CyGenerator_SendEx(generator, NULL); + +failed_throw: + /* Didn't use our arguments, so restore their original refcounts */ + Py_DECREF(typ); + Py_XDECREF(val); + Py_XDECREF(tb); + return NULL; +} """, proto_block='utility_code_proto_before_types') diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index f8728b4f..0c137df0 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -1479,8 +1479,9 @@ class CreateClosureClasses(CythonTransform): e.func_cname = '__CyGenerator_Close' e.signature = TypeSlots.unaryfunc - #e = klass.declare_pyfunction('throw', pos) - #e.func_cname = '__CyGenerator_Throw' + e = klass.declare_pyfunction('throw', pos) + e.func_cname = '__CyGenerator_Throw' + e.signature = TypeSlots.pyfunction_signature e = klass.declare_var('__iter__', PyrexTypes.py_object_type, pos, visibility='public') e.func_cname = 'PyObject_SelfIter' diff --git a/tests/run/generators.pyx b/tests/run/generators.pyx index e091b63d..dca2b86a 100644 --- a/tests/run/generators.pyx +++ b/tests/run/generators.pyx @@ -115,6 +115,33 @@ def test_ignore_close(): except GeneratorExit: yield +def check_throw(): + """ + >>> x = check_throw() + >>> x.throw(ValueError) + Traceback (most recent call last): + ValueError + >>> next(x) + Traceback (most recent call last): + StopIteration + >>> x = check_throw() + >>> next(x) + >>> x.throw(ValueError) + >>> next(x) + >>> x.throw(IndexError, "oops") + Traceback (most recent call last): + IndexError: oops + >>> next(x) + Traceback (most recent call last): + StopIteration + """ + while True: + try: + yield + except ValueError: + pass + + class Foo(object): """ >>> obj = Foo()