# HG changeset patch # User cmlenz # Date 1237394642 0 # Node ID 004f81b59d9706c125dcb2c460de12a1cc98b5ff # Parent 67ec9a402bdc33d14da13a306e6526612993655f Refactored the template flattening method to be less recursive. diff --git a/genshi/core.py b/genshi/core.py --- 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. diff --git a/genshi/template/base.py b/genshi/template/base.py --- 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 diff --git a/genshi/template/markup.py b/genshi/template/markup.py --- 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