text: """
  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 Cython.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]
  