Viewing file: _looper.py (4.06 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
""" Helper for looping over sequences, particular in templates.
Often in a loop in a template it's handy to know what's next up, previously up, if this is the first or last item in the sequence, etc. These can be awkward to manage in a normal Python loop, but using the looper you can get a better sense of the context. Use like::
>>> for loop, item in looper(['a', 'b', 'c']): ... print loop.number, item ... if not loop.last: ... print '---' 1 a --- 2 b --- 3 c
"""
import sys from tempita.compat3 import basestring_
__all__ = ['looper']
class looper(object): """ Helper for looping (particularly in templates)
Use this like::
for loop, item in looper(seq): if loop.first: ... """
def __init__(self, seq): self.seq = seq
def __iter__(self): return looper_iter(self.seq)
def __repr__(self): return '<%s for %r>' % ( self.__class__.__name__, self.seq)
class looper_iter(object):
def __init__(self, seq): self.seq = list(seq) self.pos = 0
def __iter__(self): return self
def __next__(self): if self.pos >= len(self.seq): raise StopIteration result = loop_pos(self.seq, self.pos), self.seq[self.pos] self.pos += 1 return result
if sys.version < "3": next = __next__
class loop_pos(object):
def __init__(self, seq, pos): self.seq = seq self.pos = pos
def __repr__(self): return '<loop pos=%r at %r>' % ( self.seq[self.pos], self.pos)
def index(self): return self.pos index = property(index)
def number(self): return self.pos + 1 number = property(number)
def item(self): return self.seq[self.pos] item = property(item)
def __next__(self): try: return self.seq[self.pos + 1] except IndexError: return None __next__ = property(__next__)
if sys.version < "3": next = __next__
def previous(self): if self.pos == 0: return None return self.seq[self.pos - 1] previous = property(previous)
def odd(self): return not self.pos % 2 odd = property(odd)
def even(self): return self.pos % 2 even = property(even)
def first(self): return self.pos == 0 first = property(first)
def last(self): return self.pos == len(self.seq) - 1 last = property(last)
def length(self): return len(self.seq) length = property(length)
def first_group(self, getter=None): """ Returns true if this item is the start of a new group, where groups mean that some attribute has changed. The getter can be None (the item itself changes), an attribute name like ``'.attr'``, a function, or a dict key or list index. """ if self.first: return True return self._compare_group(self.item, self.previous, getter)
def last_group(self, getter=None): """ Returns true if this item is the end of a new group, where groups mean that some attribute has changed. The getter can be None (the item itself changes), an attribute name like ``'.attr'``, a function, or a dict key or list index. """ if self.last: return True return self._compare_group(self.item, self.__next__, getter)
def _compare_group(self, item, other, getter): if getter is None: return item != other elif (isinstance(getter, basestring_) and getter.startswith('.')): getter = getter[1:] if getter.endswith('()'): getter = getter[:-2] return getattr(item, getter)() != getattr(other, getter)() else: return getattr(item, getter) != getattr(other, getter) elif hasattr(getter, '__call__'): return getter(item) != getter(other) else: return item[getter] != other[getter]
|