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