50
44
>>> c = template.Context({'test':True, 'varvalue': 'Hello'})
52
'\n<html>\n\n <h1>Hello</h1>\n\n</html>\n'
46
u'<html><h1>Hello</h1></html>'
53
47
>>> c = template.Context({'test':False, 'varvalue': 'Hello'})
55
'\n<html>\n\n</html>\n'
58
52
from inspect import getargspec
59
53
from django.conf import settings
60
54
from django.template.context import Context, RequestContext, ContextPopException
61
from django.utils.functional import curry
55
from django.utils.itercompat import is_iterable
56
from django.utils.functional import curry, Promise
62
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
64
63
__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
91
90
tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
92
91
re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
93
92
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
94
# global dictionary of libraries that have been loaded using get_library
99
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
102
103
class TemplateSyntaxError(Exception):
103
104
def __str__(self):
151
158
class Template(object):
152
159
def __init__(self, template_string, origin=None, name='<Unknown Template>'):
154
if settings.TEMPLATE_DEBUG and origin == None:
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:
155
165
origin = StringOrigin(template_string)
156
# Could do some crazy stack-frame stuff to record where this string
158
166
self.nodelist = compile_string(template_string, origin)
170
178
def compile_string(template_string, origin):
171
179
"Compiles template_string into NodeList ready for rendering"
172
lexer = lexer_factory(template_string, origin)
173
parser = parser_factory(lexer.tokenize())
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())
174
187
return parser.parse()
176
189
class Token(object):
177
190
def __init__(self, token_type, contents):
178
"The token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT"
191
# token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT.
179
192
self.token_type, self.contents = token_type, contents
181
194
def __str__(self):
192
217
self.origin = origin
194
219
def tokenize(self):
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)
220
"Return a list of tokens from a given template_string."
223
for bit in tag_re.split(self.template_string):
225
result.append(self.create_token(bit, in_tag))
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, '')
229
def create_token(self, token_string, in_tag):
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.
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
token = Token(TOKEN_COMMENT, '')
209
243
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
246
class Parser(object):
237
247
def __init__(self, tokens):
238
248
self.tokens = tokens
302
320
def exit_command(self):
305
def error(self, token, msg ):
323
def error(self, token, msg):
306
324
return TemplateSyntaxError(msg)
308
326
def empty_variable(self, token):
309
raise self.error( token, "Empty variable tag")
327
raise self.error(token, "Empty variable tag")
311
329
def empty_block_tag(self, token):
312
raise self.error( token, "Empty block tag")
330
raise self.error(token, "Empty block tag")
314
332
def invalid_block_tag(self, token, command):
315
raise self.error( token, "Invalid block tag: '%s'" % command)
333
raise self.error(token, "Invalid block tag: '%s'" % command)
317
335
def unclosed_block_tag(self, parse_until):
318
336
raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until))
338
356
return FilterExpression(token, self)
340
358
def find_filter(self, filter_name):
341
if self.filters.has_key(filter_name):
359
if filter_name in self.filters:
342
360
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)
362
raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name)
396
364
class TokenParser(object):
442
410
subject = self.subject
444
412
if i >= len(subject):
445
raise TemplateSyntaxError, "Searching for value. Expected another value but found end of string: %s" % subject
413
raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject)
446
414
if subject[i] in ('"', "'"):
449
417
while i < len(subject) and subject[i] != subject[p]:
451
419
if i >= len(subject):
452
raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject)
420
raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
454
422
res = subject[p:i]
455
423
while i < len(subject) and subject[i] in (' ', '\t'):
527
493
for match in matches:
528
494
start = match.start()
529
495
if upto != start:
530
raise TemplateSyntaxError, "Could not parse some characters: %s|%s|%s" % \
531
(token[:upto], token[upto:start], token[start:])
496
raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s" % \
497
(token[:upto], token[upto:start], token[start:]))
533
499
var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
535
var = '"%s"' % _(i18n_constant)
537
var = '"%s"' % constant
500
if i18n_constant is not None:
501
# Don't pass the empty string to gettext, because the empty
502
# string translates to meta information.
503
if i18n_constant == "":
506
var = '"%s"' % _(i18n_constant.replace(r'\"', '"'))
507
elif constant is not None:
508
var = '"%s"' % constant.replace(r'\"', '"')
538
509
upto = match.end()
540
raise TemplateSyntaxError, "Could not find variable at start of %s" % token
511
raise TemplateSyntaxError("Could not find variable at start of %s" % token)
541
512
elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
542
raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
513
raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
544
515
filter_name = match.group("filter_name")
549
520
elif constant_arg is not None:
550
521
args.append((False, constant_arg.replace(r'\"', '"')))
552
args.append((True, var_arg))
523
args.append((True, Variable(var_arg)))
553
524
filter_func = parser.find_filter(filter_name)
554
525
self.args_check(filter_name,filter_func, args)
555
526
filters.append( (filter_func,args))
556
527
upto = match.end()
557
528
if upto != len(token):
558
raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
559
self.var, self.filters = var, filters
529
raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token))
530
self.filters = filters
531
self.var = Variable(var)
561
533
def resolve(self, context, ignore_failures=False):
563
obj = resolve_variable(self.var, context)
535
obj = self.var.resolve(context)
564
536
except VariableDoesNotExist:
565
537
if ignore_failures:
568
540
if settings.TEMPLATE_STRING_IF_INVALID:
541
global invalid_var_format_string
542
if invalid_var_format_string is None:
543
invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
544
if invalid_var_format_string:
545
return settings.TEMPLATE_STRING_IF_INVALID % self.var
569
546
return settings.TEMPLATE_STRING_IF_INVALID
571
548
obj = settings.TEMPLATE_STRING_IF_INVALID
574
551
for lookup, arg in args:
553
arg_vals.append(mark_safe(arg))
578
arg_vals.append(resolve_variable(arg, context))
579
obj = func(obj, *arg_vals)
555
arg_vals.append(arg.resolve(context))
556
if getattr(func, 'needs_autoescape', False):
557
new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
559
new_obj = func(obj, *arg_vals)
560
if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
561
obj = mark_safe(new_obj)
562
elif isinstance(obj, EscapeData):
563
obj = mark_for_escaping(new_obj)
582
568
def args_check(name, func, provided):
617
603
def resolve_variable(path, context):
619
605
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)
608
Deprecated; use the Variable class instead.
610
return Variable(path).resolve(context)
612
class Variable(object):
614
A template variable, resolvable against a given context. The variable may be
615
a hard-coded string (if it begins and ends with single or double quote
618
>>> c = {'article': {'section':u'News'}}
619
>>> Variable('article.section').resolve(c)
621
>>> Variable('article').resolve(c)
623
>>> class AClass: pass
625
>>> c.article = AClass()
626
>>> c.article.section = u'News'
627
>>> Variable('article.section').resolve(c)
635
630
(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]:
633
def __init__(self, var):
637
self.translate = False
640
# First try to treat this variable as a number.
642
# Note that this could cause an OverflowError here that we're not
643
# catching. Since this should only happen at compile time, that's
645
self.literal = float(var)
647
# So it's a float... is it an int? If the original value contained a
648
# dot or an "e" then it was a float, not an int.
649
if '.' not in var and 'e' not in var.lower():
650
self.literal = int(self.literal)
653
if var.endswith('.'):
657
# A ValueError means that the variable isn't a number.
658
if var.startswith('_(') and var.endswith(')'):
659
# The result of the lookup should be translated at rendering
661
self.translate = True
663
# If it's wrapped with quotes (single or double), then
664
# we're also dealing with a literal.
665
if var[0] in "\"'" and var[0] == var[-1]:
666
self.literal = mark_safe(var[1:-1])
668
# Otherwise we'll set self.lookups so that resolve() knows we're
669
# dealing with a bonafide variable
670
self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
672
def resolve(self, context):
673
"""Resolve this variable against a given context."""
674
if self.lookups is not None:
675
# We're dealing with a variable that needs to be resolved
676
value = self._resolve_lookup(context)
678
# We're dealing with a literal, so it's already been "resolved"
685
return "<%s: %r>" % (self.__class__.__name__, self.var)
690
def _resolve_lookup(self, context):
692
Performs resolution of a real variable (i.e. not a literal) against the
695
As indicated by the method's name, this method is an implementation
696
detail and shouldn't be called by external code. Use Variable.resolve()
643
699
current = context
644
bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
700
for bit in self.lookups:
646
701
try: # dictionary lookup
647
current = current[bits[0]]
702
current = current[bit]
648
703
except (TypeError, AttributeError, KeyError):
649
704
try: # attribute lookup
650
current = getattr(current, bits[0])
705
current = getattr(current, bit)
651
706
if callable(current):
652
707
if getattr(current, 'alters_data', False):
653
708
current = settings.TEMPLATE_STRING_IF_INVALID
666
721
except (TypeError, AttributeError):
667
722
try: # list-index lookup
668
current = current[int(bits[0])]
723
current = current[int(bit)]
669
724
except (IndexError, # list index out of range
670
725
ValueError, # invalid literal for int()
671
KeyError, # current is a dict without `int(bits[0])` key
726
KeyError, # current is a dict without `int(bit)` key
672
727
TypeError, # unsubscriptable object
674
raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bits[0], current)) # missing attribute
729
raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
675
730
except Exception, e:
676
731
if getattr(e, 'silent_variable_failure', False):
677
732
current = settings.TEMPLATE_STRING_IF_INVALID
683
738
class Node(object):
739
# Set this to True for nodes that must be first in the template (although
740
# they can be preceded by text nodes.
741
must_be_first = False
684
743
def render(self, context):
685
744
"Return the node rendered as a string"
717
780
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()
781
return node.render(context)
736
783
class TextNode(Node):
737
784
def __init__(self, s):
750
797
def __repr__(self):
751
798
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)
800
def render(self, context):
802
output = force_unicode(self.filter_expression.resolve(context))
803
except UnicodeDecodeError:
804
# Unicode conversion can fail sometimes for reasons out of our
805
# control (e.g. exception rendering). In that case, we fail quietly.
807
if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
808
return force_unicode(escape(output))
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)
810
return force_unicode(output)
780
812
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
781
813
"Returns a template.Node subclass."
850
882
class SimpleNode(Node):
851
883
def __init__(self, vars_to_resolve):
852
self.vars_to_resolve = vars_to_resolve
884
self.vars_to_resolve = map(Variable, vars_to_resolve)
854
886
def render(self, context):
855
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
887
resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
856
888
return func(*resolved_vars)
858
890
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
867
899
if params[0] == 'context':
868
900
params = params[1:]
870
raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
902
raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")
872
904
class InclusionNode(Node):
873
905
def __init__(self, vars_to_resolve):
874
self.vars_to_resolve = vars_to_resolve
906
self.vars_to_resolve = map(Variable, vars_to_resolve)
876
908
def render(self, context):
877
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
909
resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
878
910
if takes_context:
879
911
args = [context] + resolved_vars
885
917
if not getattr(self, 'nodelist', False):
886
918
from django.template.loader import get_template, select_template
887
if hasattr(file_name, '__iter__'):
919
if not isinstance(file_name, basestring) and is_iterable(file_name):
888
920
t = select_template(file_name)
890
922
t = get_template(file_name)
891
923
self.nodelist = t.nodelist
892
return self.nodelist.render(context_class(dict))
924
return self.nodelist.render(context_class(dict,
925
autoescape=context.autoescape))
894
927
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
895
928
compile_func.__doc__ = func.__doc__
904
937
mod = __import__(module_name, {}, {}, [''])
905
938
except ImportError, e:
906
raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e)
939
raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e))
908
941
lib = mod.register
909
942
libraries[module_name] = lib
910
943
except AttributeError:
911
raise InvalidTemplateLibrary, "Template library %s does not have a variable named 'register'" % module_name
944
raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name)
914
947
def add_to_builtins(module_name):