[Python-checkins] cpython (merge 3.4 -> default): Issue #21321: itertools.islice() now releases the reference to the source

antoine.pitrou python-checkins at python.org
Tue Apr 29 12:15:01 CEST 2014


http://hg.python.org/cpython/rev/a627b3e3c9c8
changeset:   90511:a627b3e3c9c8
parent:      90509:7f50e1836ddb
parent:      90510:b795105db23a
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Tue Apr 29 12:14:47 2014 +0200
summary:
  Issue #21321: itertools.islice() now releases the reference to the source iterator when the slice is exhausted.

Patch by Anton Afanasyev.

files:
  Lib/test/test_itertools.py |  13 +++++++++++--
  Misc/ACKS                  |   1 +
  Misc/NEWS                  |   3 +++
  Modules/itertoolsmodule.c  |  25 ++++++++++++++++++++++---
  4 files changed, 37 insertions(+), 5 deletions(-)


diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -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 @@
                              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 @@
 
         # 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__')
diff --git a/Misc/ACKS b/Misc/ACKS
--- 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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -60,6 +60,9 @@
 Library
 -------
 
+- Issue #21321: itertools.islice() now releases the reference to the source
+  iterator when the slice is exhausted.  Patch by Anton Afanasyev.
+
 - Issue #21057: TextIOWrapper now allows the underlying binary stream's
   read() or read1() method to return an arbitrary bytes-like object
   (such as a memoryview).  Patch by Nikolaus Rath.
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -1492,19 +1492,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
@@ -1513,6 +1516,10 @@
     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 @@
      * 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);

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list