--- a/web/lib/django/template/__init__.py Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/template/__init__.py Tue May 25 02:43:45 2010 +0200
@@ -48,6 +48,7 @@
>>> t.render(c)
u'<html></html>'
"""
+import imp
import re
from inspect import getargspec
@@ -56,11 +57,13 @@
from django.utils.importlib import import_module
from django.utils.itercompat import is_iterable
from django.utils.functional import curry, Promise
-from django.utils.text import smart_split, unescape_string_literal
+from django.utils.text import smart_split, unescape_string_literal, get_text_list
from django.utils.encoding import smart_unicode, force_unicode, smart_str
from django.utils.translation import ugettext as _
from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
+from django.utils.formats import localize
from django.utils.html import escape
+from django.utils.module_loading import module_has_submodule
__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
@@ -86,7 +89,7 @@
# what to report as the origin for templates that come from non-loader sources
# (e.g. strings)
-UNKNOWN_SOURCE="<unknown source>"
+UNKNOWN_SOURCE = '<unknown source>'
# match a variable or block tag and capture the entire tag, including start/end delimiters
tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
@@ -103,20 +106,7 @@
invalid_var_format_string = None
class TemplateSyntaxError(Exception):
- def __str__(self):
- try:
- import cStringIO as StringIO
- except ImportError:
- import StringIO
- output = StringIO.StringIO()
- output.write(Exception.__str__(self))
- # Check if we wrapped an exception and print that too.
- if hasattr(self, 'exc_info'):
- import traceback
- output.write('\n\nOriginal ')
- e = self.exc_info
- traceback.print_exception(e[0], e[1], e[2], 500, output)
- return output.getvalue()
+ pass
class TemplateDoesNotExist(Exception):
pass
@@ -173,9 +163,16 @@
for subnode in node:
yield subnode
+ def _render(self, context):
+ return self.nodelist.render(context)
+
def render(self, context):
"Display stage -- can be called many times"
- return self.nodelist.render(context)
+ context.render_context.push()
+ try:
+ return self._render(context)
+ finally:
+ context.render_context.pop()
def compile_string(template_string, origin):
"Compiles template_string into NodeList ready for rendering"
@@ -280,7 +277,7 @@
try:
compile_func = self.tags[command]
except KeyError:
- self.invalid_block_tag(token, command)
+ self.invalid_block_tag(token, command, parse_until)
try:
compiled_result = compile_func(self, token)
except TemplateSyntaxError, e:
@@ -331,7 +328,9 @@
def empty_block_tag(self, token):
raise self.error(token, "Empty block tag")
- def invalid_block_tag(self, token, command):
+ def invalid_block_tag(self, token, command, parse_until=None):
+ if parse_until:
+ raise self.error(token, "Invalid block tag: '%s', expected %s" % (command, get_text_list(["'%s'" % p for p in parse_until])))
raise self.error(token, "Invalid block tag: '%s'" % command)
def unclosed_block_tag(self, parse_until):
@@ -411,6 +410,20 @@
"A microparser that parses for a value: some string constant or variable name."
subject = self.subject
i = self.pointer
+
+ def next_space_index(subject, i):
+ "Increment pointer until a real space (i.e. a space not within quotes) is encountered"
+ while i < len(subject) and subject[i] not in (' ', '\t'):
+ if subject[i] in ('"', "'"):
+ c = subject[i]
+ i += 1
+ while i < len(subject) and subject[i] != c:
+ i += 1
+ if i >= len(subject):
+ raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
+ i += 1
+ return i
+
if i >= len(subject):
raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject)
if subject[i] in ('"', "'"):
@@ -421,6 +434,10 @@
if i >= len(subject):
raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
i += 1
+
+ # Continue parsing until next "real" space, so that filters are also included
+ i = next_space_index(subject, i)
+
res = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
i += 1
@@ -429,15 +446,7 @@
return res
else:
p = i
- while i < len(subject) and subject[i] not in (' ', '\t'):
- if subject[i] in ('"', "'"):
- c = subject[i]
- i += 1
- while i < len(subject) and subject[i] != c:
- i += 1
- if i >= len(subject):
- raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
- i += 1
+ i = next_space_index(subject, i)
s = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
i += 1
@@ -518,8 +527,6 @@
var_obj = None
elif var is None:
raise TemplateSyntaxError("Could not find variable at start of %s." % token)
- elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
- raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
else:
var_obj = Variable(var)
else:
@@ -531,8 +538,8 @@
elif var_arg:
args.append((True, Variable(var_arg)))
filter_func = parser.find_filter(filter_name)
- self.args_check(filter_name,filter_func, args)
- filters.append( (filter_func,args))
+ self.args_check(filter_name, filter_func, args)
+ filters.append((filter_func, args))
upto = match.end()
if upto != len(token):
raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token))
@@ -678,6 +685,8 @@
except ValueError:
# Otherwise we'll set self.lookups so that resolve() knows we're
# dealing with a bonafide variable
+ if var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
+ raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
def resolve(self, context):
@@ -743,6 +752,11 @@
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
+ except Exception, e:
+ if getattr(e, 'silent_variable_failure', False):
+ current = settings.TEMPLATE_STRING_IF_INVALID
+ else:
+ raise
return current
@@ -750,6 +764,7 @@
# Set this to True for nodes that must be first in the template (although
# they can be preceded by text nodes.
must_be_first = False
+ child_nodelists = ('nodelist',)
def render(self, context):
"Return the node rendered as a string"
@@ -763,8 +778,10 @@
nodes = []
if isinstance(self, nodetype):
nodes.append(self)
- if hasattr(self, 'nodelist'):
- nodes.extend(self.nodelist.get_nodes_by_type(nodetype))
+ for attr in self.child_nodelists:
+ nodelist = getattr(self, attr, None)
+ if nodelist:
+ nodes.extend(nodelist.get_nodes_by_type(nodetype))
return nodes
class NodeList(list):
@@ -801,13 +818,14 @@
def render(self, context):
return self.s
-
+
def _render_value_in_context(value, context):
"""
Converts any value to a string to become part of a rendered template. This
means escaping, if required, and conversion to a unicode object. If value
is a string, it is expected to have already been translated.
"""
+ value = localize(value)
value = force_unicode(value)
if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData):
return escape(value)
@@ -942,8 +960,14 @@
else:
t = get_template(file_name)
self.nodelist = t.nodelist
- return self.nodelist.render(context_class(dict,
- autoescape=context.autoescape))
+ new_context = context_class(dict, autoescape=context.autoescape)
+ # Copy across the CSRF token, if present, because inclusion
+ # tags are often used for forms, and we need instructions
+ # for using CSRF protection to be as simple as possible.
+ csrf_token = context.get('csrf_token', None)
+ if csrf_token is not None:
+ new_context['csrf_token'] = csrf_token
+ return self.nodelist.render(new_context)
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
compile_func.__doc__ = func.__doc__
@@ -951,22 +975,78 @@
return func
return dec
-def get_library(module_name):
- lib = libraries.get(module_name, None)
+def import_library(taglib_module):
+ """Load a template tag library module.
+
+ Verifies that the library contains a 'register' attribute, and
+ returns that attribute as the representation of the library
+ """
+ app_path, taglib = taglib_module.rsplit('.',1)
+ app_module = import_module(app_path)
+ try:
+ mod = import_module(taglib_module)
+ except ImportError, e:
+ # If the ImportError is because the taglib submodule does not exist, that's not
+ # an error that should be raised. If the submodule exists and raised an ImportError
+ # on the attempt to load it, that we want to raise.
+ if not module_has_submodule(app_module, taglib):
+ return None
+ else:
+ raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % (taglib_module, e))
+ try:
+ return mod.register
+ except AttributeError:
+ raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % taglib_module)
+
+templatetags_modules = []
+
+def get_templatetags_modules():
+ """Return the list of all available template tag modules.
+
+ Caches the result for faster access.
+ """
+ global templatetags_modules
+ if not templatetags_modules:
+ _templatetags_modules = []
+ # Populate list once per thread.
+ for app_module in ['django'] + list(settings.INSTALLED_APPS):
+ try:
+ templatetag_module = '%s.templatetags' % app_module
+ import_module(templatetag_module)
+ _templatetags_modules.append(templatetag_module)
+ except ImportError:
+ continue
+ templatetags_modules = _templatetags_modules
+ return templatetags_modules
+
+def get_library(library_name):
+ """
+ Load the template library module with the given name.
+
+ If library is not already loaded loop over all templatetags modules to locate it.
+
+ {% load somelib %} and {% load someotherlib %} loops twice.
+
+ Subsequent loads eg. {% load somelib %} in the same process will grab the cached
+ module from libraries.
+ """
+ lib = libraries.get(library_name, None)
if not lib:
- try:
- mod = import_module(module_name)
- except ImportError, e:
- raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e))
- try:
- lib = mod.register
- libraries[module_name] = lib
- except AttributeError:
- raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name)
+ templatetags_modules = get_templatetags_modules()
+ tried_modules = []
+ for module in templatetags_modules:
+ taglib_module = '%s.%s' % (module, library_name)
+ tried_modules.append(taglib_module)
+ lib = import_library(taglib_module)
+ if lib:
+ libraries[library_name] = lib
+ break
+ if not lib:
+ raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules)))
return lib
-def add_to_builtins(module_name):
- builtins.append(get_library(module_name))
+def add_to_builtins(module):
+ builtins.append(import_library(module))
add_to_builtins('django.template.defaulttags')
add_to_builtins('django.template.defaultfilters')