Index: Objects/genobject.c |
=================================================================== |
--- a/Objects/genobject.c |
+++ b/Objects/genobject.c |
@@ -7,6 +7,8 @@ |
#include "structmember.h" |
#include "opcode.h" |
+static void gen_undelegate(PyGenObject *); |
+ |
static int |
gen_traverse(PyGenObject *gen, visitproc visit, void *arg) |
{ |
@@ -92,12 +94,15 @@ gen_send_ex(PyGenObject *gen, PyObject * |
/* If the generator just returned (as opposed to yielding), signal |
* that the generator is exhausted. */ |
- if (result == Py_None && f->f_stacktop == NULL) { |
+ if (result && f->f_stacktop == NULL) { |
+ if (result == Py_None) |
+ PyErr_SetNone(PyExc_StopIteration); |
+ else { |
+ PyObject *e = PyStopIteration_New(result); |
+ PyErr_SetObject(PyExc_StopIteration, e); |
+ } |
Py_DECREF(result); |
result = NULL; |
- /* Set exception if not called by gen_iternext() */ |
- if (arg) |
- PyErr_SetNone(PyExc_StopIteration); |
} |
if (!result || f->f_stacktop == NULL) { |
@@ -116,7 +121,35 @@ static PyObject * |
static PyObject * |
gen_send(PyGenObject *gen, PyObject *arg) |
{ |
- return gen_send_ex(gen, arg, 0); |
+ int exc = 0; |
+ PyObject *ret; |
+ PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; |
+ Py_INCREF(arg); |
+ if (yf) { |
+ Py_INCREF(yf); |
+ if (PyGen_CheckExact(yf)) |
+ ret = gen_send((PyGenObject *)yf, arg); |
+ else { |
+ if (arg == Py_None) |
+ ret = PyObject_CallMethod(yf, "next", ""); |
+ else |
+ ret = PyObject_CallMethod(yf, "send", "O", arg); |
+ } |
+ if (ret) { |
+ Py_DECREF(yf); |
+ goto done; |
+ } |
+ gen_undelegate(gen); |
+ Py_DECREF(arg); |
+ arg = NULL; |
+ if (PyGen_FetchStopIterationValue(&arg) < 0) |
+ exc = 1; |
+ Py_DECREF(yf); |
+ } |
+ ret = gen_send_ex(gen, arg, exc); |
+done: |
+ Py_XDECREF(arg); |
+ return ret; |
} |
PyDoc_STRVAR(close_doc, |
@@ -126,7 +159,24 @@ gen_close(PyGenObject *gen, PyObject *ar |
gen_close(PyGenObject *gen, PyObject *args) |
{ |
PyObject *retval; |
- PyErr_SetNone(PyExc_GeneratorExit); |
+ PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; |
+ if (yf) { |
+ if (PyGen_CheckExact(yf)) |
+ gen_close((PyGenObject *)yf, args); |
+ else { |
+ PyObject *meth = PyObject_GetAttrString(yf, "close"); |
+ if (meth) { |
+ retval = PyObject_CallFunction(meth, ""); |
+ Py_DECREF(meth); |
+ Py_XDECREF(retval); |
+ } |
+ else |
+ PyErr_Clear(); |
+ } |
+ gen_undelegate(gen); |
+ } |
+ if (!PyErr_Occurred()) |
+ PyErr_SetNone(PyExc_GeneratorExit); |
retval = gen_send_ex(gen, Py_None, 1); |
if (retval) { |
Py_DECREF(retval); |
@@ -217,7 +267,32 @@ gen_throw(PyGenObject *gen, PyObject *ar |
PyObject *typ; |
PyObject *tb = NULL; |
PyObject *val = NULL; |
- |
+ PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; |
+ if (yf) { |
+ PyObject *ret; |
+ Py_INCREF(yf); |
+ if (PyGen_CheckExact(yf)) |
+ ret = gen_throw((PyGenObject *)yf, args); |
+ else { |
+ PyObject *meth = PyObject_GetAttrString(yf, "throw"); |
+ if (!meth) { |
+ PyErr_Clear(); |
+ Py_DECREF(yf); |
+ gen_undelegate(gen); |
+ goto throw_here; |
+ } |
+ ret = PyObject_CallObject(meth, args); |
+ Py_DECREF(meth); |
+ } |
+ Py_DECREF(yf); |
+ if (!ret) { |
+ gen_undelegate(gen); |
+ ret = gen_send_ex(gen, Py_None, 1); |
+ } |
+ return ret; |
+ } |
+ |
+throw_here: |
if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) |
return NULL; |
@@ -277,9 +352,72 @@ static PyObject * |
static PyObject * |
gen_iternext(PyGenObject *gen) |
{ |
- return gen_send_ex(gen, NULL, 0); |
-} |
- |
+ PyObject *val = NULL; |
+ PyObject *ret; |
+ int exc = 0; |
+ PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; |
+ if (yf) { |
+ Py_INCREF(yf); |
+ /* ceval.c ensures that yf is an iterator */ |
+ ret = yf->ob_type->tp_iternext(yf); |
+ if (ret) |
+ return ret; |
+ gen_undelegate(gen); |
+ if (PyGen_FetchStopIterationValue(&val) < 0) |
+ exc = 1; |
+ Py_DECREF(yf); |
+ } |
+ ret = gen_send_ex(gen, val, exc); |
+ Py_XDECREF(val); |
+ return ret; |
+} |
+ |
+/* |
+ * In certain recursive situations, a generator may lose its frame |
+ * before we get a chance to clear f_yieldfrom, so we use this |
+ * helper function. |
+ */ |
+ |
+static void |
+gen_undelegate(PyGenObject *gen) { |
+ if (gen->gi_frame) { |
+ Py_XDECREF(gen->gi_frame->f_yieldfrom); |
+ gen->gi_frame->f_yieldfrom = NULL; |
+ } |
+} |
+ |
+/* |
+ * If StopIteration exception is set, fetches its 'value' |
+ * attribute if any, otherwise sets pvalue to None. |
+ * |
+ * Returns 0 if no exception or StopIteration is set. |
+ * If any other exception is set, returns -1 and leaves |
+ * pvalue unchanged. |
+ */ |
+int PyGen_FetchStopIterationValue(PyObject **pvalue) |
+{ |
+ PyObject *et, *ev, *tb; |
+ PyObject *value = NULL; |
+ |
+ if (PyErr_ExceptionMatches(PyExc_StopIteration)) { |
+ PyErr_Fetch(&et, &ev, &tb); |
+ Py_XDECREF(et); Py_XDECREF(tb); |
+ if (ev) { |
+ value = PyObject_GetAttrString(ev, "value"); |
+ if (!value) |
+ PyErr_Clear(); |
+ Py_DECREF(ev); |
+ } |
+ } |
+ else if (PyErr_Occurred()) |
+ return -1; |
+ if (!value) { |
+ value = Py_None; |
+ Py_INCREF(value); |
+ } |
+ *pvalue = value; |
+ return 0; |
+} |
static PyObject * |
gen_repr(PyGenObject *gen) |