[pypy-svn] r77423 - in pypy/branch/fast-forward/pypy/module/itertools: . test

afa at codespeak.net afa at codespeak.net
Tue Sep 28 00:53:14 CEST 2010


Author: afa
Date: Tue Sep 28 00:53:12 2010
New Revision: 77423

Modified:
   pypy/branch/fast-forward/pypy/module/itertools/interp_itertools.py
   pypy/branch/fast-forward/pypy/module/itertools/test/test_itertools.py
Log:
- itertools.chain() is now lazy: its iterables are not checked by the constructor,
but when they are consumed.

- add itertools.from_iterable()


Modified: pypy/branch/fast-forward/pypy/module/itertools/interp_itertools.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/itertools/interp_itertools.py	(original)
+++ pypy/branch/fast-forward/pypy/module/itertools/interp_itertools.py	Tue Sep 28 00:53:12 2010
@@ -372,49 +372,30 @@
 
 
 class W_Chain(Wrappable):
-    def __init__(self, space, args_w):
+    def __init__(self, space, w_iterables):
         self.space = space
-        iterators_w = []
-        i = 0
-        for iterable_w in args_w:
-            try:
-                iterator_w = space.iter(iterable_w)
-            except OperationError, e:
-                if e.match(self.space, self.space.w_TypeError):
-                    raise OperationError(space.w_TypeError, space.wrap("chain argument #" + str(i + 1) + " must support iteration"))
-                else:
-                    raise
-            else:
-                iterators_w.append(iterator_w)
-
-            i += 1
-
-        self.iterators_w = iterators_w
-        self.current_iterator = 0
-        self.num_iterators = len(iterators_w)
-        self.started = False
+        self.w_iterables = w_iterables
+        self.w_it = None
 
     def iter_w(self):
         return self.space.wrap(self)
 
+    def _advance(self):
+        self.w_it = self.space.iter(self.space.next(self.w_iterables))
+
     def next_w(self):
-        if self.current_iterator >= self.num_iterators:
+        if not self.w_iterables:
+            # already stopped
             raise OperationError(self.space.w_StopIteration, self.space.w_None)
-        if not self.started:
-            self.current_iterator = 0
-            self.w_it = self.iterators_w[self.current_iterator]
-            self.started = True
+        if not self.w_it:
+            self._advance()
 
         while True:
             try:
                 w_obj = self.space.next(self.w_it)
             except OperationError, e:
                 if e.match(self.space, self.space.w_StopIteration):
-                    self.current_iterator += 1
-                    if self.current_iterator >= self.num_iterators:
-                        raise OperationError(self.space.w_StopIteration, self.space.w_None)
-                    else:
-                        self.w_it = self.iterators_w[self.current_iterator]
+                    self._advance() # may raise StopIteration itself
                 else:
                     raise
             else:
@@ -422,13 +403,23 @@
         return w_obj
 
 def W_Chain___new__(space, w_subtype, args_w):
-    return space.wrap(W_Chain(space, args_w))
+    w_args = space.newtuple(args_w)
+    return space.wrap(W_Chain(space, space.iter(w_args)))
+
+def chain_from_iterable(space, w_cls, w_arg):
+    """chain.from_iterable(iterable) --> chain object
+
+    Alternate chain() contructor taking a single iterable argument
+    that evaluates lazily."""
+    return space.wrap(W_Chain(space, space.iter(w_arg)))
 
 W_Chain.typedef = TypeDef(
         'chain',
         __new__  = interp2app(W_Chain___new__, unwrap_spec=[ObjSpace, W_Root, 'args_w']),
         __iter__ = interp2app(W_Chain.iter_w, unwrap_spec=['self']),
         next     = interp2app(W_Chain.next_w, unwrap_spec=['self']),
+        from_iterable = interp2app(chain_from_iterable, unwrap_spec=[ObjSpace, W_Root, W_Root],
+                                   as_classmethod=True),
         __doc__  = """Make an iterator that returns elements from the first iterable
     until it is exhausted, then proceeds to the next iterable, until
     all of the iterables are exhausted. Used for treating consecutive

Modified: pypy/branch/fast-forward/pypy/module/itertools/test/test_itertools.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/itertools/test/test_itertools.py	(original)
+++ pypy/branch/fast-forward/pypy/module/itertools/test/test_itertools.py	Tue Sep 28 00:53:12 2010
@@ -271,21 +271,11 @@
         assert it.next() == 1
         raises(StopIteration, it.next)
 
-    def test_chain_wrongargs(self):
+    def test_chain_fromiterable(self):
         import itertools
-        
-        raises(TypeError, itertools.chain, None)
-        raises(TypeError, itertools.chain, [], None)
-
-        # The error message should indicate which argument was dodgy
-        for x in range(10):
-            args = [()] * x + [None] + [()] * (9 - x)
-            try:
-                itertools.chain(*args)
-            except TypeError, e:
-                assert str(e).find("#" + str(x + 1) + " ") >= 0
-            else:
-                fail("TypeError expected")
+        l = [[1, 2, 3], [4], [5, 6]]
+        it = itertools.chain.from_iterable(l)
+        assert list(it) == sum(l, [])
 
     def test_imap(self):
         import itertools



More information about the Pypy-commit mailing list