mirror of https://github.com/python/cpython
bpo-41756: Refactor gen_send_ex(). (GH-22330)
This commit is contained in:
parent
0063ff4e58
commit
6c33385e3a
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue