small refactoring

--HG--
branch : trunk
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index 17a5996..9a61040 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -185,7 +185,7 @@
             len(self)
 
     def cycle(self, *args):
-        """A replacement for the old ``{% cycle %}`` tag."""
+        """Cycles among the arguments with the current loop index."""
         if not args:
             raise TypeError('no items for cycling given')
         return args[self.index0 % len(args)]
@@ -200,7 +200,7 @@
         return self.length
 
     def __iter__(self):
-        return self
+        return LoopContextIterator(self)
 
     def loop(self, iterable):
         if self._recurse is None:
@@ -212,16 +212,17 @@
     # the the loop without or with too many arguments.
     __call__ = loop; del loop
 
-    def next(self):
-        self.index0 += 1
-        return self._next(), self
-
     @property
     def length(self):
         if self._length is None:
             try:
+                # first try to get the length from the iterable (if the
+                # iterable is a sequence)
                 length = len(self._iterable)
             except TypeError:
+                # if that's not possible (ie: iterating over a generator)
+                # we have to convert the iterable into a sequence and
+                # use the length of that.
                 self._iterable = tuple(self._iterable)
                 self._next = iter(self._iterable).next
                 length = len(tuple(self._iterable)) + self.index0 + 1
@@ -236,6 +237,22 @@
         )
 
 
+class LoopContextIterator(object):
+    """The iterator for a loop context."""
+    __slots__ = ('context',)
+
+    def __init__(self, context):
+        self.context = context
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        ctx = self.context
+        ctx.index0 += 1
+        return ctx._next(), ctx
+
+
 class Macro(object):
     """Wraps a macro."""