diff options
| author | Boulder Sprinters <boulder-sprinters@djangoproject.com> | 2007-06-22 16:56:04 +0000 |
|---|---|---|
| committer | Boulder Sprinters <boulder-sprinters@djangoproject.com> | 2007-06-22 16:56:04 +0000 |
| commit | 0cb8e31823b2e9f05c4ae868c19f5f38e78a5f2e (patch) | |
| tree | ac6cc6152cbb5fe1968dc399bc1716f29cd41165 /django/template | |
| parent | 829b25833a8e8ee6ce1fecdcd0733ededebe065b (diff) | |
boulder-oracle-sprint: Merged to [5511]archive/attic/boulder-oracle-sprint
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5512 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/template')
| -rw-r--r-- | django/template/__init__.py | 85 | ||||
| -rw-r--r-- | django/template/defaulttags.py | 112 | ||||
| -rw-r--r-- | django/template/loader.py | 33 | ||||
| -rw-r--r-- | django/template/loader_tags.py | 25 |
4 files changed, 97 insertions, 158 deletions
diff --git a/django/template/__init__.py b/django/template/__init__.py index 7495eea878..4cda9bc8d0 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -55,7 +55,6 @@ 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 @@ -168,12 +167,9 @@ class Template(object): for subnode in node: yield subnode - def iter_render(self, context): - "Display stage -- can be called many times" - return self.nodelist.iter_render(context) - def render(self, context): - return ''.join(self.iter_render(context)) + "Display stage -- can be called many times" + return self.nodelist.render(context) def compile_string(template_string, origin): "Compiles template_string into NodeList ready for rendering" @@ -699,26 +695,10 @@ 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" - return ''.join(self.iter_render(context)) + pass def __iter__(self): yield self @@ -734,12 +714,13 @@ class Node(object): class NodeList(list): def render(self, context): - return ''.join(self.iter_render(context)) - - def iter_render(self, context): + bits = [] for node in self: - for chunk in node.iter_render(context): - yield chunk + if isinstance(node, Node): + bits.append(self.render_node(node, context)) + else: + bits.append(node) + return ''.join(bits) def get_nodes_by_type(self, nodetype): "Return a list of all nodes of the given type" @@ -748,25 +729,24 @@ 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 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 + 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 class TextNode(Node): def __init__(self, s): @@ -775,9 +755,6 @@ 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 @@ -801,9 +778,6 @@ 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) @@ -892,9 +866,6 @@ 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) @@ -917,7 +888,7 @@ class Library(object): def __init__(self, vars_to_resolve): self.vars_to_resolve = vars_to_resolve - def iter_render(self, context): + def render(self, context): resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] if takes_context: args = [context] + resolved_vars @@ -933,7 +904,7 @@ class Library(object): else: t = get_template(file_name) self.nodelist = t.nodelist - return self.nodelist.iter_render(context_class(dict)) + return self.nodelist.render(context_class(dict)) compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode) compile_func.__doc__ = func.__doc__ diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 77fac6bec5..0e26af0cac 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -15,11 +15,12 @@ if not hasattr(__builtins__, 'reversed'): for index in xrange(len(data)-1, -1, -1): yield data[index] + register = Library() class CommentNode(Node): - def iter_render(self, context): - return () + def render(self, context): + return '' class CycleNode(Node): def __init__(self, cyclevars, variable_name=None): @@ -28,9 +29,6 @@ class CycleNode(Node): self.counter = -1 self.variable_name = variable_name - def iter_render(self, context): - return (self.render(context),) - def render(self, context): self.counter += 1 value = self.cyclevars[self.counter % self.cyclevars_len] @@ -39,32 +37,29 @@ class CycleNode(Node): return value class DebugNode(Node): - def iter_render(self, context): + def render(self, context): from pprint import pformat - for val in context: - yield pformat(val) - yield "\n\n" - yield pformat(sys.modules) + output = [pformat(val) for val in context] + output.append('\n\n') + output.append(pformat(sys.modules)) + return ''.join(output) class FilterNode(Node): def __init__(self, filter_expr, nodelist): self.filter_expr, self.nodelist = filter_expr, nodelist - def iter_render(self, context): + def render(self, context): output = self.nodelist.render(context) # apply filters context.update({'var': output}) filtered = self.filter_expr.resolve(context) context.pop() - return (filtered,) + return filtered class FirstOfNode(Node): def __init__(self, vars): self.vars = vars - def iter_render(self, context): - return (self.render(context),) - def render(self, context): for var in self.vars: try: @@ -100,7 +95,8 @@ class ForNode(Node): nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype)) return nodes - def iter_render(self, context): + def render(self, context): + nodelist = NodeList() if 'forloop' in context: parentloop = context['forloop'] else: @@ -108,12 +104,12 @@ class ForNode(Node): context.push() try: values = self.sequence.resolve(context, True) - if values is None: - values = () - elif not hasattr(values, '__len__'): - values = list(values) except VariableDoesNotExist: - values = () + values = [] + if values is None: + values = [] + if not hasattr(values, '__len__'): + values = list(values) len_values = len(values) if self.reversed: values = reversed(values) @@ -132,17 +128,12 @@ class ForNode(Node): 'parentloop': parentloop, } if unpack: - # If there are multiple loop variables, unpack the item into - # them. + # If there are multiple loop variables, unpack the item into them. context.update(dict(zip(self.loopvars, item))) else: context[self.loopvars[0]] = item - - # We inline this to avoid the overhead since ForNode is pretty - # common. for node in self.nodelist_loop: - for chunk in node.iter_render(context): - yield chunk + nodelist.append(node.render(context)) if unpack: # The loop variables were pushed on to the context so pop them # off again. This is necessary because the tag lets the length @@ -151,6 +142,7 @@ class ForNode(Node): # context. context.pop() context.pop() + return nodelist.render(context) class IfChangedNode(Node): def __init__(self, nodelist, *varlist): @@ -158,7 +150,7 @@ class IfChangedNode(Node): self._last_seen = None self._varlist = varlist - def iter_render(self, context): + def render(self, context): if 'forloop' in context and context['forloop']['first']: self._last_seen = None try: @@ -176,9 +168,11 @@ class IfChangedNode(Node): self._last_seen = compare_to context.push() context['ifchanged'] = {'firstloop': firstloop} - for chunk in self.nodelist.iter_render(context): - yield chunk + content = self.nodelist.render(context) context.pop() + return content + else: + return '' class IfEqualNode(Node): def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): @@ -189,7 +183,7 @@ class IfEqualNode(Node): def __repr__(self): return "<IfEqualNode>" - def iter_render(self, context): + def render(self, context): try: val1 = resolve_variable(self.var1, context) except VariableDoesNotExist: @@ -199,8 +193,8 @@ class IfEqualNode(Node): except VariableDoesNotExist: val2 = None if (self.negate and val1 != val2) or (not self.negate and val1 == val2): - return self.nodelist_true.iter_render(context) - return self.nodelist_false.iter_render(context) + return self.nodelist_true.render(context) + return self.nodelist_false.render(context) class IfNode(Node): def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type): @@ -225,7 +219,7 @@ class IfNode(Node): nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype)) return nodes - def iter_render(self, context): + def render(self, context): if self.link_type == IfNode.LinkTypes.or_: for ifnot, bool_expr in self.bool_exprs: try: @@ -233,8 +227,8 @@ class IfNode(Node): except VariableDoesNotExist: value = None if (value and not ifnot) or (ifnot and not value): - return self.nodelist_true.iter_render(context) - return self.nodelist_false.iter_render(context) + return self.nodelist_true.render(context) + return self.nodelist_false.render(context) else: for ifnot, bool_expr in self.bool_exprs: try: @@ -242,8 +236,8 @@ class IfNode(Node): except VariableDoesNotExist: value = None if not ((value and not ifnot) or (ifnot and not value)): - return self.nodelist_false.iter_render(context) - return self.nodelist_true.iter_render(context) + return self.nodelist_false.render(context) + return self.nodelist_true.render(context) class LinkTypes: and_ = 0, @@ -254,16 +248,16 @@ class RegroupNode(Node): self.target, self.expression = target, expression self.var_name = var_name - def iter_render(self, context): + def render(self, context): obj_list = self.target.resolve(context, True) if obj_list == None: # target_var wasn't found in context; fail silently context[self.var_name] = [] - return () + return '' # List of dictionaries in the format # {'grouper': 'key', 'list': [list of contents]}. context[self.var_name] = [{'grouper':key, 'list':list(val)} for key, val in groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True))] - return () + return '' def include_is_allowed(filepath): for root in settings.ALLOWED_INCLUDE_ROOTS: @@ -275,10 +269,10 @@ class SsiNode(Node): def __init__(self, filepath, parsed): self.filepath, self.parsed = filepath, parsed - def iter_render(self, context): + def render(self, context): if not include_is_allowed(self.filepath): if settings.DEBUG: - return ("[Didn't have permission to include file]",) + return "[Didn't have permission to include file]" else: return '' # Fail silently for invalid includes. try: @@ -289,25 +283,23 @@ class SsiNode(Node): output = '' if self.parsed: try: - return Template(output, name=self.filepath).iter_render(context) + t = Template(output, name=self.filepath) + return t.render(context) except TemplateSyntaxError, e: if settings.DEBUG: return "[Included template had syntax error: %s]" % e else: return '' # Fail silently for invalid included templates. - return (output,) + return output class LoadNode(Node): - def iter_render(self, context): - return () + def render(self, context): + return '' class NowNode(Node): def __init__(self, format_string): self.format_string = format_string - def iter_render(self, context): - return (self.render(context),) - def render(self, context): from datetime import datetime from django.utils.dateformat import DateFormat @@ -336,9 +328,6 @@ class TemplateTagNode(Node): def __init__(self, tagtype): self.tagtype = tagtype - def iter_render(self, context): - return (self.render(context),) - def render(self, context): return self.mapping.get(self.tagtype, '') @@ -348,18 +337,18 @@ class URLNode(Node): self.args = args self.kwargs = kwargs - def iter_render(self, context): + def render(self, context): from django.core.urlresolvers import reverse, NoReverseMatch args = [arg.resolve(context) for arg in self.args] kwargs = dict([(k, v.resolve(context)) for k, v in self.kwargs.items()]) try: - return (reverse(self.view_name, args=args, kwargs=kwargs),) + return reverse(self.view_name, args=args, kwargs=kwargs) except NoReverseMatch: try: project_name = settings.SETTINGS_MODULE.split('.')[0] return reverse(project_name + '.' + self.view_name, args=args, kwargs=kwargs) except NoReverseMatch: - return () + return '' class WidthRatioNode(Node): def __init__(self, val_expr, max_expr, max_width): @@ -367,9 +356,6 @@ class WidthRatioNode(Node): self.max_expr = max_expr self.max_width = max_width - def iter_render(self, context): - return (self.render(context),) - def render(self, context): try: value = self.val_expr.resolve(context) @@ -393,13 +379,13 @@ class WithNode(Node): def __repr__(self): return "<WithNode>" - def iter_render(self, context): + def render(self, context): val = self.var.resolve(context) context.push() context[self.name] = val - for chunk in self.nodelist.iter_render(context): - yield chunk + output = self.nodelist.render(context) context.pop() + return output #@register.tag def comment(parser, token): diff --git a/django/template/loader.py b/django/template/loader.py index 45cf5a9d7c..03e6f8d49d 100644 --- a/django/template/loader.py +++ b/django/template/loader.py @@ -87,12 +87,14 @@ def get_template_from_string(source, origin=None, name=None): """ return Template(source, origin, name) -def _render_setup(template_name, dictionary=None, context_instance=None): +def render_to_string(template_name, dictionary=None, context_instance=None): """ - Common setup code for render_to_string and render_to_iter. + Loads the given template_name and renders it with the given dictionary as + context. The template_name may be a string to load a single template using + get_template, or it may be a tuple to use select_template to find one of + the templates in the list. Returns a string. """ - if dictionary is None: - dictionary = {} + dictionary = dictionary or {} if isinstance(template_name, (list, tuple)): t = select_template(template_name) else: @@ -101,28 +103,7 @@ def _render_setup(template_name, dictionary=None, context_instance=None): context_instance.update(dictionary) else: context_instance = Context(dictionary) - return t, context_instance - -def render_to_string(template_name, dictionary=None, context_instance=None): - """ - Loads the given template_name and renders it with the given dictionary as - context. The template_name may be a string to load a single template using - get_template, or it may be a tuple to use select_template to find one of - the templates in the list. Returns a string. - """ - t, c = _render_setup(template_name, dictionary=dictionary, context_instance=context_instance) - return t.render(c) - -def render_to_iter(template_name, dictionary=None, context_instance=None): - """ - Loads the given template_name and renders it with the given dictionary as - context. The template_name may be a string to load a single template using - get_template, or it may be a tuple to use select_template to find one of - the templates in the list. Returns a string. - """ - t, c = _render_setup(template_name, dictionary=dictionary, context_instance=context_instance) - return t.iter_render(c) - + return t.render(context_instance) def select_template(template_name_list): "Given a list of template names, returns the first that can be loaded." diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index d12d0b55ad..4439e0b010 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -15,14 +15,14 @@ class BlockNode(Node): def __repr__(self): return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist) - def iter_render(self, context): + def render(self, context): context.push() # Save context in case of block.super(). self.context = context context['block'] = self - for chunk in self.nodelist.iter_render(context): - yield chunk + result = self.nodelist.render(context) context.pop() + return result def super(self): if self.parent: @@ -59,7 +59,7 @@ class ExtendsNode(Node): else: return get_template_from_string(source, origin, parent) - def iter_render(self, context): + def render(self, context): compiled_parent = self.get_parent(context) parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode) parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)]) @@ -79,7 +79,7 @@ class ExtendsNode(Node): parent_block.parent = block_node.parent parent_block.add_parent(parent_block.nodelist) parent_block.nodelist = block_node.nodelist - return compiled_parent.iter_render(context) + return compiled_parent.render(context) class ConstantIncludeNode(Node): def __init__(self, template_path): @@ -91,26 +91,27 @@ class ConstantIncludeNode(Node): raise self.template = None - def iter_render(self, context): + def render(self, context): if self.template: - return self.template.iter_render(context) - return () + return self.template.render(context) + else: + return '' class IncludeNode(Node): def __init__(self, template_name): self.template_name = template_name - def iter_render(self, context): + def render(self, context): try: template_name = resolve_variable(self.template_name, context) t = get_template(template_name) - return t.iter_render(context) + return t.render(context) except TemplateSyntaxError, e: if settings.TEMPLATE_DEBUG: raise - return () + return '' except: - return () # Fail silently for invalid included templates. + return '' # Fail silently for invalid included templates. def do_block(parser, token): """ |
