Skip to content

Commit c740e4f

Browse files
bpo-30347: Stop crashes when concurrently iterate over itertools.groupby() iterators. (#1557)
1 parent 114454e commit c740e4f

File tree

3 files changed

+56
-36
lines changed

3 files changed

+56
-36
lines changed

‎Lib/test/test_itertools.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,30 @@ def test_long_chain_of_empty_iterables(self):
20172017
with self.assertRaises(StopIteration):
20182018
next(it)
20192019

2020+
def test_issue30347_1(self):
2021+
def f(n):
2022+
if n == 5:
2023+
list(b)
2024+
return n != 6
2025+
for (k, b) in groupby(range(10), f):
2026+
list(b) # shouldn't crash
2027+
2028+
def test_issue30347_2(self):
2029+
class K:
2030+
def __init__(self, v):
2031+
pass
2032+
def __eq__(self, other):
2033+
nonlocal i
2034+
i += 1
2035+
if i == 1:
2036+
next(g, None)
2037+
return True
2038+
i = 0
2039+
g = next(groupby(range(10), K))[1]
2040+
for j in range(2):
2041+
next(g, None) # shouldn't crash
2042+
2043+
20202044
class SubclassWithKwargsTest(unittest.TestCase):
20212045
def test_keywords_in_subclass(self):
20222046
# count is not subclassable...
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Stop crashes when concurrently iterate over itertools.groupby() iterators.

‎Modules/itertoolsmodule.c‎

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,37 @@ groupby_traverse(groupbyobject *gbo, visitproc visit, void *arg)
7373
return 0;
7474
}
7575

76+
Py_LOCAL_INLINE(int)
77+
groupby_step(groupbyobject *gbo)
78+
{
79+
PyObject *newvalue, *newkey, *oldvalue;
80+
81+
newvalue = PyIter_Next(gbo->it);
82+
if (newvalue == NULL)
83+
return -1;
84+
85+
if (gbo->keyfunc == Py_None) {
86+
newkey = newvalue;
87+
Py_INCREF(newvalue);
88+
} else {
89+
newkey = PyObject_CallFunctionObjArgs(gbo->keyfunc, newvalue, NULL);
90+
if (newkey == NULL) {
91+
Py_DECREF(newvalue);
92+
return -1;
93+
}
94+
}
95+
96+
oldvalue = gbo->currvalue;
97+
gbo->currvalue = newvalue;
98+
Py_XSETREF(gbo->currkey, newkey);
99+
Py_XDECREF(oldvalue);
100+
return 0;
101+
}
102+
76103
static PyObject *
77104
groupby_next(groupbyobject *gbo)
78105
{
79-
PyObject *newvalue, *newkey, *r, *grouper;
106+
PyObject *r, *grouper;
80107

81108
gbo->currgrouper = NULL;
82109
/* skip to next iteration group */
@@ -95,25 +122,9 @@ groupby_next(groupbyobject *gbo)
95122
break;
96123
}
97124

98-
newvalue = PyIter_Next(gbo->it);
99-
if (newvalue == NULL)
125+
if (groupby_step(gbo) < 0)
100126
return NULL;
101-
102-
if (gbo->keyfunc == Py_None) {
103-
newkey = newvalue;
104-
Py_INCREF(newvalue);
105-
} else {
106-
newkey = PyObject_CallFunctionObjArgs(gbo->keyfunc, newvalue, NULL);
107-
if (newkey == NULL) {
108-
Py_DECREF(newvalue);
109-
return NULL;
110-
}
111-
}
112-
113-
Py_XSETREF(gbo->currkey, newkey);
114-
Py_XSETREF(gbo->currvalue, newvalue);
115127
}
116-
117128
Py_INCREF(gbo->currkey);
118129
Py_XSETREF(gbo->tgtkey, gbo->currkey);
119130

@@ -285,30 +296,14 @@ static PyObject *
285296
_grouper_next(_grouperobject *igo)
286297
{
287298
groupbyobject *gbo = (groupbyobject *)igo->parent;
288-
PyObject *newvalue, *newkey, *r;
299+
PyObject *r;
289300
int rcmp;
290301

291302
if (gbo->currgrouper != igo)
292303
return NULL;
293304
if (gbo->currvalue == NULL) {
294-
newvalue = PyIter_Next(gbo->it);
295-
if (newvalue == NULL)
305+
if (groupby_step(gbo) < 0)
296306
return NULL;
297-
298-
if (gbo->keyfunc == Py_None) {
299-
newkey = newvalue;
300-
Py_INCREF(newvalue);
301-
} else {
302-
newkey = PyObject_CallFunctionObjArgs(gbo->keyfunc, newvalue, NULL);
303-
if (newkey == NULL) {
304-
Py_DECREF(newvalue);
305-
return NULL;
306-
}
307-
}
308-
309-
assert(gbo->currkey == NULL);
310-
gbo->currkey = newkey;
311-
gbo->currvalue = newvalue;
312307
}
313308

314309
assert(gbo->currkey != NULL);

0 commit comments

Comments
 (0)