changeset 843:004f81b59d97

Refactored the template flattening method to be less recursive.
author cmlenz
date Wed, 18 Mar 2009 16:44:02 +0000
parents 67ec9a402bdc
children 0af77f663a65
files genshi/core.py genshi/template/base.py genshi/template/markup.py
diffstat 3 files changed, 63 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/genshi/core.py
+++ b/genshi/core.py
@@ -265,6 +265,7 @@
 PI = Stream.PI
 COMMENT = Stream.COMMENT
 
+
 def _ensure(stream):
     """Ensure that every item on the stream is actually a markup event."""
     stream = iter(stream)
@@ -549,8 +550,10 @@
 except ImportError:
     pass # just use the Python implementation
 
+
 escape = Markup.escape
 
+
 def unescape(text):
     """Reverse-escapes &, <, >, and \" and returns a `unicode` object.
     
--- a/genshi/template/base.py
+++ b/genshi/template/base.py
@@ -519,55 +519,65 @@
 
     def _flatten(self, stream, ctxt, **vars):
         number_conv = self._number_conv
-
-        for kind, data, pos in stream:
-
-            if kind is START and data[1]:
-                # Attributes may still contain expressions in start tags at
-                # this point, so do some evaluation
-                tag, attrs = data
-                new_attrs = []
-                for name, value in attrs:
-                    if type(value) is list: # this is an interpolated string
-                        values = [event[1]
-                            for event in self._flatten(value, ctxt, **vars)
-                            if event[0] is TEXT and event[1] is not None
-                        ]
-                        if not values:
-                            continue
-                        value = u''.join(values)
-                    new_attrs.append((name, value))
-                yield kind, (tag, Attrs(new_attrs)), pos
+        stack = []
+        push = stack.append
+        pop = stack.pop
+        stream = iter(stream)
 
-            elif kind is EXPR:
-                result = _eval_expr(data, ctxt, vars)
-                if result is not None:
-                    # First check for a string, otherwise the iterable test
-                    # below succeeds, and the string will be chopped up into
-                    # individual characters
-                    if isinstance(result, basestring):
-                        yield TEXT, result, pos
-                    elif isinstance(result, (int, float, long)):
-                        yield TEXT, number_conv(result), pos
-                    elif hasattr(result, '__iter__'):
-                        for event in self._flatten(_ensure(result), ctxt,
-                                                   **vars):
-                            yield event
-                    else:
-                        yield TEXT, unicode(result), pos
+        while 1:
+            for kind, data, pos in stream:
 
-            elif kind is EXEC:
-                _exec_suite(data, ctxt, vars)
+                if kind is START and data[1]:
+                    # Attributes may still contain expressions in start tags at
+                    # this point, so do some evaluation
+                    tag, attrs = data
+                    new_attrs = []
+                    for name, value in attrs:
+                        if type(value) is list: # this is an interpolated string
+                            values = [event[1]
+                                for event in self._flatten(value, ctxt, **vars)
+                                if event[0] is TEXT and event[1] is not None
+                            ]
+                            if not values:
+                                continue
+                            value = u''.join(values)
+                        new_attrs.append((name, value))
+                    yield kind, (tag, Attrs(new_attrs)), pos
 
-            elif kind is SUB:
-                # This event is a list of directives and a list of nested
-                # events to which those directives should be applied
-                substream = _apply_directives(data[1], data[0], ctxt, vars)
-                for event in self._flatten(substream, ctxt, **vars):
-                    yield event
+                elif kind is EXPR:
+                    result = _eval_expr(data, ctxt, vars)
+                    if result is not None:
+                        # First check for a string, otherwise the iterable test
+                        # below succeeds, and the string will be chopped up into
+                        # individual characters
+                        if isinstance(result, basestring):
+                            yield TEXT, result, pos
+                        elif isinstance(result, (int, float, long)):
+                            yield TEXT, number_conv(result), pos
+                        elif hasattr(result, '__iter__'):
+                            push(stream)
+                            stream = _ensure(result)
+                            break
+                        else:
+                            yield TEXT, unicode(result), pos
+
+                elif kind is SUB:
+                    # This event is a list of directives and a list of nested
+                    # events to which those directives should be applied
+                    push(stream)
+                    stream = _apply_directives(data[1], data[0], ctxt, vars)
+                    break
+
+                elif kind is EXEC:
+                    _exec_suite(data, ctxt, vars)
+
+                else:
+                    yield kind, data, pos
 
             else:
-                yield kind, data, pos
+                if not stack:
+                    break
+                stream = pop()
 
     def _include(self, stream, ctxt, **vars):
         """Internal stream filter that performs inclusion of external
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -311,10 +311,11 @@
         match_templates = ctxt._match_templates
 
         tail = []
-        def _strip(stream):
+        def _strip(stream, append=tail.append):
             depth = 1
+            next = stream.next
             while 1:
-                event = stream.next()
+                event = next()
                 if event[0] is START:
                     depth += 1
                 elif event[0] is END:
@@ -322,7 +323,7 @@
                 if depth > 0:
                     yield event
                 else:
-                    tail[:] = [event]
+                    append(event)
                     break
 
         for event in stream:
@@ -360,13 +361,14 @@
                     content = self._include(chain([event], inner, tail), ctxt)
                     if 'not_buffered' not in hints:
                         content = list(content)
+                    content = Stream(content)
 
                     # Make the select() function available in the body of the
                     # match template
                     selected = [False]
                     def select(path):
                         selected[0] = True
-                        return Stream(content).select(path, namespaces, ctxt)
+                        return content.select(path, namespaces, ctxt)
                     vars = dict(select=select)
 
                     # Recursively process the output
Copyright (C) 2012-2017 Edgewall Software