diff options
| author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-06-17 07:11:37 +0000 |
|---|---|---|
| committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-06-17 07:11:37 +0000 |
| commit | bccb8897e6ab0fe8d2e5b9bcb725ac28b1c8e566 (patch) | |
| tree | d3abbbdf27fa5ae3c78a9dc510798cd521e46684 /django/template/__init__.py | |
| parent | 44dd91ec6d39525e52b78f7fff6de8531b980f5f (diff) | |
Fixed #4565 -- Changed template rendering to use iterators, rather than
creating large strings, as much as possible. This is all backwards compatible.
Thanks, Brian Harring.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5482 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/template/__init__.py')
| -rw-r--r-- | django/template/__init__.py | 84 |
1 files changed, 57 insertions, 27 deletions
diff --git a/django/template/__init__.py b/django/template/__init__.py index 4f2ddfc8b3..0d1256c4dc 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -55,6 +55,7 @@ times with multiple contexts) '\n<html>\n\n</html>\n' """ import re +import types from inspect import getargspec from django.conf import settings from django.template.context import Context, RequestContext, ContextPopException @@ -167,9 +168,12 @@ class Template(object): for subnode in node: yield subnode - def render(self, context): + def iter_render(self, context): "Display stage -- can be called many times" - return self.nodelist.render(context) + return self.nodelist.iter_render(context) + + def render(self, context): + return ''.join(self.iter_render(context)) def compile_string(template_string, origin): "Compiles template_string into NodeList ready for rendering" @@ -698,10 +702,26 @@ def resolve_variable(path, context): del bits[0] return current +class NodeBase(type): + def __new__(cls, name, bases, attrs): + """ + Ensures that either a 'render' or 'render_iter' method is defined on + any Node sub-class. This avoids potential infinite loops at runtime. + """ + if not (isinstance(attrs.get('render'), types.FunctionType) or + isinstance(attrs.get('iter_render'), types.FunctionType)): + raise TypeError('Unable to create Node subclass without either "render" or "iter_render" method.') + return type.__new__(cls, name, bases, attrs) + class Node(object): + __metaclass__ = NodeBase + + def iter_render(self, context): + return (self.render(context),) + def render(self, context): "Return the node rendered as a string" - pass + return ''.join(self.iter_render(context)) def __iter__(self): yield self @@ -717,13 +737,12 @@ class Node(object): class NodeList(list): def render(self, context): - bits = [] + return ''.join(self.iter_render(context)) + + def iter_render(self, context): for node in self: - if isinstance(node, Node): - bits.append(self.render_node(node, context)) - else: - bits.append(node) - return ''.join(bits) + for chunk in node.iter_render(context): + yield chunk def get_nodes_by_type(self, nodetype): "Return a list of all nodes of the given type" @@ -732,24 +751,26 @@ class NodeList(list): nodes.extend(node.get_nodes_by_type(nodetype)) return nodes - def render_node(self, node, context): - return(node.render(context)) class DebugNodeList(NodeList): - def render_node(self, node, context): - try: - result = node.render(context) - except TemplateSyntaxError, e: - if not hasattr(e, 'source'): - e.source = node.source - raise - except Exception, e: - from sys import exc_info - wrapped = TemplateSyntaxError('Caught an exception while rendering: %s' % e) - wrapped.source = node.source - wrapped.exc_info = exc_info() - raise wrapped - return result + def iter_render(self, context): + for node in self: + if not isinstance(node, Node): + yield node + continue + try: + for chunk in node.iter_render(context): + yield chunk + except TemplateSyntaxError, e: + if not hasattr(e, 'source'): + e.source = node.source + raise + except Exception, e: + from sys import exc_info + wrapped = TemplateSyntaxError('Caught an exception while rendering: %s' % e) + wrapped.source = node.source + wrapped.exc_info = exc_info() + raise wrapped class TextNode(Node): def __init__(self, s): @@ -758,6 +779,9 @@ class TextNode(Node): def __repr__(self): return "<Text Node: '%s'>" % self.s[:25] + def iter_render(self, context): + return (self.s,) + def render(self, context): return self.s @@ -781,6 +805,9 @@ class VariableNode(Node): else: return output + def iter_render(self, context): + return (self.render(context),) + def render(self, context): output = self.filter_expression.resolve(context) return self.encode_output(output) @@ -869,6 +896,9 @@ class Library(object): def __init__(self, vars_to_resolve): self.vars_to_resolve = vars_to_resolve + #def iter_render(self, context): + # return (self.render(context),) + def render(self, context): resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] return func(*resolved_vars) @@ -891,7 +921,7 @@ class Library(object): def __init__(self, vars_to_resolve): self.vars_to_resolve = vars_to_resolve - def render(self, context): + def iter_render(self, context): resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] if takes_context: args = [context] + resolved_vars @@ -907,7 +937,7 @@ class Library(object): else: t = get_template(file_name) self.nodelist = t.nodelist - return self.nodelist.render(context_class(dict)) + return self.nodelist.iter_render(context_class(dict)) compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode) compile_func.__doc__ = func.__doc__ |
