2 This is the Django template system.
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
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.
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.
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'})
46 u'<html><h1>Hello</h1></html>'
47 >>> c = template.Context({'test':False, 'varvalue': 'Hello'})
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')
70 # template syntax constants
71 FILTER_SEPARATOR
= '|'
72 FILTER_ARGUMENT_SEPARATOR
= ':'
73 VARIABLE_ATTRIBUTE_SEPARATOR
= '.'
74 BLOCK_TAG_START
= '{%'
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
87 UNKNOWN_SOURCE
="<unknown source>"
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
96 # global list of libraries to load by default for a new parser
99 # True if TEMPLATE_STRING_IF_INVALID contains a format string (%s). None means
101 invalid_var_format_string
= None
103 class TemplateSyntaxError(Exception):
106 import cStringIO
as 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'):
114 output
.write('\n\nOriginal ')
116 traceback
.print_exception(e
[0], e
[1], e
[2], 500, output
)
117 return output
.getvalue()
119 class TemplateDoesNotExist(Exception):
122 class TemplateEncodingError(Exception):
125 class VariableDoesNotExist(Exception):
127 def __init__(self
, msg
, params
=()):
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):
140 class Origin(object):
141 def __init__(self
, name
):
145 raise NotImplementedError
150 class StringOrigin(Origin
):
151 def __init__(self
, source
):
152 super(StringOrigin
, self
).__init
__(UNKNOWN_SOURCE
)
158 class Template(object):
159 def __init__(self
, template_string
, origin
=None, name
='<Unknown Template>'):
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
)
170 for node
in self
.nodelist
:
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
184 lexer_class
, parser_class
= Lexer
, Parser
185 lexer
= lexer_class(template_string
, origin
)
186 parser
= parser_class(lexer
.tokenize())
187 return parser
.parse()
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
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
))
203 def __init__(self
, template_string
, origin
):
204 self
.template_string
= template_string
208 "Return a list of tokens from a given template_string."
211 for bit
in tag_re
.split(self
.template_string
):
213 result
.append(self
.create_token(bit
, in_tag
))
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.
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
, '')
231 token
= Token(TOKEN_TEXT
, token_string
)
234 class Parser(object):
235 def __init__(self
, tokens
):
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()
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
)
261 command
= token
.contents
.split()[0]
263 self
.empty_block_tag(token
)
264 # execute callback function for this tag and append resulting node
265 self
.enter_command(command
, token
)
267 compile_func
= self
.tags
[command
]
269 self
.invalid_block_tag(token
, command
)
271 compiled_result
= compile_func(self
, token
)
272 except TemplateSyntaxError
, e
:
273 if not self
.compile_function_error(token
, e
):
275 self
.extend_nodelist(nodelist
, compiled_result
, token
)
278 self
.unclosed_block_tag(parse_until
)
281 def skip_past(self
, endtag
):
283 token
= self
.next_token()
284 if token
.token_type
== TOKEN_BLOCK
and token
.contents
== endtag
:
286 self
.unclosed_block_tag([endtag
])
288 def create_variable_node(self
, filter_expression
):
289 return VariableNode(filter_expression
)
291 def create_nodelist(self
):
294 def extend_nodelist(self
, nodelist
, node
, token
):
295 if node
.must_be_first
and nodelist
:
297 if nodelist
.contains_nontext
:
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
):
308 def exit_command(self
):
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
):
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
):
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
]
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
364 self
.tagname
= self
.tag()
367 "Overload this method to do the actual parsing and return the result."
368 raise NotImplementedError()
371 "Returns True if there is more stuff in the tag."
372 return self
.pointer
< len(self
.subject
)
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()
381 "A microparser that just returns the next tag from the line."
382 subject
= self
.subject
384 if i
>= len(subject
):
385 raise TemplateSyntaxError("expected another tag, found end of string: %s" % subject
)
387 while i
< len(subject
) and subject
[i
] not in (' ', '\t'):
390 while i
< len(subject
) and subject
[i
] in (' ', '\t'):
392 self
.backout
.append(self
.pointer
)
397 "A microparser that parses for a value: some string constant or variable name."
398 subject
= self
.subject
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 ('"', "'"):
405 while i
< len(subject
) and subject
[i
] != subject
[p
]:
407 if i
>= len(subject
):
408 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i
, subject
))
411 while i
< len(subject
) and subject
[i
] in (' ', '\t'):
413 self
.backout
.append(self
.pointer
)
418 while i
< len(subject
) and subject
[i
] not in (' ', '\t'):
419 if subject
[i
] in ('"', "'"):
422 while i
< len(subject
) and subject
[i
] != c
:
424 if i
>= len(subject
):
425 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i
, subject
))
428 while i
< len(subject
) and subject
[i
] in (' ', '\t'):
430 self
.backout
.append(self
.pointer
)
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]+)|
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]+)
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.
464 >>> token = 'variable|default:"Default value"|date:"Y-m-d"'
466 >>> fe = FilterExpression(token, p)
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
):
477 matches
= filter_re
.finditer(token
)
481 for match
in matches
:
482 start
= match
.start()
484 raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s" % \
485 (token
[:upto
], token
[upto
:start
], token
[start
:]))
487 var
, constant
, i18n_constant
= match
.group("var", "constant", "i18n_constant")
489 var
= '"%s"' % _(i18n_constant
.replace(r
'\"', '"'))
491 var
= '"%s"' % constant
.replace(r
'\"', '"')
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
)
498 filter_name
= match
.group("filter_name")
500 constant_arg
, i18n_arg
, var_arg
= match
.group("constant_arg", "i18n_arg", "var_arg")
502 args
.append((False, _(i18n_arg
.replace(r
'\"', '"'))))
503 elif constant_arg
is not None:
504 args
.append((False, constant_arg
.replace(r
'\"', '"')))
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
))
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):
518 obj
= self
.var
.resolve(context
)
519 except VariableDoesNotExist
:
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
531 obj
= settings
.TEMPLATE_STRING_IF_INVALID
532 for func
, args
in self
.filters
:
534 for lookup
, arg
in args
:
536 arg_vals
.append(mark_safe(arg
))
538 arg_vals
.append(arg
.resolve(context
))
539 if getattr(func
, 'needs_autoescape', False):
540 new_obj
= func(obj
, autoescape
=context
.autoescape
, *arg_vals
)
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
)
551 def args_check(name
, func
, provided
):
552 provided
= list(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.
560 nondefs
= args
[:-len(defaults
)]
563 # Args without defaults must be provided.
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 []
574 for parg
in provided
:
578 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name
, len(nondefs
), plen
))
581 args_check
= staticmethod(args_check
)
586 def resolve_variable(path
, context
):
588 Returns the resolved variable, which may contain attribute syntax, within
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
601 >>> c = {'article': {'section':u'News'}}
602 >>> Variable('article.section').resolve(c)
604 >>> Variable('article').resolve(c)
606 >>> class AClass: pass
608 >>> c.article = AClass()
609 >>> c.article.section = u'News'
610 >>> Variable('article.section').resolve(c)
613 (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
616 def __init__(self
, var
):
620 self
.translate
= False
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
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
)
636 if var
.endswith('.'):
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
644 self
.translate
= True
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])
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
)
661 # We're dealing with a literal, so it's already been "resolved"
668 return "<%s: %r>" % (self
.__class
__.__name
__, self
.var
)
673 def _resolve_lookup(self
, context
):
675 Performs resolution of a real variable (i.e. not a literal) against the
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()
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
693 try: # method call (assuming no args required)
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
700 if getattr(e
, 'silent_variable_failure', False):
701 current
= settings
.TEMPLATE_STRING_IF_INVALID
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
714 if getattr(e
, 'silent_variable_failure', False):
715 current
= settings
.TEMPLATE_STRING_IF_INVALID
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"
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"
736 if isinstance(self
, nodetype
):
738 if hasattr(self
, 'nodelist'):
739 nodes
.extend(self
.nodelist
.get_nodes_by_type(nodetype
))
742 class NodeList(list):
743 # Set to True the first time a non-TextNode is inserted by
745 contains_nontext
= False
747 def render(self
, context
):
750 if isinstance(node
, Node
):
751 bits
.append(self
.render_node(node
, context
))
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"
760 nodes
.extend(node
.get_nodes_by_type(nodetype
))
763 def render_node(self
, node
, context
):
764 return node
.render(context
)
766 class TextNode(Node
):
767 def __init__(self
, s
):
771 return "<Text Node: '%s'>" % self
.s
[:25]
773 def render(self
, context
):
776 class VariableNode(Node
):
777 def __init__(self
, filter_expression
):
778 self
.filter_expression
= filter_expression
781 return "<Variable Node: %s>" % self
.filter_expression
783 def render(self
, context
):
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.
790 if (context
.autoescape
and not isinstance(output
, SafeData
)) or isinstance(output
, EscapeData
):
791 return force_unicode(escape(output
))
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:]
799 def_len
= defaults
and len(defaults
) or 0
800 bmin
= bmax
- def_len
801 if(len(bits
) < bmin
or len(bits
) > bmax
):
803 message
= "%s takes %s arguments" % (name
, bmin
)
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):
814 def tag(self
, name
=None, compile_function
=None):
815 if name
== None and compile_function
== None:
817 return self
.tag_function
818 elif name
!= None and compile_function
== None:
821 return self
.tag_function(name
)
823 # @register.tag('somename') or @register.tag(name='somename')
825 return self
.tag(name
, func
)
827 elif name
!= None and compile_function
!= None:
828 # register.tag('somename', somefunc)
829 self
.tags
[name
] = compile_function
830 return compile_function
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
838 def filter(self
, name
=None, filter_func
=None):
839 if name
== None and filter_func
== None:
841 return self
.filter_function
842 elif filter_func
== None:
845 return self
.filter_function(name
)
847 # @register.filter('somename') or @register.filter(name='somename')
849 return self
.filter(name
, func
)
851 elif name
!= None and filter_func
!= None:
852 # register.filter('somename', somefunc)
853 self
.filters
[name
] = filter_func
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
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
)
878 def inclusion_tag(self
, file_name
, context_class
=Context
, takes_context
=False):
880 params
, xx
, xxx
, defaults
= getargspec(func
)
882 if params
[0] == 'context':
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
]
894 args
= [context
] + resolved_vars
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
)
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
)
916 def get_library(module_name
):
917 lib
= libraries
.get(module_name
, None)
920 mod
= __import__(module_name
, {}, {}, [''])
921 except ImportError, e
:
922 raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name
, e
))
925 libraries
[module_name
] = lib
926 except AttributeError:
927 raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name
)
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')