diff options
author | T. Wouters <thomas@python.org> | 2017-03-30 19:48:23 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-30 19:48:23 (GMT) |
commit | 599bb181036f724629a515317f0f39520950d51c (patch) | |
tree | 3cf9853bf9edd48a8f04b0592e59ecb34f07e26a /Modules | |
parent | 7b5b1379ac2c1e89ebf90b88b5d32457910e975e (diff) | |
download | cpython-599bb181036f724629a515317f0f39520950d51c.zip cpython-599bb181036f724629a515317f0f39520950d51c.tar.gz cpython-599bb181036f724629a515317f0f39520950d51c.tar.bz2 |
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 5466d4af5fe76ec0a5fbc8a05675287d9e8e9d14)
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/itertoolsmodule.c | 52 |
1 files changed, 28 insertions, 24 deletions
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 6bf04cb..8e22ec9 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -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 * |