mirror of
https://github.com/python/cpython.git
synced 2025-10-05 22:51:56 +00:00
bpo-29942: Fix the use of recursion in itertools.chain.from_iterable. (#911)
* bpo-29942: Fix the use of recursion in itertools.chain.from_iterable.
Fix the use of recursion in itertools.chain.from_iterable. Using recursion
is unnecessary, and can easily cause stack overflows, especially when
building in low optimization modes or with Py_DEBUG enabled.
(cherry picked from commit 5466d4af5f
)
This commit is contained in:
parent
7b5b1379ac
commit
599bb18103
3 changed files with 39 additions and 24 deletions
|
@ -1864,33 +1864,37 @@ chain_next(chainobject *lz)
|
|||
{
|
||||
PyObject *item;
|
||||
|
||||
if (lz->source == NULL)
|
||||
return NULL; /* already stopped */
|
||||
|
||||
if (lz->active == NULL) {
|
||||
PyObject *iterable = PyIter_Next(lz->source);
|
||||
if (iterable == NULL) {
|
||||
Py_CLEAR(lz->source);
|
||||
return NULL; /* no more input sources */
|
||||
}
|
||||
lz->active = PyObject_GetIter(iterable);
|
||||
Py_DECREF(iterable);
|
||||
/* lz->source is the iterator of iterables. If it's NULL, we've already
|
||||
* consumed them all. lz->active is the current iterator. If it's NULL,
|
||||
* we should grab a new one from lz->source. */
|
||||
while (lz->source != NULL) {
|
||||
if (lz->active == NULL) {
|
||||
Py_CLEAR(lz->source);
|
||||
return NULL; /* input not iterable */
|
||||
PyObject *iterable = PyIter_Next(lz->source);
|
||||
if (iterable == NULL) {
|
||||
Py_CLEAR(lz->source);
|
||||
return NULL; /* no more input sources */
|
||||
}
|
||||
lz->active = PyObject_GetIter(iterable);
|
||||
Py_DECREF(iterable);
|
||||
if (lz->active == NULL) {
|
||||
Py_CLEAR(lz->source);
|
||||
return NULL; /* input not iterable */
|
||||
}
|
||||
}
|
||||
item = (*Py_TYPE(lz->active)->tp_iternext)(lz->active);
|
||||
if (item != NULL)
|
||||
return item;
|
||||
if (PyErr_Occurred()) {
|
||||
if (PyErr_ExceptionMatches(PyExc_StopIteration))
|
||||
PyErr_Clear();
|
||||
else
|
||||
return NULL; /* input raised an exception */
|
||||
}
|
||||
/* lz->active is consumed, try with the next iterable. */
|
||||
Py_CLEAR(lz->active);
|
||||
}
|
||||
item = (*Py_TYPE(lz->active)->tp_iternext)(lz->active);
|
||||
if (item != NULL)
|
||||
return item;
|
||||
if (PyErr_Occurred()) {
|
||||
if (PyErr_ExceptionMatches(PyExc_StopIteration))
|
||||
PyErr_Clear();
|
||||
else
|
||||
return NULL; /* input raised an exception */
|
||||
}
|
||||
Py_CLEAR(lz->active);
|
||||
return chain_next(lz); /* recurse and use next active */
|
||||
/* Everything had been consumed already. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue