[ZPT] CVS: Releases/Zope/lib/python/ZTUtils - CHANGES.txt:1.6.8.1 Iterator.py:1.3.2.1

Evan Simpson evan@zope.com
Thu, 13 Dec 2001 15:26:03 -0500


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

Modified Files:
      Tag: Zope-2_5-branch
	CHANGES.txt Iterator.py 
Log Message:
Merge changes from trunk


=== Releases/Zope/lib/python/ZTUtils/CHANGES.txt 1.6 => 1.6.8.1 ===
     Version 1.1.4
 
+      Features Added
+
+        - Under Python 2.2, Iterator both accepts and produces Python
+          iterator objects.
+
+        - first() and last() methods allow you to tell whether the
+          current element is different from the next or previous
+          element.  This is most useful when the sequence is sorted.
+
       Bugs Fixed
 
         - Handle both string and class Unauthorized exceptions.


=== Releases/Zope/lib/python/ZTUtils/Iterator.py 1.3 => 1.3.2.1 ===
 __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
+        for inner in seqInner, iterInner:
+            if inner._supports(seq):
+                self._inner = inner
+                self._prep_next = inner.prep_next
+                return
+        raise TypeError, "Iterator does not support %s" % `seq`
 
-    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,108 @@
     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 _supports(self, ob):
+        try: ob[0]
+        except TypeError: return 0
+        except: pass
+        return 1
+
+    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"
 
-    def length(self):
-        return len(self.seq)
+class IterInner(InnerBase):
+    '''Iterator inner class for Python iterators'''
+
+    def _supports(self, ob):
+        try:
+            if hasattr(ob, 'next') and (ob is iter(ob)):
+                return 1
+        except:
+            return 0
+
+    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()