1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE
11 #
12 ##############################################################################
13 __doc__='''Iterator class
15 Unlike the builtin iterators of Python 2.2+, these classes are
16 designed to maintain information about the state of an iteration.
17 The Iterator() function accepts either a sequence or a Python
18 iterator. The next() method fetches the next item, and returns
19 true if it succeeds.
21 $Id: Iterator.py,v 1.4 2005-02-16 22:07:33 richard Exp $'''
22 __docformat__ = 'restructuredtext'
23 __version__='$Revision: 1.4 $'[11:-2]
25 import string
27 class Iterator:
28 '''Simple Iterator class'''
30 __allow_access_to_unprotected_subobjects__ = 1
32 nextIndex = 0
33 def __init__(self, seq):
34 self.seq = iter(seq) # force seq to be an iterator
35 self._inner = iterInner
36 self._prep_next = iterInner.prep_next
38 def __getattr__(self, name):
39 try:
40 inner = getattr(self._inner, 'it_' + name)
41 except AttributeError:
42 raise AttributeError, name
43 return inner(self)
45 def next(self):
46 if not (hasattr(self, '_next') or self._prep_next(self)):
47 return 0
48 self.index = i = self.nextIndex
49 self.nextIndex = i+1
50 self._advance(self)
51 return 1
53 def _advance(self, it):
54 self.item = self._next
55 del self._next
56 del self.end
57 self._advance = self._inner.advance
58 self.start = 1
60 def number(self): return self.nextIndex
62 def even(self): return not self.index % 2
64 def odd(self): return self.index % 2
66 def letter(self, base=ord('a'), radix=26):
67 index = self.index
68 s = ''
69 while 1:
70 index, off = divmod(index, radix)
71 s = chr(base + off) + s
72 if not index: return s
74 def Letter(self):
75 return self.letter(base=ord('A'))
77 def Roman(self, rnvalues=(
78 (1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
79 (100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
80 (10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')) ):
81 n = self.index + 1
82 s = ''
83 for v, r in rnvalues:
84 rct, n = divmod(n, v)
85 s = s + r * rct
86 return s
88 def roman(self, lower=string.lower):
89 return lower(self.Roman())
91 def first(self, name=None):
92 if self.start: return 1
93 return not self.same_part(name, self._last, self.item)
95 def last(self, name=None):
96 if self.end: return 1
97 return not self.same_part(name, self.item, self._next)
99 def same_part(self, name, ob1, ob2):
100 if name is None:
101 return ob1 == ob2
102 no = []
103 return getattr(ob1, name, no) == getattr(ob2, name, no) is not no
105 def __iter__(self):
106 return IterIter(self)
108 class InnerBase:
109 '''Base Inner class for Iterators'''
110 # Prep sets up ._next and .end
111 def prep_next(self, it):
112 it.next = self.no_next
113 it.end = 1
114 return 0
116 # Advance knocks them down
117 def advance(self, it):
118 it._last = it.item
119 it.item = it._next
120 del it._next
121 del it.end
122 it.start = 0
124 def no_next(self, it):
125 return 0
127 def it_end(self, it):
128 if hasattr(it, '_next'):
129 return 0
130 return not self.prep_next(it)
132 class SeqInner(InnerBase):
133 '''Inner class for sequence Iterators'''
135 def _supports(self, ob):
136 try: ob[0]
137 except (TypeError, AttributeError): return 0
138 except: pass
139 return 1
141 def prep_next(self, it):
142 i = it.nextIndex
143 try:
144 it._next = it.seq[i]
145 except IndexError:
146 it._prep_next = self.no_next
147 it.end = 1
148 return 0
149 it.end = 0
150 return 1
152 def it_length(self, it):
153 it.length = l = len(it.seq)
154 return l
156 try:
157 StopIteration=StopIteration
158 except NameError:
159 StopIteration="StopIteration"
161 class IterInner(InnerBase):
162 '''Iterator inner class for Python iterators'''
164 def _supports(self, ob):
165 try:
166 if hasattr(ob, 'next') and (ob is iter(ob)):
167 return 1
168 except:
169 return 0
171 def prep_next(self, it):
172 try:
173 it._next = it.seq.next()
174 except StopIteration:
175 it._prep_next = self.no_next
176 it.end = 1
177 return 0
178 it.end = 0
179 return 1
181 class IterIter:
182 def __init__(self, it):
183 self.it = it
184 self.skip = it.nextIndex > 0 and not it.end
185 def next(self):
186 it = self.it
187 if self.skip:
188 self.skip = 0
189 return it.item
190 if it.next():
191 return it.item
192 raise StopIteration
194 seqInner = SeqInner()
195 iterInner = IterInner()