Issue #21321: itertools.islice() now releases the reference to the source iterator when the slice is exhausted.
Patch by Anton Afanasyev.
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 7233e7c..291aeb0 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -1,7 +1,7 @@
import unittest
from test import test_support
from itertools import *
-from weakref import proxy
+import weakref
from decimal import Decimal
from fractions import Fraction
import sys
@@ -792,6 +792,15 @@
self.assertEqual(list(islice(c, 1, 3, 50)), [1])
self.assertEqual(next(c), 3)
+ # Issue #21321: check source iterator is not referenced
+ # from islice() after the latter has been exhausted
+ it = (x for x in (1, 2))
+ wr = weakref.ref(it)
+ it = islice(it, 1)
+ self.assertIsNotNone(wr())
+ list(it) # exhaust the iterator
+ self.assertIsNone(wr())
+
def test_takewhile(self):
data = [1, 3, 5, 20, 2, 4, 6, 8]
underten = lambda x: x<10
@@ -901,7 +910,7 @@
# test that tee objects are weak referencable
a, b = tee(xrange(10))
- p = proxy(a)
+ p = weakref.proxy(a)
self.assertEqual(getattr(p, '__class__'), type(b))
del a
self.assertRaises(ReferenceError, getattr, p, '__class__')
diff --git a/Misc/ACKS b/Misc/ACKS
index 2d08627..c8c3880 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -17,6 +17,7 @@
David Abrahams
Marc Abramowitz
Ron Adam
+Anton Afanasyev
Ali Afshar
Nitika Agarwal
Jim Ahlstrom
diff --git a/Misc/NEWS b/Misc/NEWS
index f086cc3..ab71e01 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -46,6 +46,9 @@
Library
-------
+- Issue #21321: itertools.islice() now releases the reference to the source
+ iterator when the slice is exhausted. Patch by Anton Afanasyev.
+
- Issue #9291: Do not attempt to re-encode mimetype data read from registry in
ANSI mode. Initial patches by Dmitry Jemerov & Vladimir Iofik.
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c
index c0456b8..a7cd3f4 100644
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -1241,19 +1241,22 @@
Py_ssize_t oldnext;
PyObject *(*iternext)(PyObject *);
+ if (it == NULL)
+ return NULL;
+
iternext = *Py_TYPE(it)->tp_iternext;
while (lz->cnt < lz->next) {
item = iternext(it);
if (item == NULL)
- return NULL;
+ goto empty;
Py_DECREF(item);
lz->cnt++;
}
if (stop != -1 && lz->cnt >= stop)
- return NULL;
+ goto empty;
item = iternext(it);
if (item == NULL)
- return NULL;
+ goto empty;
lz->cnt++;
oldnext = lz->next;
/* The (size_t) cast below avoids the danger of undefined
@@ -1262,6 +1265,10 @@
if (lz->next < oldnext || (stop != -1 && lz->next > stop))
lz->next = stop;
return item;
+
+empty:
+ Py_CLEAR(lz->it);
+ return NULL;
}
PyDoc_STRVAR(islice_doc,