]> granicus.if.org Git - python/commitdiff
Issue #21321: itertools.islice() now releases the reference to the source iterator...
authorAntoine Pitrou <solipsis@pitrou.net>
Tue, 29 Apr 2014 10:13:46 +0000 (12:13 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Tue, 29 Apr 2014 10:13:46 +0000 (12:13 +0200)
Patch by Anton Afanasyev.

Lib/test/test_itertools.py
Misc/ACKS
Misc/NEWS
Modules/itertoolsmodule.c

index 7769f1c83ed11a416aa3b519944f1154056aab81..70517f06700705dec9e4acc637ede768bc675cbf 100644 (file)
@@ -1,7 +1,7 @@
 import unittest
 from test import support
 from itertools import *
-from weakref import proxy
+import weakref
 from decimal import Decimal
 from fractions import Fraction
 import sys
@@ -1087,6 +1087,15 @@ class TestBasicOps(unittest.TestCase):
                              list(range(*args)))
             self.pickletest(islice(range(100), *args))
 
+        # 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]
         self.assertEqual(list(takewhile(underten, data)), [1, 3, 5])
@@ -1203,7 +1212,7 @@ class TestBasicOps(unittest.TestCase):
 
         # test that tee objects are weak referencable
         a, b = tee(range(10))
-        p = proxy(a)
+        p = weakref.proxy(a)
         self.assertEqual(getattr(p, '__class__'), type(b))
         del a
         self.assertRaises(ReferenceError, getattr, p, '__class__')
index 129e0a475f7f5e012085b94f9fef1454b6487ba1..4068836cf1c4508644fc11d87282f608f6596b51 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -17,6 +17,7 @@ Rajiv Abraham
 David Abrahams
 Marc Abramowitz
 Ron Adam
+Anton Afanasyev
 Ali Afshar
 Nitika Agarwal
 Jim Ahlstrom
index 3975afa462556956a618b347e99e0e7f5df5ce38..7739bcbe0df16b41f69efc7f48153784142a59e7 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -39,6 +39,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #21321: itertools.islice() now releases the reference to the source
+  iterator when the slice is exhausted.  Patch by Anton Afanasyev.
+
 - Issue #9815: assertRaises now tries to clear references to local variables
   in the exception's traceback.
 
index db7cdfeefeb5404b4d86f3d7f87c0e52c10a6f3e..cec1f8746fec7fe32f2853739d2a9d0c96354622 100644 (file)
@@ -1492,19 +1492,22 @@ islice_next(isliceobject *lz)
     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
@@ -1513,6 +1516,10 @@ islice_next(isliceobject *lz)
     if (lz->next < oldnext || (stop != -1 && lz->next > stop))
         lz->next = stop;
     return item;
+
+empty:
+    Py_CLEAR(lz->it);
+    return NULL;
 }
 
 static PyObject *
@@ -1522,6 +1529,18 @@ islice_reduce(isliceobject *lz)
      * then 'setstate' with the next and count
      */
     PyObject *stop;
+    if (lz->it == NULL) {
+        PyObject *empty_list;
+        PyObject *empty_it;
+        empty_list = PyList_New(0);
+        if (empty_list == NULL)
+            return NULL;
+        empty_it = PyObject_GetIter(empty_list);
+        Py_DECREF(empty_list);
+        if (empty_it == NULL)
+            return NULL;
+        return Py_BuildValue("O(Nn)n", Py_TYPE(lz), empty_it, 0, 0);
+    }
     if (lz->stop == -1) {
         stop = Py_None;
         Py_INCREF(stop);