Ticket #7799: tplrf.diff
File tplrf.diff, 115.7 KB (added by , 16 years ago) |
---|
-
django/template/nodes.py
1 from django.utils.encoding import force_unicode 2 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping 3 from django.utils.html import escape 4 from django.template.expressions import LookupError 5 from django.conf import settings 6 7 class Node(object): 8 # Set this to True for nodes that must be first in the template (although 9 # they can be preceded by text nodes. 10 must_be_first = False 11 12 def render(self, context): 13 "Return the node rendered as a string" 14 pass 15 16 def __iter__(self): 17 yield self 18 19 def get_nodes_by_type(self, nodetype): 20 "Return a list of all nodes (within this node and its nodelist) of the given type" 21 nodes = [] 22 if isinstance(self, nodetype): 23 nodes.append(self) 24 if hasattr(self, 'nodelist'): 25 nodes.extend(self.nodelist.get_nodes_by_type(nodetype)) 26 return nodes 27 28 class NodeList(list): 29 # Set to True the first time a non-TextNode is inserted by 30 # extend_nodelist(). 31 contains_nontext = False 32 33 def render(self, context): 34 bits = [] 35 for node in self: 36 if isinstance(node, Node): 37 bits.append(self.render_node(node, context)) 38 else: 39 bits.append(node) 40 return mark_safe(''.join([force_unicode(b) for b in bits])) 41 42 def get_nodes_by_type(self, nodetype): 43 "Return a list of all nodes of the given type" 44 nodes = [] 45 for node in self: 46 nodes.extend(node.get_nodes_by_type(nodetype)) 47 return nodes 48 49 def render_node(self, node, context): 50 return node.render(context) 51 52 class TextNode(Node): 53 def __init__(self, s): 54 self.s = s 55 56 def __repr__(self): 57 return "<Text Node: '%s'>" % self.s[:25] 58 59 def render(self, context): 60 return self.s 61 62 class ExpressionNode(Node): 63 def __init__(self, expression): 64 self.expression = expression 65 66 def __repr__(self): 67 return "<Variable Node: %s>" % self.expression 68 69 def render(self, context): 70 try: 71 output = force_unicode(self.expression.resolve(context)) 72 except LookupError: 73 if settings.TEMPLATE_STRING_IF_INVALID: 74 from django.template import invalid_var_format_string 75 if invalid_var_format_string is None: 76 invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID 77 if invalid_var_format_string: 78 return settings.TEMPLATE_STRING_IF_INVALID % self.expression 79 return settings.TEMPLATE_STRING_IF_INVALID 80 else: 81 return '' 82 except UnicodeDecodeError: 83 # Unicode conversion can fail sometimes for reasons out of our 84 # control (e.g. exception rendering). In that case, we fail quietly. 85 return '' 86 if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData): 87 output = escape(output) 88 return output -
django/template/compiler.py
1 import re 2 from inspect import getargspec 3 from django.conf import settings 4 from django.template.context import Context, RequestContext, ContextPopException 5 from django.template.expressions import Expression, Literal, Lookup, FilterExpression, VARIABLE_ATTRIBUTE_SEPARATOR 6 from django.template.nodes import Node, NodeList, ExpressionNode, TextNode 7 from django.utils.text import smart_split 8 from django.utils.encoding import smart_unicode, smart_str 9 from django.utils.safestring import mark_safe 10 from django.utils.translation import ugettext 11 12 __all__ = ('Template', 'TemplateSyntaxError', 'TokenSyntaxError', 'TokenStream') 13 14 TOKEN_TEXT = 0 15 TOKEN_VAR = 1 16 TOKEN_BLOCK = 2 17 TOKEN_COMMENT = 3 18 19 # template syntax constants 20 FILTER_SEPARATOR = '|' 21 FILTER_ARGUMENT_SEPARATOR = ':' 22 BLOCK_TAG_START = '{%' 23 BLOCK_TAG_END = '%}' 24 VARIABLE_TAG_START = '{{' 25 VARIABLE_TAG_END = '}}' 26 COMMENT_TAG_START = '{#' 27 COMMENT_TAG_END = '#}' 28 SINGLE_BRACE_START = '{' 29 SINGLE_BRACE_END = '}' 30 31 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' 32 33 # what to report as the origin for templates that come from non-loader sources 34 # (e.g. strings) 35 UNKNOWN_SOURCE="<unknown source>" 36 37 # match a variable or block tag and capture the entire tag, including start/end delimiters 38 tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 39 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), 40 re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))) 41 42 class TemplateSyntaxError(Exception): 43 def __str__(self): 44 try: 45 import cStringIO as StringIO 46 except ImportError: 47 import StringIO 48 output = StringIO.StringIO() 49 output.write(Exception.__str__(self)) 50 # Check if we wrapped an exception and print that too. 51 if hasattr(self, 'exc_info'): 52 import traceback 53 output.write('\n\nOriginal ') 54 e = self.exc_info 55 traceback.print_exception(e[0], e[1], e[2], 500, output) 56 return output.getvalue() 57 58 class TemplateEncodingError(Exception): 59 pass 60 61 class Origin(object): 62 def __init__(self, name): 63 self.name = name 64 65 def reload(self): 66 raise NotImplementedError 67 68 def __str__(self): 69 return self.name 70 71 class StringOrigin(Origin): 72 def __init__(self, source): 73 super(StringOrigin, self).__init__(UNKNOWN_SOURCE) 74 self.source = source 75 76 def reload(self): 77 return self.source 78 79 class Template(object): 80 def __init__(self, template_string, origin=None, name='<Unknown Template>'): 81 try: 82 template_string = smart_unicode(template_string) 83 except UnicodeDecodeError: 84 raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.") 85 if settings.TEMPLATE_DEBUG and origin is None: 86 origin = StringOrigin(template_string) 87 self.nodelist = compile_string(template_string, origin) 88 self.name = name 89 90 def __iter__(self): 91 for node in self.nodelist: 92 for subnode in node: 93 yield subnode 94 95 def render(self, context): 96 "Display stage -- can be called many times" 97 return self.nodelist.render(context) 98 99 def compile_string(template_string, origin): 100 "Compiles template_string into NodeList ready for rendering" 101 if settings.TEMPLATE_DEBUG: 102 from debug import DebugLexer, DebugParser 103 lexer_class, parser_class = DebugLexer, DebugParser 104 else: 105 lexer_class, parser_class = Lexer, Parser 106 lexer = lexer_class(template_string, origin) 107 parser = parser_class(lexer.tokenize()) 108 return parser.parse() 109 110 class Token(object): 111 def __init__(self, token_type, contents): 112 # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT. 113 self.token_type, self.contents = token_type, contents 114 115 def __str__(self): 116 return '<%s token: "%s...">' % \ 117 ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_COMMENT: 'Comment'}[self.token_type], 118 self.contents[:20].replace('\n', '')) 119 120 def split_contents(self): 121 return list(smart_split(self.contents)) 122 123 class Lexer(object): 124 def __init__(self, template_string, origin): 125 self.template_string = template_string 126 self.origin = origin 127 128 def tokenize(self): 129 "Return a list of tokens from a given template_string." 130 in_tag = False 131 result = [] 132 for bit in tag_re.split(self.template_string): 133 if bit: 134 result.append(self.create_token(bit, in_tag)) 135 in_tag = not in_tag 136 return result 137 138 def create_token(self, token_string, in_tag): 139 """ 140 Convert the given token string into a new Token object and return it. 141 If in_tag is True, we are processing something that matched a tag, 142 otherwise it should be treated as a literal string. 143 """ 144 if in_tag: 145 if token_string.startswith(VARIABLE_TAG_START): 146 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 147 elif token_string.startswith(BLOCK_TAG_START): 148 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) 149 elif token_string.startswith(COMMENT_TAG_START): 150 token = Token(TOKEN_COMMENT, '') 151 else: 152 token = Token(TOKEN_TEXT, token_string) 153 return token 154 155 class Parser(object): 156 def __init__(self, tokens): 157 self.tokens = tokens 158 self.tags = {} 159 self.filters = {} 160 from django.template.library import builtins 161 for lib in builtins: 162 self.add_library(lib) 163 164 def parse(self, parse_until=None): 165 if parse_until is None: parse_until = [] 166 nodelist = self.create_nodelist() 167 while self.tokens: 168 token = self.next_token() 169 if token.token_type == TOKEN_TEXT: 170 self.extend_nodelist(nodelist, TextNode(token.contents), token) 171 elif token.token_type == TOKEN_VAR: 172 if not token.contents: 173 self.empty_variable(token) 174 filter_expression = self.compile_filter(token.contents) 175 var_node = self.create_variable_node(filter_expression) 176 self.extend_nodelist(nodelist, var_node,token) 177 elif token.token_type == TOKEN_BLOCK: 178 if token.contents in parse_until: 179 # put token back on token list so calling code knows why it terminated 180 self.prepend_token(token) 181 return nodelist 182 try: 183 command = token.contents.split()[0] 184 except IndexError: 185 self.empty_block_tag(token) 186 # execute callback function for this tag and append resulting node 187 self.enter_command(command, token) 188 try: 189 compile_func = self.tags[command] 190 except KeyError: 191 self.invalid_block_tag(token, command) 192 try: 193 compiled_result = compile_func(self, token) 194 except TemplateSyntaxError, e: 195 if not self.compile_function_error(token, e): 196 raise 197 self.extend_nodelist(nodelist, compiled_result, token) 198 self.exit_command() 199 if parse_until: 200 self.unclosed_block_tag(parse_until) 201 return nodelist 202 203 def parse_nodelist(self, parse_until=None): 204 nodelist = self.parse(parse_until=parse_until) 205 self.delete_first_token() 206 return nodelist 207 208 def skip_past(self, endtag): 209 while self.tokens: 210 token = self.next_token() 211 if token.token_type == TOKEN_BLOCK and token.contents == endtag: 212 return 213 self.unclosed_block_tag([endtag]) 214 215 def create_variable_node(self, expression): 216 return ExpressionNode(expression) 217 218 def create_nodelist(self): 219 return NodeList() 220 221 def extend_nodelist(self, nodelist, node, token): 222 if node.must_be_first and nodelist: 223 try: 224 if nodelist.contains_nontext: 225 raise AttributeError 226 except AttributeError: 227 raise TemplateSyntaxError("%r must be the first tag in the template." % node) 228 if isinstance(nodelist, NodeList) and not isinstance(node, TextNode): 229 nodelist.contains_nontext = True 230 nodelist.append(node) 231 232 def enter_command(self, command, token): 233 pass 234 235 def exit_command(self): 236 pass 237 238 def error(self, token, msg): 239 return TemplateSyntaxError(msg) 240 241 def empty_variable(self, token): 242 raise self.error(token, "Empty variable tag") 243 244 def empty_block_tag(self, token): 245 raise self.error(token, "Empty block tag") 246 247 def invalid_block_tag(self, token, command): 248 raise self.error(token, "Invalid block tag: '%s'" % command) 249 250 def unclosed_block_tag(self, parse_until): 251 raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until)) 252 253 def compile_function_error(self, token, e): 254 pass 255 256 def next_token(self): 257 return self.tokens.pop(0) 258 259 def prepend_token(self, token): 260 self.tokens.insert(0, token) 261 262 def delete_first_token(self): 263 del self.tokens[0] 264 265 def add_library(self, lib): 266 self.tags.update(lib.tags) 267 self.filters.update(lib.filters) 268 269 def token_stream(self, token): 270 return TokenStream(self, token) 271 272 def compile_filter(self, token): 273 stream = TokenStream(self, token) 274 try: 275 expr = stream.parse_expression() 276 except TokenSyntaxError: 277 stream.expected("expression") 278 stream.assert_consumed("invalid filter expression") 279 return expr 280 281 def find_filter(self, filter_name): 282 if filter_name in self.filters: 283 return self.filters[filter_name] 284 else: 285 raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name) 286 287 def filter_args_check(name, func, provided): 288 provided = list(provided) 289 plen = len(provided) 290 # Check to see if a decorator is providing the real function. 291 func = getattr(func, '_decorated_function', func) 292 args, varargs, varkw, defaults = getargspec(func) 293 294 if plen + 1 == len(args) or (defaults and plen + 1 <= len(args) + len(defaults)): 295 return True 296 297 # First argument is filter input. 298 args.pop(0) 299 if defaults: 300 nondefs = args[:-len(defaults)] 301 else: 302 nondefs = args 303 # Args without defaults must be provided. 304 try: 305 for arg in nondefs: 306 provided.pop(0) 307 except IndexError: 308 # Not enough 309 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen)) 310 311 # Defaults can be overridden. 312 defaults = defaults and list(defaults) or [] 313 try: 314 for parg in provided: 315 defaults.pop(0) 316 except IndexError: 317 # Too many. 318 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen)) 319 320 return True 321 322 323 bit_re = re.compile(r""" 324 (?P<string_literal>"(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)') 325 |(?P<numeric_literal>[+-]?\.?\d[\d\.e]*) 326 |(?P<name>[\w.]+) # keyword or variable 327 |(?P<char>\S) # punctuation 328 """, re.VERBOSE) 329 330 class TokenSyntaxError(Exception): 331 pass 332 333 def token_stream_parser(func): 334 def wrapper(self, *args, **kwargs): 335 mark = self.offset 336 try: 337 return func(self, *args, **kwargs) 338 except TokenSyntaxError: 339 self.offset = mark 340 raise 341 return wrapper 342 343 class TokenStream(object): 344 exception = TokenSyntaxError() 345 def __init__(self, parser, source): 346 self.parser = parser 347 self.source = source 348 self.offset = 0 349 self.name = None 350 if isinstance(source, Token): 351 bits = source.contents.split(None, 1) 352 self.source = len(bits) == 2 and bits[1] or '' 353 self.name = bits[0] 354 self.tokens = [(bit.lastgroup, bit.group(0)) for bit in bit_re.finditer(self.source)] 355 356 def peek(self): 357 return self.tokens[self.offset] 358 359 def consumed(self): 360 return self.offset == len(self.tokens) 361 362 def pop(self): 363 if self.offset == len(self.tokens): 364 raise self.exception 365 next = self.tokens[self.offset] 366 self.offset += 1 367 return next 368 369 def pop_lexem(self, lexem): 370 if self.offset == len(self.tokens): 371 return False 372 _, next = self.tokens[self.offset] 373 if next == lexem: 374 self.offset += 1 375 return True 376 return False 377 378 def pop_name(self): 379 if self.offset == len(self.tokens): 380 return None 381 tokentype, lexem = self.tokens[self.offset] 382 if tokentype == 'name': 383 self.offset += 1 384 return lexem 385 return None 386 387 def pushback(self): 388 self.offset -= 1 389 390 def assert_consumed(self, msg=None): 391 if self.offset != len(self.tokens): 392 raise TemplateSyntaxError, (msg or "unmatched input") + repr(self.tokens[self.offset:]) 393 394 def expected(self, what): 395 if self.consumed(): 396 found = "<EOT>" 397 else: 398 found = "<%s> %s" % self.tokens[self.offset] 399 raise TemplateSyntaxError, "expected %s, found %s" % (what, smart_str(found, encoding='ascii', errors='backslashreplace')) 400 401 @token_stream_parser 402 def parse_string(self, bare=False): 403 tokentype, lexem = self.pop() 404 if tokentype == 'string_literal': 405 return lexem.replace(r'\%s' % lexem[0], lexem[0]).replace(r'\\', '')[1:-1] 406 if bare and tokentype == 'name': 407 return lexem 408 raise self.exception 409 410 @token_stream_parser 411 def parse_value(self): 412 translate = False 413 if self.pop_lexem('_'): 414 if not self.pop_lexem('('): 415 raise self.exception 416 translate = True 417 tokentype, lexem = self.pop() 418 419 if tokentype == 'char': 420 raise self.exception 421 422 if tokentype == 'name': 423 if lexem.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or lexem[0] == '_': 424 raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % lexem) 425 value = Lookup(lexem) 426 elif tokentype == 'string_literal': 427 value = lexem.replace(r'\%s' % lexem[0], lexem[0]).replace(r'\\', '')[1:-1] 428 value = Literal(mark_safe(value)) 429 elif tokentype == 'numeric_literal': 430 try: 431 value = float(lexem) 432 except ValueError: 433 raise self.exception 434 if '.' not in lexem and 'e' not in lexem.lower(): 435 value = int(value) 436 #FIXME: this causes a test failure: `ifequal-numeric07` 437 if lexem.endswith('.'): 438 raise TemplateSyntaxError, "Numeric literals may not end with '.': %s" % lexem 439 value = Literal(value) 440 if translate: 441 if not self.pop_lexem(')'): 442 raise self.exception 443 value = FilterExpression(value, [(ugettext, ())]) 444 return value 445 446 447 @token_stream_parser 448 def parse_filter(self): 449 if not self.pop_lexem('|'): 450 raise self.exception 451 name = self.pop_name() 452 if not name: 453 raise self.exception 454 args = [] 455 if self.pop_lexem(':'): 456 args.append(self.parse_value()) 457 func = self.parser.find_filter(name) 458 filter_args_check(name, func, args) 459 return func, args 460 461 462 @token_stream_parser 463 def parse_expression(self): 464 var = self.parse_value() 465 filters = [] 466 try: 467 while True: 468 filters.append(self.parse_filter()) 469 except TokenSyntaxError: 470 pass 471 if filters: 472 return FilterExpression(var, filters) 473 return var 474 475 476 @token_stream_parser 477 def parse_expression_list(self, minimum=0, maximum=None, count=None): 478 expressions = [] 479 if count: 480 minimum = count 481 maximum = count 482 try: 483 while True: 484 if len(expressions) == maximum: 485 break 486 expressions.append(self.parse_expression()) 487 except TokenSyntaxError: 488 pass 489 if len(expressions) < minimum: 490 self.expected("expression") 491 return expressions 492 -
django/template/__init__.py
12 12 Node objects. 13 13 14 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 basic15 (TextNode), variable values in a given context (ExpressionNode), results of basic 16 16 logic (IfNode), results of looping (ForNode), or anything else. The core Node 17 types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can17 types are TextNode, ExpressionNode, IfNode and ForNode, but plugin modules can 18 18 define their own custom node types. 19 19 20 20 Each Node has a render() method, which takes a Context and returns a string of … … 48 48 >>> t.render(c) 49 49 u'<html></html>' 50 50 """ 51 import re 52 from inspect import getargspec 53 from django.conf import settings 51 54 52 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 53 from django.template.nodes import Node, NodeList, TextNode, ExpressionNode 54 from django.template.compiler import Origin, StringOrigin, Template, \ 55 TemplateSyntaxError, compile_string, TokenSyntaxError 56 from django.template.library import Library, InvalidTemplateLibrary, \ 57 get_library, add_to_builtins, libraries 58 from django.template.expressions import Expression, LookupError 62 59 63 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string') 60 #backcompat 61 from django.template.compat import Variable, VariableDoesNotExist, VariableNode, \ 62 resolve_variable, TokenParser 63 from django.template.loader import TemplateDoesNotExist 64 64 65 TOKEN_TEXT = 0 66 TOKEN_VAR = 1 67 TOKEN_BLOCK = 2 68 TOKEN_COMMENT = 3 65 __all__ = ( 66 'Template', 'TemplateSyntaxError', 'compile_string', 'Origin', 'TokenSyntaxError', 67 'Context', 'RequestContext', 68 'Expression', 'LookupError', 69 #FIXME: should these be public? 'Literal', 'Lookup', 'FilterExpression' 70 'Library', 'InvalidTemplateLibrary', 'get_library', 'add_to_builtins', 'libraries', 71 #backcompat 72 'Variable', 'VariableDoesNotExist', 'resolve_variable', 'TemplateDoesNotExist', 'TokenParser', 'VariableNode', 73 ) 69 74 70 # template syntax constants71 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 = '}'82 83 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.'84 85 # what to report as the origin for templates that come from non-loader sources86 # (e.g. strings)87 UNKNOWN_SOURCE="<unknown source>"88 89 # match a variable or block tag and capture the entire tag, including start/end delimiters90 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)))93 94 # global dictionary of libraries that have been loaded using get_library95 libraries = {}96 # global list of libraries to load by default for a new parser97 builtins = []98 99 75 # True if TEMPLATE_STRING_IF_INVALID contains a format string (%s). None means 100 76 # uninitialised. 101 77 invalid_var_format_string = None 102 78 103 class TemplateSyntaxError(Exception):104 def __str__(self):105 try:106 import cStringIO as StringIO107 except ImportError:108 import StringIO109 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 traceback114 output.write('\n\nOriginal ')115 e = self.exc_info116 traceback.print_exception(e[0], e[1], e[2], 500, output)117 return output.getvalue()118 119 class TemplateDoesNotExist(Exception):120 pass121 122 class TemplateEncodingError(Exception):123 pass124 125 class VariableDoesNotExist(Exception):126 127 def __init__(self, msg, params=()):128 self.msg = msg129 self.params = params130 131 def __str__(self):132 return unicode(self).encode('utf-8')133 134 def __unicode__(self):135 return self.msg % tuple([force_unicode(p, errors='replace') for p in self.params])136 137 class InvalidTemplateLibrary(Exception):138 pass139 140 class Origin(object):141 def __init__(self, name):142 self.name = name143 144 def reload(self):145 raise NotImplementedError146 147 def __str__(self):148 return self.name149 150 class StringOrigin(Origin):151 def __init__(self, source):152 super(StringOrigin, self).__init__(UNKNOWN_SOURCE)153 self.source = source154 155 def reload(self):156 return self.source157 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 = name168 169 def __iter__(self):170 for node in self.nodelist:171 for subnode in node:172 yield subnode173 174 def render(self, context):175 "Display stage -- can be called many times"176 return self.nodelist.render(context)177 178 def compile_string(template_string, origin):179 "Compiles template_string into NodeList ready for rendering"180 if settings.TEMPLATE_DEBUG:181 from debug import DebugLexer, DebugParser182 lexer_class, parser_class = DebugLexer, DebugParser183 else:184 lexer_class, parser_class = Lexer, Parser185 lexer = lexer_class(template_string, origin)186 parser = parser_class(lexer.tokenize())187 return parser.parse()188 189 class Token(object):190 def __init__(self, token_type, contents):191 # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT.192 self.token_type, self.contents = token_type, contents193 194 def __str__(self):195 return '<%s token: "%s...">' % \196 ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_COMMENT: 'Comment'}[self.token_type],197 self.contents[:20].replace('\n', ''))198 199 def split_contents(self):200 return list(smart_split(self.contents))201 202 class Lexer(object):203 def __init__(self, template_string, origin):204 self.template_string = template_string205 self.origin = origin206 207 def tokenize(self):208 "Return a list of tokens from a given template_string."209 in_tag = False210 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_tag215 return result216 217 def create_token(self, token_string, in_tag):218 """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.222 """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 token233 234 class Parser(object):235 def __init__(self, tokens):236 self.tokens = tokens237 self.tags = {}238 self.filters = {}239 for lib in builtins:240 self.add_library(lib)241 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 terminated258 self.prepend_token(token)259 return nodelist260 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 node265 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 raise275 self.extend_nodelist(nodelist, compiled_result, token)276 self.exit_command()277 if parse_until:278 self.unclosed_block_tag(parse_until)279 return nodelist280 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 return286 self.unclosed_block_tag([endtag])287 288 def create_variable_node(self, filter_expression):289 return VariableNode(filter_expression)290 291 def create_nodelist(self):292 return NodeList()293 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 AttributeError299 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 = True303 nodelist.append(node)304 305 def enter_command(self, command, token):306 pass307 308 def exit_command(self):309 pass310 311 def error(self, token, msg):312 return TemplateSyntaxError(msg)313 314 def empty_variable(self, token):315 raise self.error(token, "Empty variable tag")316 317 def empty_block_tag(self, token):318 raise self.error(token, "Empty block tag")319 320 def invalid_block_tag(self, token, command):321 raise self.error(token, "Invalid block tag: '%s'" % command)322 323 def unclosed_block_tag(self, parse_until):324 raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until))325 326 def compile_function_error(self, token, e):327 pass328 329 def next_token(self):330 return self.tokens.pop(0)331 332 def prepend_token(self, token):333 self.tokens.insert(0, token)334 335 def delete_first_token(self):336 del self.tokens[0]337 338 def add_library(self, lib):339 self.tags.update(lib.tags)340 self.filters.update(lib.filters)341 342 def compile_filter(self, token):343 "Convenient wrapper for FilterExpression"344 return FilterExpression(token, self)345 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)351 352 class TokenParser(object):353 """354 Subclass this and implement the top() method to parse a template line. When355 instantiating the parser, pass in the line from the Django template parser.356 357 The parser's "tagname" instance-variable stores the name of the tag that358 the filter was called with.359 """360 def __init__(self, subject):361 self.subject = subject362 self.pointer = 0363 self.backout = []364 self.tagname = self.tag()365 366 def top(self):367 "Overload this method to do the actual parsing and return the result."368 raise NotImplementedError()369 370 def more(self):371 "Returns True if there is more stuff in the tag."372 return self.pointer < len(self.subject)373 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()379 380 def tag(self):381 "A microparser that just returns the next tag from the line."382 subject = self.subject383 i = self.pointer384 if i >= len(subject):385 raise TemplateSyntaxError("expected another tag, found end of string: %s" % subject)386 p = i387 while i < len(subject) and subject[i] not in (' ', '\t'):388 i += 1389 s = subject[p:i]390 while i < len(subject) and subject[i] in (' ', '\t'):391 i += 1392 self.backout.append(self.pointer)393 self.pointer = i394 return s395 396 def value(self):397 "A microparser that parses for a value: some string constant or variable name."398 subject = self.subject399 i = self.pointer400 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 = i404 i += 1405 while i < len(subject) and subject[i] != subject[p]:406 i += 1407 if i >= len(subject):408 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))409 i += 1410 res = subject[p:i]411 while i < len(subject) and subject[i] in (' ', '\t'):412 i += 1413 self.backout.append(self.pointer)414 self.pointer = i415 return res416 else:417 p = i418 while i < len(subject) and subject[i] not in (' ', '\t'):419 if subject[i] in ('"', "'"):420 c = subject[i]421 i += 1422 while i < len(subject) and subject[i] != c:423 i += 1424 if i >= len(subject):425 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))426 i += 1427 s = subject[p:i]428 while i < len(subject) and subject[i] in (' ', '\t'):429 i += 1430 self.backout.append(self.pointer)431 self.pointer = i432 return s433 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)s439 (?P<filter_name>\w+)440 (?:%(arg_sep)s441 (?: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]+)445 )446 )?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(")"),454 }455 456 filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")457 filter_re = re.compile(filter_raw_string, re.UNICODE)458 459 class FilterExpression(object):460 """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)468 2469 >>> fe.var470 <Variable: 'variable'>471 472 This class should never be instantiated outside of the473 get_filters_from_token helper function.474 """475 def __init__(self, token, parser):476 self.token = token477 matches = filter_re.finditer(token)478 var = None479 filters = []480 upto = 0481 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 = filters514 self.var = Variable(var)515 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 = None522 else:523 if settings.TEMPLATE_STRING_IF_INVALID:524 global invalid_var_format_string525 if invalid_var_format_string is None:526 invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID527 if invalid_var_format_string:528 return settings.TEMPLATE_STRING_IF_INVALID % self.var529 return settings.TEMPLATE_STRING_IF_INVALID530 else:531 obj = settings.TEMPLATE_STRING_IF_INVALID532 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_obj549 return obj550 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 = args563 # Args without defaults must be provided.564 try:565 for arg in nondefs:566 provided.pop(0)567 except IndexError:568 # Not enough569 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen))570 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))579 580 return True581 args_check = staticmethod(args_check)582 583 def __str__(self):584 return self.token585 586 def resolve_variable(path, context):587 """588 Returns the resolved variable, which may contain attribute syntax, within589 the given context.590 591 Deprecated; use the Variable class instead.592 """593 return Variable(path).resolve(context)594 595 class Variable(object):596 """597 A template variable, resolvable against a given context. The variable may be598 a hard-coded string (if it begins and ends with single or double quote599 marks)::600 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: pass607 >>> c = AClass()608 >>> c.article = AClass()609 >>> c.article.section = u'News'610 >>> Variable('article.section').resolve(c)611 u'News'612 613 (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')614 """615 616 def __init__(self, var):617 self.var = var618 self.literal = None619 self.lookups = None620 self.translate = False621 622 try:623 # First try to treat this variable as a number.624 #625 # Note that this could cause an OverflowError here that we're not626 # catching. Since this should only happen at compile time, that's627 # probably OK.628 self.literal = float(var)629 630 # So it's a float... is it an int? If the original value contained a631 # 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)634 635 # "2." is invalid636 if var.endswith('.'):637 raise ValueError638 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 rendering643 # time.644 self.translate = True645 var = var[2:-1]646 # If it's wrapped with quotes (single or double), then647 # 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're652 # dealing with a bonafide variable653 self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))654 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 resolved659 value = self._resolve_lookup(context)660 else:661 # We're dealing with a literal, so it's already been "resolved"662 value = self.literal663 if self.translate:664 return _(value)665 return value666 667 def __repr__(self):668 return "<%s: %r>" % (self.__class__.__name__, self.var)669 670 def __str__(self):671 return self.var672 673 def _resolve_lookup(self, context):674 """675 Performs resolution of a real variable (i.e. not a literal) against the676 given context.677 678 As indicated by the method's name, this method is an implementation679 detail and shouldn't be called by external code. Use Variable.resolve()680 instead.681 """682 current = context683 for bit in self.lookups:684 try: # dictionary lookup685 current = current[bit]686 except (TypeError, AttributeError, KeyError):687 try: # attribute lookup688 current = getattr(current, bit)689 if callable(current):690 if getattr(current, 'alters_data', False):691 current = settings.TEMPLATE_STRING_IF_INVALID692 else:693 try: # method call (assuming no args required)694 current = current()695 except TypeError: # arguments *were* required696 # GOTCHA: This will also catch any TypeError697 # raised in the function itself.698 current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call699 except Exception, e:700 if getattr(e, 'silent_variable_failure', False):701 current = settings.TEMPLATE_STRING_IF_INVALID702 else:703 raise704 except (TypeError, AttributeError):705 try: # list-index lookup706 current = current[int(bit)]707 except (IndexError, # list index out of range708 ValueError, # invalid literal for int()709 KeyError, # current is a dict without `int(bit)` key710 TypeError, # unsubscriptable object711 ):712 raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute713 except Exception, e:714 if getattr(e, 'silent_variable_failure', False):715 current = settings.TEMPLATE_STRING_IF_INVALID716 else:717 raise718 719 return current720 721 class Node(object):722 # Set this to True for nodes that must be first in the template (although723 # they can be preceded by text nodes.724 must_be_first = False725 726 def render(self, context):727 "Return the node rendered as a string"728 pass729 730 def __iter__(self):731 yield self732 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 nodes741 742 class NodeList(list):743 # Set to True the first time a non-TextNode is inserted by744 # extend_nodelist().745 contains_nontext = False746 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]))755 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 nodes762 763 def render_node(self, node, context):764 return node.render(context)765 766 class TextNode(Node):767 def __init__(self, s):768 self.s = s769 770 def __repr__(self):771 return "<Text Node: '%s'>" % self.s[:25]772 773 def render(self, context):774 return self.s775 776 class VariableNode(Node):777 def __init__(self, filter_expression):778 self.filter_expression = filter_expression779 780 def __repr__(self):781 return "<Variable Node: %s>" % self.filter_expression782 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 our788 # 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)794 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 0800 bmin = bmax - def_len801 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)808 809 class Library(object):810 def __init__(self):811 self.filters = {}812 self.tags = {}813 814 def tag(self, name=None, compile_function=None):815 if name == None and compile_function == None:816 # @register.tag()817 return self.tag_function818 elif name != None and compile_function == None:819 if(callable(name)):820 # @register.tag821 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 dec827 elif name != None and compile_function != None:828 # register.tag('somename', somefunc)829 self.tags[name] = compile_function830 return compile_function831 else:832 raise InvalidTemplateLibrary("Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function))833 834 def tag_function(self,func):835 self.tags[getattr(func, "_decorated_function", func).__name__] = func836 return func837 838 def filter(self, name=None, filter_func=None):839 if name == None and filter_func == None:840 # @register.filter()841 return self.filter_function842 elif filter_func == None:843 if(callable(name)):844 # @register.filter845 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 dec851 elif name != None and filter_func != None:852 # register.filter('somename', somefunc)853 self.filters[name] = filter_func854 return filter_func855 else:856 raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func))857 858 def filter_function(self, func):859 self.filters[getattr(func, "_decorated_function", func).__name__] = func860 return func861 862 def simple_tag(self,func):863 params, xx, xxx, defaults = getargspec(func)864 865 class SimpleNode(Node):866 def __init__(self, vars_to_resolve):867 self.vars_to_resolve = map(Variable, vars_to_resolve)868 869 def render(self, context):870 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]871 return func(*resolved_vars)872 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 func877 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'")886 887 class InclusionNode(Node):888 def __init__(self, vars_to_resolve):889 self.vars_to_resolve = map(Variable, vars_to_resolve)890 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_vars895 else:896 args = resolved_vars897 898 dict = func(*args)899 900 if not getattr(self, 'nodelist', False):901 from django.template.loader import get_template, select_template902 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.nodelist907 return self.nodelist.render(context_class(dict,908 autoescape=context.autoescape))909 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 func914 return dec915 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.register925 libraries[module_name] = lib926 except AttributeError:927 raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name)928 return lib929 930 def add_to_builtins(module_name):931 builtins.append(get_library(module_name))932 933 79 add_to_builtins('django.template.defaulttags') 934 80 add_to_builtins('django.template.defaultfilters') -
django/template/utils.py
1 import re 2 3 from django.template import Node, NodeList, TokenSyntaxError 4 5 class EmptyNode(Node): 6 def render(self, context): 7 return u'' 8 9 class ConditionalNode(Node): 10 def __init__(self, nodelist_true, nodelist_false): 11 self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false 12 13 def __iter__(self): 14 for node in self.nodelist_true: 15 yield node 16 for node in self.nodelist_false: 17 yield node 18 19 def get_nodes_by_type(self, nodetype): 20 nodes = [] 21 if isinstance(self, nodetype): 22 nodes.append(self) 23 nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype)) 24 nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype)) 25 return nodes 26 27 def check_condition(self, context): 28 return False 29 30 def render(self, context): 31 if self.check_condition(context): 32 return self.nodelist_true.render(context) 33 else: 34 return self.nodelist_false.render(context) 35 36 def parse_conditional_nodelists(parser, name): 37 end_tag = 'end' + name 38 nodelist_true = parser.parse(('else', end_tag)) 39 token = parser.next_token() 40 if token.contents == 'else': 41 nodelist_false = parser.parse((end_tag,)) 42 parser.delete_first_token() 43 else: 44 nodelist_false = NodeList() 45 return nodelist_true, nodelist_false 46 47 def parse_args_and_kwargs(self): 48 args = [] 49 kwargs = {} 50 while True: 51 name = self.pop_name() 52 if name and self.pop_lexem('='): 53 try: 54 kwargs[name] = self.parse_expression() 55 except TokenSyntaxError: 56 raise TemplateSyntaxError, "expected expression in kwargs" 57 else: 58 if name: 59 self.pushback() 60 try: 61 args.append(self.parse_expression()) 62 except TokenSyntaxError: 63 break 64 if not self.pop_lexem(','): 65 break 66 return args, kwargs 67 68 def parse_as(bits): 69 if bits.pop_lexem('as'): 70 name = bits.pop_name() 71 if name: 72 return name 73 raise bits.exception 74 75 def parse_context_map(bits): 76 context_map = {} 77 try: 78 while True: 79 expr = bits.parse_expression() 80 name = parse_as(bits) 81 context_map[name] = expr 82 if not bits.pop_lexem(','): 83 break 84 except TokenSyntaxError: 85 bits.expected("context map") 86 return context_map 87 88 def resolve_args_and_kwargs(args, kwargs, context): 89 resolved_args = [arg.resolve(context, True) for arg in args] 90 resolved_kwargs = {} 91 for name in kwargs: 92 resolved_kwargs[name] = kwargs[name].resolve(context, True) 93 return resolved_args, resolved_kwargs 94 95 def render_with_context_map(renderable, context_map, context): 96 """ 97 renderable: a NodeList or Template 98 context_map: a dict mapping strings to FilterExpressions 99 context: a Context object 100 101 """ 102 if context_map is None: 103 return renderable.render(context) 104 d = {} 105 for name in context_map: 106 d[name] = context_map[name].resolve_safe(context) 107 context.update(d) 108 output = renderable.render(context) 109 context.pop() 110 return output 111 -
django/template/expressions.py
1 from django.conf import settings 2 from django.utils.encoding import force_unicode 3 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping 4 5 __all__ = ('LookupError', 'Expression', 'Variable', 'Literal', 'Lookup', 'FilterExpression') 6 7 VARIABLE_ATTRIBUTE_SEPARATOR = '.' 8 9 class LookupError(Exception): 10 def __init__(self, var, msg, params=()): 11 self.var = var 12 self.msg = msg 13 self.params = params 14 15 def __str__(self): 16 return unicode(self).encode('utf-8') 17 18 def __unicode__(self): 19 return self.msg % tuple([force_unicode(p, errors='replace') for p in self.params]) 20 21 22 class Expression(object): 23 def resolve_safe(self, context, default=None): 24 try: 25 return self.resolve(context) 26 except LookupError: 27 return default 28 29 def resolve(self, context): 30 pass 31 32 class Literal(Expression): 33 def __init__(self, value): 34 self.value = value 35 36 def resolve(self, context): 37 return self.value 38 39 class Lookup(Expression): 40 def __init__(self, var): 41 self.var = var 42 self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) 43 44 def __str__(self): 45 return self.var 46 47 def resolve(self, context): 48 current = context 49 for bit in self.lookups: 50 try: # dictionary lookup 51 current = current[bit] 52 except (TypeError, AttributeError, KeyError): 53 try: # attribute lookup 54 current = getattr(current, bit) 55 if callable(current): 56 if getattr(current, 'alters_data', False): 57 current = settings.TEMPLATE_STRING_IF_INVALID 58 else: 59 try: # method call (assuming no args required) 60 current = current() 61 except TypeError: # arguments *were* required 62 # GOTCHA: This will also catch any TypeError 63 # raised in the function itself. 64 current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call 65 except Exception, e: 66 if getattr(e, 'silent_variable_failure', False): 67 current = settings.TEMPLATE_STRING_IF_INVALID 68 else: 69 raise 70 except (TypeError, AttributeError): 71 try: # list-index lookup 72 current = current[int(bit)] 73 except (IndexError, # list index out of range 74 ValueError, # invalid literal for int() 75 KeyError, # current is a dict without `int(bit)` key 76 TypeError, # unsubscriptable object 77 ): 78 raise LookupError(self.var, "Failed lookup for key [%s] in %r", (bit, current)) 79 except Exception, e: 80 if getattr(e, 'silent_variable_failure', False): 81 current = settings.TEMPLATE_STRING_IF_INVALID 82 else: 83 raise 84 85 return current 86 87 class FilterExpression(Expression): 88 def __init__(self, root, filters): 89 self.root = root 90 self.filters = filters 91 92 def resolve(self, context): 93 try: 94 obj = self.root.resolve(context) 95 except LookupError: 96 if not self.filters: 97 raise 98 obj = settings.TEMPLATE_STRING_IF_INVALID 99 for func, args in self.filters: 100 arg_vals = [] 101 for arg in args: 102 arg_vals.append(arg.resolve(context)) 103 if getattr(func, 'needs_autoescape', False): 104 new_obj = func(obj, autoescape=context.autoescape, *arg_vals) 105 else: 106 new_obj = func(obj, *arg_vals) 107 if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 108 obj = mark_safe(new_obj) 109 elif isinstance(obj, EscapeData): 110 obj = mark_for_escaping(new_obj) 111 else: 112 obj = new_obj 113 return obj 114 115 def __str__(self): 116 return str(self.root)+'|<filtered>' -
django/template/defaulttags.py
9 9 from django.utils.itercompat import reversed # Python 2.3 fallback 10 10 11 11 from django.template import Node, NodeList, Template, Context, Variable 12 from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END 13 from django.template import get_library, Library, InvalidTemplateLibrary 12 from django.template import TemplateSyntaxError, LookupError, TokenSyntaxError 13 from django.template.library import get_library, Library, InvalidTemplateLibrary 14 from django.template.compiler import BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END 15 from django.template.utils import EmptyNode, ConditionalNode, render_with_context_map, parse_context_map, parse_conditional_nodelists, parse_as, parse_args_and_kwargs 14 16 from django.conf import settings 15 from django.utils.encoding import smart_str, smart_unicode17 from django.utils.encoding import smart_str, force_unicode 16 18 from django.utils.itercompat import groupby 17 19 from django.utils.safestring import mark_safe 18 20 … … 33 35 else: 34 36 return output 35 37 36 class CommentNode(Node): 37 def render(self, context): 38 return '' 38 class CommentNode(EmptyNode): pass 39 39 40 40 class CycleNode(Node): 41 def __init__(self, cycleva rs, variable_name=None):42 self.cycle_iter = itertools_cycle( [Variable(v) for v in cyclevars])41 def __init__(self, cyclevals, variable_name=None): 42 self.cycle_iter = itertools_cycle(cyclevals) 43 43 self.variable_name = variable_name 44 44 45 45 def render(self, context): 46 value = self.cycle_iter.next().resolve (context)46 value = self.cycle_iter.next().resolve_safe(context) 47 47 if self.variable_name: 48 48 context[self.variable_name] = value 49 49 return value … … 62 62 63 63 def render(self, context): 64 64 output = self.nodelist.render(context) 65 # Apply filters.66 65 context.update({'var': output}) 67 filtered = self.filter_expr.resolve (context)66 filtered = self.filter_expr.resolve_safe(context, default='') 68 67 context.pop() 69 68 return filtered 70 69 71 70 class FirstOfNode(Node): 72 def __init__(self, va rs):73 self.va rs = map(Variable, vars)71 def __init__(self, vals): 72 self.vals = vals 74 73 75 74 def render(self, context): 76 for var in self.vars: 77 try: 78 value = var.resolve(context) 79 except VariableDoesNotExist: 80 continue 75 for val in self.vals: 76 value = val.resolve_safe(context) 81 77 if value: 82 return smart_unicode(value)78 return value 83 79 return u'' 84 80 85 81 class ForNode(Node): 86 def __init__(self, loopvars, sequence, is_reversed, nodelist _loop):82 def __init__(self, loopvars, sequence, is_reversed, nodelist): 87 83 self.loopvars, self.sequence = loopvars, sequence 88 84 self.is_reversed = is_reversed 89 self.nodelist _loop = nodelist_loop85 self.nodelist = nodelist 90 86 91 87 def __repr__(self): 92 88 reversed_text = self.is_reversed and ' reversed' or '' … … 98 94 for node in self.nodelist_loop: 99 95 yield node 100 96 101 def get_nodes_by_type(self, nodetype):102 nodes = []103 if isinstance(self, nodetype):104 nodes.append(self)105 nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))106 return nodes107 108 97 def render(self, context): 109 nodelist = NodeList()98 result = [] 110 99 if 'forloop' in context: 111 100 parentloop = context['forloop'] 112 101 else: 113 102 parentloop = {} 114 103 context.push() 115 try: 116 values = self.sequence.resolve(context, True) 117 except VariableDoesNotExist: 118 values = [] 104 values = self.sequence.resolve_safe(context, default=[]) 119 105 if values is None: 120 106 values = [] 121 107 if not hasattr(values, '__len__'): … … 144 130 context.update(dict(zip(self.loopvars, item))) 145 131 else: 146 132 context[self.loopvars[0]] = item 147 for node in self.nodelist _loop:148 nodelist.append(node.render(context))133 for node in self.nodelist: 134 result.append(node.render(context)) 149 135 if unpack: 150 136 # The loop variables were pushed on to the context so pop them 151 137 # off again. This is necessary because the tag lets the length … … 154 140 # context. 155 141 context.pop() 156 142 context.pop() 157 return nodelist.render(context)143 return mark_safe(''.join([force_unicode(b) for b in result])) 158 144 159 145 class IfChangedNode(Node): 160 def __init__(self, nodelist, *va rlist):146 def __init__(self, nodelist, *vallist): 161 147 self.nodelist = nodelist 162 148 self._last_seen = None 163 self._va rlist = map(Variable, varlist)149 self._vallist = vallist 164 150 self._id = str(id(self)) 165 151 166 152 def render(self, context): … … 168 154 self._last_seen = None 169 155 context['forloop'][self._id] = 1 170 156 try: 171 if self._va rlist:157 if self._vallist: 172 158 # Consider multiple parameters. This automatically behaves 173 159 # like an OR evaluation of the multiple variables. 174 compare_to = [var.resolve(context) for var in self._va rlist]160 compare_to = [var.resolve(context) for var in self._vallist] 175 161 else: 176 162 compare_to = self.nodelist.render(context) 177 163 except VariableDoesNotExist: … … 188 174 else: 189 175 return '' 190 176 191 class IfEqualNode( Node):192 def __init__(self, va r1, var2, nodelist_true, nodelist_false, negate):193 s elf.var1, self.var2 = Variable(var1), Variable(var2)194 self. nodelist_true, self.nodelist_false = nodelist_true, nodelist_false177 class IfEqualNode(ConditionalNode): 178 def __init__(self, val1, val2, nodelist_true, nodelist_false, negate): 179 super(IfEqualNode, self).__init__(nodelist_true, nodelist_false) 180 self.val1, self.val2 = val1, val2 195 181 self.negate = negate 196 182 197 183 def __repr__(self): 198 184 return "<IfEqualNode>" 199 185 200 def render(self, context): 201 try: 202 val1 = self.var1.resolve(context) 203 except VariableDoesNotExist: 204 val1 = None 205 try: 206 val2 = self.var2.resolve(context) 207 except VariableDoesNotExist: 208 val2 = None 209 if (self.negate and val1 != val2) or (not self.negate and val1 == val2): 210 return self.nodelist_true.render(context) 211 return self.nodelist_false.render(context) 186 def check_condition(self, context): 187 val1, val2 = self.val1.resolve_safe(context), self.val2.resolve_safe(context) 188 return self.negate == (val1 != val2) 212 189 213 class IfNode( Node):190 class IfNode(ConditionalNode): 214 191 def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type): 215 s elf.bool_exprs = bool_exprs216 self. nodelist_true, self.nodelist_false = nodelist_true, nodelist_false192 super(IfNode, self).__init__(nodelist_true, nodelist_false) 193 self.bool_exprs = bool_exprs 217 194 self.link_type = link_type 218 195 219 196 def __repr__(self): 220 197 return "<If node>" 221 198 222 def __iter__(self): 223 for node in self.nodelist_true: 224 yield node 225 for node in self.nodelist_false: 226 yield node 227 228 def get_nodes_by_type(self, nodetype): 229 nodes = [] 230 if isinstance(self, nodetype): 231 nodes.append(self) 232 nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype)) 233 nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype)) 234 return nodes 235 236 def render(self, context): 237 if self.link_type == IfNode.LinkTypes.or_: 238 for ifnot, bool_expr in self.bool_exprs: 239 try: 240 value = bool_expr.resolve(context, True) 241 except VariableDoesNotExist: 242 value = None 243 if (value and not ifnot) or (ifnot and not value): 244 return self.nodelist_true.render(context) 245 return self.nodelist_false.render(context) 199 def check_condition(self, context): 200 if self.link_type == 'or': 201 for negated, bool_expr in self.bool_exprs: 202 value = bool_expr.resolve_safe(context, default=False) 203 if bool(value) != negated: 204 return True 205 return False 246 206 else: 247 for ifnot, bool_expr in self.bool_exprs: 248 try: 249 value = bool_expr.resolve(context, True) 250 except VariableDoesNotExist: 251 value = None 252 if not ((value and not ifnot) or (ifnot and not value)): 253 return self.nodelist_false.render(context) 254 return self.nodelist_true.render(context) 207 for negated, bool_expr in self.bool_exprs: 208 value = bool_expr.resolve_safe(context, default=False) 209 if bool(value) == negated: 210 return False 211 return True 212 255 213 256 class LinkTypes:257 and_ = 0,258 or_ = 1259 260 214 class RegroupNode(Node): 261 215 def __init__(self, target, expression, var_name): 262 216 self.target, self.expression = target, expression 263 217 self.var_name = var_name 264 218 265 219 def render(self, context): 266 obj_list = self.target.resolve (context, True)220 obj_list = self.target.resolve_safe(context) 267 221 if obj_list == None: 268 222 # target variable wasn't found in context; fail silently. 269 223 context[self.var_name] = [] … … 273 227 context[self.var_name] = [ 274 228 {'grouper': key, 'list': list(val)} 275 229 for key, val in 276 groupby(obj_list, lambda v, f=self.expression.resolve: f(v , True))230 groupby(obj_list, lambda v, f=self.expression.resolve: f(v)) 277 231 ] 278 232 return '' 279 233 … … 310 264 return '' # Fail silently for invalid included templates. 311 265 return output 312 266 313 class LoadNode(Node): 314 def render(self, context): 315 return '' 267 class LoadNode(EmptyNode): pass 316 268 317 269 class NowNode(Node): 318 270 def __init__(self, format_string): … … 322 274 from datetime import datetime 323 275 from django.utils.dateformat import DateFormat 324 276 df = DateFormat(datetime.now()) 325 return df.format(self.format_string )277 return df.format(self.format_string.resolve_safe(context)) 326 278 327 279 class SpacelessNode(Node): 328 280 def __init__(self, nodelist): … … 357 309 358 310 def render(self, context): 359 311 from django.core.urlresolvers import reverse, NoReverseMatch 360 args = [arg.resolve (context) for arg in self.args]361 kwargs = dict([(smart_str(k,'ascii'), v.resolve (context))312 args = [arg.resolve_safe(context) for arg in self.args] 313 kwargs = dict([(smart_str(k,'ascii'), v.resolve_safe(context)) 362 314 for k, v in self.kwargs.items()]) 363 315 try: 364 316 return reverse(self.view_name, args=args, kwargs=kwargs) … … 391 343 return str(int(round(ratio))) 392 344 393 345 class WithNode(Node): 394 def __init__(self, var, name, nodelist): 395 self.var = var 396 self.name = name 346 def __init__(self, context_map, nodelist): 347 self.context_map = context_map 397 348 self.nodelist = nodelist 398 349 399 350 def __repr__(self): 400 351 return "<WithNode>" 401 352 402 353 def render(self, context): 403 val = self.var.resolve(context) 404 context.push() 405 context[self.name] = val 406 output = self.nodelist.render(context) 407 context.pop() 408 return output 354 return render_with_context_map(self.nodelist, self.context_map, context) 409 355 410 356 #@register.tag 411 357 def autoescape(parser, token): … … 418 364 arg = args[1] 419 365 if arg not in (u'on', u'off'): 420 366 raise TemplateSyntaxError("'Autoescape' argument should be 'on' or 'off'") 421 nodelist = parser.parse(('endautoescape',)) 422 parser.delete_first_token() 367 nodelist = parser.parse_nodelist(('endautoescape',)) 423 368 return AutoEscapeControlNode((arg == 'on'), nodelist) 424 369 autoescape = register.tag(autoescape) 425 370 … … 489 434 490 435 if len(args) > 4 and args[-2] == 'as': 491 436 name = args[-1] 492 node = CycleNode(args[1:-2], name) 437 values = [parser.compile_filter(arg) for arg in args[1:-2]] 438 node = CycleNode(values, name) 493 439 if not hasattr(parser, '_namedCycleNodes'): 494 440 parser._namedCycleNodes = {} 495 441 parser._namedCycleNodes[name] = node 496 442 else: 497 node = CycleNode(args[1:]) 443 values = [parser.compile_filter(arg) for arg in args[1:]] 444 node = CycleNode(values) 498 445 return node 499 446 cycle = register.tag(cycle) 500 447 … … 527 474 {% endfilter %} 528 475 """ 529 476 _, rest = token.contents.split(None, 1) 530 filter_expr = parser.compile_filter("var|%s" % (rest))477 filter_expr = parser.compile_filter("var|%s" % rest) 531 478 for func, unused in filter_expr.filters: 532 479 if getattr(func, '_decorated_function', func).__name__ in ('escape', 'safe'): 533 480 raise TemplateSyntaxError('"filter %s" is not permitted. Use the "autoescape" tag instead.' % func.__name__) 534 nodelist = parser.parse(('endfilter',)) 535 parser.delete_first_token() 481 nodelist = parser.parse_nodelist(('endfilter',)) 536 482 return FilterNode(filter_expr, nodelist) 537 483 do_filter = register.tag("filter", do_filter) 538 484 … … 565 511 {% firstof var1 var2 var3 "fallback value" %} 566 512 567 513 """ 568 bits = token.split_contents()[1:] 569 if len(bits) < 1: 570 raise TemplateSyntaxError("'firstof' statement requires at least one" 571 " argument") 572 return FirstOfNode(bits) 514 bits = parser.token_stream(token) 515 expressions = bits.parse_expression_list(minimum=1) 516 bits.assert_consumed() 517 return FirstOfNode(expressions) 573 518 firstof = register.tag(firstof) 574 519 575 520 #@register.tag(name="for") … … 612 557 ========================== ================================================ 613 558 614 559 """ 615 bits = token.contents.split() 616 if len(bits) < 4: 617 raise TemplateSyntaxError("'for' statements should have at least four" 618 " words: %s" % token.contents) 619 620 is_reversed = bits[-1] == 'reversed' 621 in_index = is_reversed and -3 or -2 622 if bits[in_index] != 'in': 623 raise TemplateSyntaxError("'for' statements should use the format" 624 " 'for x in y': %s" % token.contents) 625 626 loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',') 627 for var in loopvars: 628 if not var or ' ' in var: 629 raise TemplateSyntaxError("'for' tag received an invalid argument:" 630 " %s" % token.contents) 631 632 sequence = parser.compile_filter(bits[in_index+1]) 633 nodelist_loop = parser.parse(('endfor',)) 634 parser.delete_first_token() 635 return ForNode(loopvars, sequence, is_reversed, nodelist_loop) 560 bits = parser.token_stream(token) 561 loopvars = [] 562 while True: 563 var = bits.pop_name() 564 if var: 565 loopvars.append(var) 566 if not bits.pop_lexem(','): 567 break 568 else: 569 break 570 if not loopvars: 571 raise TemplateSyntaxError("'for' tag requires at least one loopvar") 572 573 if not bits.pop_lexem('in'): 574 raise TemplateSyntaxError("'for' tag requires 'in' keyword") 575 try: 576 sequence = bits.parse_expression() 577 except TokenSyntaxError: 578 raise bits.expected("expression") 579 reversed = bits.pop_lexem('reversed') 580 bits.assert_consumed() 581 nodelist = parser.parse_nodelist(('endfor',)) 582 return ForNode(loopvars, sequence, reversed, nodelist) 636 583 do_for = register.tag("for", do_for) 637 584 638 585 def do_ifequal(parser, token, negate): 639 bits = list(token.split_contents()) 640 if len(bits) != 3: 641 raise TemplateSyntaxError, "%r takes two arguments" % bits[0] 642 end_tag = 'end' + bits[0] 643 nodelist_true = parser.parse(('else', end_tag)) 644 token = parser.next_token() 645 if token.contents == 'else': 646 nodelist_false = parser.parse((end_tag,)) 647 parser.delete_first_token() 648 else: 649 nodelist_false = NodeList() 650 return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate) 586 bits = parser.token_stream(token) 587 val1, val2 = bits.parse_expression_list(count=2) 588 bits.assert_consumed() 589 nodelist_true, nodelist_false = parse_conditional_nodelists(parser, bits.name) 590 return IfEqualNode(val1, val2, nodelist_true, nodelist_false, negate) 651 591 652 592 #@register.tag 653 593 def ifequal(parser, token): … … 737 677 {% endif %} 738 678 {% endif %} 739 679 """ 740 bits = token.contents.split() 741 del bits[0] 742 if not bits: 743 raise TemplateSyntaxError("'if' statement requires at least one argument") 744 # Bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d'] 745 bitstr = ' '.join(bits) 746 boolpairs = bitstr.split(' and ') 680 bits = parser.token_stream(token) 681 link_type = None 682 link = None 747 683 boolvars = [] 748 if len(boolpairs) == 1: 749 link_type = IfNode.LinkTypes.or_ 750 boolpairs = bitstr.split(' or ') 751 else: 752 link_type = IfNode.LinkTypes.and_ 753 if ' or ' in bitstr: 754 raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'" 755 for boolpair in boolpairs: 756 if ' ' in boolpair: 757 try: 758 not_, boolvar = boolpair.split() 759 except ValueError: 760 raise TemplateSyntaxError, "'if' statement improperly formatted" 761 if not_ != 'not': 762 raise TemplateSyntaxError, "Expected 'not' in if statement" 763 boolvars.append((True, parser.compile_filter(boolvar))) 684 while True: 685 negated = False 686 if bits.pop_lexem('not'): 687 negated = True 688 try: 689 expr = bits.parse_expression() 690 except TokenSyntaxError: 691 if link: 692 raise TemplateSyntaxError("'if' statement improperly formatted") 693 else: 694 raise TemplateSyntaxError("'if' statement requires at least one argument") 695 boolvars.append((negated, expr)) 696 link = bits.pop_name() 697 if not link: 698 break 699 if link_type: 700 if link_type != link: 701 raise TemplateSyntaxError("'if' tags can't mix 'and' and 'or'") 764 702 else: 765 boolvars.append((False, parser.compile_filter(boolpair))) 766 nodelist_true = parser.parse(('else', 'endif')) 767 token = parser.next_token() 768 if token.contents == 'else': 769 nodelist_false = parser.parse(('endif',)) 770 parser.delete_first_token() 771 else: 772 nodelist_false = NodeList() 703 if not link in ('and', 'or'): 704 raise TemplateSyntaxError("'if' tag expects 'and' or 'or', got: %s" % link) 705 link_type = link 706 bits.assert_consumed() 707 708 nodelist_true, nodelist_false = parse_conditional_nodelists(parser, 'if') 709 773 710 return IfNode(boolvars, nodelist_true, nodelist_false, link_type) 774 711 do_if = register.tag("if", do_if) 775 712 … … 802 739 {% endifchanged %} 803 740 {% endfor %} 804 741 """ 805 bits = token.contents.split() 806 nodelist = parser.parse(('endifchanged',)) 807 parser.delete_first_token() 808 return IfChangedNode(nodelist, *bits[1:]) 742 bits = parser.token_stream(token) 743 nodelist = parser.parse_nodelist(('endifchanged',)) 744 values = bits.parse_expression_list() 745 bits.assert_consumed() 746 return IfChangedNode(nodelist, *values) 809 747 ifchanged = register.tag(ifchanged) 810 748 811 749 #@register.tag … … 872 810 873 811 It is {% now "jS F Y H:i" %} 874 812 """ 875 bits = token.contents.split('"') 876 if len(bits) != 3: 877 raise TemplateSyntaxError, "'now' statement takes one argument" 878 format_string = bits[1] 813 bits = parser.token_stream(token) 814 try: 815 format_string = bits.parse_expression() 816 except TokenSyntaxError: 817 bits.expected("expression") 818 bits.assert_consumed() 879 819 return NowNode(format_string) 880 820 now = register.tag(now) 881 821 … … 926 866 {% regroup people|dictsort:"gender" by gender as grouped %} 927 867 928 868 """ 929 firstbits = token.contents.split(None, 3) 930 if len(firstbits) != 4: 931 raise TemplateSyntaxError, "'regroup' tag takes five arguments" 932 target = parser.compile_filter(firstbits[1]) 933 if firstbits[2] != 'by': 934 raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'") 935 lastbits_reversed = firstbits[3][::-1].split(None, 2) 936 if lastbits_reversed[1][::-1] != 'as': 937 raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must" 938 " be 'as'") 939 940 expression = parser.compile_filter(lastbits_reversed[2][::-1]) 941 942 var_name = lastbits_reversed[0][::-1] 869 bits = parser.token_stream(token) 870 try: 871 target = bits.parse_expression() 872 except TokenSyntaxError: 873 bits.expected("expression") 874 875 if not bits.pop_lexem('by'): 876 raise bits.expected("'by'") 877 try: 878 expression = bits.parse_expression() 879 except TokenSyntaxError: 880 raise TemplateSyntaxError() 881 try: 882 var_name = parse_as(bits) 883 except TokenSyntaxError: 884 raise bits.expected("as <name>") 885 bits.assert_consumed() 943 886 return RegroupNode(target, expression, var_name) 944 887 regroup = register.tag(regroup) 945 888 … … 968 911 </strong> 969 912 {% endspaceless %} 970 913 """ 971 nodelist = parser.parse(('endspaceless',)) 972 parser.delete_first_token() 973 return SpacelessNode(nodelist) 914 return SpacelessNode(parser.parse_nodelist(('endspaceless',))) 974 915 spaceless = register.tag(spaceless) 975 916 976 917 #@register.tag … … 1038 979 1039 980 The URL will look like ``/clients/client/123/``. 1040 981 """ 1041 bits = token.contents.split(' ', 2) 1042 if len(bits) < 2: 1043 raise TemplateSyntaxError("'%s' takes at least one argument" 1044 " (path to a view)" % bits[0]) 1045 args = [] 1046 kwargs = {} 1047 if len(bits) > 2: 1048 for arg in bits[2].split(','): 1049 if '=' in arg: 1050 k, v = arg.split('=', 1) 1051 k = k.strip() 1052 kwargs[k] = parser.compile_filter(v) 1053 else: 1054 args.append(parser.compile_filter(arg)) 1055 return URLNode(bits[1], args, kwargs) 982 bits = parser.token_stream(token) 983 try: 984 view = bits.parse_string(bare=True) 985 except TokenSyntaxError: 986 raise bits.expected("viewname") 987 args, kwargs = parse_args_and_kwargs(bits) 988 bits.assert_consumed() 989 return URLNode(view, args, kwargs) 1056 990 url = register.tag(url) 1057 991 1058 992 #@register.tag … … 1084 1018 #@register.tag 1085 1019 def do_with(parser, token): 1086 1020 """ 1087 Adds a valueto the context (inside of this block) for caching and easy1021 Adds values to the context (inside of this block) for caching and easy 1088 1022 access. 1089 1023 1090 1024 For example:: … … 1092 1026 {% with person.some_sql_method as total %} 1093 1027 {{ total }} object{{ total|pluralize }} 1094 1028 {% endwith %} 1029 1030 {% with person.some_sql_method as total, person.get_full_name as full_name %} 1031 {{ full_name }}: {{ total }} object{{ total|pluralize }} 1032 {% endwith %} 1033 1095 1034 """ 1096 bits = list(token.split_contents()) 1097 if len(bits) != 4 or bits[2] != "as": 1098 raise TemplateSyntaxError("%r expected format is 'value as name'" % 1099 bits[0]) 1100 var = parser.compile_filter(bits[1]) 1101 name = bits[3] 1102 nodelist = parser.parse(('endwith',)) 1103 parser.delete_first_token() 1104 return WithNode(var, name, nodelist) 1035 bits = parser.token_stream(token) 1036 context_map = parse_context_map(bits) 1037 bits.assert_consumed() 1038 nodelist = parser.parse_nodelist(('endwith',)) 1039 return WithNode(context_map, nodelist) 1105 1040 do_with = register.tag('with', do_with) -
django/template/compat.py
1 import warnings 2 from django.template.expressions import Expression, LookupError 3 from django.template.compiler import TemplateSyntaxError 4 from django.template.nodes import ExpressionNode 5 6 VariableDoesNotExist = LookupError 7 VariableNode = ExpressionNode 8 9 class Variable(Expression): 10 def __init__(self, var): 11 warnings.warn('Use Lookup instead of Variable.', DeprecationWarning, stacklevel=2) 12 self.var = var 13 from django.template.compiler import TokenStream 14 stream = TokenStream(None, var) 15 self.expression = stream.parse_value() 16 stream.assert_consumed("Invalid variable: %s" % var) 17 18 def resolve(self, context): 19 return self.expression.resolve(context) 20 21 def __repr__(self): 22 return "<%s: %r>" % (self.__class__.__name__, self.var) 23 24 def __str__(self): 25 return self.var 26 27 28 def resolve_variable(path, context): 29 """ 30 Returns the resolved variable, which may contain attribute syntax, within 31 the given context. 32 33 Deprecated. 34 """ 35 warnings.warn('Use Lookup instead of resolve_variable.', DeprecationWarning, stacklevel=2) 36 from django.template.compiler import TokenStream 37 stream = TokenStream(None, path) 38 val = stream.parse_value() 39 stream.assert_consumed("Invalid variable: %s" % path) 40 return val.resolve(context) 41 42 43 class TokenParser(object): 44 """ 45 Subclass this and implement the top() method to parse a template line. When 46 instantiating the parser, pass in the line from the Django template parser. 47 48 The parser's "tagname" instance-variable stores the name of the tag that 49 the filter was called with. 50 """ 51 def __init__(self, subject): 52 self.subject = subject 53 self.pointer = 0 54 self.backout = [] 55 self.tagname = self.tag() 56 57 def top(self): 58 "Overload this method to do the actual parsing and return the result." 59 raise NotImplementedError() 60 61 def more(self): 62 "Returns True if there is more stuff in the tag." 63 return self.pointer < len(self.subject) 64 65 def back(self): 66 "Undoes the last microparser. Use this for lookahead and backtracking." 67 if not len(self.backout): 68 raise TemplateSyntaxError("back called without some previous parsing") 69 self.pointer = self.backout.pop() 70 71 def tag(self): 72 "A microparser that just returns the next tag from the line." 73 subject = self.subject 74 i = self.pointer 75 if i >= len(subject): 76 raise TemplateSyntaxError("expected another tag, found end of string: %s" % subject) 77 p = i 78 while i < len(subject) and subject[i] not in (' ', '\t'): 79 i += 1 80 s = subject[p:i] 81 while i < len(subject) and subject[i] in (' ', '\t'): 82 i += 1 83 self.backout.append(self.pointer) 84 self.pointer = i 85 return s 86 87 def value(self): 88 "A microparser that parses for a value: some string constant or variable name." 89 subject = self.subject 90 i = self.pointer 91 if i >= len(subject): 92 raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject) 93 if subject[i] in ('"', "'"): 94 p = i 95 i += 1 96 while i < len(subject) and subject[i] != subject[p]: 97 i += 1 98 if i >= len(subject): 99 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) 100 i += 1 101 res = subject[p:i] 102 while i < len(subject) and subject[i] in (' ', '\t'): 103 i += 1 104 self.backout.append(self.pointer) 105 self.pointer = i 106 return res 107 else: 108 p = i 109 while i < len(subject) and subject[i] not in (' ', '\t'): 110 if subject[i] in ('"', "'"): 111 c = subject[i] 112 i += 1 113 while i < len(subject) and subject[i] != c: 114 i += 1 115 if i >= len(subject): 116 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) 117 i += 1 118 s = subject[p:i] 119 while i < len(subject) and subject[i] in (' ', '\t'): 120 i += 1 121 self.backout.append(self.pointer) 122 self.pointer = i 123 return s -
django/template/context.py
9 9 10 10 class Context(object): 11 11 "A stack container for variable context" 12 def __init__(self, dict_=None, autoescape=True ):12 def __init__(self, dict_=None, autoescape=True, loader=None): 13 13 dict_ = dict_ or {} 14 14 self.dicts = [dict_] 15 15 self.autoescape = autoescape 16 self.template_cache = {} 17 self.loader = loader 16 18 17 19 def __repr__(self): 18 20 return repr(self.dicts) … … 65 67 self.dicts = [other_dict] + self.dicts 66 68 return other_dict 67 69 70 def get_template(self, template_name): 71 if not template_name in self.template_cache: 72 if self.loader is None: 73 from django.template import loader 74 tpl = loader.get_template(template_name) 75 else: 76 tpl = self.loader.get_template(template_name) 77 self.template_cache[template_name] = tpl 78 return self.template_cache[template_name] 79 80 def select_template(self, template_name_list): 81 from django.template import TemplateDoesNotExist 82 for template_name in template_name_list: 83 try: 84 return self.get_template(template_name) 85 except TemplateDoesNotExist: 86 continue 87 raise TemplateDoesNotExist, ', '.join(template_name_list) 88 68 89 # This is a function rather than module-level procedural code because we only 69 90 # want it to execute if somebody uses RequestContext. 70 91 def get_standard_processors(): … … 93 114 Additional processors can be specified as a list of callables 94 115 using the "processors" keyword argument. 95 116 """ 96 def __init__(self, request, dict=None, processors=None ):97 Context.__init__(self, dict )117 def __init__(self, request, dict=None, processors=None, loader=None): 118 Context.__init__(self, dict, loader=loader) 98 119 if processors is None: 99 120 processors = () 100 121 else: -
django/template/loader_tags.py
1 from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable2 from django.template import Library, Node, TextNode3 from django.template.loader import get_template, get_template_from_string, find_template_source1 from django.template import TemplateSyntaxError, Variable, Library, Node, TextNode 2 from django.template.utils import render_with_context_map, parse_context_map 3 from django.template.loader import TemplateDoesNotExist, get_template, get_template_from_string, find_template_source 4 4 from django.conf import settings 5 5 from django.utils.safestring import mark_safe 6 6 … … 39 39 class ExtendsNode(Node): 40 40 must_be_first = True 41 41 42 def __init__(self, nodelist, parent_name , parent_name_expr, template_dirs=None):42 def __init__(self, nodelist, parent_name): 43 43 self.nodelist = nodelist 44 self.parent_name, self.parent_name_expr = parent_name, parent_name_expr 45 self.template_dirs = template_dirs 44 self.parent_name = parent_name 46 45 47 46 def __repr__(self): 48 if self.parent_name_expr:49 return "<ExtendsNode: extends %s>" % self.parent_name_expr.token50 47 return '<ExtendsNode: extends "%s">' % self.parent_name 51 48 52 49 def get_parent(self, context): 53 if self.parent_name_expr: 54 self.parent_name = self.parent_name_expr.resolve(context) 55 parent = self.parent_name 50 parent = self.parent_name.resolve_safe(context) 56 51 if not parent: 57 52 error_msg = "Invalid template name in 'extends' tag: %r." % parent 58 if self.parent_name_expr:59 error_msg += " Got this from the '%s' variable." % self.parent_name_expr.token60 53 raise TemplateSyntaxError, error_msg 61 54 if hasattr(parent, 'render'): 62 55 return parent # parent is a Template object 63 56 try: 64 source, origin = find_template_source(parent, self.template_dirs)57 return context.get_template(parent) 65 58 except TemplateDoesNotExist: 66 59 raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent 67 else:68 return get_template_from_string(source, origin, parent)69 60 70 61 def render(self, context): 71 62 compiled_parent = self.get_parent(context) … … 96 87 parent_block.nodelist = block_node.nodelist 97 88 return compiled_parent.render(context) 98 89 99 class ConstantIncludeNode(Node):100 def __init__(self, template_path):101 try:102 t = get_template(template_path)103 self.template = t104 except:105 if settings.TEMPLATE_DEBUG:106 raise107 self.template = None108 109 def render(self, context):110 if self.template:111 return self.template.render(context)112 else:113 return ''114 115 90 class IncludeNode(Node): 116 def __init__(self, template_name): 117 self.template_name = Variable(template_name) 91 def __init__(self, template_name, context_map=None): 92 self.template_name = template_name 93 self.context_map = context_map 118 94 119 95 def render(self, context): 120 96 try: 121 template_name = self.template_name.resolve (context)122 t = get_template(template_name)123 return t.render(context)97 template_name = self.template_name.resolve_safe(context) 98 tpl = context.get_template(template_name) 99 return render_with_context_map(tpl, self.context_map, context) 124 100 except TemplateSyntaxError, e: 125 101 if settings.TEMPLATE_DEBUG: 126 102 raise … … 161 137 bits = token.contents.split() 162 138 if len(bits) != 2: 163 139 raise TemplateSyntaxError, "'%s' takes one argument" % bits[0] 164 parent_name, parent_name_expr = None, None 165 if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]: 166 parent_name = bits[1][1:-1] 167 else: 168 parent_name_expr = parser.compile_filter(bits[1]) 140 parent_name = parser.compile_filter(bits[1]) 169 141 nodelist = parser.parse() 170 142 if nodelist.get_nodes_by_type(ExtendsNode): 171 143 raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0] 172 return ExtendsNode(nodelist, parent_name , parent_name_expr)144 return ExtendsNode(nodelist, parent_name) 173 145 174 146 def do_include(parser, token): 175 147 """ 176 148 Loads a template and renders it with the current context. 149 Optionally takes a "with value as name (, value as name)*" clause. 177 150 178 151 Example:: 179 152 180 153 {% include "foo/some_include" %} 154 {% include "foo" with value as name %} 155 {% include "foo" with value as name, bar as baz %} 181 156 """ 182 bits = token.contents.split()183 if len(bits) != 2:184 raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]185 path = bits[1]186 if path[0] in ('"', "'") and path[-1] == path[0]:187 return ConstantIncludeNode(path[1:-1])188 return IncludeNode( bits[1])157 bits = parser.token_stream(token) 158 template_name = bits.parse_expression() 159 context_map = None 160 if bits.pop_lexem('with'): 161 context_map = parse_context_map(bits) 162 bits.assert_consumed() 163 return IncludeNode(template_name, context_map) 189 164 190 165 register.tag('block', do_block) 191 166 register.tag('extends', do_extends) -
django/template/library.py
1 import re 2 from inspect import getargspec 3 from django.conf import settings 4 from django.template.context import Context 5 from django.template.nodes import Node 6 from django.template.compiler import TemplateSyntaxError 7 from django.utils.itercompat import is_iterable 8 from django.utils.functional import curry 9 10 __all__ = ('InvalidTemplateLibrary', 'Library', 'get_library', 'add_to_builtins') 11 12 # global dictionary of libraries that have been loaded using get_library 13 libraries = {} 14 # global list of libraries to load by default for a new parser 15 builtins = [] 16 17 class InvalidTemplateLibrary(Exception): 18 pass 19 20 def generic_tag_compiler(params, defaults, name, node_class, parser, token, takes_context=False, takes_nodelist=False): 21 "Returns a template.Node subclass." 22 bits = token.split_contents()[1:] 23 bmax = len(params) 24 def_len = defaults and len(defaults) or 0 25 bmin = bmax - def_len 26 stream = TokenStream(token.contents) 27 args = stream.parse_expression_list() 28 stream.assert_comsumed() 29 if(len(bits) < bmin or len(bits) > bmax): 30 if bmin == bmax: 31 message = "%s takes %s arguments" % (name, bmin) 32 else: 33 message = "%s takes between %s and %s arguments" % (name, bmin, bmax) 34 raise TemplateSyntaxError(message) 35 if takes_context: 36 node_class = curry(node_class, takes_context=takes_context) 37 if takes_nodelist: 38 nodelist = parser.parse(('end%s' % name,)) 39 parser.delete_first_token() 40 node_class = curry(node_class, nodelist=nodelist) 41 return node_class(args) 42 43 class Library(object): 44 def __init__(self): 45 self.filters = {} 46 self.tags = {} 47 48 def tag(self, name=None, compile_function=None): 49 if name == None and compile_function == None: 50 # @register.tag() 51 return self.tag_function 52 elif name != None and compile_function == None: 53 if(callable(name)): 54 # @register.tag 55 return self.tag_function(name) 56 else: 57 # @register.tag('somename') or @register.tag(name='somename') 58 def dec(func): 59 return self.tag(name, func) 60 return dec 61 elif name != None and compile_function != None: 62 # register.tag('somename', somefunc) 63 self.tags[name] = compile_function 64 return compile_function 65 else: 66 raise InvalidTemplateLibrary("Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)) 67 68 def tag_function(self,func): 69 self.tags[getattr(func, "_decorated_function", func).__name__] = func 70 return func 71 72 def filter(self, name=None, filter_func=None): 73 if name == None and filter_func == None: 74 # @register.filter() 75 return self.filter_function 76 elif filter_func == None: 77 if(callable(name)): 78 # @register.filter 79 return self.filter_function(name) 80 else: 81 # @register.filter('somename') or @register.filter(name='somename') 82 def dec(func): 83 return self.filter(name, func) 84 return dec 85 elif name != None and filter_func != None: 86 # register.filter('somename', somefunc) 87 self.filters[name] = filter_func 88 return filter_func 89 else: 90 raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func)) 91 92 def filter_function(self, func): 93 self.filters[getattr(func, "_decorated_function", func).__name__] = func 94 return func 95 96 def simple_tag(self, compile_function=None, takes_context=None, takes_nodelist=None): 97 def dec(func): 98 params, xx, xxx, defaults = getargspec(func) 99 if takes_context and takes_nodelist: 100 if params[0] == 'context' and params[1] == 'nodelist': 101 params = params[2:] 102 else: 103 raise TemplateSyntaxError("Any tag function decorated both with takes_context=True and with takes_nodelist=True must have a first argument of 'context', and a second argument of 'nodelist'") 104 elif takes_nodelist: 105 if params[0] == 'nodelist': 106 params = params[1:] 107 else: 108 raise TemplateSyntaxError("Any tag function decorated with takes_nodelist=True must have a first argument of 'nodelist'") 109 elif takes_context: 110 if params[0] == 'context': 111 params = params[1:] 112 else: 113 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 114 115 class SimpleNode(Node): 116 def __init__(self, args, takes_context=False, nodelist=None): 117 self.args = args 118 self.takes_context = takes_context 119 if nodelist is not None: 120 # Only save the 'nodelist' attribute if it's not None, so that it is picked by the Node.get_nodes_by_type() method. 121 self.nodelist = nodelist 122 123 def render(self, context): 124 func_args = [] 125 if self.takes_context: 126 func_args.append(context) 127 if hasattr(self, 'nodelist'): 128 func_args.append(self.nodelist) 129 func_args += [arg.resolve_safe(context) for arg in self.args] 130 return func(*func_args) 131 132 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode, takes_nodelist=takes_nodelist, takes_context=takes_context) 133 compile_func.__doc__ = func.__doc__ 134 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 135 return func 136 137 if callable(compile_function): 138 # @register.simple_tag 139 return dec(compile_function) 140 return dec 141 142 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 143 def dec(func): 144 params, xx, xxx, defaults = getargspec(func) 145 if takes_context: 146 if params[0] == 'context': 147 params = params[1:] 148 else: 149 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 150 151 class InclusionNode(Node): 152 def __init__(self, args): 153 self.args = args 154 155 def render(self, context): 156 resolved_vars = [arg.resolve_safe(context) for arg in self.args] 157 if takes_context: 158 args = [context] + resolved_vars 159 else: 160 args = resolved_vars 161 162 dict = func(*args) 163 164 if not getattr(self, 'nodelist', False): 165 if not isinstance(file_name, basestring) and is_iterable(file_name): 166 t = context.select_template(file_name) 167 else: 168 t = context.get_template(file_name) 169 self.nodelist = t.nodelist 170 return self.nodelist.render(context_class(dict, autoescape=context.autoescape)) 171 172 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode) 173 compile_func.__doc__ = func.__doc__ 174 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 175 return func 176 return dec 177 178 def get_library(module_name): 179 lib = libraries.get(module_name, None) 180 if not lib: 181 try: 182 mod = __import__(module_name, {}, {}, ['']) 183 except ImportError, e: 184 raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e)) 185 try: 186 lib = mod.register 187 libraries[module_name] = lib 188 except AttributeError: 189 raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) 190 return lib 191 192 def add_to_builtins(module_name): 193 builtins.append(get_library(module_name)) -
django/template/debug.py
1 from django.template import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError 1 from django.template.compiler import Lexer, Parser, tag_re 2 from django.template import NodeList, ExpressionNode, TemplateSyntaxError 2 3 from django.utils.encoding import force_unicode 3 4 from django.utils.html import escape 4 5 from django.utils.safestring import SafeData, EscapeData … … 50 51 return DebugNodeList() 51 52 52 53 def create_variable_node(self, contents): 53 return Debug VariableNode(contents)54 return DebugExpressionNode(contents) 54 55 55 56 def extend_nodelist(self, nodelist, node, token): 56 57 node.source = token.source … … 81 82 raise wrapped 82 83 return result 83 84 84 class Debug VariableNode(VariableNode):85 class DebugExpressionNode(ExpressionNode): 85 86 def render(self, context): 86 87 try: 87 output = force_unicode(self.filter_expression.resolve(context))88 return super(DebugExpressionNode, self).render(context) 88 89 except TemplateSyntaxError, e: 89 90 if not hasattr(e, 'source'): 90 91 e.source = self.source 91 92 raise 92 except UnicodeDecodeError:93 return ''94 if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):95 return escape(output)96 else:97 return output -
django/template/loader.py
21 21 # installed, because pkg_resources is necessary to read eggs. 22 22 23 23 from django.core.exceptions import ImproperlyConfigured 24 from django.template import Origin, Template, Context, TemplateDoesNotExist,add_to_builtins24 from django.template import Origin, Template, Context, add_to_builtins 25 25 from django.conf import settings 26 26 27 27 template_source_loaders = None 28 28 29 class TemplateDoesNotExist(Exception): 30 pass 31 29 32 class LoaderOrigin(Origin): 30 33 def __init__(self, display_name, loader, name, dirs): 31 34 super(LoaderOrigin, self).__init__(display_name) -
django/templatetags/i18n.py
1 1 import re 2 2 3 from django.template import Node, Variable, VariableNode 4 from django.template import TemplateSyntaxError, TokenParser, Library 5 from django.template import TOKEN_TEXT, TOKEN_VAR 3 from django.template import Node, VariableNode, TokenParser, Variable, TemplateSyntaxError, Library 4 from django.template.compiler import TOKEN_TEXT, TOKEN_VAR 6 5 from django.utils import translation 7 6 from django.utils.encoding import force_unicode 8 7 … … 35 34 36 35 class TranslateNode(Node): 37 36 def __init__(self, value, noop): 38 self.value = Variable(value)37 self.value = value 39 38 self.noop = noop 40 39 41 40 def render(self, context): … … 171 170 the variable ``variable``. Make sure that the string 172 171 in there is something that is in the .po file. 173 172 """ 174 class TranslateParser(TokenParser): 175 def top(self): 176 value = self.value() 177 if self.more(): 178 if self.tag() == 'noop': 179 noop = True 180 else: 181 raise TemplateSyntaxError, "only option for 'trans' is 'noop'" 182 else: 183 noop = False 184 return (value, noop) 185 value, noop = TranslateParser(token.contents).top() 173 bits = parser.token_stream(token) 174 try: 175 value = bits.parse_expression() 176 except TokenSyntaxError: 177 bits.expected("expression") 178 noop = bits.pop_lexem('noop') 179 bits.assert_consumed() 186 180 return TranslateNode(value, noop) 187 181 188 182 def do_block_translate(parser, token): -
django/templatetags/cache.py
1 from django.template import Library, Node, TemplateSyntaxError, Variable, VariableDoesNotExist 2 from django.template import resolve_variable 1 from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist, TokenSyntaxError 3 2 from django.core.cache import cache 4 3 from django.utils.encoding import force_unicode 5 4 … … 8 7 class CacheNode(Node): 9 8 def __init__(self, nodelist, expire_time_var, fragment_name, vary_on): 10 9 self.nodelist = nodelist 11 self.expire_time_var = Variable(expire_time_var)10 self.expire_time_var = expire_time_var 12 11 self.fragment_name = fragment_name 13 12 self.vary_on = vary_on 14 13 … … 22 21 except (ValueError, TypeError): 23 22 raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time) 24 23 # Build a unicode key for this fragment and all vary-on's. 25 cache_key = u':'.join([self.fragment_name] + [force_unicode( resolve_variable(var,context)) for var in self.vary_on])24 cache_key = u':'.join([self.fragment_name] + [force_unicode(var.resolve_safe(context)) for var in self.vary_on]) 26 25 value = cache.get(cache_key) 27 26 if value is None: 28 27 value = self.nodelist.render(context) … … 50 49 51 50 Each unique set of arguments will result in a unique cache entry. 52 51 """ 53 nodelist = parser.parse(('endcache',)) 54 parser.delete_first_token() 55 tokens = token.contents.split() 56 if len(tokens) < 3: 57 raise TemplateSyntaxError(u"'%r' tag requires at least 2 arguments." % tokens[0]) 58 return CacheNode(nodelist, tokens[1], tokens[2], tokens[3:]) 52 nodelist = parser.parse_nodelist(('endcache',)) 53 bits = parser.token_stream(token) 54 try: 55 expire_time = bits.parse_expression() 56 except TokenSyntaxError: 57 bits.expected("expression") 58 name = bits.pop_name() 59 if not name: 60 raise TemplateSyntaxError, "'cache' requires a fragment name" 61 vary_on = bits.parse_expression_list() 62 bits.assert_consumed() 63 return CacheNode(nodelist, expire_time, name, vary_on) 59 64 60 65 register.tag('cache', do_cache)