Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / cython / src / Cython / Tempita / _looper.py
blob4010988300ffd12da5dd136f1f173d584261191e
1 """
2 Helper for looping over sequences, particular in templates.
4 Often in a loop in a template it's handy to know what's next up,
5 previously up, if this is the first or last item in the sequence, etc.
6 These can be awkward to manage in a normal Python loop, but using the
7 looper you can get a better sense of the context. Use like::
9 >>> for loop, item in looper(['a', 'b', 'c']):
10 ... print loop.number, item
11 ... if not loop.last:
12 ... print '---'
13 1 a
14 ---
15 2 b
16 ---
17 3 c
19 """
21 import sys
22 from Cython.Tempita.compat3 import basestring_
24 __all__ = ['looper']
27 class looper(object):
28 """
29 Helper for looping (particularly in templates)
31 Use this like::
33 for loop, item in looper(seq):
34 if loop.first:
35 ...
36 """
38 def __init__(self, seq):
39 self.seq = seq
41 def __iter__(self):
42 return looper_iter(self.seq)
44 def __repr__(self):
45 return '<%s for %r>' % (
46 self.__class__.__name__, self.seq)
49 class looper_iter(object):
51 def __init__(self, seq):
52 self.seq = list(seq)
53 self.pos = 0
55 def __iter__(self):
56 return self
58 def __next__(self):
59 if self.pos >= len(self.seq):
60 raise StopIteration
61 result = loop_pos(self.seq, self.pos), self.seq[self.pos]
62 self.pos += 1
63 return result
65 if sys.version < "3":
66 next = __next__
69 class loop_pos(object):
71 def __init__(self, seq, pos):
72 self.seq = seq
73 self.pos = pos
75 def __repr__(self):
76 return '<loop pos=%r at %r>' % (
77 self.seq[self.pos], self.pos)
79 def index(self):
80 return self.pos
81 index = property(index)
83 def number(self):
84 return self.pos + 1
85 number = property(number)
87 def item(self):
88 return self.seq[self.pos]
89 item = property(item)
91 def __next__(self):
92 try:
93 return self.seq[self.pos + 1]
94 except IndexError:
95 return None
96 __next__ = property(__next__)
98 if sys.version < "3":
99 next = __next__
101 def previous(self):
102 if self.pos == 0:
103 return None
104 return self.seq[self.pos - 1]
105 previous = property(previous)
107 def odd(self):
108 return not self.pos % 2
109 odd = property(odd)
111 def even(self):
112 return self.pos % 2
113 even = property(even)
115 def first(self):
116 return self.pos == 0
117 first = property(first)
119 def last(self):
120 return self.pos == len(self.seq) - 1
121 last = property(last)
123 def length(self):
124 return len(self.seq)
125 length = property(length)
127 def first_group(self, getter=None):
129 Returns true if this item is the start of a new group,
130 where groups mean that some attribute has changed. The getter
131 can be None (the item itself changes), an attribute name like
132 ``'.attr'``, a function, or a dict key or list index.
134 if self.first:
135 return True
136 return self._compare_group(self.item, self.previous, getter)
138 def last_group(self, getter=None):
140 Returns true if this item is the end of a new group,
141 where groups mean that some attribute has changed. The getter
142 can be None (the item itself changes), an attribute name like
143 ``'.attr'``, a function, or a dict key or list index.
145 if self.last:
146 return True
147 return self._compare_group(self.item, self.__next__, getter)
149 def _compare_group(self, item, other, getter):
150 if getter is None:
151 return item != other
152 elif (isinstance(getter, basestring_)
153 and getter.startswith('.')):
154 getter = getter[1:]
155 if getter.endswith('()'):
156 getter = getter[:-2]
157 return getattr(item, getter)() != getattr(other, getter)()
158 else:
159 return getattr(item, getter) != getattr(other, getter)
160 elif hasattr(getter, '__call__'):
161 return getter(item) != getter(other)
162 else:
163 return item[getter] != other[getter]