Ticket #7806: tplrf.2.diff
File tplrf.2.diff, 116.9 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 __repr__(self): 17 return "<%s>" % self.__class__.__name__ 18 19 def __iter__(self): 20 yield self 21 22 def get_nodes_by_type(self, nodetype): 23 "Return a list of all nodes (within this node and its nodelist) of the given type" 24 nodes = [] 25 if isinstance(self, nodetype): 26 nodes.append(self) 27 if hasattr(self, 'nodelist'): 28 nodes.extend(self.nodelist.get_nodes_by_type(nodetype)) 29 return nodes 30 31 class NodeList(list): 32 # Set to True the first time a non-TextNode is inserted by 33 # extend_nodelist(). 34 contains_nontext = False 35 36 def render(self, context): 37 bits = [] 38 for node in self: 39 if isinstance(node, Node): 40 bits.append(self.render_node(node, context)) 41 else: 42 bits.append(node) 43 return mark_safe(''.join([force_unicode(b) for b in bits])) 44 45 def get_nodes_by_type(self, nodetype): 46 "Return a list of all nodes of the given type" 47 nodes = [] 48 for node in self: 49 nodes.extend(node.get_nodes_by_type(nodetype)) 50 return nodes 51 52 def render_node(self, node, context): 53 return node.render(context) 54 55 class TextNode(Node): 56 def __init__(self, s): 57 self.s = s 58 59 def __repr__(self): 60 return "<Text Node: '%s'>" % self.s[:25] 61 62 def render(self, context): 63 return self.s 64 65 class ExpressionNode(Node): 66 def __init__(self, expression): 67 self.expression = expression 68 69 def __repr__(self): 70 return "<Variable Node: %s>" % self.expression 71 72 def render(self, context): 73 try: 74 output = force_unicode(self.expression.resolve(context)) 75 except LookupError: 76 if settings.TEMPLATE_STRING_IF_INVALID: 77 from django.template import invalid_var_format_string 78 if invalid_var_format_string is None: 79 invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID 80 if invalid_var_format_string: 81 return settings.TEMPLATE_STRING_IF_INVALID % self.expression 82 return settings.TEMPLATE_STRING_IF_INVALID 83 else: 84 return '' 85 except UnicodeDecodeError: 86 # Unicode conversion can fail sometimes for reasons out of our 87 # control (e.g. exception rendering). In that case, we fail quietly. 88 return '' 89 if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData): 90 output = escape(output) 91 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.encoding import smart_unicode, smart_str 8 from django.utils.functional import wraps 9 from django.utils.safestring import mark_safe 10 from django.utils.text import smart_split 11 from django.utils.translation import ugettext 12 13 __all__ = ('Template', 'TemplateSyntaxError', 'TokenSyntaxError', 'TokenStream') 14 15 TOKEN_TEXT = 0 16 TOKEN_VAR = 1 17 TOKEN_BLOCK = 2 18 TOKEN_COMMENT = 3 19 20 # template syntax constants 21 FILTER_SEPARATOR = '|' 22 FILTER_ARGUMENT_SEPARATOR = ':' 23 BLOCK_TAG_START = '{%' 24 BLOCK_TAG_END = '%}' 25 VARIABLE_TAG_START = '{{' 26 VARIABLE_TAG_END = '}}' 27 COMMENT_TAG_START = '{#' 28 COMMENT_TAG_END = '#}' 29 SINGLE_BRACE_START = '{' 30 SINGLE_BRACE_END = '}' 31 32 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' 33 34 # what to report as the origin for templates that come from non-loader sources 35 # (e.g. strings) 36 UNKNOWN_SOURCE="<unknown source>" 37 38 # match a variable or block tag and capture the entire tag, including start/end delimiters 39 tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 40 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), 41 re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))) 42 43 class TemplateSyntaxError(Exception): 44 def __str__(self): 45 try: 46 import cStringIO as StringIO 47 except ImportError: 48 import StringIO 49 output = StringIO.StringIO() 50 output.write(Exception.__str__(self)) 51 # Check if we wrapped an exception and print that too. 52 if hasattr(self, 'exc_info'): 53 import traceback 54 output.write('\n\nOriginal ') 55 e = self.exc_info 56 traceback.print_exception(e[0], e[1], e[2], 500, output) 57 return output.getvalue() 58 59 class TemplateEncodingError(Exception): 60 pass 61 62 class Origin(object): 63 def __init__(self, name): 64 self.name = name 65 66 def reload(self): 67 raise NotImplementedError 68 69 def __str__(self): 70 return self.name 71 72 class StringOrigin(Origin): 73 def __init__(self, source): 74 super(StringOrigin, self).__init__(UNKNOWN_SOURCE) 75 self.source = source 76 77 def reload(self): 78 return self.source 79 80 class Template(object): 81 def __init__(self, template_string, origin=None, name='<Unknown Template>'): 82 try: 83 template_string = smart_unicode(template_string) 84 except UnicodeDecodeError: 85 raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.") 86 if settings.TEMPLATE_DEBUG and origin is None: 87 origin = StringOrigin(template_string) 88 self.nodelist = compile_string(template_string, origin) 89 self.name = name 90 91 def __iter__(self): 92 for node in self.nodelist: 93 for subnode in node: 94 yield subnode 95 96 def render(self, context): 97 "Display stage -- can be called many times" 98 return self.nodelist.render(context) 99 100 def compile_string(template_string, origin): 101 "Compiles template_string into NodeList ready for rendering" 102 if settings.TEMPLATE_DEBUG: 103 from debug import DebugLexer, DebugParser 104 lexer_class, parser_class = DebugLexer, DebugParser 105 else: 106 lexer_class, parser_class = Lexer, Parser 107 lexer = lexer_class(template_string, origin) 108 parser = parser_class(lexer.tokenize()) 109 return parser.parse() 110 111 class Token(object): 112 def __init__(self, token_type, contents): 113 # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT. 114 self.token_type, self.contents = token_type, contents 115 116 def __str__(self): 117 return '<%s token: "%s...">' % \ 118 ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_COMMENT: 'Comment'}[self.token_type], 119 self.contents[:20].replace('\n', '')) 120 121 def split_contents(self): 122 return list(smart_split(self.contents)) 123 124 class Lexer(object): 125 def __init__(self, template_string, origin): 126 self.template_string = template_string 127 self.origin = origin 128 129 def tokenize(self): 130 "Return a list of tokens from a given template_string." 131 in_tag = False 132 result = [] 133 for bit in tag_re.split(self.template_string): 134 if bit: 135 result.append(self.create_token(bit, in_tag)) 136 in_tag = not in_tag 137 return result 138 139 def create_token(self, token_string, in_tag): 140 """ 141 Convert the given token string into a new Token object and return it. 142 If in_tag is True, we are processing something that matched a tag, 143 otherwise it should be treated as a literal string. 144 """ 145 if in_tag: 146 if token_string.startswith(VARIABLE_TAG_START): 147 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 148 elif token_string.startswith(BLOCK_TAG_START): 149 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) 150 elif token_string.startswith(COMMENT_TAG_START): 151 token = Token(TOKEN_COMMENT, '') 152 else: 153 token = Token(TOKEN_TEXT, token_string) 154 return token 155 156 class Parser(object): 157 def __init__(self, tokens): 158 self.tokens = tokens 159 self.tags = {} 160 self.filters = {} 161 from django.template.library import builtins 162 for lib in builtins: 163 self.add_library(lib) 164 165 def parse(self, parse_until=None): 166 if parse_until is None: parse_until = [] 167 nodelist = self.create_nodelist() 168 while self.tokens: 169 token = self.next_token() 170 if token.token_type == TOKEN_TEXT: 171 self.extend_nodelist(nodelist, TextNode(token.contents), token) 172 elif token.token_type == TOKEN_VAR: 173 if not token.contents: 174 self.empty_variable(token) 175 filter_expression = self.compile_filter(token.contents) 176 var_node = self.create_variable_node(filter_expression) 177 self.extend_nodelist(nodelist, var_node,token) 178 elif token.token_type == TOKEN_BLOCK: 179 if token.contents in parse_until: 180 # put token back on token list so calling code knows why it terminated 181 self.prepend_token(token) 182 return nodelist 183 try: 184 command = token.contents.split()[0] 185 except IndexError: 186 self.empty_block_tag(token) 187 # execute callback function for this tag and append resulting node 188 self.enter_command(command, token) 189 try: 190 compile_func = self.tags[command] 191 except KeyError: 192 self.invalid_block_tag(token, command) 193 try: 194 compiled_result = compile_func(self, token) 195 except TemplateSyntaxError, e: 196 if not self.compile_function_error(token, e): 197 raise 198 self.extend_nodelist(nodelist, compiled_result, token) 199 self.exit_command() 200 if parse_until: 201 self.unclosed_block_tag(parse_until) 202 return nodelist 203 204 def parse_nodelist(self, parse_until=None): 205 nodelist = self.parse(parse_until=parse_until) 206 self.delete_first_token() 207 return nodelist 208 209 def skip_past(self, endtag): 210 while self.tokens: 211 token = self.next_token() 212 if token.token_type == TOKEN_BLOCK and token.contents == endtag: 213 return 214 self.unclosed_block_tag([endtag]) 215 216 def create_variable_node(self, expression): 217 return ExpressionNode(expression) 218 219 def create_nodelist(self): 220 return NodeList() 221 222 def extend_nodelist(self, nodelist, node, token): 223 if node.must_be_first and nodelist: 224 try: 225 if nodelist.contains_nontext: 226 raise AttributeError 227 except AttributeError: 228 raise TemplateSyntaxError("%r must be the first tag in the template." % node) 229 if isinstance(nodelist, NodeList) and not isinstance(node, TextNode): 230 nodelist.contains_nontext = True 231 nodelist.append(node) 232 233 def enter_command(self, command, token): 234 pass 235 236 def exit_command(self): 237 pass 238 239 def error(self, token, msg): 240 return TemplateSyntaxError(msg) 241 242 def empty_variable(self, token): 243 raise self.error(token, "Empty variable tag") 244 245 def empty_block_tag(self, token): 246 raise self.error(token, "Empty block tag") 247 248 def invalid_block_tag(self, token, command): 249 raise self.error(token, "Invalid block tag: '%s'" % command) 250 251 def unclosed_block_tag(self, parse_until): 252 raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until)) 253 254 def compile_function_error(self, token, e): 255 pass 256 257 def next_token(self): 258 return self.tokens.pop(0) 259 260 def prepend_token(self, token): 261 self.tokens.insert(0, token) 262 263 def delete_first_token(self): 264 del self.tokens[0] 265 266 def add_library(self, lib): 267 self.tags.update(lib.tags) 268 self.filters.update(lib.filters) 269 270 def token_stream(self, token): 271 return TokenStream(self, token) 272 273 def compile_filter(self, token): 274 stream = TokenStream(self, token) 275 expr = stream.parse_expression(required=True) 276 stream.assert_consumed("invalid filter expression") 277 return expr 278 279 def find_filter(self, filter_name): 280 if filter_name in self.filters: 281 return self.filters[filter_name] 282 else: 283 raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name) 284 285 def filter_args_check(name, func, provided): 286 provided = list(provided) 287 plen = len(provided) 288 # Check to see if a decorator is providing the real function. 289 func = getattr(func, '_decorated_function', func) 290 args, varargs, varkw, defaults = getargspec(func) 291 292 if plen + 1 == len(args) or (defaults and plen + 1 <= len(args) + len(defaults)): 293 return True 294 295 # First argument is filter input. 296 args.pop(0) 297 if defaults: 298 nondefs = args[:-len(defaults)] 299 else: 300 nondefs = args 301 # Args without defaults must be provided. 302 try: 303 for arg in nondefs: 304 provided.pop(0) 305 except IndexError: 306 # Not enough 307 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen)) 308 309 # Defaults can be overridden. 310 defaults = defaults and list(defaults) or [] 311 try: 312 for parg in provided: 313 defaults.pop(0) 314 except IndexError: 315 # Too many. 316 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen)) 317 318 return True 319 320 321 bit_re = re.compile(r""" 322 (?P<string_literal>"(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)') 323 |(?P<numeric_literal>[+-]?\.?\d[\d\.e]*) 324 |(?P<name>[\w.]+) # keyword or variable 325 |(?P<char>\S) # punctuation 326 """, re.VERBOSE) 327 328 class TokenSyntaxError(Exception): 329 pass 330 331 def token_stream_parser(func): 332 def wrapper(self, required=False, *args, **kwargs): 333 mark = self.offset 334 try: 335 return func(self, *args, **kwargs) 336 except TokenSyntaxError: 337 if required: 338 #FIXME: hack 339 self.expected("<%s>" % ' '.join(func.__name__.split('_')[1:])) 340 self.offset = mark 341 raise 342 return wraps(func)(wrapper) 343 344 class TokenStream(object): 345 exception = TokenSyntaxError() 346 def __init__(self, parser, source): 347 self.parser = parser 348 self.source = source 349 self.offset = 0 350 self.name = None 351 if isinstance(source, Token): 352 bits = source.contents.split(None, 1) 353 self.source = len(bits) == 2 and bits[1] or '' 354 self.name = bits[0] 355 self.tokens = [(bit.lastgroup, bit.group(0)) for bit in bit_re.finditer(self.source)] 356 357 def peek(self): 358 return self.tokens[self.offset] 359 360 def consumed(self): 361 return self.offset == len(self.tokens) 362 363 def pop(self): 364 if self.offset == len(self.tokens): 365 raise self.exception 366 next = self.tokens[self.offset] 367 self.offset += 1 368 return next 369 370 def pop_lexem(self, lexem): 371 if self.offset == len(self.tokens): 372 return False 373 _, next = self.tokens[self.offset] 374 if next == lexem: 375 self.offset += 1 376 return True 377 return False 378 379 def pop_name(self): 380 if self.offset == len(self.tokens): 381 return None 382 tokentype, lexem = self.tokens[self.offset] 383 if tokentype == 'name': 384 self.offset += 1 385 return lexem 386 return None 387 388 def pushback(self): 389 self.offset -= 1 390 391 def assert_consumed(self, msg=None): 392 if self.offset != len(self.tokens): 393 raise TemplateSyntaxError, (msg or "unmatched input") + repr(self.tokens[self.offset:]) 394 395 def expected(self, what): 396 if self.consumed(): 397 found = "<EOT>" 398 else: 399 found = "<%s> %s" % self.tokens[self.offset] 400 raise TemplateSyntaxError, "expected %s, found %s" % (what, smart_str(found, encoding='ascii', errors='backslashreplace')) 401 402 403 #@token_stream_parser 404 def parse_string(self, bare=False): 405 tokentype, lexem = self.pop() 406 if tokentype == 'string_literal': 407 return lexem.replace(r'\%s' % lexem[0], lexem[0]).replace(r'\\', '')[1:-1] 408 if bare and tokentype == 'name': 409 return lexem 410 raise self.exception 411 parse_string = token_stream_parser(parse_string) 412 413 414 #@token_stream_parser 415 def parse_int(self): 416 token_type, lexem = self.pop() 417 if token_type == 'numeric_literal': 418 try: 419 return int(lexem) 420 except ValueError: 421 pass 422 raise self.exception 423 parse_int = token_stream_parser(parse_int) 424 425 426 #@token_stream_parser 427 def parse_value(self): 428 translate = False 429 if self.pop_lexem('_'): 430 if not self.pop_lexem('('): 431 raise self.exception 432 translate = True 433 tokentype, lexem = self.pop() 434 435 if tokentype == 'char': 436 raise self.exception 437 438 if tokentype == 'name': 439 if lexem.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or lexem[0] == '_': 440 raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % lexem) 441 value = Lookup(lexem) 442 elif tokentype == 'string_literal': 443 value = lexem.replace(r'\%s' % lexem[0], lexem[0]).replace(r'\\', '')[1:-1] 444 value = Literal(mark_safe(value)) 445 elif tokentype == 'numeric_literal': 446 try: 447 value = float(lexem) 448 except ValueError: 449 raise self.exception 450 if '.' not in lexem and 'e' not in lexem.lower(): 451 value = int(value) 452 #FIXME: this causes a test failure: `ifequal-numeric07` 453 if lexem.endswith('.'): 454 raise TemplateSyntaxError, "Numeric literals may not end with '.': %s" % lexem 455 value = Literal(value) 456 if translate: 457 if not self.pop_lexem(')'): 458 raise self.exception 459 value = FilterExpression(value, [(ugettext, ())]) 460 return value 461 parse_value = token_stream_parser(parse_value) 462 463 464 #@token_stream_parser 465 def parse_filter(self): 466 if not self.pop_lexem('|'): 467 raise self.exception 468 name = self.pop_name() 469 if not name: 470 raise self.exception 471 args = [] 472 if self.pop_lexem(':'): 473 args.append(self.parse_value()) 474 func = self.parser.find_filter(name) 475 filter_args_check(name, func, args) 476 return func, args 477 parse_filter = token_stream_parser(parse_filter) 478 479 480 #@token_stream_parser 481 def parse_expression(self): 482 var = self.parse_value() 483 filters = [] 484 try: 485 while True: 486 filters.append(self.parse_filter()) 487 except TokenSyntaxError: 488 pass 489 if filters: 490 return FilterExpression(var, filters) 491 return var 492 parse_expression = token_stream_parser(parse_expression) 493 494 495 #@token_stream_parser 496 def parse_expression_list(self, minimum=0, maximum=None, count=None): 497 expressions = [] 498 if count: 499 minimum = count 500 maximum = count 501 try: 502 while True: 503 if len(expressions) == maximum: 504 break 505 expressions.append(self.parse_expression()) 506 except TokenSyntaxError: 507 pass 508 if len(expressions) < minimum: 509 self.expected("expression") 510 return expressions 511 parse_expression_list = token_stream_parser(parse_expression_list) 512 513 514 def token_stream_parsed(func): 515 def decorator(parser, token): 516 bits = parser.token_stream(token) 517 result = func(parser, bits) 518 bits.assert_consumed() 519 return result 520 return wraps(func)(decorator) 521 -
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 from django.template import Node, NodeList, TokenSyntaxError 3 4 class EmptyNode(Node): 5 def render(self, context): 6 return u'' 7 8 class ConditionalNode(Node): 9 def __init__(self, nodelist_true, nodelist_false): 10 self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false 11 12 def __iter__(self): 13 for node in self.nodelist_true: 14 yield node 15 for node in self.nodelist_false: 16 yield node 17 18 def get_nodes_by_type(self, nodetype): 19 nodes = [] 20 if isinstance(self, nodetype): 21 nodes.append(self) 22 nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype)) 23 nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype)) 24 return nodes 25 26 def check_condition(self, context): 27 return False 28 29 def render(self, context): 30 if self.check_condition(context): 31 return self.nodelist_true.render(context) 32 else: 33 return self.nodelist_false.render(context) 34 35 def parse_conditional_nodelists(parser, name): 36 end_tag = 'end' + name 37 nodelist_true = parser.parse(('else', end_tag)) 38 token = parser.next_token() 39 if token.contents == 'else': 40 nodelist_false = parser.parse((end_tag,)) 41 parser.delete_first_token() 42 else: 43 nodelist_false = NodeList() 44 return nodelist_true, nodelist_false 45 46 def parse_args_and_kwargs(self): 47 args = [] 48 kwargs = {} 49 while True: 50 name = self.pop_name() 51 if name and self.pop_lexem('='): 52 try: 53 kwargs[name] = self.parse_expression() 54 except TokenSyntaxError: 55 raise TemplateSyntaxError, "expected expression in kwargs" 56 else: 57 if name: 58 self.pushback() 59 try: 60 args.append(self.parse_expression()) 61 except TokenSyntaxError: 62 break 63 if not self.pop_lexem(','): 64 break 65 return args, kwargs 66 67 def parse_as(bits): 68 if bits.pop_lexem('as'): 69 name = bits.pop_name() 70 if name: 71 return name 72 raise bits.exception 73 74 def resolve_args_and_kwargs(args, kwargs, context): 75 resolved_args = [arg.resolve(context, True) for arg in args] 76 resolved_kwargs = {} 77 for name in kwargs: 78 resolved_kwargs[name] = kwargs[name].resolve(context, True) 79 return resolved_args, resolved_kwargs 80 -
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
8 8 except NameError: 9 9 from django.utils.itercompat import reversed # Python 2.3 fallback 10 10 11 from django.template import Node, NodeList, Template, Context , Variable12 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_END11 from django.template import Node, NodeList, Template, Context 12 from django.template import TemplateSyntaxError, LookupError, TokenSyntaxError 13 13 from django.template import get_library, Library, InvalidTemplateLibrary 14 from django.template.compiler import token_stream_parsed, 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, 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 '' 93 89 return "<For Node: for %s in %s, tail_len: %d%s>" % \ 94 (', '.join(self.loopvars), self.sequence, len(self.nodelist _loop),90 (', '.join(self.loopvars), self.sequence, len(self.nodelist), 95 91 reversed_text) 96 92 97 93 def __iter__(self): 98 for node in self.nodelist _loop:94 for node in self.nodelist: 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 except VariableDoesNotExist:163 except LookupError: 178 164 compare_to = None 179 165 180 166 if compare_to != self._last_seen: … … 188 174 else: 189 175 return '' 190 176 191 class IfEqualNode(Node): 192 def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): 193 self.var1, self.var2 = Variable(var1), Variable(var2) 194 self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false 177 178 class IfEqualNode(ConditionalNode): 179 def __init__(self, val1, val2, nodelist_true, nodelist_false, negate): 180 super(IfEqualNode, self).__init__(nodelist_true, nodelist_false) 181 self.val1, self.val2 = val1, val2 195 182 self.negate = negate 196 183 197 def __repr__(self): 198 return "<IfEqualNode>" 184 def check_condition(self, context): 185 val1, val2 = self.val1.resolve_safe(context), self.val2.resolve_safe(context) 186 return self.negate == (val1 != val2) 199 187 200 def render(self, context):201 try:202 val1 = self.var1.resolve(context)203 except VariableDoesNotExist:204 val1 = None205 try:206 val2 = self.var2.resolve(context)207 except VariableDoesNotExist:208 val2 = None209 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)212 188 213 class IfNode( Node):189 class IfNode(ConditionalNode): 214 190 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_false191 super(IfNode, self).__init__(nodelist_true, nodelist_false) 192 self.bool_exprs = bool_exprs 217 193 self.link_type = link_type 218 194 219 def __repr__(self): 220 return "<If node>" 221 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) 195 def check_condition(self, context): 196 if self.link_type == 'or': 197 for negated, bool_expr in self.bool_exprs: 198 value = bool_expr.resolve_safe(context, default=False) 199 if bool(value) != negated: 200 return True 201 return False 246 202 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) 203 for negated, bool_expr in self.bool_exprs: 204 value = bool_expr.resolve_safe(context, default=False) 205 if bool(value) == negated: 206 return False 207 return True 208 255 209 256 class LinkTypes:257 and_ = 0,258 or_ = 1259 260 210 class RegroupNode(Node): 261 211 def __init__(self, target, expression, var_name): 262 212 self.target, self.expression = target, expression 263 213 self.var_name = var_name 264 214 265 215 def render(self, context): 266 obj_list = self.target.resolve (context, True)216 obj_list = self.target.resolve_safe(context) 267 217 if obj_list == None: 268 218 # target variable wasn't found in context; fail silently. 269 219 context[self.var_name] = [] … … 273 223 context[self.var_name] = [ 274 224 {'grouper': key, 'list': list(val)} 275 225 for key, val in 276 groupby(obj_list, lambda v, f=self.expression.resolve : f(v, True))226 groupby(obj_list, lambda v, f=self.expression.resolve_safe: f(v)) 277 227 ] 278 228 return '' 279 229 … … 310 260 return '' # Fail silently for invalid included templates. 311 261 return output 312 262 313 class LoadNode(Node): 314 def render(self, context): 315 return '' 263 class LoadNode(EmptyNode): pass 316 264 317 265 class NowNode(Node): 318 266 def __init__(self, format_string): … … 322 270 from datetime import datetime 323 271 from django.utils.dateformat import DateFormat 324 272 df = DateFormat(datetime.now()) 325 return df.format(self.format_string )273 return df.format(self.format_string.resolve_safe(context)) 326 274 327 275 class SpacelessNode(Node): 328 276 def __init__(self, nodelist): … … 357 305 358 306 def render(self, context): 359 307 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))308 args = [arg.resolve_safe(context) for arg in self.args] 309 kwargs = dict([(smart_str(k,'ascii'), v.resolve_safe(context)) 362 310 for k, v in self.kwargs.items()]) 363 311 try: 364 312 return reverse(self.view_name, args=args, kwargs=kwargs) … … 380 328 try: 381 329 value = self.val_expr.resolve(context) 382 330 maxvalue = self.max_expr.resolve(context) 383 except VariableDoesNotExist:331 except LookupError: 384 332 return '' 385 333 try: 386 value = float(value) 387 maxvalue = float(maxvalue) 388 ratio = (value / maxvalue) * int(self.max_width) 334 ratio = (float(value) / float(maxvalue)) * int(self.max_width) 389 335 except (ValueError, ZeroDivisionError): 390 336 return '' 391 337 return str(int(round(ratio))) 392 338 393 339 class WithNode(Node): 394 def __init__(self, var, name, nodelist):395 self. var = var396 self.name = name 340 def __init__(self, expr, name, nodelist): 341 self.expr = expr 342 self.name = name 397 343 self.nodelist = nodelist 398 344 399 def __repr__(self):400 return "<WithNode>"401 402 345 def render(self, context): 403 val = self.var.resolve(context)404 346 context.push() 405 context[self.name] = val347 context[self.name] = self.expr.resolve_safe(context) 406 348 output = self.nodelist.render(context) 407 349 context.pop() 408 350 return output … … 418 360 arg = args[1] 419 361 if arg not in (u'on', u'off'): 420 362 raise TemplateSyntaxError("'Autoescape' argument should be 'on' or 'off'") 421 nodelist = parser.parse(('endautoescape',)) 422 parser.delete_first_token() 363 nodelist = parser.parse_nodelist(('endautoescape',)) 423 364 return AutoEscapeControlNode((arg == 'on'), nodelist) 424 365 autoescape = register.tag(autoescape) 425 366 … … 489 430 490 431 if len(args) > 4 and args[-2] == 'as': 491 432 name = args[-1] 492 node = CycleNode(args[1:-2], name) 433 values = [parser.compile_filter(arg) for arg in args[1:-2]] 434 node = CycleNode(values, name) 493 435 if not hasattr(parser, '_namedCycleNodes'): 494 436 parser._namedCycleNodes = {} 495 437 parser._namedCycleNodes[name] = node 496 438 else: 497 node = CycleNode(args[1:]) 439 values = [parser.compile_filter(arg) for arg in args[1:]] 440 node = CycleNode(values) 498 441 return node 499 442 cycle = register.tag(cycle) 500 443 … … 526 469 This text will be HTML-escaped, and will appear in lowercase. 527 470 {% endfilter %} 528 471 """ 529 _, rest = token.contents.split(None, 1) 530 filter_expr = parser.compile_filter("var|%s" % (rest)) 531 for func, unused in filter_expr.filters: 472 name, filter_ = token.contents.split(None, 1) 473 bits = parser.token_stream("var|%s" % filter_) 474 try: 475 filter_expr = bits.parse_expression() 476 except TokenSyntaxError: 477 raise TemplateSyntaxError("'%s' requires a valid filter chain, got: '%s'" % name, filter_) 478 for func, args 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 539 485 #@register.tag 540 def firstof(parser, token): 486 #@token_stream_parsed 487 def firstof(parser, bits): 541 488 """ 542 489 Outputs the first variable passed that is not False. 543 490 … … 565 512 {% firstof var1 var2 var3 "fallback value" %} 566 513 567 514 """ 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) 573 firstof = register.tag(firstof) 515 expressions = bits.parse_expression_list(minimum=1) 516 return FirstOfNode(expressions) 517 firstof = register.tag(token_stream_parsed(firstof)) 574 518 575 519 #@register.tag(name="for") 576 def do_for(parser, token): 520 #@token_stream_parsed 521 def do_for(parser, bits): 577 522 """ 578 523 Loops over each item in an array. 579 524 … … 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) 560 loopvars = [] 561 while True: 562 var = bits.pop_name() 563 if not var: 564 break 565 loopvars.append(var) 566 if not bits.pop_lexem(','): 567 break 568 if not loopvars: 569 raise TemplateSyntaxError("'for' tag requires at least one loopvar") 570 571 if not bits.pop_lexem('in'): 572 raise TemplateSyntaxError("'for' tag requires 'in' keyword") 573 sequence = bits.parse_expression(required=True) 574 reversed = bits.pop_lexem('reversed') 575 nodelist = parser.parse_nodelist(('endfor',)) 576 return ForNode(loopvars, sequence, reversed, nodelist) 577 do_for = register.tag("for", token_stream_parsed(do_for)) 619 578 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) 579 def do_ifequal(parser, bits, negate): 580 val1, val2 = bits.parse_expression_list(count=2) 581 nodelist_true, nodelist_false = parse_conditional_nodelists(parser, bits.name) 582 return IfEqualNode(val1, val2, nodelist_true, nodelist_false, negate) 625 583 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)636 do_for = register.tag("for", do_for)637 638 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)651 652 584 #@register.tag 653 def ifequal(parser, token): 585 #@token_stream_parsed 586 def ifequal(parser, bits): 654 587 """ 655 588 Outputs the contents of the block if the two arguments equal each other. 656 589 … … 666 599 ... 667 600 {% endifnotequal %} 668 601 """ 669 return do_ifequal(parser, token, False)670 ifequal = register.tag( ifequal)602 return do_ifequal(parser, bits, False) 603 ifequal = register.tag(token_stream_parsed(ifequal)) 671 604 672 605 #@register.tag 673 def ifnotequal(parser, token): 606 #@token_stream_parsed 607 def ifnotequal(parser, bits): 674 608 """ 675 609 Outputs the contents of the block if the two arguments are not equal. 676 610 See ifequal. 677 611 """ 678 return do_ifequal(parser, token, True)679 ifnotequal = register.tag( ifnotequal)612 return do_ifequal(parser, bits, True) 613 ifnotequal = register.tag(token_stream_parsed(ifnotequal)) 680 614 681 615 #@register.tag(name="if") 682 def do_if(parser, token): 616 #@token_stream_parsed 617 def do_if(parser, bits): 683 618 """ 684 619 The ``{% if %}`` tag evaluates a variable, and if that variable is "true" 685 620 (i.e., exists, is not empty, and is not a false boolean value), the … … 737 672 {% endif %} 738 673 {% endif %} 739 674 """ 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 ') 675 link_type = None 676 link = None 747 677 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))) 678 while True: 679 negated = False 680 if bits.pop_lexem('not'): 681 negated = True 682 try: 683 expr = bits.parse_expression() 684 except TokenSyntaxError: 685 if link: 686 raise TemplateSyntaxError("'if' statement improperly formatted") 687 else: 688 raise TemplateSyntaxError("'if' statement requires at least one argument") 689 boolvars.append((negated, expr)) 690 link = bits.pop_name() 691 if not link: 692 break 693 if link_type: 694 if link_type != link: 695 raise TemplateSyntaxError("'if' tags can't mix 'and' and 'or'") 764 696 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() 697 if not link in ('and', 'or'): 698 raise TemplateSyntaxError("'if' tag expects 'and' or 'or', got: %s" % link) 699 link_type = link 700 701 nodelist_true, nodelist_false = parse_conditional_nodelists(parser, 'if') 773 702 return IfNode(boolvars, nodelist_true, nodelist_false, link_type) 774 do_if = register.tag("if", do_if)703 do_if = register.tag("if", token_stream_parsed(do_if)) 775 704 776 705 #@register.tag 777 def ifchanged(parser, token): 706 #@token_stream_parsed 707 def ifchanged(parser, bits): 778 708 """ 779 709 Checks if a value has changed from the last iteration of a loop. 780 710 … … 802 732 {% endifchanged %} 803 733 {% endfor %} 804 734 """ 805 bits = token.contents.split() 806 nodelist = parser.parse(('endifchanged',)) 807 parser.delete_first_token() 808 return IfChangedNode(nodelist, *bits[1:]) 809 ifchanged = register.tag(ifchanged) 735 nodelist = parser.parse_nodelist(('endifchanged',)) 736 values = bits.parse_expression_list() 737 return IfChangedNode(nodelist, *values) 738 ifchanged = register.tag(token_stream_parsed(ifchanged)) 810 739 811 740 #@register.tag 812 741 def ssi(parser, token): … … 861 790 load = register.tag(load) 862 791 863 792 #@register.tag 864 def now(parser, token): 793 #@token_stream_parsed 794 def now(parser, bits): 865 795 """ 866 796 Displays the date, formatted according to the given string. 867 797 … … 872 802 873 803 It is {% now "jS F Y H:i" %} 874 804 """ 875 bits = token.contents.split('"') 876 if len(bits) != 3: 877 raise TemplateSyntaxError, "'now' statement takes one argument" 878 format_string = bits[1] 805 format_string = bits.parse_expression(required=True) 879 806 return NowNode(format_string) 880 now = register.tag( now)807 now = register.tag(token_stream_parsed(now)) 881 808 882 809 #@register.tag 883 def regroup(parser, token): 810 #@token_stream_parsed 811 def regroup(parser, bits): 884 812 """ 885 813 Regroups a list of alike objects by a common attribute. 886 814 … … 926 854 {% regroup people|dictsort:"gender" by gender as grouped %} 927 855 928 856 """ 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] 857 target = bits.parse_expression(required=True) 858 if not bits.pop_lexem('by'): 859 raise bits.expected("'by'") 860 expression = bits.parse_expression(required=True) 861 try: 862 var_name = parse_as(bits) 863 except TokenSyntaxError: 864 raise bits.expected("as <name>") 943 865 return RegroupNode(target, expression, var_name) 944 regroup = register.tag( regroup)866 regroup = register.tag(token_stream_parsed(regroup)) 945 867 946 868 def spaceless(parser, token): 947 869 """ … … 968 890 </strong> 969 891 {% endspaceless %} 970 892 """ 971 nodelist = parser.parse(('endspaceless',)) 972 parser.delete_first_token() 973 return SpacelessNode(nodelist) 893 return SpacelessNode(parser.parse_nodelist(('endspaceless',))) 974 894 spaceless = register.tag(spaceless) 975 895 976 896 #@register.tag … … 1007 927 return TemplateTagNode(tag) 1008 928 templatetag = register.tag(templatetag) 1009 929 1010 def url(parser, token): 930 #@register.tag 931 #@token_stream_parsed 932 def url(parser, bits): 1011 933 """ 1012 934 Returns an absolute URL matching given view with its parameters. 1013 935 … … 1038 960 1039 961 The URL will look like ``/clients/client/123/``. 1040 962 """ 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) 1056 url = register.tag(url) 963 view = bits.parse_string(bare=True, required=True) 964 args, kwargs = parse_args_and_kwargs(bits) 965 return URLNode(view, args, kwargs) 966 url = register.tag(token_stream_parsed(url)) 1057 967 1058 968 #@register.tag 1059 def widthratio(parser, token): 969 #@token_stream_parsed 970 def widthratio(parser, bits): 1060 971 """ 1061 972 For creating bar charts and such, this tag calculates the ratio of a given 1062 973 value to a maximum value, and then applies that ratio to a constant. … … 1069 980 the above example will be 88 pixels wide (because 175/200 = .875; 1070 981 .875 * 100 = 87.5 which is rounded up to 88). 1071 982 """ 1072 bits = token.contents.split() 1073 if len(bits) != 4: 1074 raise TemplateSyntaxError("widthratio takes three arguments") 1075 tag, this_value_expr, max_value_expr, max_width = bits 1076 try: 1077 max_width = int(max_width) 1078 except ValueError: 1079 raise TemplateSyntaxError("widthratio final argument must be an integer") 1080 return WidthRatioNode(parser.compile_filter(this_value_expr), 1081 parser.compile_filter(max_value_expr), max_width) 1082 widthratio = register.tag(widthratio) 983 this_value_expr, max_value_expr = bits.parse_expression_list(count=2) 984 max_width = bits.parse_int(required=True) 985 return WidthRatioNode(this_value_expr, max_value_expr, max_width) 986 widthratio = register.tag(token_stream_parsed(widthratio)) 1083 987 1084 988 #@register.tag 1085 def do_with(parser, token): 989 #@token_stream_parsed 990 def do_with(parser, bits): 1086 991 """ 1087 Adds a valueto the context (inside of this block) for caching and easy992 Adds values to the context (inside of this block) for caching and easy 1088 993 access. 1089 994 1090 995 For example:: … … 1092 997 {% with person.some_sql_method as total %} 1093 998 {{ total }} object{{ total|pluralize }} 1094 999 {% endwith %} 1000 1001 {% with person.some_sql_method as total, person.get_full_name as full_name %} 1002 {{ full_name }}: {{ total }} object{{ total|pluralize }} 1003 {% endwith %} 1004 1095 1005 """ 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) 1105 do_with = register.tag('with', do_with) 1006 try: 1007 expr = bits.parse_expression() 1008 name = parse_as(bits) 1009 except TokenSyntaxError: 1010 raise TemplateSyntaxError("%r expected format is 'value as name'" % bits.name) 1011 nodelist = parser.parse_nodelist(('endwith',)) 1012 return WithNode(expr, name, nodelist) 1013 do_with = register.tag('with', token_stream_parsed(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/loader_tags.py
1 from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable 2 from django.template import Library, Node, TextNode 3 from django.template.loader import get_template, get_template_from_string, find_template_source 1 from django.template import TemplateSyntaxError, Library, Node, TextNode 2 from django.template.compiler import token_stream_parsed 3 from django.template.loader import TemplateDoesNotExist, get_template, get_template_from_string, find_template_source 4 from django.template.expressions import Literal 4 5 from django.conf import settings 5 6 from django.utils.safestring import mark_safe 6 7 … … 39 40 class ExtendsNode(Node): 40 41 must_be_first = True 41 42 42 def __init__(self, nodelist, parent_name, parent_name_expr,template_dirs=None):43 def __init__(self, nodelist, parent_name, template_dirs=None): 43 44 self.nodelist = nodelist 44 self.parent_name , self.parent_name_expr = parent_name, parent_name_expr45 self.parent_name = parent_name 45 46 self.template_dirs = template_dirs 46 47 47 48 def __repr__(self): 48 if self.parent_name_expr:49 return "<ExtendsNode: extends %s>" % self.parent_name_expr.token50 49 return '<ExtendsNode: extends "%s">' % self.parent_name 51 50 52 51 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 52 parent = self.parent_name.resolve_safe(context) 56 53 if not parent: 57 54 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 55 raise TemplateSyntaxError, error_msg 61 56 if hasattr(parent, 'render'): 62 57 return parent # parent is a Template object … … 114 109 115 110 class IncludeNode(Node): 116 111 def __init__(self, template_name): 117 self.template_name = Variable(template_name)112 self.template_name = template_name 118 113 119 114 def render(self, context): 120 115 try: 121 template_name = self.template_name.resolve (context)116 template_name = self.template_name.resolve_safe(context) 122 117 t = get_template(template_name) 123 118 return t.render(context) 124 119 except TemplateSyntaxError, e: … … 128 123 except: 129 124 return '' # Fail silently for invalid included templates. 130 125 126 127 #@register.tag('block') 131 128 def do_block(parser, token): 132 129 """ 133 130 Define a block that can be overridden by child templates. … … 147 144 nodelist = parser.parse(('endblock', 'endblock %s' % block_name)) 148 145 parser.delete_first_token() 149 146 return BlockNode(block_name, nodelist) 147 register.tag('block', do_block) 150 148 151 def do_extends(parser, token): 149 150 #@register.tag('extends') 151 #@token_stream_parsed 152 def do_extends(parser, bits): 152 153 """ 153 154 Signal that this template extends a parent template. 154 155 155 156 This tag may be used in two ways: ``{% extends "base" %}`` (with quotes) 156 157 uses the literal value "base" as the name of the parent template to extend, 157 or ``{% extends variable %}`` uses the value of ``variable`` as either the158 or ``{% extends expression %}`` uses the value of ``expression`` as either the 158 159 name of the parent template to extend (if it evaluates to a string) or as 159 160 the parent tempate itelf (if it evaluates to a Template object). 160 161 """ 161 bits = token.contents.split() 162 if len(bits) != 2: 163 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]) 162 parent_name = bits.parse_expression(required=True) 169 163 nodelist = parser.parse() 170 164 if nodelist.get_nodes_by_type(ExtendsNode): 171 raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0] 172 return ExtendsNode(nodelist, parent_name, parent_name_expr) 165 raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits.name 166 return ExtendsNode(nodelist, parent_name) 167 register.tag('extends', token_stream_parsed(do_extends)) 173 168 174 def do_include(parser, token): 169 170 #@register.tag('include') 171 #@token_stream_parsed 172 def do_include(parser, bits): 175 173 """ 176 174 Loads a template and renders it with the current context. 177 175 … … 179 177 180 178 {% include "foo/some_include" %} 181 179 """ 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]) 189 190 register.tag('block', do_block) 191 register.tag('extends', do_extends) 192 register.tag('include', do_include) 180 template_name = bits.parse_expression(required=True) 181 if isinstance(template_name, Literal): 182 # remove ConstantIncludeNode and this hack will be gone 183 return ConstantIncludeNode(template_name.resolve(None)) 184 return IncludeNode(template_name) 185 register.tag('include', token_stream_parsed(do_include)) -
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 = parser.token_stream(token) 23 bmax = len(params) 24 def_len = defaults and len(defaults) or 0 25 bmin = bmax - def_len 26 args = stream.parse_expression_list(minimum=bmin, maximum=bmax) 27 stream.assert_comsumed() 28 if takes_context: 29 node_class = curry(node_class, takes_context=takes_context) 30 if takes_nodelist: 31 nodelist = parser.parse_nodelist(('end%s' % name,)) 32 node_class = curry(node_class, nodelist=nodelist) 33 return node_class(args) 34 35 class Library(object): 36 def __init__(self): 37 self.filters = {} 38 self.tags = {} 39 40 def tag(self, name=None, compile_function=None): 41 if name == None and compile_function == None: 42 # @register.tag() 43 return self.tag_function 44 elif name != None and compile_function == None: 45 if(callable(name)): 46 # @register.tag 47 return self.tag_function(name) 48 else: 49 # @register.tag('somename') or @register.tag(name='somename') 50 def dec(func): 51 return self.tag(name, func) 52 return dec 53 elif name != None and compile_function != None: 54 # register.tag('somename', somefunc) 55 self.tags[name] = compile_function 56 return compile_function 57 else: 58 raise InvalidTemplateLibrary("Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)) 59 60 def tag_function(self,func): 61 self.tags[getattr(func, "_decorated_function", func).__name__] = func 62 return func 63 64 def filter(self, name=None, filter_func=None): 65 if name == None and filter_func == None: 66 # @register.filter() 67 return self.filter_function 68 elif filter_func == None: 69 if(callable(name)): 70 # @register.filter 71 return self.filter_function(name) 72 else: 73 # @register.filter('somename') or @register.filter(name='somename') 74 def dec(func): 75 return self.filter(name, func) 76 return dec 77 elif name != None and filter_func != None: 78 # register.filter('somename', somefunc) 79 self.filters[name] = filter_func 80 return filter_func 81 else: 82 raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func)) 83 84 def filter_function(self, func): 85 self.filters[getattr(func, "_decorated_function", func).__name__] = func 86 return func 87 88 def simple_tag(self, compile_function=None): 89 def dec(func): 90 params, xx, xxx, defaults = getargspec(func) 91 92 class SimpleNode(Node): 93 def __init__(self, args): 94 self.args = args 95 96 def render(self, context): 97 return func(*[arg.resolve_safe(context) for arg in self.args]) 98 99 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode) 100 compile_func.__doc__ = func.__doc__ 101 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 102 return func 103 104 if callable(compile_function): 105 # @register.simple_tag 106 return dec(compile_function) 107 return dec 108 109 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 110 def dec(func): 111 params, xx, xxx, defaults = getargspec(func) 112 if takes_context: 113 if params[0] == 'context': 114 params = params[1:] 115 else: 116 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 117 118 class InclusionNode(Node): 119 def __init__(self, args): 120 self.args = args 121 122 def render(self, context): 123 resolved_vars = [arg.resolve_safe(context) for arg in self.args] 124 if takes_context: 125 args = [context] + resolved_vars 126 else: 127 args = resolved_vars 128 129 dict = func(*args) 130 131 if not getattr(self, 'nodelist', False): 132 from django.template.loader import get_template, select_template 133 if not isinstance(file_name, basestring) and is_iterable(file_name): 134 t = select_template(file_name) 135 else: 136 t = get_template(file_name) 137 self.nodelist = t.nodelist 138 return self.nodelist.render(context_class(dict, autoescape=context.autoescape)) 139 140 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode) 141 compile_func.__doc__ = func.__doc__ 142 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 143 return func 144 return dec 145 146 def get_library(module_name): 147 lib = libraries.get(module_name, None) 148 if not lib: 149 try: 150 mod = __import__(module_name, {}, {}, ['']) 151 except ImportError, e: 152 raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e)) 153 try: 154 lib = mod.register 155 libraries[module_name] = lib 156 except AttributeError: 157 raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) 158 return lib 159 160 def add_to_builtins(module_name): 161 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_stream_parsed, 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): … … 141 140 raise TemplateSyntaxError, "'get_current_language_bidi' requires 'as variable' (got %r)" % args 142 141 return GetCurrentLanguageBidiNode(args[2]) 143 142 144 def do_translate(parser, token): 143 144 #@token_stream_parsed 145 def do_translate(parser, bits): 145 146 """ 146 147 This will mark a string for translation and will 147 148 translate the string for the current language. … … 171 172 the variable ``variable``. Make sure that the string 172 173 in there is something that is in the .po file. 173 174 """ 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() 175 value = bits.parse_expression(required=True) 176 noop = bits.pop_lexem('noop') 186 177 return TranslateNode(value, noop) 187 178 188 179 def do_block_translate(parser, token): … … 255 246 register.tag('get_available_languages', do_get_available_languages) 256 247 register.tag('get_current_language', do_get_current_language) 257 248 register.tag('get_current_language_bidi', do_get_current_language_bidi) 258 register.tag('trans', do_translate)249 register.tag('trans', token_stream_parsed(do_translate)) 259 250 register.tag('blocktrans', do_block_translate) -
django/templatetags/cache.py
1 from django.template import Library, Node, TemplateSyntaxError, Variable , VariableDoesNotExist2 from django.template import resolve_variable1 from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist, TokenSyntaxError 2 from django.template.compiler import token_stream_parsed 3 3 from django.core.cache import cache 4 4 from django.utils.encoding import force_unicode 5 5 … … 8 8 class CacheNode(Node): 9 9 def __init__(self, nodelist, expire_time_var, fragment_name, vary_on): 10 10 self.nodelist = nodelist 11 self.expire_time_var = Variable(expire_time_var)11 self.expire_time_var = expire_time_var 12 12 self.fragment_name = fragment_name 13 13 self.vary_on = vary_on 14 14 … … 22 22 except (ValueError, TypeError): 23 23 raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time) 24 24 # 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])25 cache_key = u':'.join([self.fragment_name] + [force_unicode(var.resolve_safe(context)) for var in self.vary_on]) 26 26 value = cache.get(cache_key) 27 27 if value is None: 28 28 value = self.nodelist.render(context) 29 29 cache.set(cache_key, value, expire_time) 30 30 return value 31 31 32 def do_cache(parser, token):32 def do_cache(parser, bits): 33 33 """ 34 34 This will cache the contents of a template fragment for a given amount 35 35 of time. … … 50 50 51 51 Each unique set of arguments will result in a unique cache entry. 52 52 """ 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:]) 53 nodelist = parser.parse_nodelist(('endcache',)) 54 expire_time = bits.parse_expression(required=True) 55 name = bits.pop_name() 56 if not name: 57 raise TemplateSyntaxError, "'cache' requires a fragment name" 58 vary_on = bits.parse_expression_list() 59 return CacheNode(nodelist, expire_time, name, vary_on) 59 60 60 register.tag('cache', do_cache)61 register.tag('cache', token_stream_parsed(do_cache))