Generators: add throw() support
authorVitja Makarov <vitja.makarov@gmail.com>
Fri, 10 Dec 2010 20:55:45 +0000 (23:55 +0300)
committerVitja Makarov <vitja.makarov@gmail.com>
Fri, 10 Dec 2010 20:55:45 +0000 (23:55 +0300)
Cython/Compiler/ExprNodes.py
Cython/Compiler/ParseTreeTransforms.py
tests/run/generators.pyx

index c7e0adf8afbe9e468d94ac17b0a0b22906929b42..7f61bf707c7ba8726ae5b31aa1d4f46ef8b0a8a3 100755 (executable)
@@ -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 <class>, <instance> */
+            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')
index f8728b4f0754fe121fe2b7cad5f004a28384883f..0c137df0c493d78cafdab1a5c0b8d59f829acf4d 100644 (file)
@@ -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'
index e091b63d2fbbba367aee44299109650a2b1ba750..dca2b86a22c004f34350f681dd5a6aafaad73d5f 100644 (file)
@@ -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()