summaryrefslogtreecommitdiff
path: root/django/template/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/template/__init__.py')
-rw-r--r--django/template/__init__.py86
1 files changed, 57 insertions, 29 deletions
diff --git a/django/template/__init__.py b/django/template/__init__.py
index 1cfd85be06..761c08d6c9 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -57,6 +57,8 @@ from django.utils.functional import curry, Promise
from django.utils.text import smart_split
from django.utils.encoding import smart_unicode, force_unicode
from django.utils.translation import ugettext as _
+from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
+from django.utils.html import escape
__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
@@ -592,10 +594,19 @@ class FilterExpression(object):
arg_vals = []
for lookup, arg in args:
if not lookup:
- arg_vals.append(arg)
+ arg_vals.append(mark_safe(arg))
else:
arg_vals.append(arg.resolve(context))
- obj = func(obj, *arg_vals)
+ if getattr(func, 'needs_autoescape', False):
+ new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
+ else:
+ new_obj = func(obj, *arg_vals)
+ if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
+ obj = mark_safe(new_obj)
+ elif isinstance(obj, EscapeData):
+ obj = mark_for_escaping(new_obj)
+ else:
+ obj = new_obj
return obj
def args_check(name, func, provided):
@@ -637,7 +648,7 @@ def resolve_variable(path, context):
"""
Returns the resolved variable, which may contain attribute syntax, within
the given context.
-
+
Deprecated; use the Variable class instead.
"""
return Variable(path).resolve(context)
@@ -647,7 +658,7 @@ class Variable(object):
A template variable, resolvable against a given context. The variable may be
a hard-coded string (if it begins and ends with single or double quote
marks)::
-
+
>>> c = {'article': {'section':'News'}}
>>> Variable('article.section').resolve(c)
u'News'
@@ -662,61 +673,69 @@ class Variable(object):
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
"""
-
+
def __init__(self, var):
self.var = var
self.literal = None
self.lookups = None
-
+ self.translate = False
+
try:
# First try to treat this variable as a number.
#
- # Note that this could cause an OverflowError here that we're not
+ # Note that this could cause an OverflowError here that we're not
# catching. Since this should only happen at compile time, that's
# probably OK.
self.literal = float(var)
-
+
# So it's a float... is it an int? If the original value contained a
# dot or an "e" then it was a float, not an int.
if '.' not in var and 'e' not in var.lower():
self.literal = int(self.literal)
-
+
# "2." is invalid
if var.endswith('.'):
raise ValueError
except ValueError:
# A ValueError means that the variable isn't a number.
+ if var.startswith('_(') and var.endswith(')'):
+ # The result of the lookup should be translated at rendering
+ # time.
+ self.translate = True
+ var = var[2:-1]
# If it's wrapped with quotes (single or double), then
# we're also dealing with a literal.
if var[0] in "\"'" and var[0] == var[-1]:
- self.literal = var[1:-1]
-
+ self.literal = mark_safe(var[1:-1])
else:
# Otherwise we'll set self.lookups so that resolve() knows we're
# dealing with a bonafide variable
self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
-
+
def resolve(self, context):
"""Resolve this variable against a given context."""
if self.lookups is not None:
# We're dealing with a variable that needs to be resolved
- return self._resolve_lookup(context)
+ value = self._resolve_lookup(context)
else:
# We're dealing with a literal, so it's already been "resolved"
- return self.literal
-
+ value = self.literal
+ if self.translate:
+ return _(value)
+ return value
+
def __repr__(self):
return "<%s: %r>" % (self.__class__.__name__, self.var)
-
+
def __str__(self):
return self.var
def _resolve_lookup(self, context):
"""
Performs resolution of a real variable (i.e. not a literal) against the
- given context.
-
+ given context.
+
As indicated by the method's name, this method is an implementation
detail and shouldn't be called by external code. Use Variable.resolve()
instead.
@@ -757,14 +776,7 @@ class Variable(object):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
-
- if isinstance(current, (basestring, Promise)):
- try:
- current = force_unicode(current)
- except UnicodeDecodeError:
- # Failing to convert to unicode can happen sometimes (e.g. debug
- # tracebacks). So we allow it in this particular instance.
- pass
+
return current
class Node(object):
@@ -838,16 +850,31 @@ class VariableNode(Node):
return "<Variable Node: %s>" % self.filter_expression
def render(self, context):
- return self.filter_expression.resolve(context)
+ try:
+ output = force_unicode(self.filter_expression.resolve(context))
+ except UnicodeDecodeError:
+ # Unicode conversion can fail sometimes for reasons out of our
+ # control (e.g. exception rendering). In that case, we fail quietly.
+ return ''
+ if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
+ return force_unicode(escape(output))
+ else:
+ return force_unicode(output)
class DebugVariableNode(VariableNode):
def render(self, context):
try:
- return self.filter_expression.resolve(context)
+ output = force_unicode(self.filter_expression.resolve(context))
except TemplateSyntaxError, e:
if not hasattr(e, 'source'):
e.source = self.source
raise
+ except UnicodeDecodeError:
+ return ''
+ if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
+ return escape(output)
+ else:
+ return output
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
"Returns a template.Node subclass."
@@ -961,7 +988,8 @@ class Library(object):
else:
t = get_template(file_name)
self.nodelist = t.nodelist
- return self.nodelist.render(context_class(dict))
+ return self.nodelist.render(context_class(dict,
+ autoescape=context.autoescape))
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
compile_func.__doc__ = func.__doc__