Skip to content

Commit 6c33385

Browse files
bpo-41756: Refactor gen_send_ex(). (GH-22330)
1 parent 0063ff4 commit 6c33385

File tree

1 file changed

+99
-94
lines changed

1 file changed

+99
-94
lines changed

Objects/genobject.c

Lines changed: 99 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,15 @@ gen_dealloc(PyGenObject *gen)
136136
PyObject_GC_Del(gen);
137137
}
138138

139-
static PyObject *
140-
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_return_value)
139+
static PySendResult
140+
gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
141+
int exc, int closing)
141142
{
142143
PyThreadState *tstate = _PyThreadState_GET();
143144
PyFrameObject *f = gen->gi_frame;
144145
PyObject *result;
145146

147+
*presult = NULL;
146148
if (f != NULL && _PyFrame_IsExecuting(f)) {
147149
const char *msg = "generator already executing";
148150
if (PyCoro_CheckExact(gen)) {
@@ -152,7 +154,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
152154
msg = "async generator already executing";
153155
}
154156
PyErr_SetString(PyExc_ValueError, msg);
155-
return NULL;
157+
return PYGEN_ERROR;
156158
}
157159
if (f == NULL || _PyFrameHasCompleted(f)) {
158160
if (PyCoro_CheckExact(gen) && !closing) {
@@ -165,19 +167,12 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
165167
}
166168
else if (arg && !exc) {
167169
/* `gen` is an exhausted generator:
168-
only set exception if called from send(). */
169-
if (PyAsyncGen_CheckExact(gen)) {
170-
PyErr_SetNone(PyExc_StopAsyncIteration);
171-
}
172-
else {
173-
if (is_return_value != NULL) {
174-
*is_return_value = 1;
175-
Py_RETURN_NONE;
176-
}
177-
PyErr_SetNone(PyExc_StopIteration);
178-
}
170+
only return value if called from send(). */
171+
*presult = Py_None;
172+
Py_INCREF(*presult);
173+
return PYGEN_RETURN;
179174
}
180-
return NULL;
175+
return PYGEN_ERROR;
181176
}
182177

183178
assert(_PyFrame_IsRunnable(f));
@@ -193,7 +188,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
193188
"just-started async generator";
194189
}
195190
PyErr_SetString(PyExc_TypeError, msg);
196-
return NULL;
191+
return PYGEN_ERROR;
197192
}
198193
} else {
199194
/* Push arg onto the frame's value stack */
@@ -229,92 +224,94 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing, int *is_retur
229224

230225
/* If the generator just returned (as opposed to yielding), signal
231226
* that the generator is exhausted. */
232-
if (result && _PyFrameHasCompleted(f)) {
233-
if (result == Py_None) {
234-
/* Delay exception instantiation if we can */
235-
if (PyAsyncGen_CheckExact(gen)) {
236-
PyErr_SetNone(PyExc_StopAsyncIteration);
237-
Py_CLEAR(result);
238-
}
239-
else if (arg) {
240-
if (is_return_value != NULL) {
241-
*is_return_value = 1;
242-
}
243-
else {
244-
/* Set exception if not called by gen_iternext() */
245-
PyErr_SetNone(PyExc_StopIteration);
246-
Py_CLEAR(result);
247-
}
248-
}
249-
else {
250-
Py_CLEAR(result);
251-
}
227+
if (result) {
228+
if (!_PyFrameHasCompleted(f)) {
229+
*presult = result;
230+
return PYGEN_NEXT;
252231
}
253-
else {
254-
/* Async generators cannot return anything but None */
255-
assert(!PyAsyncGen_CheckExact(gen));
256-
if (is_return_value != NULL) {
257-
*is_return_value = 1;
258-
}
259-
else {
260-
_PyGen_SetStopIterationValue(result);
261-
Py_CLEAR(result);
262-
}
232+
assert(result == Py_None || !PyAsyncGen_CheckExact(gen));
233+
if (result == Py_None && !PyAsyncGen_CheckExact(gen) && !arg) {
234+
/* Return NULL if called by gen_iternext() */
235+
Py_CLEAR(result);
263236
}
264237
}
265-
else if (!result && PyErr_ExceptionMatches(PyExc_StopIteration)) {
266-
const char *msg = "generator raised StopIteration";
267-
if (PyCoro_CheckExact(gen)) {
268-
msg = "coroutine raised StopIteration";
238+
else {
239+
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
240+
const char *msg = "generator raised StopIteration";
241+
if (PyCoro_CheckExact(gen)) {
242+
msg = "coroutine raised StopIteration";
243+
}
244+
else if (PyAsyncGen_CheckExact(gen)) {
245+
msg = "async generator raised StopIteration";
246+
}
247+
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
269248
}
270-
else if (PyAsyncGen_CheckExact(gen)) {
271-
msg = "async generator raised StopIteration";
249+
else if (PyAsyncGen_CheckExact(gen) &&
250+
PyErr_ExceptionMatches(PyExc_StopAsyncIteration))
251+
{
252+
/* code in `gen` raised a StopAsyncIteration error:
253+
raise a RuntimeError.
254+
*/
255+
const char *msg = "async generator raised StopAsyncIteration";
256+
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
272257
}
273-
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
274-
275-
}
276-
else if (!result && PyAsyncGen_CheckExact(gen) &&
277-
PyErr_ExceptionMatches(PyExc_StopAsyncIteration))
278-
{
279-
/* code in `gen` raised a StopAsyncIteration error:
280-
raise a RuntimeError.
281-
*/
282-
const char *msg = "async generator raised StopAsyncIteration";
283-
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
284258
}
285259

286-
if ((is_return_value && *is_return_value) || !result || _PyFrameHasCompleted(f)) {
287-
/* generator can't be rerun, so release the frame */
288-
/* first clean reference cycle through stored exception traceback */
289-
_PyErr_ClearExcState(&gen->gi_exc_state);
290-
gen->gi_frame->f_gen = NULL;
291-
gen->gi_frame = NULL;
292-
Py_DECREF(f);
293-
}
260+
/* generator can't be rerun, so release the frame */
261+
/* first clean reference cycle through stored exception traceback */
262+
_PyErr_ClearExcState(&gen->gi_exc_state);
263+
gen->gi_frame->f_gen = NULL;
264+
gen->gi_frame = NULL;
265+
Py_DECREF(f);
266+
267+
*presult = result;
268+
return result ? PYGEN_RETURN : PYGEN_ERROR;
269+
}
270+
271+
PySendResult
272+
PyGen_Send(PyGenObject *gen, PyObject *arg, PyObject **result)
273+
{
274+
assert(PyGen_CheckExact(gen) || PyCoro_CheckExact(gen));
275+
assert(result != NULL);
276+
assert(arg != NULL);
277+
278+
return gen_send_ex2(gen, arg, result, 0, 0);
279+
}
294280

281+
static PyObject *
282+
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
283+
{
284+
PyObject *result;
285+
if (gen_send_ex2(gen, arg, &result, exc, closing) == PYGEN_RETURN) {
286+
if (PyAsyncGen_CheckExact(gen)) {
287+
assert(result == Py_None);
288+
PyErr_SetNone(PyExc_StopAsyncIteration);
289+
}
290+
else if (result == Py_None) {
291+
PyErr_SetNone(PyExc_StopIteration);
292+
}
293+
else {
294+
_PyGen_SetStopIterationValue(result);
295+
}
296+
Py_CLEAR(result);
297+
}
295298
return result;
296299
}
297300

298301
PyDoc_STRVAR(send_doc,
299302
"send(arg) -> send 'arg' into generator,\n\
300303
return next yielded value or raise StopIteration.");
301304

302-
PyObject *
303-
_PyGen_Send(PyGenObject *gen, PyObject *arg)
305+
static PyObject *
306+
gen_send(PyGenObject *gen, PyObject *arg)
304307
{
305-
return gen_send_ex(gen, arg, 0, 0, NULL);
308+
return gen_send_ex(gen, arg, 0, 0);
306309
}
307310

308-
PySendResult
309-
PyGen_Send(PyGenObject *gen, PyObject *arg, PyObject **result)
311+
PyObject *
312+
_PyGen_Send(PyGenObject *gen, PyObject *arg)
310313
{
311-
assert(result != NULL);
312-
313-
int is_return_value = 0;
314-
if ((*result = gen_send_ex(gen, arg, 0, 0, &is_return_value)) == NULL) {
315-
return PYGEN_ERROR;
316-
}
317-
return is_return_value ? PYGEN_RETURN : PYGEN_NEXT;
314+
return gen_send(gen, arg);
318315
}
319316

320317
PyDoc_STRVAR(close_doc,
@@ -396,7 +393,7 @@ gen_close(PyGenObject *gen, PyObject *args)
396393
}
397394
if (err == 0)
398395
PyErr_SetNone(PyExc_GeneratorExit);
399-
retval = gen_send_ex(gen, Py_None, 1, 1, NULL);
396+
retval = gen_send_ex(gen, Py_None, 1, 1);
400397
if (retval) {
401398
const char *msg = "generator ignored GeneratorExit";
402399
if (PyCoro_CheckExact(gen)) {
@@ -444,7 +441,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
444441
gen->gi_frame->f_state = state;
445442
Py_DECREF(yf);
446443
if (err < 0)
447-
return gen_send_ex(gen, Py_None, 1, 0, NULL);
444+
return gen_send_ex(gen, Py_None, 1, 0);
448445
goto throw_here;
449446
}
450447
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
@@ -496,10 +493,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
496493
assert(gen->gi_frame->f_lasti >= 0);
497494
gen->gi_frame->f_lasti += sizeof(_Py_CODEUNIT);
498495
if (_PyGen_FetchStopIterationValue(&val) == 0) {
499-
ret = gen_send_ex(gen, val, 0, 0, NULL);
496+
ret = gen_send(gen, val);
500497
Py_DECREF(val);
501498
} else {
502-
ret = gen_send_ex(gen, Py_None, 1, 0, NULL);
499+
ret = gen_send_ex(gen, Py_None, 1, 0);
503500
}
504501
}
505502
return ret;
@@ -553,7 +550,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
553550
}
554551

555552
PyErr_Restore(typ, val, tb);
556-
return gen_send_ex(gen, Py_None, 1, 0, NULL);
553+
return gen_send_ex(gen, Py_None, 1, 0);
557554

558555
failed_throw:
559556
/* Didn't use our arguments, so restore their original refcounts */
@@ -582,7 +579,15 @@ gen_throw(PyGenObject *gen, PyObject *args)
582579
static PyObject *
583580
gen_iternext(PyGenObject *gen)
584581
{
585-
return gen_send_ex(gen, NULL, 0, 0, NULL);
582+
PyObject *result;
583+
assert(PyGen_CheckExact(gen) || PyCoro_CheckExact(gen));
584+
if (gen_send_ex2(gen, NULL, &result, 0, 0) == PYGEN_RETURN) {
585+
if (result != Py_None) {
586+
_PyGen_SetStopIterationValue(result);
587+
}
588+
Py_CLEAR(result);
589+
}
590+
return result;
586591
}
587592

588593
/*
@@ -767,7 +772,7 @@ static PyMemberDef gen_memberlist[] = {
767772
};
768773

769774
static PyMethodDef gen_methods[] = {
770-
{"send",(PyCFunction)_PyGen_Send, METH_O, send_doc},
775+
{"send",(PyCFunction)gen_send, METH_O, send_doc},
771776
{"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc},
772777
{"close",(PyCFunction)gen_close, METH_NOARGS, close_doc},
773778
{NULL, NULL} /* Sentinel */
@@ -1082,13 +1087,13 @@ coro_wrapper_dealloc(PyCoroWrapper *cw)
10821087
static PyObject *
10831088
coro_wrapper_iternext(PyCoroWrapper *cw)
10841089
{
1085-
return gen_send_ex((PyGenObject *)cw->cw_coroutine, NULL, 0, 0, NULL);
1090+
return gen_iternext((PyGenObject *)cw->cw_coroutine);
10861091
}
10871092

10881093
static PyObject *
10891094
coro_wrapper_send(PyCoroWrapper *cw, PyObject *arg)
10901095
{
1091-
return gen_send_ex((PyGenObject *)cw->cw_coroutine, arg, 0, 0, NULL);
1096+
return gen_send((PyGenObject *)cw->cw_coroutine, arg);
10921097
}
10931098

10941099
static PyObject *
@@ -1601,7 +1606,7 @@ async_gen_asend_send(PyAsyncGenASend *o, PyObject *arg)
16011606
}
16021607

16031608
o->ags_gen->ag_running_async = 1;
1604-
result = gen_send_ex((PyGenObject*)o->ags_gen, arg, 0, 0, NULL);
1609+
result = gen_send((PyGenObject*)o->ags_gen, arg);
16051610
result = async_gen_unwrap_value(o->ags_gen, result);
16061611

16071612
if (result == NULL) {
@@ -1957,7 +1962,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
19571962

19581963
assert(o->agt_state == AWAITABLE_STATE_ITER);
19591964

1960-
retval = gen_send_ex((PyGenObject *)gen, arg, 0, 0, NULL);
1965+
retval = gen_send((PyGenObject *)gen, arg);
19611966
if (o->agt_args) {
19621967
return async_gen_unwrap_value(o->agt_gen, retval);
19631968
} else {

0 commit comments

Comments
 (0)