Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #12248 -- Refactored django.template to get code out of __init_…

…_.py, to help with avoiding circular import dependencies. Thanks to Tom Tobin for the patch.

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