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.
41 ... <h1>{{ varvalue }}</h1>
45 >>> t = template.Template(s)
47 (t is now a compiled template, and its render() method can be called multiple
48 times with multiple contexts)
50 >>> c = template.Context({'test':True, 'varvalue': 'Hello'})
52 '\n<html>\n\n <h1>Hello</h1>\n\n</html>\n'
53 >>> c = template.Context({'test':False, 'varvalue': 'Hello'})
55 '\n<html>\n\n</html>\n'
58 from inspect
import getargspec
59 from django
.conf
import settings
60 from django
.template
.context
import Context
, RequestContext
, ContextPopException
61 from django
.utils
.functional
import curry
62 from django
.utils
.text
import smart_split
64 __all__
= ('Template', 'Context', 'RequestContext', 'compile_string')
71 # template syntax constants
72 FILTER_SEPARATOR
= '|'
73 FILTER_ARGUMENT_SEPARATOR
= ':'
74 VARIABLE_ATTRIBUTE_SEPARATOR
= '.'
75 BLOCK_TAG_START
= '{%'
77 VARIABLE_TAG_START
= '{{'
78 VARIABLE_TAG_END
= '}}'
79 COMMENT_TAG_START
= '{#'
80 COMMENT_TAG_END
= '#}'
81 SINGLE_BRACE_START
= '{'
82 SINGLE_BRACE_END
= '}'
84 ALLOWED_VARIABLE_CHARS
= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.'
86 # what to report as the origin for templates that come from non-loader sources
88 UNKNOWN_SOURCE
="<unknown source>"
90 # match a variable or block tag and capture the entire tag, including start/end delimiters
91 tag_re
= re
.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re
.escape(BLOCK_TAG_START
), re
.escape(BLOCK_TAG_END
),
92 re
.escape(VARIABLE_TAG_START
), re
.escape(VARIABLE_TAG_END
),
93 re
.escape(COMMENT_TAG_START
), re
.escape(COMMENT_TAG_END
)))
94 # matches if the string is valid number
95 number_re
= re
.compile(r
'[-+]?(\d+|\d*\.\d+)$')
97 # global dictionary of libraries that have been loaded using get_library
99 # global list of libraries to load by default for a new parser
102 class TemplateSyntaxError(Exception):
105 import cStringIO
as StringIO
108 output
= StringIO
.StringIO()
109 output
.write(Exception.__str
__(self
))
110 # Check if we wrapped an exception and print that too.
111 if hasattr(self
, 'exc_info'):
113 output
.write('\n\nOriginal ')
115 traceback
.print_exception(e
[0], e
[1], e
[2], 500, output
)
116 return output
.getvalue()
118 class TemplateDoesNotExist(Exception):
121 class VariableDoesNotExist(Exception):
123 def __init__(self
, msg
, params
=()):
128 return self
.msg
% self
.params
130 class InvalidTemplateLibrary(Exception):
133 class Origin(object):
134 def __init__(self
, name
):
138 raise NotImplementedError
143 class StringOrigin(Origin
):
144 def __init__(self
, source
):
145 super(StringOrigin
, self
).__init
__(UNKNOWN_SOURCE
)
151 class Template(object):
152 def __init__(self
, template_string
, origin
=None, name
='<Unknown Template>'):
154 if settings
.TEMPLATE_DEBUG
and origin
== None:
155 origin
= StringOrigin(template_string
)
156 # Could do some crazy stack-frame stuff to record where this string
158 self
.nodelist
= compile_string(template_string
, origin
)
162 for node
in self
.nodelist
:
166 def render(self
, context
):
167 "Display stage -- can be called many times"
168 return self
.nodelist
.render(context
)
170 def compile_string(template_string
, origin
):
171 "Compiles template_string into NodeList ready for rendering"
172 lexer
= lexer_factory(template_string
, origin
)
173 parser
= parser_factory(lexer
.tokenize())
174 return parser
.parse()
177 def __init__(self
, token_type
, contents
):
178 "The token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT"
179 self
.token_type
, self
.contents
= token_type
, contents
182 return '<%s token: "%s...">' % \
183 ({TOKEN_TEXT
: 'Text', TOKEN_VAR
: 'Var', TOKEN_BLOCK
: 'Block', TOKEN_COMMENT
: 'Comment'}[self
.token_type
],
184 self
.contents
[:20].replace('\n', ''))
186 def split_contents(self
):
187 return list(smart_split(self
.contents
))
190 def __init__(self
, template_string
, origin
):
191 self
.template_string
= template_string
195 "Return a list of tokens from a given template_string"
196 # remove all empty strings, because the regex has a tendency to add them
197 bits
= filter(None, tag_re
.split(self
.template_string
))
198 return map(self
.create_token
, bits
)
200 def create_token(self
,token_string
):
201 "Convert the given token string into a new Token object and return it"
202 if token_string
.startswith(VARIABLE_TAG_START
):
203 token
= Token(TOKEN_VAR
, token_string
[len(VARIABLE_TAG_START
):-len(VARIABLE_TAG_END
)].strip())
204 elif token_string
.startswith(BLOCK_TAG_START
):
205 token
= Token(TOKEN_BLOCK
, token_string
[len(BLOCK_TAG_START
):-len(BLOCK_TAG_END
)].strip())
206 elif token_string
.startswith(COMMENT_TAG_START
):
207 token
= Token(TOKEN_COMMENT
, '')
209 token
= Token(TOKEN_TEXT
, token_string
)
212 class DebugLexer(Lexer
):
213 def __init__(self
, template_string
, origin
):
214 super(DebugLexer
, self
).__init
__(template_string
, origin
)
217 "Return a list of tokens from a given template_string"
218 token_tups
, upto
= [], 0
219 for match
in tag_re
.finditer(self
.template_string
):
220 start
, end
= match
.span()
222 token_tups
.append( (self
.template_string
[upto
:start
], (upto
, start
)) )
224 token_tups
.append( (self
.template_string
[start
:end
], (start
,end
)) )
226 last_bit
= self
.template_string
[upto
:]
228 token_tups
.append( (last_bit
, (upto
, upto
+ len(last_bit
))) )
229 return [self
.create_token(tok
, (self
.origin
, loc
)) for tok
, loc
in token_tups
]
231 def create_token(self
, token_string
, source
):
232 token
= super(DebugLexer
, self
).create_token(token_string
)
233 token
.source
= source
236 class Parser(object):
237 def __init__(self
, tokens
):
242 self
.add_library(lib
)
244 def parse(self
, parse_until
=None):
245 if parse_until
is None: parse_until
= []
246 nodelist
= self
.create_nodelist()
248 token
= self
.next_token()
249 if token
.token_type
== TOKEN_TEXT
:
250 self
.extend_nodelist(nodelist
, TextNode(token
.contents
), token
)
251 elif token
.token_type
== TOKEN_VAR
:
252 if not token
.contents
:
253 self
.empty_variable(token
)
254 filter_expression
= self
.compile_filter(token
.contents
)
255 var_node
= self
.create_variable_node(filter_expression
)
256 self
.extend_nodelist(nodelist
, var_node
,token
)
257 elif token
.token_type
== TOKEN_BLOCK
:
258 if token
.contents
in parse_until
:
259 # put token back on token list so calling code knows why it terminated
260 self
.prepend_token(token
)
263 command
= token
.contents
.split()[0]
265 self
.empty_block_tag(token
)
266 # execute callback function for this tag and append resulting node
267 self
.enter_command(command
, token
)
269 compile_func
= self
.tags
[command
]
271 self
.invalid_block_tag(token
, command
)
273 compiled_result
= compile_func(self
, token
)
274 except TemplateSyntaxError
, e
:
275 if not self
.compile_function_error(token
, e
):
277 self
.extend_nodelist(nodelist
, compiled_result
, token
)
280 self
.unclosed_block_tag(parse_until
)
283 def skip_past(self
, endtag
):
285 token
= self
.next_token()
286 if token
.token_type
== TOKEN_BLOCK
and token
.contents
== endtag
:
288 self
.unclosed_block_tag([endtag
])
290 def create_variable_node(self
, filter_expression
):
291 return VariableNode(filter_expression
)
293 def create_nodelist(self
):
296 def extend_nodelist(self
, nodelist
, node
, token
):
297 nodelist
.append(node
)
299 def enter_command(self
, command
, token
):
302 def exit_command(self
):
305 def error(self
, token
, msg
):
306 return TemplateSyntaxError(msg
)
308 def empty_variable(self
, token
):
309 raise self
.error( token
, "Empty variable tag")
311 def empty_block_tag(self
, token
):
312 raise self
.error( token
, "Empty block tag")
314 def invalid_block_tag(self
, token
, command
):
315 raise self
.error( token
, "Invalid block tag: '%s'" % command
)
317 def unclosed_block_tag(self
, parse_until
):
318 raise self
.error(None, "Unclosed tags: %s " % ', '.join(parse_until
))
320 def compile_function_error(self
, token
, e
):
323 def next_token(self
):
324 return self
.tokens
.pop(0)
326 def prepend_token(self
, token
):
327 self
.tokens
.insert(0, token
)
329 def delete_first_token(self
):
332 def add_library(self
, lib
):
333 self
.tags
.update(lib
.tags
)
334 self
.filters
.update(lib
.filters
)
336 def compile_filter(self
, token
):
337 "Convenient wrapper for FilterExpression"
338 return FilterExpression(token
, self
)
340 def find_filter(self
, filter_name
):
341 if self
.filters
.has_key(filter_name
):
342 return self
.filters
[filter_name
]
344 raise TemplateSyntaxError
, "Invalid filter: '%s'" % filter_name
346 class DebugParser(Parser
):
347 def __init__(self
, lexer
):
348 super(DebugParser
, self
).__init
__(lexer
)
349 self
.command_stack
= []
351 def enter_command(self
, command
, token
):
352 self
.command_stack
.append( (command
, token
.source
) )
354 def exit_command(self
):
355 self
.command_stack
.pop()
357 def error(self
, token
, msg
):
358 return self
.source_error(token
.source
, msg
)
360 def source_error(self
, source
,msg
):
361 e
= TemplateSyntaxError(msg
)
365 def create_nodelist(self
):
366 return DebugNodeList()
368 def create_variable_node(self
, contents
):
369 return DebugVariableNode(contents
)
371 def extend_nodelist(self
, nodelist
, node
, token
):
372 node
.source
= token
.source
373 super(DebugParser
, self
).extend_nodelist(nodelist
, node
, token
)
375 def unclosed_block_tag(self
, parse_until
):
376 command
, source
= self
.command_stack
.pop()
377 msg
= "Unclosed tag '%s'. Looking for one of: %s " % (command
, ', '.join(parse_until
))
378 raise self
.source_error( source
, msg
)
380 def compile_function_error(self
, token
, e
):
381 if not hasattr(e
, 'source'):
382 e
.source
= token
.source
384 def lexer_factory(*args
, **kwargs
):
385 if settings
.TEMPLATE_DEBUG
:
386 return DebugLexer(*args
, **kwargs
)
388 return Lexer(*args
, **kwargs
)
390 def parser_factory(*args
, **kwargs
):
391 if settings
.TEMPLATE_DEBUG
:
392 return DebugParser(*args
, **kwargs
)
394 return Parser(*args
, **kwargs
)
396 class TokenParser(object):
398 Subclass this and implement the top() method to parse a template line. When
399 instantiating the parser, pass in the line from the Django template parser.
401 The parser's "tagname" instance-variable stores the name of the tag that
402 the filter was called with.
404 def __init__(self
, subject
):
405 self
.subject
= subject
408 self
.tagname
= self
.tag()
411 "Overload this method to do the actual parsing and return the result."
415 "Returns True if there is more stuff in the tag."
416 return self
.pointer
< len(self
.subject
)
419 "Undoes the last microparser. Use this for lookahead and backtracking."
420 if not len(self
.backout
):
421 raise TemplateSyntaxError
, "back called without some previous parsing"
422 self
.pointer
= self
.backout
.pop()
425 "A microparser that just returns the next tag from the line."
426 subject
= self
.subject
428 if i
>= len(subject
):
429 raise TemplateSyntaxError
, "expected another tag, found end of string: %s" % subject
431 while i
< len(subject
) and subject
[i
] not in (' ', '\t'):
434 while i
< len(subject
) and subject
[i
] in (' ', '\t'):
436 self
.backout
.append(self
.pointer
)
441 "A microparser that parses for a value: some string constant or variable name."
442 subject
= self
.subject
444 if i
>= len(subject
):
445 raise TemplateSyntaxError
, "Searching for value. Expected another value but found end of string: %s" % subject
446 if subject
[i
] in ('"', "'"):
449 while i
< len(subject
) and subject
[i
] != subject
[p
]:
451 if i
>= len(subject
):
452 raise TemplateSyntaxError
, "Searching for value. Unexpected end of string in column %d: %s" % (i
, subject
)
455 while i
< len(subject
) and subject
[i
] in (' ', '\t'):
457 self
.backout
.append(self
.pointer
)
462 while i
< len(subject
) and subject
[i
] not in (' ', '\t'):
463 if subject
[i
] in ('"', "'"):
466 while i
< len(subject
) and subject
[i
] != c
:
468 if i
>= len(subject
):
469 raise TemplateSyntaxError
, "Searching for value. Unexpected end of string in column %d: %s" % subject
472 while i
< len(subject
) and subject
[i
] in (' ', '\t'):
474 self
.backout
.append(self
.pointer
)
481 filter_raw_string
= r
"""
482 ^%(i18n_open)s"(?P<i18n_constant>%(str)s)"%(i18n_close)s|
483 ^"(?P<constant>%(str)s)"|
484 ^(?P<var>[%(var_chars)s]+)|
489 %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
490 "(?P<constant_arg>%(str)s)"|
491 (?P<var_arg>[%(var_chars)s]+)
495 'str': r
"""[^"\\]*(?:\\.[^"\\]*)*""",
496 'var_chars': "A-Za-z0-9\_\." ,
497 'filter_sep': re
.escape(FILTER_SEPARATOR
),
498 'arg_sep': re
.escape(FILTER_ARGUMENT_SEPARATOR
),
499 'i18n_open' : re
.escape("_("),
500 'i18n_close' : re
.escape(")"),
503 filter_raw_string
= filter_raw_string
.replace("\n", "").replace(" ", "")
504 filter_re
= re
.compile(filter_raw_string
)
506 class FilterExpression(object):
508 Parses a variable token and its optional filters (all as a single string),
509 and return a list of tuples of the filter name and arguments.
511 >>> token = 'variable|default:"Default value"|date:"Y-m-d"'
512 >>> p = FilterParser(token)
514 [('default', 'Default value'), ('date', 'Y-m-d')]
518 This class should never be instantiated outside of the
519 get_filters_from_token helper function.
521 def __init__(self
, token
, parser
):
523 matches
= filter_re
.finditer(token
)
527 for match
in matches
:
528 start
= match
.start()
530 raise TemplateSyntaxError
, "Could not parse some characters: %s|%s|%s" % \
531 (token
[:upto
], token
[upto
:start
], token
[start
:])
533 var
, constant
, i18n_constant
= match
.group("var", "constant", "i18n_constant")
535 var
= '"%s"' % _(i18n_constant
)
537 var
= '"%s"' % constant
540 raise TemplateSyntaxError
, "Could not find variable at start of %s" % token
541 elif var
.find(VARIABLE_ATTRIBUTE_SEPARATOR
+ '_') > -1 or var
[0] == '_':
542 raise TemplateSyntaxError
, "Variables and attributes may not begin with underscores: '%s'" % var
544 filter_name
= match
.group("filter_name")
546 constant_arg
, i18n_arg
, var_arg
= match
.group("constant_arg", "i18n_arg", "var_arg")
548 args
.append((False, _(i18n_arg
.replace(r
'\"', '"'))))
549 elif constant_arg
is not None:
550 args
.append((False, constant_arg
.replace(r
'\"', '"')))
552 args
.append((True, var_arg
))
553 filter_func
= parser
.find_filter(filter_name
)
554 self
.args_check(filter_name
,filter_func
, args
)
555 filters
.append( (filter_func
,args
))
557 if upto
!= len(token
):
558 raise TemplateSyntaxError
, "Could not parse the remainder: %s" % token
[upto
:]
559 self
.var
, self
.filters
= var
, filters
561 def resolve(self
, context
, ignore_failures
=False):
563 obj
= resolve_variable(self
.var
, context
)
564 except VariableDoesNotExist
:
568 if settings
.TEMPLATE_STRING_IF_INVALID
:
569 return settings
.TEMPLATE_STRING_IF_INVALID
571 obj
= settings
.TEMPLATE_STRING_IF_INVALID
572 for func
, args
in self
.filters
:
574 for lookup
, arg
in args
:
578 arg_vals
.append(resolve_variable(arg
, context
))
579 obj
= func(obj
, *arg_vals
)
582 def args_check(name
, func
, provided
):
583 provided
= list(provided
)
585 # Check to see if a decorator is providing the real function.
586 func
= getattr(func
, '_decorated_function', func
)
587 args
, varargs
, varkw
, defaults
= getargspec(func
)
588 # First argument is filter input.
591 nondefs
= args
[:-len(defaults
)]
594 # Args without defaults must be provided.
600 raise TemplateSyntaxError
, "%s requires %d arguments, %d provided" % (name
, len(nondefs
), plen
)
602 # Defaults can be overridden.
603 defaults
= defaults
and list(defaults
) or []
605 for parg
in provided
:
609 raise TemplateSyntaxError
, "%s requires %d arguments, %d provided" % (name
, len(nondefs
), plen
)
612 args_check
= staticmethod(args_check
)
617 def resolve_variable(path
, context
):
619 Returns the resolved variable, which may contain attribute syntax, within
620 the given context. The variable may be a hard-coded string (if it begins
621 and ends with single or double quote marks).
623 >>> c = {'article': {'section':'News'}}
624 >>> resolve_variable('article.section', c)
626 >>> resolve_variable('article', c)
628 >>> class AClass: pass
630 >>> c.article = AClass()
631 >>> c.article.section = 'News'
632 >>> resolve_variable('article.section', c)
635 (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
637 if number_re
.match(path
):
638 number_type
= '.' in path
and float or int
639 current
= number_type(path
)
640 elif path
[0] in ('"', "'") and path
[0] == path
[-1]:
644 bits
= path
.split(VARIABLE_ATTRIBUTE_SEPARATOR
)
646 try: # dictionary lookup
647 current
= current
[bits
[0]]
648 except (TypeError, AttributeError, KeyError):
649 try: # attribute lookup
650 current
= getattr(current
, bits
[0])
651 if callable(current
):
652 if getattr(current
, 'alters_data', False):
653 current
= settings
.TEMPLATE_STRING_IF_INVALID
655 try: # method call (assuming no args required)
657 except TypeError: # arguments *were* required
658 # GOTCHA: This will also catch any TypeError
659 # raised in the function itself.
660 current
= settings
.TEMPLATE_STRING_IF_INVALID
# invalid method call
662 if getattr(e
, 'silent_variable_failure', False):
663 current
= settings
.TEMPLATE_STRING_IF_INVALID
666 except (TypeError, AttributeError):
667 try: # list-index lookup
668 current
= current
[int(bits
[0])]
669 except (IndexError, # list index out of range
670 ValueError, # invalid literal for int()
671 KeyError, # current is a dict without `int(bits[0])` key
672 TypeError, # unsubscriptable object
674 raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bits
[0], current
)) # missing attribute
676 if getattr(e
, 'silent_variable_failure', False):
677 current
= settings
.TEMPLATE_STRING_IF_INVALID
684 def render(self
, context
):
685 "Return the node rendered as a string"
691 def get_nodes_by_type(self
, nodetype
):
692 "Return a list of all nodes (within this node and its nodelist) of the given type"
694 if isinstance(self
, nodetype
):
696 if hasattr(self
, 'nodelist'):
697 nodes
.extend(self
.nodelist
.get_nodes_by_type(nodetype
))
700 class NodeList(list):
701 def render(self
, context
):
704 if isinstance(node
, Node
):
705 bits
.append(self
.render_node(node
, context
))
710 def get_nodes_by_type(self
, nodetype
):
711 "Return a list of all nodes of the given type"
714 nodes
.extend(node
.get_nodes_by_type(nodetype
))
717 def render_node(self
, node
, context
):
718 return(node
.render(context
))
720 class DebugNodeList(NodeList
):
721 def render_node(self
, node
, context
):
723 result
= node
.render(context
)
724 except TemplateSyntaxError
, e
:
725 if not hasattr(e
, 'source'):
726 e
.source
= node
.source
729 from sys
import exc_info
730 wrapped
= TemplateSyntaxError('Caught an exception while rendering: %s' % e
)
731 wrapped
.source
= node
.source
732 wrapped
.exc_info
= exc_info()
736 class TextNode(Node
):
737 def __init__(self
, s
):
741 return "<Text Node: '%s'>" % self
.s
[:25]
743 def render(self
, context
):
746 class VariableNode(Node
):
747 def __init__(self
, filter_expression
):
748 self
.filter_expression
= filter_expression
751 return "<Variable Node: %s>" % self
.filter_expression
753 def encode_output(self
, output
):
754 # Check type so that we don't run str() on a Unicode object
755 if not isinstance(output
, basestring
):
758 except UnicodeEncodeError:
759 # If __str__() returns a Unicode object, convert it to bytestring.
760 return unicode(output
).encode(settings
.DEFAULT_CHARSET
)
761 elif isinstance(output
, unicode):
762 return output
.encode(settings
.DEFAULT_CHARSET
)
766 def render(self
, context
):
767 output
= self
.filter_expression
.resolve(context
)
768 return self
.encode_output(output
)
770 class DebugVariableNode(VariableNode
):
771 def render(self
, context
):
773 output
= self
.filter_expression
.resolve(context
)
774 except TemplateSyntaxError
, e
:
775 if not hasattr(e
, 'source'):
776 e
.source
= self
.source
778 return self
.encode_output(output
)
780 def generic_tag_compiler(params
, defaults
, name
, node_class
, parser
, token
):
781 "Returns a template.Node subclass."
782 bits
= token
.split_contents()[1:]
784 def_len
= defaults
and len(defaults
) or 0
785 bmin
= bmax
- def_len
786 if(len(bits
) < bmin
or len(bits
) > bmax
):
788 message
= "%s takes %s arguments" % (name
, bmin
)
790 message
= "%s takes between %s and %s arguments" % (name
, bmin
, bmax
)
791 raise TemplateSyntaxError
, message
792 return node_class(bits
)
794 class Library(object):
799 def tag(self
, name
=None, compile_function
=None):
800 if name
== None and compile_function
== None:
802 return self
.tag_function
803 elif name
!= None and compile_function
== None:
806 return self
.tag_function(name
)
808 # @register.tag('somename') or @register.tag(name='somename')
810 return self
.tag(name
, func
)
812 elif name
!= None and compile_function
!= None:
813 # register.tag('somename', somefunc)
814 self
.tags
[name
] = compile_function
815 return compile_function
817 raise InvalidTemplateLibrary
, "Unsupported arguments to Library.tag: (%r, %r)", (name
, compile_function
)
819 def tag_function(self
,func
):
820 self
.tags
[getattr(func
, "_decorated_function", func
).__name
__] = func
823 def filter(self
, name
=None, filter_func
=None):
824 if name
== None and filter_func
== None:
826 return self
.filter_function
827 elif filter_func
== None:
830 return self
.filter_function(name
)
832 # @register.filter('somename') or @register.filter(name='somename')
834 return self
.filter(name
, func
)
836 elif name
!= None and filter_func
!= None:
837 # register.filter('somename', somefunc)
838 self
.filters
[name
] = filter_func
841 raise InvalidTemplateLibrary
, "Unsupported arguments to Library.filter: (%r, %r)", (name
, filter_func
)
843 def filter_function(self
, func
):
844 self
.filters
[getattr(func
, "_decorated_function", func
).__name
__] = func
847 def simple_tag(self
,func
):
848 params
, xx
, xxx
, defaults
= getargspec(func
)
850 class SimpleNode(Node
):
851 def __init__(self
, vars_to_resolve
):
852 self
.vars_to_resolve
= vars_to_resolve
854 def render(self
, context
):
855 resolved_vars
= [resolve_variable(var
, context
) for var
in self
.vars_to_resolve
]
856 return func(*resolved_vars
)
858 compile_func
= curry(generic_tag_compiler
, params
, defaults
, getattr(func
, "_decorated_function", func
).__name
__, SimpleNode
)
859 compile_func
.__doc
__ = func
.__doc
__
860 self
.tag(getattr(func
, "_decorated_function", func
).__name
__, compile_func
)
863 def inclusion_tag(self
, file_name
, context_class
=Context
, takes_context
=False):
865 params
, xx
, xxx
, defaults
= getargspec(func
)
867 if params
[0] == 'context':
870 raise TemplateSyntaxError
, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
872 class InclusionNode(Node
):
873 def __init__(self
, vars_to_resolve
):
874 self
.vars_to_resolve
= vars_to_resolve
876 def render(self
, context
):
877 resolved_vars
= [resolve_variable(var
, context
) for var
in self
.vars_to_resolve
]
879 args
= [context
] + resolved_vars
885 if not getattr(self
, 'nodelist', False):
886 from django
.template
.loader
import get_template
, select_template
887 if hasattr(file_name
, '__iter__'):
888 t
= select_template(file_name
)
890 t
= get_template(file_name
)
891 self
.nodelist
= t
.nodelist
892 return self
.nodelist
.render(context_class(dict))
894 compile_func
= curry(generic_tag_compiler
, params
, defaults
, getattr(func
, "_decorated_function", func
).__name
__, InclusionNode
)
895 compile_func
.__doc
__ = func
.__doc
__
896 self
.tag(getattr(func
, "_decorated_function", func
).__name
__, compile_func
)
900 def get_library(module_name
):
901 lib
= libraries
.get(module_name
, None)
904 mod
= __import__(module_name
, {}, {}, [''])
905 except ImportError, e
:
906 raise InvalidTemplateLibrary
, "Could not load template library from %s, %s" % (module_name
, e
)
909 libraries
[module_name
] = lib
910 except AttributeError:
911 raise InvalidTemplateLibrary
, "Template library %s does not have a variable named 'register'" % module_name
914 def add_to_builtins(module_name
):
915 builtins
.append(get_library(module_name
))
917 add_to_builtins('django.template.defaulttags')
918 add_to_builtins('django.template.defaultfilters')