bpo-41756: Add PyIter_Send function (#22443)
This commit is contained in:
parent
9975cc5008
commit
037245c5ac
|
@ -15,11 +15,6 @@ 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`.
|
||||
|
||||
|
||||
.. c:var:: PyTypeObject PyGen_Type
|
||||
|
||||
The type object corresponding to generator objects.
|
||||
|
|
|
@ -44,3 +44,17 @@ something like this::
|
|||
else {
|
||||
/* continue doing useful work */
|
||||
}
|
||||
|
||||
|
||||
.. c:type:: PySendResult
|
||||
|
||||
The enum value used to represent different results of :c:func:`PyIter_Send`.
|
||||
|
||||
|
||||
.. c:function:: PySendResult PyIter_Send(PyObject *iter, PyObject *arg, PyObject **presult)
|
||||
|
||||
Sends the *arg* value into the iterator *iter*. Returns:
|
||||
|
||||
- ``PYGEN_RETURN`` if iterator returns. Return value is returned via *presult*.
|
||||
- ``PYGEN_NEXT`` if iterator yields. Yielded value is returned via *presult*.
|
||||
- ``PYGEN_ERROR`` if iterator has raised and exception. *presult* is set to ``NULL``.
|
||||
|
|
|
@ -1081,6 +1081,11 @@ PyIter_Check:PyObject*:o:0:
|
|||
PyIter_Next:PyObject*::+1:
|
||||
PyIter_Next:PyObject*:o:0:
|
||||
|
||||
PyIter_Send:int:::
|
||||
PyIter_Send:PyObject*:iter:0:
|
||||
PyIter_Send:PyObject*:arg:0:
|
||||
PyIter_Send:PyObject**:presult:+1:
|
||||
|
||||
PyList_Append:int:::
|
||||
PyList_Append:PyObject*:list:0:
|
||||
PyList_Append:PyObject*:item:+1:
|
||||
|
|
|
@ -314,6 +314,10 @@ New Features
|
|||
search function.
|
||||
(Contributed by Hai Shi in :issue:`41842`.)
|
||||
|
||||
* The :c:func:`PyIter_Send` and :c:func:`PyGen_Send` functions were added to allow
|
||||
sending value into iterator without raising ``StopIteration`` exception.
|
||||
(Contributed by Vladimir Matveev in :issue:`41756`.)
|
||||
|
||||
Porting to Python 3.10
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -338,6 +338,22 @@ PyAPI_FUNC(int) PyIter_Check(PyObject *);
|
|||
NULL with an exception means an error occurred. */
|
||||
PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *);
|
||||
|
||||
typedef enum {
|
||||
PYGEN_RETURN = 0,
|
||||
PYGEN_ERROR = -1,
|
||||
PYGEN_NEXT = 1,
|
||||
} PySendResult;
|
||||
|
||||
/* Takes generator, coroutine or iterator object and sends the value into it.
|
||||
Returns:
|
||||
- PYGEN_RETURN (0) if generator has returned.
|
||||
'result' parameter is filled with return value
|
||||
- PYGEN_ERROR (-1) if exception was raised.
|
||||
'result' parameter is NULL
|
||||
- PYGEN_NEXT (1) if generator has yielded.
|
||||
'result' parameter is filled with yielded value. */
|
||||
PyAPI_FUNC(PySendResult) PyIter_Send(PyObject *, PyObject *, PyObject **);
|
||||
|
||||
|
||||
/* === Number Protocol ================================================== */
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include "pystate.h" /* _PyErr_StackItem */
|
||||
#include "abstract.h" /* PySendResult */
|
||||
|
||||
/* _PyGenObject_HEAD defines the initial segment of generator
|
||||
and coroutine objects. */
|
||||
|
@ -41,16 +42,9 @@ PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *,
|
|||
PyObject *name, PyObject *qualname);
|
||||
PyAPI_FUNC(int) _PyGen_SetStopIterationValue(PyObject *);
|
||||
PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **);
|
||||
PyAPI_FUNC(PyObject *) _PyGen_Send(PyGenObject *, PyObject *);
|
||||
PyObject *_PyGen_yf(PyGenObject *);
|
||||
PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
|
||||
|
||||
typedef enum {
|
||||
PYGEN_RETURN = 0,
|
||||
PYGEN_ERROR = -1,
|
||||
PYGEN_NEXT = 1,
|
||||
} PySendResult;
|
||||
|
||||
/* Sends the value into the generator or the coroutine. Returns:
|
||||
- PYGEN_RETURN (0) if generator has returned.
|
||||
'result' parameter is filled with return value
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Add `PyIter_Send` function to allow sending value into
|
||||
generator/coroutine/iterator without raising StopIteration exception to
|
||||
signal return.
|
|
@ -16,7 +16,6 @@ _Py_IDENTIFIER(add_done_callback);
|
|||
_Py_IDENTIFIER(call_soon);
|
||||
_Py_IDENTIFIER(cancel);
|
||||
_Py_IDENTIFIER(get_event_loop);
|
||||
_Py_IDENTIFIER(send);
|
||||
_Py_IDENTIFIER(throw);
|
||||
|
||||
|
||||
|
@ -2695,13 +2694,7 @@ task_step_impl(TaskObj *task, PyObject *exc)
|
|||
|
||||
int gen_status = PYGEN_ERROR;
|
||||
if (exc == NULL) {
|
||||
if (PyGen_CheckExact(coro) || PyCoro_CheckExact(coro)) {
|
||||
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);
|
||||
}
|
||||
gen_status = PyIter_Send(coro, Py_None, &result);
|
||||
}
|
||||
else {
|
||||
result = _PyObject_CallMethodIdOneArg(coro, &PyId_throw, exc);
|
||||
|
|
|
@ -5028,6 +5028,7 @@ dict_get_version(PyObject *self, PyObject *args)
|
|||
static PyObject *
|
||||
raise_SIGINT_then_send_None(PyObject *self, PyObject *args)
|
||||
{
|
||||
_Py_IDENTIFIER(send);
|
||||
PyGenObject *gen;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!", &PyGen_Type, &gen))
|
||||
|
@ -5044,7 +5045,7 @@ raise_SIGINT_then_send_None(PyObject *self, PyObject *args)
|
|||
because we check for signals before every bytecode operation.
|
||||
*/
|
||||
raise(SIGINT);
|
||||
return _PyGen_Send(gen, Py_None);
|
||||
return _PyObject_CallMethodIdOneArg((PyObject *)gen, &PyId_send, Py_None);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2669,6 +2669,30 @@ PyIter_Next(PyObject *iter)
|
|||
return result;
|
||||
}
|
||||
|
||||
PySendResult
|
||||
PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result)
|
||||
{
|
||||
_Py_IDENTIFIER(send);
|
||||
assert(result != NULL);
|
||||
|
||||
if (PyGen_CheckExact(iter) || PyCoro_CheckExact(iter)) {
|
||||
return PyGen_Send((PyGenObject *)iter, arg, result);
|
||||
}
|
||||
|
||||
if (arg == Py_None && PyIter_Check(iter)) {
|
||||
*result = Py_TYPE(iter)->tp_iternext(iter);
|
||||
}
|
||||
else {
|
||||
*result = _PyObject_CallMethodIdOneArg(iter, &PyId_send, arg);
|
||||
}
|
||||
if (*result != NULL) {
|
||||
return PYGEN_NEXT;
|
||||
}
|
||||
if (_PyGen_FetchStopIterationValue(result) == 0) {
|
||||
return PYGEN_RETURN;
|
||||
}
|
||||
return PYGEN_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flatten a sequence of bytes() objects into a C array of
|
||||
|
|
|
@ -308,12 +308,6 @@ gen_send(PyGenObject *gen, PyObject *arg)
|
|||
return gen_send_ex(gen, arg, 0, 0);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyGen_Send(PyGenObject *gen, PyObject *arg)
|
||||
{
|
||||
return gen_send(gen, arg);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(close_doc,
|
||||
"close() -> raise GeneratorExit inside generator.");
|
||||
|
||||
|
@ -1012,7 +1006,7 @@ PyDoc_STRVAR(coro_close_doc,
|
|||
"close() -> raise GeneratorExit inside coroutine.");
|
||||
|
||||
static PyMethodDef coro_methods[] = {
|
||||
{"send",(PyCFunction)_PyGen_Send, METH_O, coro_send_doc},
|
||||
{"send",(PyCFunction)gen_send, METH_O, coro_send_doc},
|
||||
{"throw",(PyCFunction)gen_throw, METH_VARARGS, coro_throw_doc},
|
||||
{"close",(PyCFunction)gen_close, METH_NOARGS, coro_close_doc},
|
||||
{NULL, NULL} /* Sentinel */
|
||||
|
|
|
@ -2210,24 +2210,17 @@ main_loop:
|
|||
case TARGET(YIELD_FROM): {
|
||||
PyObject *v = POP();
|
||||
PyObject *receiver = TOP();
|
||||
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);
|
||||
PySendResult gen_status;
|
||||
if (tstate->c_tracefunc == NULL) {
|
||||
gen_status = PyIter_Send(receiver, v, &retval);
|
||||
} else {
|
||||
if (is_gen_or_coro) {
|
||||
retval = _PyGen_Send((PyGenObject *)receiver, v);
|
||||
_Py_IDENTIFIER(send);
|
||||
if (v == Py_None && PyIter_Check(receiver)) {
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
}
|
||||
else {
|
||||
_Py_IDENTIFIER(send);
|
||||
if (v == Py_None) {
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
}
|
||||
else {
|
||||
retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v);
|
||||
}
|
||||
retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v);
|
||||
}
|
||||
|
||||
if (retval == NULL) {
|
||||
if (tstate->c_tracefunc != NULL
|
||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
||||
|
|
Loading…
Reference in New Issue