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