OLD | NEW |
(Empty) | |
| 1 """ |
| 2 Helper for looping over sequences, particular in templates. |
| 3 |
| 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:: |
| 8 |
| 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 |
| 18 |
| 19 """ |
| 20 |
| 21 import sys |
| 22 from Cython.Tempita.compat3 import basestring_ |
| 23 |
| 24 __all__ = ['looper'] |
| 25 |
| 26 |
| 27 class looper(object): |
| 28 """ |
| 29 Helper for looping (particularly in templates) |
| 30 |
| 31 Use this like:: |
| 32 |
| 33 for loop, item in looper(seq): |
| 34 if loop.first: |
| 35 ... |
| 36 """ |
| 37 |
| 38 def __init__(self, seq): |
| 39 self.seq = seq |
| 40 |
| 41 def __iter__(self): |
| 42 return looper_iter(self.seq) |
| 43 |
| 44 def __repr__(self): |
| 45 return '<%s for %r>' % ( |
| 46 self.__class__.__name__, self.seq) |
| 47 |
| 48 |
| 49 class looper_iter(object): |
| 50 |
| 51 def __init__(self, seq): |
| 52 self.seq = list(seq) |
| 53 self.pos = 0 |
| 54 |
| 55 def __iter__(self): |
| 56 return self |
| 57 |
| 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 |
| 64 |
| 65 if sys.version < "3": |
| 66 next = __next__ |
| 67 |
| 68 |
| 69 class loop_pos(object): |
| 70 |
| 71 def __init__(self, seq, pos): |
| 72 self.seq = seq |
| 73 self.pos = pos |
| 74 |
| 75 def __repr__(self): |
| 76 return '<loop pos=%r at %r>' % ( |
| 77 self.seq[self.pos], self.pos) |
| 78 |
| 79 def index(self): |
| 80 return self.pos |
| 81 index = property(index) |
| 82 |
| 83 def number(self): |
| 84 return self.pos + 1 |
| 85 number = property(number) |
| 86 |
| 87 def item(self): |
| 88 return self.seq[self.pos] |
| 89 item = property(item) |
| 90 |
| 91 def __next__(self): |
| 92 try: |
| 93 return self.seq[self.pos + 1] |
| 94 except IndexError: |
| 95 return None |
| 96 __next__ = property(__next__) |
| 97 |
| 98 if sys.version < "3": |
| 99 next = __next__ |
| 100 |
| 101 def previous(self): |
| 102 if self.pos == 0: |
| 103 return None |
| 104 return self.seq[self.pos - 1] |
| 105 previous = property(previous) |
| 106 |
| 107 def odd(self): |
| 108 return not self.pos % 2 |
| 109 odd = property(odd) |
| 110 |
| 111 def even(self): |
| 112 return self.pos % 2 |
| 113 even = property(even) |
| 114 |
| 115 def first(self): |
| 116 return self.pos == 0 |
| 117 first = property(first) |
| 118 |
| 119 def last(self): |
| 120 return self.pos == len(self.seq) - 1 |
| 121 last = property(last) |
| 122 |
| 123 def length(self): |
| 124 return len(self.seq) |
| 125 length = property(length) |
| 126 |
| 127 def first_group(self, getter=None): |
| 128 """ |
| 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. |
| 133 """ |
| 134 if self.first: |
| 135 return True |
| 136 return self._compare_group(self.item, self.previous, getter) |
| 137 |
| 138 def last_group(self, getter=None): |
| 139 """ |
| 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. |
| 144 """ |
| 145 if self.last: |
| 146 return True |
| 147 return self._compare_group(self.item, self.__next__, getter) |
| 148 |
| 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] |
OLD | NEW |