15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)""" 25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)Helper for looping over sequences, particular in templates. 35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)Often in a loop in a template it's handy to know what's next up, 55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)previously up, if this is the first or last item in the sequence, etc. 65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)These can be awkward to manage in a normal Python loop, but using the 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)looper you can get a better sense of the context. Use like:: 85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) >>> for loop, item in looper(['a', 'b', 'c']): 105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ... print loop.number, item 115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ... if not loop.last: 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ... print '---' 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1 a 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) --- 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2 b 165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) --- 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3 c 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)""" 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import sys 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from Cython.Tempita.compat3 import basestring_ 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)__all__ = ['looper'] 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class looper(object): 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Helper for looping (particularly in templates) 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Use this like:: 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for loop, item in looper(seq): 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if loop.first: 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ... 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self, seq): 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.seq = seq 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __iter__(self): 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return looper_iter(self.seq) 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __repr__(self): 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return '<%s for %r>' % ( 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.__class__.__name__, self.seq) 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class looper_iter(object): 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self, seq): 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.seq = list(seq) 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.pos = 0 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __iter__(self): 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __next__(self): 595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.pos >= len(self.seq): 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) raise StopIteration 615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) result = loop_pos(self.seq, self.pos), self.seq[self.pos] 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.pos += 1 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return result 645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if sys.version < "3": 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) next = __next__ 675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class loop_pos(object): 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self, seq, pos): 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.seq = seq 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.pos = pos 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __repr__(self): 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return '<loop pos=%r at %r>' % ( 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.seq[self.pos], self.pos) 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def index(self): 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.pos 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) index = property(index) 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def number(self): 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.pos + 1 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) number = property(number) 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def item(self): 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.seq[self.pos] 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) item = property(item) 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __next__(self): 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.seq[self.pos + 1] 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) except IndexError: 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return None 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) __next__ = property(__next__) 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if sys.version < "3": 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) next = __next__ 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def previous(self): 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.pos == 0: 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return None 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.seq[self.pos - 1] 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous = property(previous) 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def odd(self): 1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return not self.pos % 2 1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) odd = property(odd) 1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def even(self): 1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.pos % 2 1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) even = property(even) 1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def first(self): 1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.pos == 0 1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) first = property(first) 1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def last(self): 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.pos == len(self.seq) - 1 1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) last = property(last) 1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def length(self): 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return len(self.seq) 1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) length = property(length) 1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def first_group(self, getter=None): 1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns true if this item is the start of a new group, 1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) where groups mean that some attribute has changed. The getter 1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) can be None (the item itself changes), an attribute name like 1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ``'.attr'``, a function, or a dict key or list index. 1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.first: 1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return True 1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self._compare_group(self.item, self.previous, getter) 1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def last_group(self, getter=None): 1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns true if this item is the end of a new group, 1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) where groups mean that some attribute has changed. The getter 1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) can be None (the item itself changes), an attribute name like 1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ``'.attr'``, a function, or a dict key or list index. 1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.last: 1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return True 1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self._compare_group(self.item, self.__next__, getter) 1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def _compare_group(self, item, other, getter): 1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if getter is None: 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return item != other 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif (isinstance(getter, basestring_) 1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) and getter.startswith('.')): 1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) getter = getter[1:] 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if getter.endswith('()'): 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) getter = getter[:-2] 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return getattr(item, getter)() != getattr(other, getter)() 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return getattr(item, getter) != getattr(other, getter) 1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif hasattr(getter, '__call__'): 1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return getter(item) != getter(other) 1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return item[getter] != other[getter] 164