gh-105201: Add PyIter_NextItem() (#122331)

Return -1 and set an exception on error; return 0 if the iterator is
exhausted, and return 1 if the next item was fetched successfully.

Prefer this API to PyIter_Next(), which requires the caller to use
PyErr_Occurred() to differentiate between iterator exhaustion and errors.

Co-authered-by: Irit Katriel <iritkatriel@yahoo.com>
This commit is contained in:
Erlend E. Aasland 2024-08-08 00:47:15 +02:00 committed by GitHub
parent 540fcc62f5
commit e006c7371d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 156 additions and 40 deletions

View file

@ -2881,7 +2881,50 @@ PyAIter_Check(PyObject *obj)
tp->tp_as_async->am_anext != &_PyObject_NextNotImplemented);
}
static int
iternext(PyObject *iter, PyObject **item)
{
iternextfunc tp_iternext = Py_TYPE(iter)->tp_iternext;
if ((*item = tp_iternext(iter))) {
return 1;
}
PyThreadState *tstate = _PyThreadState_GET();
/* When the iterator is exhausted it must return NULL;
* a StopIteration exception may or may not be set. */
if (!_PyErr_Occurred(tstate)) {
return 0;
}
if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
_PyErr_Clear(tstate);
return 0;
}
/* Error case: an exception (different than StopIteration) is set. */
return -1;
}
/* Return 1 and set 'item' to the next item of 'iter' on success.
* Return 0 and set 'item' to NULL when there are no remaining values.
* Return -1, set 'item' to NULL and set an exception on error.
*/
int
PyIter_NextItem(PyObject *iter, PyObject **item)
{
assert(iter != NULL);
assert(item != NULL);
if (Py_TYPE(iter)->tp_iternext == NULL) {
*item = NULL;
PyErr_Format(PyExc_TypeError, "expected an iterator, got '%T'", iter);
return -1;
}
return iternext(iter, item);
}
/* Return next item.
*
* If an error occurs, return NULL. PyErr_Occurred() will be true.
* If the iteration terminates normally, return NULL and clear the
* PyExc_StopIteration exception (if it was set). PyErr_Occurred()
@ -2891,17 +2934,9 @@ PyAIter_Check(PyObject *obj)
PyObject *
PyIter_Next(PyObject *iter)
{
PyObject *result;
result = (*Py_TYPE(iter)->tp_iternext)(iter);
if (result == NULL) {
PyThreadState *tstate = _PyThreadState_GET();
if (_PyErr_Occurred(tstate)
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
{
_PyErr_Clear(tstate);
}
}
return result;
PyObject *item;
(void)iternext(iter, &item);
return item;
}
PySendResult