Add close() support
authorVitja Makarov <vitja.makarov@gmail.com>
Fri, 10 Dec 2010 18:43:33 +0000 (21:43 +0300)
committerVitja Makarov <vitja.makarov@gmail.com>
Fri, 10 Dec 2010 18:43:33 +0000 (21:43 +0300)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/ParseTreeTransforms.py
tests/run/generators.pyx

index 582e34d22802c485c64f8f9f5b47a01209fc4fd2..fd1e66a5c1b4645e4ea2e0f439da32a698950928 100755 (executable)
@@ -5019,7 +5019,8 @@ class YieldExprNode(ExprNode):
             code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
             if type.is_pyobject:
                 code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
-        code.putln('%s = __pyx_send_value;' % self.result())
+        code.putln('%s = __pyx_send_value; %s' %
+                   (self.result(), code.error_goto_if_null(self.result(), self.pos)))
         code.put_incref(self.result(), py_object_type)
 
 class StopIterationNode(Node):
@@ -8306,6 +8307,7 @@ generator_utility_code = UtilityCode(
 proto="""
 static PyObject *__CyGenerator_Next(PyObject *self);
 static PyObject *__CyGenerator_Send(PyObject *self, PyObject *value);
+static PyObject *__CyGenerator_Close(PyObject *self);
 typedef PyObject *(*__cygenerator_body_t)(PyObject *, PyObject *, int);
 """,
 impl="""
@@ -8355,4 +8357,26 @@ static PyObject *__CyGenerator_Send(PyObject *self, PyObject *value)
 {
     return __CyGenerator_SendEx((struct __CyGenerator *) self, value, 0);
 }
+
+static PyObject *__CyGenerator_Close(PyObject *self)
+{
+    struct __CyGenerator *generator = (struct __CyGenerator *) self;
+    PyObject *retval;
+    PyErr_SetNone(PyExc_GeneratorExit);
+    retval = __CyGenerator_SendEx(generator, NULL, 0);
+    if (retval) {
+        Py_DECREF(retval);
+        PyErr_SetString(PyExc_RuntimeError,
+                        "generator ignored GeneratorExit");
+        return NULL;
+    }
+    if (PyErr_ExceptionMatches(PyExc_StopIteration)
+        || PyErr_ExceptionMatches(PyExc_GeneratorExit))
+    {
+        PyErr_Clear();          /* ignore these errors */
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    return NULL;
+}
 """, proto_block='utility_code_proto_before_types')
index 2abe6126604d05f5a220cb4691af75e4fbb2be3b..48eb131bf76fdaea33c301eeb3f4db40e41b2deb 100644 (file)
@@ -1362,6 +1362,8 @@ class FuncDefNode(StatNode, BlockNode):
             first_run_label = code.new_label('first_run')
             code.use_label(first_run_label)
             code.put_label(first_run_label)
+            code.putln('%s' %
+                       (code.error_goto_if_null('__pyx_send_value', self.pos)))
         if not self.is_generator:
             self.generate_argument_parsing_code(env, code)
         # If an argument is assigned to in the body, we must 
index dd746a0fb9c7396342b2820011cd026ff59fa20e..f8728b4f0754fe121fe2b7cad5f004a28384883f 100644 (file)
@@ -1475,9 +1475,9 @@ class CreateClosureClasses(CythonTransform):
         e.func_cname = '__CyGenerator_Send'
         e.signature = TypeSlots.binaryfunc
 
-        #e = klass.declare_pyfunction('close', pos)
-        #e.func_cname = '__CyGenerator_Close'
-        #e.signature = TypeSlots.unaryfunc
+        e = klass.declare_pyfunction('close', pos)
+        e.func_cname = '__CyGenerator_Close'
+        e.signature = TypeSlots.unaryfunc
 
         #e = klass.declare_pyfunction('throw', pos)
         #e.func_cname = '__CyGenerator_Throw'
index b6176f007b45f9114c1877ab84c9376a28dc6380..e091b63d2fbbba367aee44299109650a2b1ba750 100644 (file)
@@ -9,9 +9,14 @@ def very_simple():
     >>> next(x)
     Traceback (most recent call last):
     StopIteration
+    >>> x = very_simple()
+    >>> x.send(1)
+    Traceback (most recent call last):
+    TypeError: can't send non-None value to a just-started generator
     """
     yield 1
 
+
 def simple():
     """
     >>> x = simple()
@@ -80,3 +85,42 @@ def with_outer_raising(*args):
             yield i
         raise StopIteration
     return generator
+
+def test_close():
+    """
+    >>> x = test_close()
+    >>> x.close()
+    >>> x = test_close()
+    >>> next(x)
+    >>> x.close()
+    >>> x.next()
+    Traceback (most recent call last):
+    StopIteration
+    """
+    while True:
+        yield
+
+def test_ignore_close():
+    """
+    >>> x = test_ignore_close()
+    >>> x.close()
+    >>> x = test_ignore_close()
+    >>> next(x)
+    >>> x.close()
+    Traceback (most recent call last):
+    RuntimeError: generator ignored GeneratorExit
+    """
+    try:
+        yield
+    except GeneratorExit:
+        yield
+
+class Foo(object):
+    """
+    >>> obj = Foo()
+    >>> list(obj.simple(1, 2, 3))
+    [1, 2, 3]
+    """
+    def simple(self, *args):
+        for i in args:
+            yield i