[ZPT] CVS: Releases/Zope/lib/python/ZTUtils - Iterator.py:1.4

Evan Simpson evan@zope.com
Tue, 11 Dec 2001 21:21:57 -0500


Update of /cvs-repository/Releases/Zope/lib/python/ZTUtils
In directory cvs.zope.org:/tmp/cvs-serv32107

Modified Files:
	Iterator.py 
Log Message:
Handle Python 2.2 iterators, implement first() and last().


=== Releases/Zope/lib/python/ZTUtils/Iterator.py 1.3 => 1.4 ===
 __doc__='''Iterator class
 
+Unlike the builtin iterators of Python 2.2+, these classes are
+designed to maintain information about the state of an iteration.
+The Iterator() function accepts either a sequence or a Python
+iterator.  The next() method fetches the next item, and returns
+true if it succeeds.
+
 $Id$'''
 __version__='$Revision$'[11:-2]
 
@@ -21,21 +27,39 @@
     '''Simple Iterator class'''
 
     __allow_access_to_unprotected_subobjects__ = 1
-    
+
+    nextIndex = 0
     def __init__(self, seq):
         self.seq = seq
-        self.nextIndex = 0
+        if hasattr(seq, 'next'):
+            inner = iterInner
+        else:
+            inner = seqInner
+        self._inner = inner
+        self._prep_next = inner.prep_next
 
-    def next(self):
-        i = self.nextIndex
+    def __getattr__(self, name):
         try:
-            self.seq[i]
-        except IndexError:
+            inner = getattr(self._inner, 'it_' + name)
+        except AttributeError:
+            raise AttributeError, name
+        return inner(self)
+
+    def next(self):
+        if not (hasattr(self, '_next') or self._prep_next(self)):
             return 0
-        self.index = i
+        self.index = i = self.nextIndex
         self.nextIndex = i+1
+        self._advance(self)
         return 1
 
+    def _advance(self, it):
+        self.item = self._next
+        del self._next
+        del self.end
+        self._advance = self._inner.advance
+        self.start = 1
+            
     def number(self): return self.nextIndex
 
     def even(self): return not self.index % 2
@@ -67,16 +91,94 @@
     def roman(self, lower=string.lower):
         return lower(self.Roman())
 
-    def start(self): return self.nextIndex == 1
+    def first(self, name=None):
+        if self.start: return 1
+        return not self.same_part(name, self._last, self.item)
+
+    def last(self, name=None):
+        if self.end: return 1
+        return not self.same_part(name, self.item, self._next)
+
+    def same_part(self, name, ob1, ob2):
+        if name is None:
+            return ob1 == ob2
+        no = []
+        return getattr(ob1, name, no) == getattr(ob2, name, no) is not no
+
+    def __iter__(self):
+        return IterIter(self)
+
+class InnerBase:
+    '''Base Inner class for Iterators'''
+    # Prep sets up ._next and .end
+    def prep_next(self, it):
+        it.next = self.no_next
+        it.end = 1
+        return 0
 
-    def end(self):
-        try: self.seq[self.nextIndex]
-        except IndexError: return 1
+    # Advance knocks them down
+    def advance(self, it):
+        it._last = it.item
+        it.item = it._next
+        del it._next
+        del it.end
+        it.start = 0
+            
+    def no_next(self, it):
         return 0
 
-    def item(self):
-        return self.seq[self.index]
+    def it_end(self, it):
+        if hasattr(it, '_next'):
+            return 0
+        return not self.prep_next(it)
+
+class SeqInner(InnerBase):
+    '''Inner class for sequence Iterators'''
+    def prep_next(self, it):
+        i = it.nextIndex
+        try:
+            it._next = it.seq[i]
+        except IndexError:
+            it._prep_next = self.no_next
+            it.end = 1
+            return 0
+        it.end = 0
+        return 1
+
+    def it_length(self, it):
+        it.length = l = len(it.seq)
+        return l
+
+try:
+    StopIteration=StopIteration
+except NameError:
+    StopIteration="StopIteration"
+
+class IterInner(InnerBase):
+    '''Iterator inner class for Python iterators'''
 
-    def length(self):
-        return len(self.seq)
+    def prep_next(self, it):
+        try:
+            it._next = it.seq.next()
+        except StopIteration:
+            it._prep_next = self.no_next
+            it.end = 1
+            return 0
+        it.end = 0
+        return 1
+
+class IterIter:
+    def __init__(self, it):
+        self.it = it
+        self.skip = it.nextIndex > 0 and not it.end
+    def next(self):
+        it = self.it
+        if self.skip:
+            self.skip = 0
+            return it.item
+        if it.next():
+            return it.item
+        raise StopIteration
 
+seqInner = SeqInner()
+iterInner = IterInner()