[pypy-commit] pypy py3.5: itertools.islice: use same logic as CPython and fix #2643

rlamy pypy.commits at gmail.com
Sun Sep 3 22:32:39 EDT 2017


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.5
Changeset: r92315:17c8c1d27c41
Date: 2017-09-04 03:32 +0100
http://bitbucket.org/pypy/pypy/changeset/17c8c1d27c41/

Log:	itertools.islice: use same logic as CPython and fix #2643

diff --git a/pypy/module/itertools/interp_itertools.py b/pypy/module/itertools/interp_itertools.py
--- a/pypy/module/itertools/interp_itertools.py
+++ b/pypy/module/itertools/interp_itertools.py
@@ -310,8 +310,7 @@
 
 islice_ignore_items_driver = jit.JitDriver(name='islice_ignore_items',
                                            greens=['tp'],
-                                           reds=['num', 'w_islice',
-                                                 'w_iterator'])
+                                           reds=['w_islice', 'w_iterator'])
 
 class W_ISlice(W_Root):
     def __init__(self, space, w_iterable, w_startstop, args_w):
@@ -325,7 +324,7 @@
             w_stop = w_startstop
         elif num_args <= 2:
             if space.is_w(w_startstop, space.w_None):
-                start = -1
+                start = 0
             else:
                 start = self.arg_int_w(w_startstop, 0,
                  "Indicies for islice() must be None or non-negative integers")
@@ -352,9 +351,10 @@
         else:
             step = 1
 
-        self.ignore = step - 1
-        self.start = start
+        self.count = 0
+        self.next = start
         self.stop = stop
+        self.step = step
 
     def arg_int_w(self, w_obj, minimum, errormsg):
         space = self.space
@@ -372,70 +372,52 @@
         return self
 
     def next_w(self):
-        if self.start >= 0:               # first call only
-            ignore = self.start
-            self.start = -1
-        else:                             # all following calls
-            ignore = self.ignore
-        stop = self.stop
-        if stop >= 0:
-            if stop <= ignore:
-                self.stop = 0   # reset the state so that a following next_w()
-                                # has no effect any more
-                if stop > 0:
-                    self._ignore_items(stop)
-                self.iterable = None
-                raise OperationError(self.space.w_StopIteration,
-                                     self.space.w_None)
-            self.stop = stop - (ignore + 1)
-        if ignore > 0:
-            self._ignore_items(ignore)
         if self.iterable is None:
             raise OperationError(self.space.w_StopIteration, self.space.w_None)
+        self._ignore_items()
+        stop = self.stop
+        if 0 <= stop <= self.count:
+            self.iterable = None
+            raise OperationError(self.space.w_StopIteration,
+                                    self.space.w_None)
         try:
-            return self.space.next(self.iterable)
+            item = self.space.next(self.iterable)
         except OperationError as e:
             if e.match(self.space, self.space.w_StopIteration):
                 self.iterable = None
             raise
+        self.count += 1
+        oldnext = self.next
+        self.next += self.step
+        if self.next < oldnext or self.next > stop >= 0:
+            self.next = stop
+        return item
 
-    def _ignore_items(self, num):
+    def _ignore_items(self):
         w_iterator = self.iterable
-        if w_iterator is None:
-            raise OperationError(self.space.w_StopIteration, self.space.w_None)
-
         tp = self.space.type(w_iterator)
         while True:
-            islice_ignore_items_driver.jit_merge_point(tp=tp,
-                                                       num=num,
-                                                       w_islice=self,
-                                                       w_iterator=w_iterator)
+            islice_ignore_items_driver.jit_merge_point(
+                tp=tp, w_islice=self, w_iterator=w_iterator)
+            if self.count >= self.next:
+                break
             try:
                 self.space.next(w_iterator)
             except OperationError as e:
                 if e.match(self.space, self.space.w_StopIteration):
                     self.iterable = None
                 raise
-            num -= 1
-            if num <= 0:
-                break
+            self.count += 1
 
     def descr_reduce(self, space):
         if self.iterable is None:
             return space.newtuple([
                 space.type(self),
                 space.newtuple([space.iter(space.newlist([])),
-                                space.newint(0),
-                                space.newint(0),
-                                space.newint(1),
-                            ]),
+                                space.newint(0)]),
+                space.newint(0),
             ])
-        start = self.start
         stop = self.stop
-        if start == -1:
-            w_start = space.w_None
-        else:
-            w_start = space.newint(start)
         if stop == -1:
             w_stop = space.w_None
         else:
@@ -443,11 +425,15 @@
         return space.newtuple([
             space.type(self),
             space.newtuple([self.iterable,
-                            w_start,
+                            space.newint(self.next),
                             w_stop,
-                            space.newint(self.ignore + 1)]),
+                            space.newint(self.step)]),
+            space.newint(self.count),
         ])
 
+    def descr_setstate(self, space, w_state):
+        self.count = space.int_w(w_state)
+
 def W_ISlice___new__(space, w_subtype, w_iterable, w_startstop, args_w):
     r = space.allocate_instance(W_ISlice, w_subtype)
     r.__init__(space, w_iterable, w_startstop, args_w)
diff --git a/pypy/module/itertools/test/test_itertools.py b/pypy/module/itertools/test/test_itertools.py
--- a/pypy/module/itertools/test/test_itertools.py
+++ b/pypy/module/itertools/test/test_itertools.py
@@ -214,6 +214,9 @@
         assert list(itertools.islice(range(10), None,None)) == list(range(10))
         assert list(itertools.islice(range(10), None,None,None)) == list(range(10))
 
+        it = itertools.islice([0, 1, 2], None, None, 2)
+        assert list(it) == [0, 2]
+
         import weakref
         for args in [(1,), (None,), (0, None, 2)]:
             it = (x for x in (1, 2, 3))


More information about the pypy-commit mailing list