Fix a small typo in docs/upload_handling.txt
[django.git] / django / template / __init__.py
blob5c4ab3052ad3cda41128a73c301e6b872df24441
1 """
2 This is the Django template system.
4 How it works:
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).
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.
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.
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.
25 The Template class is a convenient wrapper that takes care of template
26 compilation and rendering.
28 Usage:
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.
35 Sample code:
37 >>> from django import template
38 >>> s = u'<html>{% if test %}<h1>{{ varvalue }}</h1>{% endif %}</html>'
39 >>> t = template.Template(s)
41 (t is now a compiled template, and its render() method can be called multiple
42 times with multiple contexts)
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
63 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
65 TOKEN_TEXT = 0
66 TOKEN_VAR = 1
67 TOKEN_BLOCK = 2
68 TOKEN_COMMENT = 3
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 = '}'
83 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.'
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;"
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)))
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 = []
99 # True if TEMPLATE_STRING_IF_INVALID contains a format string (%s). None means
100 # uninitialised.
101 invalid_var_format_string = None
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()
119 class TemplateDoesNotExist(Exception):
120 pass
122 class TemplateEncodingError(Exception):
123 pass
125 class VariableDoesNotExist(Exception):
127 def __init__(self, msg, params=()):
128 self.msg = msg
129 self.params = params
131 def __str__(self):
132 return unicode(self).encode('utf-8')
134 def __unicode__(self):
135 return self.msg % tuple([force_unicode(p, errors='replace') for p in self.params])
137 class InvalidTemplateLibrary(Exception):
138 pass
140 class Origin(object):
141 def __init__(self, name):
142 self.name = name
144 def reload(self):
145 raise NotImplementedError
147 def __str__(self):
148 return self.name
150 class StringOrigin(Origin):
151 def __init__(self, source):
152 super(StringOrigin, self).__init__(UNKNOWN_SOURCE)
153 self.source = source
155 def reload(self):
156 return self.source
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
169 def __iter__(self):
170 for node in self.nodelist:
171 for subnode in node:
172 yield subnode
174 def render(self, context):
175 "Display stage -- can be called many times"
176 return self.nodelist.render(context)
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()
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
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', ''))
199 def split_contents(self):
200 return list(smart_split(self.contents))
202 class Lexer(object):
203 def __init__(self, template_string, origin):
204 self.template_string = template_string
205 self.origin = origin
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
217 def create_token(self, token_string, in_tag):
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.
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
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)
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
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])
288 def create_variable_node(self, filter_expression):
289 return VariableNode(filter_expression)
291 def create_nodelist(self):
292 return NodeList()
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)
305 def enter_command(self, command, token):
306 pass
308 def exit_command(self):
309 pass
311 def error(self, token, msg):
312 return TemplateSyntaxError(msg)
314 def empty_variable(self, token):
315 raise self.error(token, "Empty variable tag")
317 def empty_block_tag(self, token):
318 raise self.error(token, "Empty block tag")
320 def invalid_block_tag(self, token, command):
321 raise self.error(token, "Invalid block tag: '%s'" % command)
323 def unclosed_block_tag(self, parse_until):
324 raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until))
326 def compile_function_error(self, token, e):
327 pass
329 def next_token(self):
330 return self.tokens.pop(0)
332 def prepend_token(self, token):
333 self.tokens.insert(0, token)
335 def delete_first_token(self):
336 del self.tokens[0]
338 def add_library(self, lib):
339 self.tags.update(lib.tags)
340 self.filters.update(lib.filters)
342 def compile_filter(self, token):
343 "Convenient wrapper for FilterExpression"
344 return FilterExpression(token, self)
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)
352 class TokenParser(object):
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.
357 The parser's "tagname" instance-variable stores the name of the tag that
358 the filter was called with.
360 def __init__(self, subject):
361 self.subject = subject
362 self.pointer = 0
363 self.backout = []
364 self.tagname = self.tag()
366 def top(self):
367 "Overload this method to do the actual parsing and return the result."
368 raise NotImplementedError()
370 def more(self):
371 "Returns True if there is more stuff in the tag."
372 return self.pointer < len(self.subject)
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()
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
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
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
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]+)
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(")"),
456 filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
457 filter_re = re.compile(filter_raw_string, re.UNICODE)
459 class FilterExpression(object):
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)
469 >>> fe.var
470 <Variable: 'variable'>
472 This class should never be instantiated outside of the
473 get_filters_from_token helper function.
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)
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
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))
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))
580 return True
581 args_check = staticmethod(args_check)
583 def __str__(self):
584 return self.token
586 def resolve_variable(path, context):
588 Returns the resolved variable, which may contain attribute syntax, within
589 the given context.
591 Deprecated; use the Variable class instead.
593 return Variable(path).resolve(context)
595 class Variable(object):
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)::
601 >>> c = {'article': {'section':u'News'}}
602 >>> Variable('article.section').resolve(c)
603 u'News'
604 >>> Variable('article').resolve(c)
605 {'section': u'News'}
606 >>> class AClass: pass
607 >>> c = AClass()
608 >>> c.article = AClass()
609 >>> c.article.section = u'News'
610 >>> Variable('article.section').resolve(c)
611 u'News'
613 (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
616 def __init__(self, var):
617 self.var = var
618 self.literal = None
619 self.lookups = None
620 self.translate = False
622 try:
623 # First try to treat this variable as a number.
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)
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)
635 # "2." is invalid
636 if var.endswith('.'):
637 raise ValueError
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))
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
667 def __repr__(self):
668 return "<%s: %r>" % (self.__class__.__name__, self.var)
670 def __str__(self):
671 return self.var
673 def _resolve_lookup(self, context):
675 Performs resolution of a real variable (i.e. not a literal) against the
676 given context.
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.
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
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
719 return current
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
726 def render(self, context):
727 "Return the node rendered as a string"
728 pass
730 def __iter__(self):
731 yield self
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
742 class NodeList(list):
743 # Set to True the first time a non-TextNode is inserted by
744 # extend_nodelist().
745 contains_nontext = False
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]))
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
763 def render_node(self, node, context):
764 return node.render(context)
766 class TextNode(Node):
767 def __init__(self, s):
768 self.s = s
770 def __repr__(self):
771 return "<Text Node: '%s'>" % self.s[:25]
773 def render(self, context):
774 return self.s
776 class VariableNode(Node):
777 def __init__(self, filter_expression):
778 self.filter_expression = filter_expression
780 def __repr__(self):
781 return "<Variable Node: %s>" % self.filter_expression
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)
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)
809 class Library(object):
810 def __init__(self):
811 self.filters = {}
812 self.tags = {}
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))
834 def tag_function(self,func):
835 self.tags[getattr(func, "_decorated_function", func).__name__] = func
836 return func
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))
858 def filter_function(self, func):
859 self.filters[getattr(func, "_decorated_function", func).__name__] = func
860 return func
862 def simple_tag(self,func):
863 params, xx, xxx, defaults = getargspec(func)
865 class SimpleNode(Node):
866 def __init__(self, vars_to_resolve):
867 self.vars_to_resolve = map(Variable, vars_to_resolve)
869 def render(self, context):
870 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
871 return func(*resolved_vars)
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
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'")
887 class InclusionNode(Node):
888 def __init__(self, vars_to_resolve):
889 self.vars_to_resolve = map(Variable, vars_to_resolve)
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
898 dict = func(*args)
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))
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
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
930 def add_to_builtins(module_name):
931 builtins.append(get_library(module_name))
933 add_to_builtins('django.template.defaulttags')
934 add_to_builtins('django.template.defaultfilters')