Py_RETURN_NONE;
}
+static inline int
+gen_status_from_result(PyObject **result)
+{
+ if (*result != NULL) {
+ return PYGEN_NEXT;
+ }
+ if (_PyGen_FetchStopIterationValue(result) == 0) {
+ return PYGEN_RETURN;
+ }
+
+ assert(PyErr_Occurred());
+ return PYGEN_ERROR;
+}
+
static PyObject *
task_step_impl(TaskObj *task, PyObject *exc)
{
return NULL;
}
+ int gen_status = PYGEN_ERROR;
if (exc == NULL) {
if (PyGen_CheckExact(coro) || PyCoro_CheckExact(coro)) {
- result = _PyGen_Send((PyGenObject*)coro, Py_None);
+ gen_status = PyGen_Send((PyGenObject*)coro, Py_None, &result);
}
else {
result = _PyObject_CallMethodIdOneArg(coro, &PyId_send, Py_None);
+ gen_status = gen_status_from_result(&result);
}
}
else {
result = _PyObject_CallMethodIdOneArg(coro, &PyId_throw, exc);
+ gen_status = gen_status_from_result(&result);
if (clear_exc) {
/* We created 'exc' during this call */
Py_DECREF(exc);
}
}
- if (result == NULL) {
+ if (gen_status == PYGEN_RETURN || gen_status == PYGEN_ERROR) {
PyObject *et, *ev, *tb;
- if (_PyGen_FetchStopIterationValue(&o) == 0) {
+ if (result != NULL) {
/* The error is StopIteration and that means that
the underlying coroutine has resolved */
res = future_cancel((FutureObj*)task, task->task_cancel_msg);
}
else {
- res = future_set_result((FutureObj*)task, o);
+ res = future_set_result((FutureObj*)task, result);
}
- Py_DECREF(o);
+ Py_DECREF(result);
if (res == NULL) {
return NULL;
}
static PyObject *
-gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
+gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_return_value)
{
PyThreadState *tstate = _PyThreadState_GET();
PyFrameObject *f = gen->gi_frame;
PyErr_SetNone(PyExc_StopAsyncIteration);
}
else {
+ if (is_return_value != NULL) {
+ *is_return_value = 1;
+ Py_RETURN_NONE;
+ }
PyErr_SetNone(PyExc_StopIteration);
}
}
/* Delay exception instantiation if we can */
if (PyAsyncGen_CheckExact(gen)) {
PyErr_SetNone(PyExc_StopAsyncIteration);
+ Py_CLEAR(result);
}
else if (arg) {
- /* Set exception if not called by gen_iternext() */
- PyErr_SetNone(PyExc_StopIteration);
+ if (is_return_value != NULL) {
+ *is_return_value = 1;
+ }
+ else {
+ /* Set exception if not called by gen_iternext() */
+ PyErr_SetNone(PyExc_StopIteration);
+ Py_CLEAR(result);
+ }
+ }
+ else {
+ Py_CLEAR(result);
}
}
else {
/* Async generators cannot return anything but None */
assert(!PyAsyncGen_CheckExact(gen));
- _PyGen_SetStopIterationValue(result);
+ if (is_return_value != NULL) {
+ *is_return_value = 1;
+ }
+ else {
+ _PyGen_SetStopIterationValue(result);
+ Py_CLEAR(result);
+ }
}
- Py_CLEAR(result);
}
else if (!result && PyErr_ExceptionMatches(PyExc_StopIteration)) {
const char *msg = "generator raised StopIteration";
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
}
- if (!result || _PyFrameHasCompleted(f)) {
+ if ((is_return_value && *is_return_value) || !result || _PyFrameHasCompleted(f)) {
/* generator can't be rerun, so release the frame */
/* first clean reference cycle through stored exception traceback */
_PyErr_ClearExcState(&gen->gi_exc_state);
PyObject *
_PyGen_Send(PyGenObject *gen, PyObject *arg)
{
- return gen_send_ex(gen, arg, 0, 0);
+ return gen_send_ex(gen, arg, 0, 0, NULL);
+}
+
+PySendResult
+PyGen_Send(PyGenObject *gen, PyObject *arg, PyObject **result)
+{
+ assert(result != NULL);
+
+ int is_return_value = 0;
+ if ((*result = gen_send_ex(gen, arg, 0, 0, &is_return_value)) == NULL) {
+ return PYGEN_ERROR;
+ }
+ return is_return_value ? PYGEN_RETURN : PYGEN_NEXT;
}
PyDoc_STRVAR(close_doc,
}
if (err == 0)
PyErr_SetNone(PyExc_GeneratorExit);
- retval = gen_send_ex(gen, Py_None, 1, 1);
+ retval = gen_send_ex(gen, Py_None, 1, 1, NULL);
if (retval) {
const char *msg = "generator ignored GeneratorExit";
if (PyCoro_CheckExact(gen)) {
gen->gi_frame->f_state = state;
Py_DECREF(yf);
if (err < 0)
- return gen_send_ex(gen, Py_None, 1, 0);
+ return gen_send_ex(gen, Py_None, 1, 0, NULL);
goto throw_here;
}
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
assert(gen->gi_frame->f_lasti >= 0);
gen->gi_frame->f_lasti += sizeof(_Py_CODEUNIT);
if (_PyGen_FetchStopIterationValue(&val) == 0) {
- ret = gen_send_ex(gen, val, 0, 0);
+ ret = gen_send_ex(gen, val, 0, 0, NULL);
Py_DECREF(val);
} else {
- ret = gen_send_ex(gen, Py_None, 1, 0);
+ ret = gen_send_ex(gen, Py_None, 1, 0, NULL);
}
}
return ret;
}
PyErr_Restore(typ, val, tb);
- return gen_send_ex(gen, Py_None, 1, 0);
+ return gen_send_ex(gen, Py_None, 1, 0, NULL);
failed_throw:
/* Didn't use our arguments, so restore their original refcounts */
static PyObject *
gen_iternext(PyGenObject *gen)
{
- return gen_send_ex(gen, NULL, 0, 0);
+ return gen_send_ex(gen, NULL, 0, 0, NULL);
}
/*
static PyObject *
coro_wrapper_iternext(PyCoroWrapper *cw)
{
- return gen_send_ex((PyGenObject *)cw->cw_coroutine, NULL, 0, 0);
+ return gen_send_ex((PyGenObject *)cw->cw_coroutine, NULL, 0, 0, NULL);
}
static PyObject *
coro_wrapper_send(PyCoroWrapper *cw, PyObject *arg)
{
- return gen_send_ex((PyGenObject *)cw->cw_coroutine, arg, 0, 0);
+ return gen_send_ex((PyGenObject *)cw->cw_coroutine, arg, 0, 0, NULL);
}
static PyObject *
}
o->ags_gen->ag_running_async = 1;
- result = gen_send_ex((PyGenObject*)o->ags_gen, arg, 0, 0);
+ result = gen_send_ex((PyGenObject*)o->ags_gen, arg, 0, 0, NULL);
result = async_gen_unwrap_value(o->ags_gen, result);
if (result == NULL) {
assert(o->agt_state == AWAITABLE_STATE_ITER);
- retval = gen_send_ex((PyGenObject *)gen, arg, 0, 0);
+ retval = gen_send_ex((PyGenObject *)gen, arg, 0, 0, NULL);
if (o->agt_args) {
return async_gen_unwrap_value(o->agt_gen, retval);
} else {
case TARGET(YIELD_FROM): {
PyObject *v = POP();
PyObject *receiver = TOP();
- int err;
- if (PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver)) {
- retval = _PyGen_Send((PyGenObject *)receiver, v);
+ int is_gen_or_coro = PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver);
+ int gen_status;
+ if (tstate->c_tracefunc == NULL && is_gen_or_coro) {
+ gen_status = PyGen_Send((PyGenObject *)receiver, v, &retval);
} else {
- _Py_IDENTIFIER(send);
- if (v == Py_None)
- retval = Py_TYPE(receiver)->tp_iternext(receiver);
- else
- retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v);
+ if (is_gen_or_coro) {
+ retval = _PyGen_Send((PyGenObject *)receiver, v);
+ }
+ else {
+ _Py_IDENTIFIER(send);
+ if (v == Py_None) {
+ retval = Py_TYPE(receiver)->tp_iternext(receiver);
+ }
+ else {
+ retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v);
+ }
+ }
+
+ if (retval == NULL) {
+ if (tstate->c_tracefunc != NULL
+ && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
+ if (_PyGen_FetchStopIterationValue(&retval) == 0) {
+ gen_status = PYGEN_RETURN;
+ }
+ else {
+ gen_status = PYGEN_ERROR;
+ }
+ }
+ else {
+ gen_status = PYGEN_NEXT;
+ }
}
Py_DECREF(v);
- if (retval == NULL) {
- PyObject *val;
- if (tstate->c_tracefunc != NULL
- && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
- call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
- err = _PyGen_FetchStopIterationValue(&val);
- if (err < 0)
- goto error;
+ if (gen_status == PYGEN_ERROR) {
+ assert (retval == NULL);
+ goto error;
+ }
+ if (gen_status == PYGEN_RETURN) {
+ assert (retval != NULL);
+
Py_DECREF(receiver);
- SET_TOP(val);
+ SET_TOP(retval);
+ retval = NULL;
DISPATCH();
}
+ assert (gen_status == PYGEN_NEXT);
/* receiver remains on stack, retval is value to be yielded */
/* and repeat... */
assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));