web/lib/django/template/__init__.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 """
       
     2 This is the Django template system.
       
     3 
       
     4 How it works:
       
     5 
       
     6 The Lexer.tokenize() function converts a template string (i.e., a string containing
       
     7 markup with custom template tags) to tokens, which can be either plain text
       
     8 (TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
       
     9 
       
    10 The Parser() class takes a list of tokens in its constructor, and its parse()
       
    11 method returns a compiled template -- which is, under the hood, a list of
       
    12 Node objects.
       
    13 
       
    14 Each Node is responsible for creating some sort of output -- e.g. simple text
       
    15 (TextNode), variable values in a given context (VariableNode), results of basic
       
    16 logic (IfNode), results of looping (ForNode), or anything else. The core Node
       
    17 types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can
       
    18 define their own custom node types.
       
    19 
       
    20 Each Node has a render() method, which takes a Context and returns a string of
       
    21 the rendered node. For example, the render() method of a Variable Node returns
       
    22 the variable's value as a string. The render() method of an IfNode returns the
       
    23 rendered output of whatever was inside the loop, recursively.
       
    24 
       
    25 The Template class is a convenient wrapper that takes care of template
       
    26 compilation and rendering.
       
    27 
       
    28 Usage:
       
    29 
       
    30 The only thing you should ever use directly in this file is the Template class.
       
    31 Create a compiled template object with a template_string, then call render()
       
    32 with a context. In the compilation stage, the TemplateSyntaxError exception
       
    33 will be raised if the template doesn't have proper syntax.
       
    34 
       
    35 Sample code:
       
    36 
       
    37 >>> from django import template
       
    38 >>> s = u'<html>{% if test %}<h1>{{ varvalue }}</h1>{% endif %}</html>'
       
    39 >>> t = template.Template(s)
       
    40 
       
    41 (t is now a compiled template, and its render() method can be called multiple
       
    42 times with multiple contexts)
       
    43 
       
    44 >>> c = template.Context({'test':True, 'varvalue': 'Hello'})
       
    45 >>> t.render(c)
       
    46 u'<html><h1>Hello</h1></html>'
       
    47 >>> c = template.Context({'test':False, 'varvalue': 'Hello'})
       
    48 >>> t.render(c)
       
    49 u'<html></html>'
       
    50 """
       
    51 import imp
       
    52 import re
       
    53 from inspect import getargspec
       
    54 
       
    55 from django.conf import settings
       
    56 from django.template.context import Context, RequestContext, ContextPopException
       
    57 from django.utils.importlib import import_module
       
    58 from django.utils.itercompat import is_iterable
       
    59 from django.utils.functional import curry, Promise
       
    60 from django.utils.text import smart_split, unescape_string_literal, get_text_list
       
    61 from django.utils.encoding import smart_unicode, force_unicode, smart_str
       
    62 from django.utils.translation import ugettext as _
       
    63 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
       
    64 from django.utils.formats import localize
       
    65 from django.utils.html import escape
       
    66 from django.utils.module_loading import module_has_submodule
       
    67 
       
    68 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
       
    69 
       
    70 TOKEN_TEXT = 0
       
    71 TOKEN_VAR = 1
       
    72 TOKEN_BLOCK = 2
       
    73 TOKEN_COMMENT = 3
       
    74 
       
    75 # template syntax constants
       
    76 FILTER_SEPARATOR = '|'
       
    77 FILTER_ARGUMENT_SEPARATOR = ':'
       
    78 VARIABLE_ATTRIBUTE_SEPARATOR = '.'
       
    79 BLOCK_TAG_START = '{%'
       
    80 BLOCK_TAG_END = '%}'
       
    81 VARIABLE_TAG_START = '{{'
       
    82 VARIABLE_TAG_END = '}}'
       
    83 COMMENT_TAG_START = '{#'
       
    84 COMMENT_TAG_END = '#}'
       
    85 SINGLE_BRACE_START = '{'
       
    86 SINGLE_BRACE_END = '}'
       
    87 
       
    88 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.'
       
    89 
       
    90 # what to report as the origin for templates that come from non-loader sources
       
    91 # (e.g. strings)
       
    92 UNKNOWN_SOURCE = '<unknown source>'
       
    93 
       
    94 # match a variable or block tag and capture the entire tag, including start/end delimiters
       
    95 tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
       
    96                                           re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
       
    97                                           re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
       
    98 
       
    99 # global dictionary of libraries that have been loaded using get_library
       
   100 libraries = {}
       
   101 # global list of libraries to load by default for a new parser
       
   102 builtins = []
       
   103 
       
   104 # True if TEMPLATE_STRING_IF_INVALID contains a format string (%s). None means
       
   105 # uninitialised.
       
   106 invalid_var_format_string = None
       
   107 
       
   108 class TemplateSyntaxError(Exception):
       
   109     pass
       
   110 
       
   111 class TemplateDoesNotExist(Exception):
       
   112     pass
       
   113 
       
   114 class TemplateEncodingError(Exception):
       
   115     pass
       
   116 
       
   117 class VariableDoesNotExist(Exception):
       
   118 
       
   119     def __init__(self, msg, params=()):
       
   120         self.msg = msg
       
   121         self.params = params
       
   122 
       
   123     def __str__(self):
       
   124         return unicode(self).encode('utf-8')
       
   125 
       
   126     def __unicode__(self):
       
   127         return self.msg % tuple([force_unicode(p, errors='replace') for p in self.params])
       
   128 
       
   129 class InvalidTemplateLibrary(Exception):
       
   130     pass
       
   131 
       
   132 class Origin(object):
       
   133     def __init__(self, name):
       
   134         self.name = name
       
   135 
       
   136     def reload(self):
       
   137         raise NotImplementedError
       
   138 
       
   139     def __str__(self):
       
   140         return self.name
       
   141 
       
   142 class StringOrigin(Origin):
       
   143     def __init__(self, source):
       
   144         super(StringOrigin, self).__init__(UNKNOWN_SOURCE)
       
   145         self.source = source
       
   146 
       
   147     def reload(self):
       
   148         return self.source
       
   149 
       
   150 class Template(object):
       
   151     def __init__(self, template_string, origin=None, name='<Unknown Template>'):
       
   152         try:
       
   153             template_string = smart_unicode(template_string)
       
   154         except UnicodeDecodeError:
       
   155             raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.")
       
   156         if settings.TEMPLATE_DEBUG and origin is None:
       
   157             origin = StringOrigin(template_string)
       
   158         self.nodelist = compile_string(template_string, origin)
       
   159         self.name = name
       
   160 
       
   161     def __iter__(self):
       
   162         for node in self.nodelist:
       
   163             for subnode in node:
       
   164                 yield subnode
       
   165 
       
   166     def _render(self, context):
       
   167         return self.nodelist.render(context)
       
   168 
       
   169     def render(self, context):
       
   170         "Display stage -- can be called many times"
       
   171         context.render_context.push()
       
   172         try:
       
   173             return self._render(context)
       
   174         finally:
       
   175             context.render_context.pop()
       
   176 
       
   177 def compile_string(template_string, origin):
       
   178     "Compiles template_string into NodeList ready for rendering"
       
   179     if settings.TEMPLATE_DEBUG:
       
   180         from debug import DebugLexer, DebugParser
       
   181         lexer_class, parser_class = DebugLexer, DebugParser
       
   182     else:
       
   183         lexer_class, parser_class = Lexer, Parser
       
   184     lexer = lexer_class(template_string, origin)
       
   185     parser = parser_class(lexer.tokenize())
       
   186     return parser.parse()
       
   187 
       
   188 class Token(object):
       
   189     def __init__(self, token_type, contents):
       
   190         # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT.
       
   191         self.token_type, self.contents = token_type, contents
       
   192 
       
   193     def __str__(self):
       
   194         return '<%s token: "%s...">' % \
       
   195             ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_COMMENT: 'Comment'}[self.token_type],
       
   196             self.contents[:20].replace('\n', ''))
       
   197 
       
   198     def split_contents(self):
       
   199         split = []
       
   200         bits = iter(smart_split(self.contents))
       
   201         for bit in bits:
       
   202             # Handle translation-marked template pieces
       
   203             if bit.startswith('_("') or bit.startswith("_('"):
       
   204                 sentinal = bit[2] + ')'
       
   205                 trans_bit = [bit]
       
   206                 while not bit.endswith(sentinal):
       
   207                     bit = bits.next()
       
   208                     trans_bit.append(bit)
       
   209                 bit = ' '.join(trans_bit)
       
   210             split.append(bit)
       
   211         return split
       
   212 
       
   213 class Lexer(object):
       
   214     def __init__(self, template_string, origin):
       
   215         self.template_string = template_string
       
   216         self.origin = origin
       
   217 
       
   218     def tokenize(self):
       
   219         "Return a list of tokens from a given template_string."
       
   220         in_tag = False
       
   221         result = []
       
   222         for bit in tag_re.split(self.template_string):
       
   223             if bit:
       
   224                 result.append(self.create_token(bit, in_tag))
       
   225             in_tag = not in_tag
       
   226         return result
       
   227 
       
   228     def create_token(self, token_string, in_tag):
       
   229         """
       
   230         Convert the given token string into a new Token object and return it.
       
   231         If in_tag is True, we are processing something that matched a tag,
       
   232         otherwise it should be treated as a literal string.
       
   233         """
       
   234         if in_tag:
       
   235             if token_string.startswith(VARIABLE_TAG_START):
       
   236                 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip())
       
   237             elif token_string.startswith(BLOCK_TAG_START):
       
   238                 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip())
       
   239             elif token_string.startswith(COMMENT_TAG_START):
       
   240                 token = Token(TOKEN_COMMENT, '')
       
   241         else:
       
   242             token = Token(TOKEN_TEXT, token_string)
       
   243         return token
       
   244 
       
   245 class Parser(object):
       
   246     def __init__(self, tokens):
       
   247         self.tokens = tokens
       
   248         self.tags = {}
       
   249         self.filters = {}
       
   250         for lib in builtins:
       
   251             self.add_library(lib)
       
   252 
       
   253     def parse(self, parse_until=None):
       
   254         if parse_until is None: parse_until = []
       
   255         nodelist = self.create_nodelist()
       
   256         while self.tokens:
       
   257             token = self.next_token()
       
   258             if token.token_type == TOKEN_TEXT:
       
   259                 self.extend_nodelist(nodelist, TextNode(token.contents), token)
       
   260             elif token.token_type == TOKEN_VAR:
       
   261                 if not token.contents:
       
   262                     self.empty_variable(token)
       
   263                 filter_expression = self.compile_filter(token.contents)
       
   264                 var_node = self.create_variable_node(filter_expression)
       
   265                 self.extend_nodelist(nodelist, var_node,token)
       
   266             elif token.token_type == TOKEN_BLOCK:
       
   267                 if token.contents in parse_until:
       
   268                     # put token back on token list so calling code knows why it terminated
       
   269                     self.prepend_token(token)
       
   270                     return nodelist
       
   271                 try:
       
   272                     command = token.contents.split()[0]
       
   273                 except IndexError:
       
   274                     self.empty_block_tag(token)
       
   275                 # execute callback function for this tag and append resulting node
       
   276                 self.enter_command(command, token)
       
   277                 try:
       
   278                     compile_func = self.tags[command]
       
   279                 except KeyError:
       
   280                     self.invalid_block_tag(token, command, parse_until)
       
   281                 try:
       
   282                     compiled_result = compile_func(self, token)
       
   283                 except TemplateSyntaxError, e:
       
   284                     if not self.compile_function_error(token, e):
       
   285                         raise
       
   286                 self.extend_nodelist(nodelist, compiled_result, token)
       
   287                 self.exit_command()
       
   288         if parse_until:
       
   289             self.unclosed_block_tag(parse_until)
       
   290         return nodelist
       
   291 
       
   292     def skip_past(self, endtag):
       
   293         while self.tokens:
       
   294             token = self.next_token()
       
   295             if token.token_type == TOKEN_BLOCK and token.contents == endtag:
       
   296                 return
       
   297         self.unclosed_block_tag([endtag])
       
   298 
       
   299     def create_variable_node(self, filter_expression):
       
   300         return VariableNode(filter_expression)
       
   301 
       
   302     def create_nodelist(self):
       
   303         return NodeList()
       
   304 
       
   305     def extend_nodelist(self, nodelist, node, token):
       
   306         if node.must_be_first and nodelist:
       
   307             try:
       
   308                 if nodelist.contains_nontext:
       
   309                     raise AttributeError
       
   310             except AttributeError:
       
   311                 raise TemplateSyntaxError("%r must be the first tag in the template." % node)
       
   312         if isinstance(nodelist, NodeList) and not isinstance(node, TextNode):
       
   313             nodelist.contains_nontext = True
       
   314         nodelist.append(node)
       
   315 
       
   316     def enter_command(self, command, token):
       
   317         pass
       
   318 
       
   319     def exit_command(self):
       
   320         pass
       
   321 
       
   322     def error(self, token, msg):
       
   323         return TemplateSyntaxError(msg)
       
   324 
       
   325     def empty_variable(self, token):
       
   326         raise self.error(token, "Empty variable tag")
       
   327 
       
   328     def empty_block_tag(self, token):
       
   329         raise self.error(token, "Empty block tag")
       
   330 
       
   331     def invalid_block_tag(self, token, command, parse_until=None):
       
   332         if parse_until:
       
   333             raise self.error(token, "Invalid block tag: '%s', expected %s" % (command, get_text_list(["'%s'" % p for p in parse_until])))
       
   334         raise self.error(token, "Invalid block tag: '%s'" % command)
       
   335 
       
   336     def unclosed_block_tag(self, parse_until):
       
   337         raise self.error(None, "Unclosed tags: %s " %  ', '.join(parse_until))
       
   338 
       
   339     def compile_function_error(self, token, e):
       
   340         pass
       
   341 
       
   342     def next_token(self):
       
   343         return self.tokens.pop(0)
       
   344 
       
   345     def prepend_token(self, token):
       
   346         self.tokens.insert(0, token)
       
   347 
       
   348     def delete_first_token(self):
       
   349         del self.tokens[0]
       
   350 
       
   351     def add_library(self, lib):
       
   352         self.tags.update(lib.tags)
       
   353         self.filters.update(lib.filters)
       
   354 
       
   355     def compile_filter(self, token):
       
   356         "Convenient wrapper for FilterExpression"
       
   357         return FilterExpression(token, self)
       
   358 
       
   359     def find_filter(self, filter_name):
       
   360         if filter_name in self.filters:
       
   361             return self.filters[filter_name]
       
   362         else:
       
   363             raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name)
       
   364 
       
   365 class TokenParser(object):
       
   366     """
       
   367     Subclass this and implement the top() method to parse a template line. When
       
   368     instantiating the parser, pass in the line from the Django template parser.
       
   369 
       
   370     The parser's "tagname" instance-variable stores the name of the tag that
       
   371     the filter was called with.
       
   372     """
       
   373     def __init__(self, subject):
       
   374         self.subject = subject
       
   375         self.pointer = 0
       
   376         self.backout = []
       
   377         self.tagname = self.tag()
       
   378 
       
   379     def top(self):
       
   380         "Overload this method to do the actual parsing and return the result."
       
   381         raise NotImplementedError()
       
   382 
       
   383     def more(self):
       
   384         "Returns True if there is more stuff in the tag."
       
   385         return self.pointer < len(self.subject)
       
   386 
       
   387     def back(self):
       
   388         "Undoes the last microparser. Use this for lookahead and backtracking."
       
   389         if not len(self.backout):
       
   390             raise TemplateSyntaxError("back called without some previous parsing")
       
   391         self.pointer = self.backout.pop()
       
   392 
       
   393     def tag(self):
       
   394         "A microparser that just returns the next tag from the line."
       
   395         subject = self.subject
       
   396         i = self.pointer
       
   397         if i >= len(subject):
       
   398             raise TemplateSyntaxError("expected another tag, found end of string: %s" % subject)
       
   399         p = i
       
   400         while i < len(subject) and subject[i] not in (' ', '\t'):
       
   401             i += 1
       
   402         s = subject[p:i]
       
   403         while i < len(subject) and subject[i] in (' ', '\t'):
       
   404             i += 1
       
   405         self.backout.append(self.pointer)
       
   406         self.pointer = i
       
   407         return s
       
   408 
       
   409     def value(self):
       
   410         "A microparser that parses for a value: some string constant or variable name."
       
   411         subject = self.subject
       
   412         i = self.pointer
       
   413 
       
   414         def next_space_index(subject, i):
       
   415             "Increment pointer until a real space (i.e. a space not within quotes) is encountered"
       
   416             while i < len(subject) and subject[i] not in (' ', '\t'):
       
   417                 if subject[i] in ('"', "'"):
       
   418                     c = subject[i]
       
   419                     i += 1
       
   420                     while i < len(subject) and subject[i] != c:
       
   421                         i += 1
       
   422                     if i >= len(subject):
       
   423                         raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
       
   424                 i += 1
       
   425             return i
       
   426 
       
   427         if i >= len(subject):
       
   428             raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject)
       
   429         if subject[i] in ('"', "'"):
       
   430             p = i
       
   431             i += 1
       
   432             while i < len(subject) and subject[i] != subject[p]:
       
   433                 i += 1
       
   434             if i >= len(subject):
       
   435                 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
       
   436             i += 1
       
   437 
       
   438             # Continue parsing until next "real" space, so that filters are also included
       
   439             i = next_space_index(subject, i)
       
   440 
       
   441             res = subject[p:i]
       
   442             while i < len(subject) and subject[i] in (' ', '\t'):
       
   443                 i += 1
       
   444             self.backout.append(self.pointer)
       
   445             self.pointer = i
       
   446             return res
       
   447         else:
       
   448             p = i
       
   449             i = next_space_index(subject, i)
       
   450             s = subject[p:i]
       
   451             while i < len(subject) and subject[i] in (' ', '\t'):
       
   452                 i += 1
       
   453             self.backout.append(self.pointer)
       
   454             self.pointer = i
       
   455             return s
       
   456 
       
   457 # This only matches constant *strings* (things in quotes or marked for
       
   458 # translation). Numbers are treated as variables for implementation reasons
       
   459 # (so that they retain their type when passed to filters).
       
   460 constant_string = r"""
       
   461 (?:%(i18n_open)s%(strdq)s%(i18n_close)s|
       
   462 %(i18n_open)s%(strsq)s%(i18n_close)s|
       
   463 %(strdq)s|
       
   464 %(strsq)s)
       
   465 """ % {
       
   466     'strdq': r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string
       
   467     'strsq': r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string
       
   468     'i18n_open' : re.escape("_("),
       
   469     'i18n_close' : re.escape(")"),
       
   470     }
       
   471 constant_string = constant_string.replace("\n", "")
       
   472 
       
   473 filter_raw_string = r"""
       
   474 ^(?P<constant>%(constant)s)|
       
   475 ^(?P<var>[%(var_chars)s]+|%(num)s)|
       
   476  (?:%(filter_sep)s
       
   477      (?P<filter_name>\w+)
       
   478          (?:%(arg_sep)s
       
   479              (?:
       
   480               (?P<constant_arg>%(constant)s)|
       
   481               (?P<var_arg>[%(var_chars)s]+|%(num)s)
       
   482              )
       
   483          )?
       
   484  )""" % {
       
   485     'constant': constant_string,
       
   486     'num': r'[-+\.]?\d[\d\.e]*',
       
   487     'var_chars': "\w\." ,
       
   488     'filter_sep': re.escape(FILTER_SEPARATOR),
       
   489     'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR),
       
   490   }
       
   491 
       
   492 filter_re = re.compile(filter_raw_string, re.UNICODE|re.VERBOSE)
       
   493 
       
   494 class FilterExpression(object):
       
   495     r"""
       
   496     Parses a variable token and its optional filters (all as a single string),
       
   497     and return a list of tuples of the filter name and arguments.
       
   498     Sample:
       
   499         >>> token = 'variable|default:"Default value"|date:"Y-m-d"'
       
   500         >>> p = Parser('')
       
   501         >>> fe = FilterExpression(token, p)
       
   502         >>> len(fe.filters)
       
   503         2
       
   504         >>> fe.var
       
   505         <Variable: 'variable'>
       
   506 
       
   507     This class should never be instantiated outside of the
       
   508     get_filters_from_token helper function.
       
   509     """
       
   510     def __init__(self, token, parser):
       
   511         self.token = token
       
   512         matches = filter_re.finditer(token)
       
   513         var_obj = None
       
   514         filters = []
       
   515         upto = 0
       
   516         for match in matches:
       
   517             start = match.start()
       
   518             if upto != start:
       
   519                 raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s"  % \
       
   520                         (token[:upto], token[upto:start], token[start:]))
       
   521             if var_obj is None:
       
   522                 var, constant = match.group("var", "constant")
       
   523                 if constant:
       
   524                     try:
       
   525                         var_obj = Variable(constant).resolve({})
       
   526                     except VariableDoesNotExist:
       
   527                         var_obj = None
       
   528                 elif var is None:
       
   529                     raise TemplateSyntaxError("Could not find variable at start of %s." % token)
       
   530                 else:
       
   531                     var_obj = Variable(var)
       
   532             else:
       
   533                 filter_name = match.group("filter_name")
       
   534                 args = []
       
   535                 constant_arg, var_arg = match.group("constant_arg", "var_arg")
       
   536                 if constant_arg:
       
   537                     args.append((False, Variable(constant_arg).resolve({})))
       
   538                 elif var_arg:
       
   539                     args.append((True, Variable(var_arg)))
       
   540                 filter_func = parser.find_filter(filter_name)
       
   541                 self.args_check(filter_name, filter_func, args)
       
   542                 filters.append((filter_func, args))
       
   543             upto = match.end()
       
   544         if upto != len(token):
       
   545             raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token))
       
   546 
       
   547         self.filters = filters
       
   548         self.var = var_obj
       
   549 
       
   550     def resolve(self, context, ignore_failures=False):
       
   551         if isinstance(self.var, Variable):
       
   552             try:
       
   553                 obj = self.var.resolve(context)
       
   554             except VariableDoesNotExist:
       
   555                 if ignore_failures:
       
   556                     obj = None
       
   557                 else:
       
   558                     if settings.TEMPLATE_STRING_IF_INVALID:
       
   559                         global invalid_var_format_string
       
   560                         if invalid_var_format_string is None:
       
   561                             invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
       
   562                         if invalid_var_format_string:
       
   563                             return settings.TEMPLATE_STRING_IF_INVALID % self.var
       
   564                         return settings.TEMPLATE_STRING_IF_INVALID
       
   565                     else:
       
   566                         obj = settings.TEMPLATE_STRING_IF_INVALID
       
   567         else:
       
   568             obj = self.var
       
   569         for func, args in self.filters:
       
   570             arg_vals = []
       
   571             for lookup, arg in args:
       
   572                 if not lookup:
       
   573                     arg_vals.append(mark_safe(arg))
       
   574                 else:
       
   575                     arg_vals.append(arg.resolve(context))
       
   576             if getattr(func, 'needs_autoescape', False):
       
   577                 new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
       
   578             else:
       
   579                 new_obj = func(obj, *arg_vals)
       
   580             if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
       
   581                 obj = mark_safe(new_obj)
       
   582             elif isinstance(obj, EscapeData):
       
   583                 obj = mark_for_escaping(new_obj)
       
   584             else:
       
   585                 obj = new_obj
       
   586         return obj
       
   587 
       
   588     def args_check(name, func, provided):
       
   589         provided = list(provided)
       
   590         plen = len(provided)
       
   591         # Check to see if a decorator is providing the real function.
       
   592         func = getattr(func, '_decorated_function', func)
       
   593         args, varargs, varkw, defaults = getargspec(func)
       
   594         # First argument is filter input.
       
   595         args.pop(0)
       
   596         if defaults:
       
   597             nondefs = args[:-len(defaults)]
       
   598         else:
       
   599             nondefs = args
       
   600         # Args without defaults must be provided.
       
   601         try:
       
   602             for arg in nondefs:
       
   603                 provided.pop(0)
       
   604         except IndexError:
       
   605             # Not enough
       
   606             raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen))
       
   607 
       
   608         # Defaults can be overridden.
       
   609         defaults = defaults and list(defaults) or []
       
   610         try:
       
   611             for parg in provided:
       
   612                 defaults.pop(0)
       
   613         except IndexError:
       
   614             # Too many.
       
   615             raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen))
       
   616 
       
   617         return True
       
   618     args_check = staticmethod(args_check)
       
   619 
       
   620     def __str__(self):
       
   621         return self.token
       
   622 
       
   623 def resolve_variable(path, context):
       
   624     """
       
   625     Returns the resolved variable, which may contain attribute syntax, within
       
   626     the given context.
       
   627 
       
   628     Deprecated; use the Variable class instead.
       
   629     """
       
   630     return Variable(path).resolve(context)
       
   631 
       
   632 class Variable(object):
       
   633     r"""
       
   634     A template variable, resolvable against a given context. The variable may be
       
   635     a hard-coded string (if it begins and ends with single or double quote
       
   636     marks)::
       
   637 
       
   638         >>> c = {'article': {'section':u'News'}}
       
   639         >>> Variable('article.section').resolve(c)
       
   640         u'News'
       
   641         >>> Variable('article').resolve(c)
       
   642         {'section': u'News'}
       
   643         >>> class AClass: pass
       
   644         >>> c = AClass()
       
   645         >>> c.article = AClass()
       
   646         >>> c.article.section = u'News'
       
   647 
       
   648     (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
       
   649     """
       
   650 
       
   651     def __init__(self, var):
       
   652         self.var = var
       
   653         self.literal = None
       
   654         self.lookups = None
       
   655         self.translate = False
       
   656 
       
   657         try:
       
   658             # First try to treat this variable as a number.
       
   659             #
       
   660             # Note that this could cause an OverflowError here that we're not
       
   661             # catching. Since this should only happen at compile time, that's
       
   662             # probably OK.
       
   663             self.literal = float(var)
       
   664 
       
   665             # So it's a float... is it an int? If the original value contained a
       
   666             # dot or an "e" then it was a float, not an int.
       
   667             if '.' not in var and 'e' not in var.lower():
       
   668                 self.literal = int(self.literal)
       
   669 
       
   670             # "2." is invalid
       
   671             if var.endswith('.'):
       
   672                 raise ValueError
       
   673 
       
   674         except ValueError:
       
   675             # A ValueError means that the variable isn't a number.
       
   676             if var.startswith('_(') and var.endswith(')'):
       
   677                 # The result of the lookup should be translated at rendering
       
   678                 # time.
       
   679                 self.translate = True
       
   680                 var = var[2:-1]
       
   681             # If it's wrapped with quotes (single or double), then
       
   682             # we're also dealing with a literal.
       
   683             try:
       
   684                 self.literal = mark_safe(unescape_string_literal(var))
       
   685             except ValueError:
       
   686                 # Otherwise we'll set self.lookups so that resolve() knows we're
       
   687                 # dealing with a bonafide variable
       
   688                 if var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
       
   689                     raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
       
   690                 self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
       
   691 
       
   692     def resolve(self, context):
       
   693         """Resolve this variable against a given context."""
       
   694         if self.lookups is not None:
       
   695             # We're dealing with a variable that needs to be resolved
       
   696             value = self._resolve_lookup(context)
       
   697         else:
       
   698             # We're dealing with a literal, so it's already been "resolved"
       
   699             value = self.literal
       
   700         if self.translate:
       
   701             return _(value)
       
   702         return value
       
   703 
       
   704     def __repr__(self):
       
   705         return "<%s: %r>" % (self.__class__.__name__, self.var)
       
   706 
       
   707     def __str__(self):
       
   708         return self.var
       
   709 
       
   710     def _resolve_lookup(self, context):
       
   711         """
       
   712         Performs resolution of a real variable (i.e. not a literal) against the
       
   713         given context.
       
   714 
       
   715         As indicated by the method's name, this method is an implementation
       
   716         detail and shouldn't be called by external code. Use Variable.resolve()
       
   717         instead.
       
   718         """
       
   719         current = context
       
   720         for bit in self.lookups:
       
   721             try: # dictionary lookup
       
   722                 current = current[bit]
       
   723             except (TypeError, AttributeError, KeyError):
       
   724                 try: # attribute lookup
       
   725                     current = getattr(current, bit)
       
   726                     if callable(current):
       
   727                         if getattr(current, 'alters_data', False):
       
   728                             current = settings.TEMPLATE_STRING_IF_INVALID
       
   729                         else:
       
   730                             try: # method call (assuming no args required)
       
   731                                 current = current()
       
   732                             except TypeError: # arguments *were* required
       
   733                                 # GOTCHA: This will also catch any TypeError
       
   734                                 # raised in the function itself.
       
   735                                 current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call
       
   736                             except Exception, e:
       
   737                                 if getattr(e, 'silent_variable_failure', False):
       
   738                                     current = settings.TEMPLATE_STRING_IF_INVALID
       
   739                                 else:
       
   740                                     raise
       
   741                 except (TypeError, AttributeError):
       
   742                     try: # list-index lookup
       
   743                         current = current[int(bit)]
       
   744                     except (IndexError, # list index out of range
       
   745                             ValueError, # invalid literal for int()
       
   746                             KeyError,   # current is a dict without `int(bit)` key
       
   747                             TypeError,  # unsubscriptable object
       
   748                             ):
       
   749                         raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
       
   750                 except Exception, e:
       
   751                     if getattr(e, 'silent_variable_failure', False):
       
   752                         current = settings.TEMPLATE_STRING_IF_INVALID
       
   753                     else:
       
   754                         raise
       
   755             except Exception, e:
       
   756                 if getattr(e, 'silent_variable_failure', False):
       
   757                     current = settings.TEMPLATE_STRING_IF_INVALID
       
   758                 else:
       
   759                     raise
       
   760 
       
   761         return current
       
   762 
       
   763 class Node(object):
       
   764     # Set this to True for nodes that must be first in the template (although
       
   765     # they can be preceded by text nodes.
       
   766     must_be_first = False
       
   767     child_nodelists = ('nodelist',)
       
   768 
       
   769     def render(self, context):
       
   770         "Return the node rendered as a string"
       
   771         pass
       
   772 
       
   773     def __iter__(self):
       
   774         yield self
       
   775 
       
   776     def get_nodes_by_type(self, nodetype):
       
   777         "Return a list of all nodes (within this node and its nodelist) of the given type"
       
   778         nodes = []
       
   779         if isinstance(self, nodetype):
       
   780             nodes.append(self)
       
   781         for attr in self.child_nodelists:
       
   782             nodelist = getattr(self, attr, None)
       
   783             if nodelist:
       
   784                 nodes.extend(nodelist.get_nodes_by_type(nodetype))
       
   785         return nodes
       
   786 
       
   787 class NodeList(list):
       
   788     # Set to True the first time a non-TextNode is inserted by
       
   789     # extend_nodelist().
       
   790     contains_nontext = False
       
   791 
       
   792     def render(self, context):
       
   793         bits = []
       
   794         for node in self:
       
   795             if isinstance(node, Node):
       
   796                 bits.append(self.render_node(node, context))
       
   797             else:
       
   798                 bits.append(node)
       
   799         return mark_safe(''.join([force_unicode(b) for b in bits]))
       
   800 
       
   801     def get_nodes_by_type(self, nodetype):
       
   802         "Return a list of all nodes of the given type"
       
   803         nodes = []
       
   804         for node in self:
       
   805             nodes.extend(node.get_nodes_by_type(nodetype))
       
   806         return nodes
       
   807 
       
   808     def render_node(self, node, context):
       
   809         return node.render(context)
       
   810 
       
   811 class TextNode(Node):
       
   812     def __init__(self, s):
       
   813         self.s = s
       
   814 
       
   815     def __repr__(self):
       
   816         return "<Text Node: '%s'>" % smart_str(self.s[:25], 'ascii',
       
   817                 errors='replace')
       
   818 
       
   819     def render(self, context):
       
   820         return self.s
       
   821 
       
   822 def _render_value_in_context(value, context):
       
   823     """
       
   824     Converts any value to a string to become part of a rendered template. This
       
   825     means escaping, if required, and conversion to a unicode object. If value
       
   826     is a string, it is expected to have already been translated.
       
   827     """
       
   828     value = localize(value)
       
   829     value = force_unicode(value)
       
   830     if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData):
       
   831         return escape(value)
       
   832     else:
       
   833         return value
       
   834 
       
   835 class VariableNode(Node):
       
   836     def __init__(self, filter_expression):
       
   837         self.filter_expression = filter_expression
       
   838 
       
   839     def __repr__(self):
       
   840         return "<Variable Node: %s>" % self.filter_expression
       
   841 
       
   842     def render(self, context):
       
   843         try:
       
   844             output = self.filter_expression.resolve(context)
       
   845         except UnicodeDecodeError:
       
   846             # Unicode conversion can fail sometimes for reasons out of our
       
   847             # control (e.g. exception rendering). In that case, we fail quietly.
       
   848             return ''
       
   849         return _render_value_in_context(output, context)
       
   850 
       
   851 def generic_tag_compiler(params, defaults, name, node_class, parser, token):
       
   852     "Returns a template.Node subclass."
       
   853     bits = token.split_contents()[1:]
       
   854     bmax = len(params)
       
   855     def_len = defaults and len(defaults) or 0
       
   856     bmin = bmax - def_len
       
   857     if(len(bits) < bmin or len(bits) > bmax):
       
   858         if bmin == bmax:
       
   859             message = "%s takes %s arguments" % (name, bmin)
       
   860         else:
       
   861             message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
       
   862         raise TemplateSyntaxError(message)
       
   863     return node_class(bits)
       
   864 
       
   865 class Library(object):
       
   866     def __init__(self):
       
   867         self.filters = {}
       
   868         self.tags = {}
       
   869 
       
   870     def tag(self, name=None, compile_function=None):
       
   871         if name == None and compile_function == None:
       
   872             # @register.tag()
       
   873             return self.tag_function
       
   874         elif name != None and compile_function == None:
       
   875             if(callable(name)):
       
   876                 # @register.tag
       
   877                 return self.tag_function(name)
       
   878             else:
       
   879                 # @register.tag('somename') or @register.tag(name='somename')
       
   880                 def dec(func):
       
   881                     return self.tag(name, func)
       
   882                 return dec
       
   883         elif name != None and compile_function != None:
       
   884             # register.tag('somename', somefunc)
       
   885             self.tags[name] = compile_function
       
   886             return compile_function
       
   887         else:
       
   888             raise InvalidTemplateLibrary("Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function))
       
   889 
       
   890     def tag_function(self,func):
       
   891         self.tags[getattr(func, "_decorated_function", func).__name__] = func
       
   892         return func
       
   893 
       
   894     def filter(self, name=None, filter_func=None):
       
   895         if name == None and filter_func == None:
       
   896             # @register.filter()
       
   897             return self.filter_function
       
   898         elif filter_func == None:
       
   899             if(callable(name)):
       
   900                 # @register.filter
       
   901                 return self.filter_function(name)
       
   902             else:
       
   903                 # @register.filter('somename') or @register.filter(name='somename')
       
   904                 def dec(func):
       
   905                     return self.filter(name, func)
       
   906                 return dec
       
   907         elif name != None and filter_func != None:
       
   908             # register.filter('somename', somefunc)
       
   909             self.filters[name] = filter_func
       
   910             return filter_func
       
   911         else:
       
   912             raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func))
       
   913 
       
   914     def filter_function(self, func):
       
   915         self.filters[getattr(func, "_decorated_function", func).__name__] = func
       
   916         return func
       
   917 
       
   918     def simple_tag(self,func):
       
   919         params, xx, xxx, defaults = getargspec(func)
       
   920 
       
   921         class SimpleNode(Node):
       
   922             def __init__(self, vars_to_resolve):
       
   923                 self.vars_to_resolve = map(Variable, vars_to_resolve)
       
   924 
       
   925             def render(self, context):
       
   926                 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
       
   927                 return func(*resolved_vars)
       
   928 
       
   929         compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
       
   930         compile_func.__doc__ = func.__doc__
       
   931         self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
       
   932         return func
       
   933 
       
   934     def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
       
   935         def dec(func):
       
   936             params, xx, xxx, defaults = getargspec(func)
       
   937             if takes_context:
       
   938                 if params[0] == 'context':
       
   939                     params = params[1:]
       
   940                 else:
       
   941                     raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")
       
   942 
       
   943             class InclusionNode(Node):
       
   944                 def __init__(self, vars_to_resolve):
       
   945                     self.vars_to_resolve = map(Variable, vars_to_resolve)
       
   946 
       
   947                 def render(self, context):
       
   948                     resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
       
   949                     if takes_context:
       
   950                         args = [context] + resolved_vars
       
   951                     else:
       
   952                         args = resolved_vars
       
   953 
       
   954                     dict = func(*args)
       
   955 
       
   956                     if not getattr(self, 'nodelist', False):
       
   957                         from django.template.loader import get_template, select_template
       
   958                         if not isinstance(file_name, basestring) and is_iterable(file_name):
       
   959                             t = select_template(file_name)
       
   960                         else:
       
   961                             t = get_template(file_name)
       
   962                         self.nodelist = t.nodelist
       
   963                     new_context = context_class(dict, autoescape=context.autoescape)
       
   964                     # Copy across the CSRF token, if present, because inclusion
       
   965                     # tags are often used for forms, and we need instructions
       
   966                     # for using CSRF protection to be as simple as possible.
       
   967                     csrf_token = context.get('csrf_token', None)
       
   968                     if csrf_token is not None:
       
   969                         new_context['csrf_token'] = csrf_token
       
   970                     return self.nodelist.render(new_context)
       
   971 
       
   972             compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
       
   973             compile_func.__doc__ = func.__doc__
       
   974             self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
       
   975             return func
       
   976         return dec
       
   977 
       
   978 def import_library(taglib_module):
       
   979     """Load a template tag library module.
       
   980 
       
   981     Verifies that the library contains a 'register' attribute, and
       
   982     returns that attribute as the representation of the library
       
   983     """
       
   984     app_path, taglib = taglib_module.rsplit('.',1)
       
   985     app_module = import_module(app_path)
       
   986     try:
       
   987         mod = import_module(taglib_module)
       
   988     except ImportError, e:
       
   989         # If the ImportError is because the taglib submodule does not exist, that's not
       
   990         # an error that should be raised. If the submodule exists and raised an ImportError
       
   991         # on the attempt to load it, that we want to raise.
       
   992         if not module_has_submodule(app_module, taglib):
       
   993             return None
       
   994         else:
       
   995             raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % (taglib_module, e))
       
   996     try:
       
   997         return mod.register
       
   998     except AttributeError:
       
   999         raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % taglib_module)
       
  1000 
       
  1001 templatetags_modules = []
       
  1002 
       
  1003 def get_templatetags_modules():
       
  1004     """Return the list of all available template tag modules.
       
  1005 
       
  1006     Caches the result for faster access.
       
  1007     """
       
  1008     global templatetags_modules
       
  1009     if not templatetags_modules:
       
  1010         _templatetags_modules = []
       
  1011         # Populate list once per thread.
       
  1012         for app_module in ['django'] + list(settings.INSTALLED_APPS):
       
  1013             try:
       
  1014                 templatetag_module = '%s.templatetags' % app_module
       
  1015                 import_module(templatetag_module)
       
  1016                 _templatetags_modules.append(templatetag_module)
       
  1017             except ImportError:
       
  1018                 continue
       
  1019         templatetags_modules = _templatetags_modules
       
  1020     return templatetags_modules
       
  1021 
       
  1022 def get_library(library_name):
       
  1023     """
       
  1024     Load the template library module with the given name.
       
  1025 
       
  1026     If library is not already loaded loop over all templatetags modules to locate it.
       
  1027 
       
  1028     {% load somelib %} and {% load someotherlib %} loops twice.
       
  1029 
       
  1030     Subsequent loads eg. {% load somelib %} in the same process will grab the cached
       
  1031     module from libraries.
       
  1032     """
       
  1033     lib = libraries.get(library_name, None)
       
  1034     if not lib:
       
  1035         templatetags_modules = get_templatetags_modules()
       
  1036         tried_modules = []
       
  1037         for module in templatetags_modules:
       
  1038             taglib_module = '%s.%s' % (module, library_name)
       
  1039             tried_modules.append(taglib_module)
       
  1040             lib = import_library(taglib_module)
       
  1041             if lib:
       
  1042                 libraries[library_name] = lib
       
  1043                 break
       
  1044         if not lib:
       
  1045             raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules)))
       
  1046     return lib
       
  1047 
       
  1048 def add_to_builtins(module):
       
  1049     builtins.append(import_library(module))
       
  1050 
       
  1051 add_to_builtins('django.template.defaulttags')
       
  1052 add_to_builtins('django.template.defaultfilters')