[Python-Dev] Change to PyIter_Next()?
Tim Peters
tim.one@home.com
Fri, 4 May 2001 16:50:03 -0400
In spare moments, I've been plugging away at making various functions work
nice with iterators (map, min, max, etc).
Over and over this requires writing code of the form:
op2 = PyIter_Next(it);
if (op2 == NULL) {
/* StopIteration is *implied* by a NULL return from
* PyIter_Next() if PyErr_Occurred() is false.
*/
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_StopIteration))
PyErr_Clear();
else
goto Fail;
}
break;
}
This is wordy, obscure, and in my experience is needed every time I call
PyIter_Next().
So I'd like to hide this in PyIter_Next instead, like so:
/* Return next item.
* If an error occurs, return NULL and set *error=1.
* If the iteration terminated normally, return NULL and set *error=0.
* Else return the next object and set *error=0.
*/
PyObject *
PyIter_Next(PyObject *iter, int *error)
{
PyObject *result;
if (!PyIter_Check(iter)) {
PyErr_Format(PyExc_TypeError,
"'%.100s' object is not an iterator",
iter->ob_type->tp_name);
*error = 1;
return NULL;
}
result = (*iter->ob_type->tp_iternext)(iter);
*error = 0;
if (result)
return result;
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_StopIteration))
PyErr_Clear();
else
*error = 1;
}
/* Else StopIteration is implicit, and there is no error. */
return NULL;
}
Then *calls* could be the simpler:
op2 = PyIter_Next(it, &error);
if (op2 == NULL) {
if {error)
goto Fail;
break;
}
Objections? So far I'm almost the only user of PyIter_Next(); the only other
use is in ceval's FOR_ITER, which goes thru a similar dance.
However, I'm not clear on why FOR_ITER doesn't clear the exception if
PyErr_Occurred() and PyErr_ExceptionMatches(PyExc_StopIteration) are both
true -- that sure smells like a bug (but, if so, the change above would
squash it by magic).
Note that I'm not proposing to change the signature of the tp_iternext slot
similarly. PyIter_Next() is a (IMO appropriately) higher-level function.