1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

Backed out the changes in [5482] for a bit whilst some more investigation into

side-effects is done. Refs #4565.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@5511 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick
2007-06-22 07:15:04 +00:00
parent dbebf54d6c
commit 880e3cfaa6
21 changed files with 162 additions and 284 deletions

View File

@@ -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__

View File

@@ -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):

View File

@@ -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."

View File

@@ -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):
"""