bpo-41756: Introduce _PyGen_SendNoStopIteration to provide alternative way to return results from coroutines by vladima · Pull Request #22196 · python/cpython

vladima

1st1

}
else {
/* Async generators cannot return anything but None */
assert(!PyAsyncGen_CheckExact(gen));
_PyGen_SetStopIterationValue(result);
if (is_return_value) {
{
PyThreadState *tstate = _PyThreadState_GET();
PyFrameObject *f = gen->gi_frame;
PyObject *result;

@@ -225,7 +224,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)

/* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */
if (result && _PyFrameHasCompleted(f)) {
@@ -235,13 +234,19 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
/* Set exception if not called by gen_iternext() */
PyErr_SetNone(PyExc_StopIteration);
}
Py_CLEAR(result);
}

PyObject *
_PyGen_SendNoStopIteration(PyGenObject *gen, PyObject *arg, int *is_return_value)

serhiy-storchaka

@@ -2695,10 +2696,13 @@ task_step_impl(TaskObj *task, PyObject *exc)
}
}

if (result == NULL) {
if ((gen_status == PYGEN_RETURN) || result == NULL) {
if (PyGen_CheckExact(coro) || PyCoro_CheckExact(coro)) {
result = _PyGen_Send((PyGenObject*)coro, Py_None);
gen_status = _PyGen_Send_NameTBD((PyGenObject*)coro, Py_None, &result);
if (result != NULL) {
o = result;
}
if (o || (_PyGen_FetchStopIterationValue(&o) == 0)) {
*is_return_value = 1;
}
else {
/* Set exception if not called by gen_iternext() */
…turn results from coroutines

1st1

#define PYGEN_ERROR -1
#define PYGEN_NEXT 1

PyAPI_FUNC(int) PyGen_Send(PyGenObject *, PyObject *, PyObject **);

1st1

}
else {
_Py_IDENTIFIER(send);
if (v == Py_None)

1st1

1st1 approved these changes Sep 16, 2020

@vladima

@vladima

@vladima

1st1

1st1 approved these changes Sep 18, 2020

1st1


.. c:function:: PySendResult PyGen_Send(PyGenObject *gen, PyObject *arg, PyObject **presult)

Sends the *arg* value into the generator *gen*. Returns:

1st1

@@ -15,6 +15,11 @@ than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`.
The C structure used for generator objects.


.. c:type:: PySendResult

The enum value used to represent different results of :c:func:`PyGen_Send`

1st1

PYGEN_NEXT = 1,
} PySendResult;

/* Sends the value into the generator. Returns:

1st1

@@ -0,0 +1,2 @@
Add PyGen_Send function to allow sending value to generator without

1st1

if (_PyGen_FetchStopIterationValue(result) == 0) {
return PYGEN_RETURN;
}
return PYGEN_ERROR;

@vladima

xzy3 pushed a commit to xzy3/cpython that referenced this pull request

Oct 18, 2020
The new API allows to efficiently send values into native generators
and coroutines avoiding use of StopIteration exceptions to signal 
returns.

ceval loop now uses this method instead of the old "private"
_PyGen_Send C API. This translates to 1.6x increased performance
of 'await' calls in micro-benchmarks.

Aside from CPython core improvements, this new API will also allow 
Cython to generate more efficient code, benefiting high-performance
IO libraries like uvloop.