bpo-41756: Refactor gen_send_ex(). (GH-22330)

This commit is contained in:
Serhiy Storchaka 2020-09-22 08:08:54 +03:00 committed by GitHub
parent 0063ff4e58
commit 6c33385e3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 102 additions and 97 deletions

View File

@ -136,13 +136,15 @@ gen_dealloc(PyGenObject *gen)
PyObject_GC_Del(gen); PyObject_GC_Del(gen);
} }
static PyObject * static PySendResult
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_return_value) gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
int exc, int closing)
{ {
PyThreadState *tstate = _PyThreadState_GET(); PyThreadState *tstate = _PyThreadState_GET();
PyFrameObject *f = gen->gi_frame; PyFrameObject *f = gen->gi_frame;
PyObject *result; PyObject *result;
*presult = NULL;
if (f != NULL && _PyFrame_IsExecuting(f)) { if (f != NULL && _PyFrame_IsExecuting(f)) {
const char *msg = "generator already executing"; const char *msg = "generator already executing";
if (PyCoro_CheckExact(gen)) { if (PyCoro_CheckExact(gen)) {
@ -152,7 +154,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
msg = "async generator already executing"; msg = "async generator already executing";
} }
PyErr_SetString(PyExc_ValueError, msg); PyErr_SetString(PyExc_ValueError, msg);
return NULL; return PYGEN_ERROR;
} }
if (f == NULL || _PyFrameHasCompleted(f)) { if (f == NULL || _PyFrameHasCompleted(f)) {
if (PyCoro_CheckExact(gen) && !closing) { if (PyCoro_CheckExact(gen) && !closing) {
@ -165,19 +167,12 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
} }
else if (arg && !exc) { else if (arg && !exc) {
/* `gen` is an exhausted generator: /* `gen` is an exhausted generator:
only set exception if called from send(). */ only return value if called from send(). */
if (PyAsyncGen_CheckExact(gen)) { *presult = Py_None;
PyErr_SetNone(PyExc_StopAsyncIteration); Py_INCREF(*presult);
} return PYGEN_RETURN;
else {
if (is_return_value != NULL) {
*is_return_value = 1;
Py_RETURN_NONE;
}
PyErr_SetNone(PyExc_StopIteration);
}
} }
return NULL; return PYGEN_ERROR;
} }
assert(_PyFrame_IsRunnable(f)); assert(_PyFrame_IsRunnable(f));
@ -193,7 +188,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
"just-started async generator"; "just-started async generator";
} }
PyErr_SetString(PyExc_TypeError, msg); PyErr_SetString(PyExc_TypeError, msg);
return NULL; return PYGEN_ERROR;
} }
} else { } else {
/* Push arg onto the frame's value stack */ /* Push arg onto the frame's value stack */
@ -229,69 +224,77 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
/* If the generator just returned (as opposed to yielding), signal /* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */ * that the generator is exhausted. */
if (result && _PyFrameHasCompleted(f)) { if (result) {
if (result == Py_None) { if (!_PyFrameHasCompleted(f)) {
/* Delay exception instantiation if we can */ *presult = result;
if (PyAsyncGen_CheckExact(gen)) { return PYGEN_NEXT;
PyErr_SetNone(PyExc_StopAsyncIteration); }
Py_CLEAR(result); assert(result == Py_None || !PyAsyncGen_CheckExact(gen));
if (result == Py_None && !PyAsyncGen_CheckExact(gen) && !arg) {
/* Return NULL if called by gen_iternext() */
Py_CLEAR(result);
}
}
else {
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
const char *msg = "generator raised StopIteration";
if (PyCoro_CheckExact(gen)) {
msg = "coroutine raised StopIteration";
} }
else if (arg) { else if (PyAsyncGen_CheckExact(gen)) {
if (is_return_value != NULL) { msg = "async generator raised StopIteration";
*is_return_value = 1;
}
else {
/* Set exception if not called by gen_iternext() */
PyErr_SetNone(PyExc_StopIteration);
Py_CLEAR(result);
}
}
else {
Py_CLEAR(result);
} }
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
}
else if (PyAsyncGen_CheckExact(gen) &&
PyErr_ExceptionMatches(PyExc_StopAsyncIteration))
{
/* code in `gen` raised a StopAsyncIteration error:
raise a RuntimeError.
*/
const char *msg = "async generator raised StopAsyncIteration";
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
}
}
/* generator can't be rerun, so release the frame */
/* first clean reference cycle through stored exception traceback */
_PyErr_ClearExcState(&gen->gi_exc_state);
gen->gi_frame->f_gen = NULL;
gen->gi_frame = NULL;
Py_DECREF(f);
*presult = result;
return result ? PYGEN_RETURN : PYGEN_ERROR;
}
PySendResult
PyGen_Send(PyGenObject *gen, PyObject *arg, PyObject **result)
{
assert(PyGen_CheckExact(gen) || PyCoro_CheckExact(gen));
assert(result != NULL);
assert(arg != NULL);
return gen_send_ex2(gen, arg, result, 0, 0);
}
static PyObject *
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
{
PyObject *result;
if (gen_send_ex2(gen, arg, &result, exc, closing) == PYGEN_RETURN) {
if (PyAsyncGen_CheckExact(gen)) {
assert(result == Py_None);
PyErr_SetNone(PyExc_StopAsyncIteration);
}
else if (result == Py_None) {
PyErr_SetNone(PyExc_StopIteration);
} }
else { else {
/* Async generators cannot return anything but None */ _PyGen_SetStopIterationValue(result);
assert(!PyAsyncGen_CheckExact(gen));
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";
if (PyCoro_CheckExact(gen)) {
msg = "coroutine raised StopIteration";
}
else if (PyAsyncGen_CheckExact(gen)) {
msg = "async generator raised StopIteration";
}
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
}
else if (!result && PyAsyncGen_CheckExact(gen) &&
PyErr_ExceptionMatches(PyExc_StopAsyncIteration))
{
/* code in `gen` raised a StopAsyncIteration error:
raise a RuntimeError.
*/
const char *msg = "async generator raised StopAsyncIteration";
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
}
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);
gen->gi_frame->f_gen = NULL;
gen->gi_frame = NULL;
Py_DECREF(f);
}
return result; return result;
} }
@ -299,22 +302,16 @@ PyDoc_STRVAR(send_doc,
"send(arg) -> send 'arg' into generator,\n\ "send(arg) -> send 'arg' into generator,\n\
return next yielded value or raise StopIteration."); return next yielded value or raise StopIteration.");
static PyObject *
gen_send(PyGenObject *gen, PyObject *arg)
{
return gen_send_ex(gen, arg, 0, 0);
}
PyObject * PyObject *
_PyGen_Send(PyGenObject *gen, PyObject *arg) _PyGen_Send(PyGenObject *gen, PyObject *arg)
{ {
return gen_send_ex(gen, arg, 0, 0, NULL); return gen_send(gen, arg);
}
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, PyDoc_STRVAR(close_doc,
@ -396,7 +393,7 @@ gen_close(PyGenObject *gen, PyObject *args)
} }
if (err == 0) if (err == 0)
PyErr_SetNone(PyExc_GeneratorExit); PyErr_SetNone(PyExc_GeneratorExit);
retval = gen_send_ex(gen, Py_None, 1, 1, NULL); retval = gen_send_ex(gen, Py_None, 1, 1);
if (retval) { if (retval) {
const char *msg = "generator ignored GeneratorExit"; const char *msg = "generator ignored GeneratorExit";
if (PyCoro_CheckExact(gen)) { if (PyCoro_CheckExact(gen)) {
@ -444,7 +441,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
gen->gi_frame->f_state = state; gen->gi_frame->f_state = state;
Py_DECREF(yf); Py_DECREF(yf);
if (err < 0) if (err < 0)
return gen_send_ex(gen, Py_None, 1, 0, NULL); return gen_send_ex(gen, Py_None, 1, 0);
goto throw_here; goto throw_here;
} }
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
@ -496,10 +493,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
assert(gen->gi_frame->f_lasti >= 0); assert(gen->gi_frame->f_lasti >= 0);
gen->gi_frame->f_lasti += sizeof(_Py_CODEUNIT); gen->gi_frame->f_lasti += sizeof(_Py_CODEUNIT);
if (_PyGen_FetchStopIterationValue(&val) == 0) { if (_PyGen_FetchStopIterationValue(&val) == 0) {
ret = gen_send_ex(gen, val, 0, 0, NULL); ret = gen_send(gen, val);
Py_DECREF(val); Py_DECREF(val);
} else { } else {
ret = gen_send_ex(gen, Py_None, 1, 0, NULL); ret = gen_send_ex(gen, Py_None, 1, 0);
} }
} }
return ret; return ret;
@ -553,7 +550,7 @@ throw_here:
} }
PyErr_Restore(typ, val, tb); PyErr_Restore(typ, val, tb);
return gen_send_ex(gen, Py_None, 1, 0, NULL); return gen_send_ex(gen, Py_None, 1, 0);
failed_throw: failed_throw:
/* Didn't use our arguments, so restore their original refcounts */ /* Didn't use our arguments, so restore their original refcounts */
@ -582,7 +579,15 @@ gen_throw(PyGenObject *gen, PyObject *args)
static PyObject * static PyObject *
gen_iternext(PyGenObject *gen) gen_iternext(PyGenObject *gen)
{ {
return gen_send_ex(gen, NULL, 0, 0, NULL); PyObject *result;
assert(PyGen_CheckExact(gen) || PyCoro_CheckExact(gen));
if (gen_send_ex2(gen, NULL, &result, 0, 0) == PYGEN_RETURN) {
if (result != Py_None) {
_PyGen_SetStopIterationValue(result);
}
Py_CLEAR(result);
}
return result;
} }
/* /*
@ -767,7 +772,7 @@ static PyMemberDef gen_memberlist[] = {
}; };
static PyMethodDef gen_methods[] = { static PyMethodDef gen_methods[] = {
{"send",(PyCFunction)_PyGen_Send, METH_O, send_doc}, {"send",(PyCFunction)gen_send, METH_O, send_doc},
{"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc}, {"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc},
{"close",(PyCFunction)gen_close, METH_NOARGS, close_doc}, {"close",(PyCFunction)gen_close, METH_NOARGS, close_doc},
{NULL, NULL} /* Sentinel */ {NULL, NULL} /* Sentinel */
@ -1082,13 +1087,13 @@ coro_wrapper_dealloc(PyCoroWrapper *cw)
static PyObject * static PyObject *
coro_wrapper_iternext(PyCoroWrapper *cw) coro_wrapper_iternext(PyCoroWrapper *cw)
{ {
return gen_send_ex((PyGenObject *)cw->cw_coroutine, NULL, 0, 0, NULL); return gen_iternext((PyGenObject *)cw->cw_coroutine);
} }
static PyObject * static PyObject *
coro_wrapper_send(PyCoroWrapper *cw, PyObject *arg) coro_wrapper_send(PyCoroWrapper *cw, PyObject *arg)
{ {
return gen_send_ex((PyGenObject *)cw->cw_coroutine, arg, 0, 0, NULL); return gen_send((PyGenObject *)cw->cw_coroutine, arg);
} }
static PyObject * static PyObject *
@ -1601,7 +1606,7 @@ async_gen_asend_send(PyAsyncGenASend *o, PyObject *arg)
} }
o->ags_gen->ag_running_async = 1; o->ags_gen->ag_running_async = 1;
result = gen_send_ex((PyGenObject*)o->ags_gen, arg, 0, 0, NULL); result = gen_send((PyGenObject*)o->ags_gen, arg);
result = async_gen_unwrap_value(o->ags_gen, result); result = async_gen_unwrap_value(o->ags_gen, result);
if (result == NULL) { if (result == NULL) {
@ -1957,7 +1962,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
assert(o->agt_state == AWAITABLE_STATE_ITER); assert(o->agt_state == AWAITABLE_STATE_ITER);
retval = gen_send_ex((PyGenObject *)gen, arg, 0, 0, NULL); retval = gen_send((PyGenObject *)gen, arg);
if (o->agt_args) { if (o->agt_args) {
return async_gen_unwrap_value(o->agt_gen, retval); return async_gen_unwrap_value(o->agt_gen, retval);
} else { } else {