diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 530c29dec4..a5acf95707 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -408,7 +408,11 @@ loops that truncate the stream. self.tgtkey = self.currkey return (self.currkey, self._grouper(self.tgtkey, self.id)) def _grouper(self, tgtkey, id): - while self.id is id and self.currkey == tgtkey: + while True: + if self.id is not id: + raise PyExc_RuntimeError('group changed during iteration') + if self.currkey != tgtkey: + break yield self.currvalue try: self.currvalue = next(self.it) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 8353e68977..bd2b89b1e6 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -758,18 +758,18 @@ class TestBasicOps(unittest.TestCase): _, g1 = next(it) _, g2 = next(it) _, g3 = next(it) - self.assertEqual(list(g1), []) - self.assertEqual(list(g2), []) - self.assertEqual(next(g3), ('A', 5)) + self.assertRaises(RuntimeError, next, g1) + self.assertRaises(RuntimeError, next, g2) + self.assertEqual(list(g3), s[5:]) list(it) # exhaust the groupby iterator - self.assertEqual(list(g3), []) + self.assertRaises(RuntimeError, next, g3) for proto in range(pickle.HIGHEST_PROTOCOL + 1): it = groupby(s, testR) _, g = next(it) next(it) next(it) - self.assertEqual(list(pickle.loads(pickle.dumps(g, proto))), []) + self.assertRaises(RuntimeError, pickle.dumps, g, proto) # Exercise pipes and filters style s = 'abracadabra' diff --git a/Misc/NEWS.d/next/Library/2017-09-24-13-08-46.bpo-30346.Csse77.rst b/Misc/NEWS.d/next/Library/2017-09-24-13-42-37.bpo-30346.0w0qb9.rst similarity index 90% rename from Misc/NEWS.d/next/Library/2017-09-24-13-08-46.bpo-30346.Csse77.rst rename to Misc/NEWS.d/next/Library/2017-09-24-13-42-37.bpo-30346.0w0qb9.rst index 81ad0534fc..2da50f5577 100644 --- a/Misc/NEWS.d/next/Library/2017-09-24-13-08-46.bpo-30346.Csse77.rst +++ b/Misc/NEWS.d/next/Library/2017-09-24-13-42-37.bpo-30346.0w0qb9.rst @@ -1,2 +1,2 @@ -An iterator produced by itertools.groupby() iterator now becames exhausted +An iterator produced by itertools.groupby() iterator now becames invalid after advancing the groupby iterator. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 2ac5ab24ec..b691918cea 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -287,8 +287,10 @@ _grouper_next(_grouperobject *igo) PyObject *newvalue, *newkey, *r; int rcmp; - if (gbo->currgrouper != igo) + if (gbo->currgrouper != igo) { + PyErr_SetString(PyExc_RuntimeError, "group changed during iteration"); return NULL; + } if (gbo->currvalue == NULL) { newvalue = PyIter_Next(gbo->it); if (newvalue == NULL) @@ -327,7 +329,8 @@ static PyObject * _grouper_reduce(_grouperobject *lz) { if (((groupbyobject *)lz->parent)->currgrouper != lz) { - return Py_BuildValue("N(())", _PyObject_GetBuiltin("iter")); + PyErr_SetString(PyExc_RuntimeError, "group changed during iteration"); + return NULL; } return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->parent, lz->tgtkey); }