Ticket #603: django-template-errors-2.patch
File django-template-errors-2.patch, 13.1 KB (added by , 19 years ago) |
---|
-
django/core/template_loader.py
1 1 "Wrapper for loading templates from storage of some sort (e.g. files or db)" 2 2 import template 3 from template_file import load_template_source3 from template_file import find_template_source 4 4 5 5 class ExtendsError(Exception): 6 6 pass … … 10 10 Returns a compiled template.Template object for the given template name, 11 11 handling template inheritance recursively. 12 12 """ 13 return get_template_from_string( load_template_source(template_name))13 return get_template_from_string(*find_template_source(template_name)) 14 14 15 def get_template_from_string(source ):15 def get_template_from_string(source, filename=template.UNKNOWN_SOURCE): 16 16 """ 17 17 Returns a compiled template.Template object for the given template code, 18 18 handling template inheritance recursively. 19 19 """ 20 return template.Template(source )20 return template.Template(source, filename) 21 21 22 22 def render_to_string(template_name, dictionary=None, context_instance=None): 23 23 """ … … 90 90 error_msg += " Got this from the %r variable." % self.parent_name_var 91 91 raise template.TemplateSyntaxError, error_msg 92 92 try: 93 return get_template_from_string( load_template_source(parent, self.template_dirs))93 return get_template_from_string(*find_template_source(parent, self.template_dirs)) 94 94 except template.TemplateDoesNotExist: 95 95 raise template.TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent 96 96 … … 142 142 143 143 This tag may be used in two ways: ``{% extends "base" %}`` (with quotes) 144 144 uses the literal value "base" as the name of the parent template to extend, 145 or ``{% e ntends variable %}`` uses the value of ``variable`` as the name145 or ``{% extends variable %}`` uses the value of ``variable`` as the name 146 146 of the parent template to extend. 147 147 """ 148 148 bits = token.contents.split() -
django/core/template_file.py
4 4 from django.core.template import TemplateDoesNotExist 5 5 import os 6 6 7 def load_template_source(template_name, template_dirs=None): 7 8 def find_template_source(template_name, template_dirs=None): 9 "Returns a tuple of (template_string, filepath)." 8 10 if not template_dirs: 9 11 template_dirs = TEMPLATE_DIRS 10 12 tried = [] 11 13 for template_dir in template_dirs: 12 14 filepath = os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION 13 15 try: 14 return open(filepath).read()16 return (open(filepath).read(), filepath) 15 17 except IOError: 16 18 tried.append(filepath) 17 19 if template_dirs: … … 19 21 else: 20 22 error_msg = "Your TEMPLATE_DIRS settings is empty. Change it to point to at least one template directory." 21 23 raise TemplateDoesNotExist, error_msg 24 25 26 def load_template_source(template_name, template_dirs=None): 27 return find_template_source(template_name, template_dirs)[0] -
django/core/template.py
55 55 '\n<html>\n\n</html>\n' 56 56 """ 57 57 import re 58 from django.conf.settings import DEFAULT_CHARSET 58 from django.conf.settings import DEFAULT_CHARSET, DEBUG 59 59 60 60 __all__ = ('Template','Context','compile_string') 61 61 … … 74 74 75 75 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' 76 76 77 #What to report as the origin of templates that come from non file sources (eg strings) 78 UNKNOWN_SOURCE="<unknown source>" 79 80 #match starts of lines 81 newline_re = re.compile("^", re.M); 82 77 83 # match a variable or block tag and capture the entire tag, including start/end delimiters 78 84 tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 79 85 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END))) … … 102 108 pass 103 109 104 110 class Template: 105 def __init__(self, template_string ):111 def __init__(self, template_string, filename=UNKNOWN_SOURCE): 106 112 "Compilation stage" 107 self.nodelist = compile_string(template_string )108 113 self.nodelist = compile_string(template_string, filename) 114 109 115 def __iter__(self): 110 116 for node in self.nodelist: 111 117 for subnode in node: … … 115 121 "Display stage -- can be called many times" 116 122 return self.nodelist.render(context) 117 123 118 def compile_string(template_string ):124 def compile_string(template_string, filename): 119 125 "Compiles template_string into NodeList ready for rendering" 120 tokens = tokenize(template_string) 121 parser = Parser(tokens) 126 if DEBUG: 127 lexer_factory = DebugLexer 128 parser_factory = DebugParser 129 else: 130 lexer_factory = Lexer 131 parser_factory = Parser 132 133 lexer = lexer_factory(template_string, filename) 134 parser = parser_factory(lexer.tokenize()) 122 135 return parser.parse() 123 136 124 137 class Context: … … 178 191 self.contents[:20].replace('\n', '') 179 192 ) 180 193 181 def tokenize(template_string): 182 "Return a list of tokens from a given template_string" 183 # remove all empty strings, because the regex has a tendency to add them 184 bits = filter(None, tag_re.split(template_string)) 185 return map(create_token, bits) 194 class Lexer(object): 195 def __init__(self, template_string, filename): 196 self.template_string = template_string 197 self.filename = filename 198 199 def tokenize(self): 200 "Return a list of tokens from a given template_string" 201 bits = filter(None, tag_re.split(self.template_string)) 202 return map(self.create_token, bits) 203 204 def create_token(self,token_string): 205 "Convert the given token string into a new Token object and return it" 206 if token_string.startswith(VARIABLE_TAG_START): 207 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 208 elif token_string.startswith(BLOCK_TAG_START): 209 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) 210 else: 211 token = Token(TOKEN_TEXT, token_string) 212 return token 186 213 187 def create_token(token_string): 188 "Convert the given token string into a new Token object and return it" 189 if token_string.startswith(VARIABLE_TAG_START): 190 return Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 191 elif token_string.startswith(BLOCK_TAG_START): 192 return Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) 193 else: 194 return Token(TOKEN_TEXT, token_string) 214 class DebugLexer(Lexer): 215 def __init__(self, template_string, filename): 216 super(DebugLexer,self).__init__(template_string, filename) 195 217 196 class Parser: 218 def find_linebreaks(self, template_string): 219 for match in newline_re.finditer(template_string): 220 yield match.start() 221 222 def tokenize(self): 223 "Return a list of tokens from a given template_string" 224 225 token_tups = [] 226 upto = 0 227 line = 1 228 #TODO:Py2.4 generator expression 229 linebreaks = self.find_linebreaks(self.template_string) 230 next_linebreak = linebreaks.next() 231 try: 232 for match in tag_re.finditer(self.template_string): 233 start, end = match.span() 234 if start > upto: 235 token_tups.append( (self.template_string[upto:start], line) ) 236 upto = start 237 238 while next_linebreak <= upto: 239 next_linebreak = linebreaks.next() 240 line += 1 241 242 token_tups.append( (self.template_string[start:end], line) ) 243 upto = end 244 245 while next_linebreak <= upto: 246 next_linebreak = linebreaks.next() 247 line += 1 248 except StopIteration: 249 pass 250 251 last_bit = self.template_string[upto:] 252 if len(last_bit): 253 token_tups.append( (last_bit, line) ) 254 255 return [ self.create_token(tok, (self.filename, line)) for tok, line in token_tups] 256 257 258 def create_token(self, token_string, source): 259 token = super(DebugLexer, self).create_token(token_string) 260 token.source = source 261 return token 262 263 264 class Parser(object): 197 265 def __init__(self, tokens): 198 266 self.tokens = tokens 199 267 … … 202 270 while self.tokens: 203 271 token = self.next_token() 204 272 if token.token_type == TOKEN_TEXT: 205 nodelist.append(TextNode(token.contents))273 self.extend_nodelist(nodelist, TextNode(token.contents), token) 206 274 elif token.token_type == TOKEN_VAR: 207 275 if not token.contents: 208 raise TemplateSyntaxError, "Empty variable tag"209 nodelist.append(VariableNode(token.contents))276 self.empty_variable(token) 277 self.extend_nodelist(nodelist, VariableNode(token.contents), token) 210 278 elif token.token_type == TOKEN_BLOCK: 211 279 if token.contents in parse_until: 212 280 # put token back on token list so calling code knows why it terminated … … 215 283 try: 216 284 command = token.contents.split()[0] 217 285 except IndexError: 218 raise TemplateSyntaxError, "Empty block tag" 286 self.empty_block_tag(token) 287 288 # execute callback function for this tag and append resulting node 289 self.enter_command(command, token); 219 290 try: 220 # execute callback function for this tag and append resulting node 221 nodelist.append(registered_tags[command](self, token)) 291 compile_func = registered_tags[command] 222 292 except KeyError: 223 raise TemplateSyntaxError, "Invalid block tag: '%s'" % command 293 self.invalid_block_tag(token, command) 294 295 self.extend_nodelist(nodelist, compile_func(self, token), token) 296 self.exit_command(); 297 224 298 if parse_until: 225 raise TemplateSyntaxError, "Unclosed tag(s): '%s'" % ', '.join(parse_until) 299 self.unclosed_block_tag(token) 300 226 301 return nodelist 227 302 303 def extend_nodelist(self, nodelist, node, token): 304 nodelist.append(node) 305 306 def enter_command(self, command, token): 307 pass 308 309 def exit_command(self): 310 pass 311 312 def empty_variable(self, token): 313 raise TemplateSyntaxError, "Empty variable tag" 314 315 def empty_block_tag(self, token): 316 raise TemplateSyntaxError, "Empty block tag" 317 318 def invalid_block_tag(self, token, command): 319 raise TemplateSyntaxError, "Invalid block tag: %s" % (command) 320 321 def unclosed_block_tag(self, token): 322 raise TemplateSyntaxError, "Unclosed tags: %s " % ', '.join(parse_until) 323 228 324 def next_token(self): 229 325 return self.tokens.pop(0) 230 326 … … 234 330 def delete_first_token(self): 235 331 del self.tokens[0] 236 332 333 334 class DebugParser(Parser): 335 def __init__(self, lexer): 336 super(DebugParser, self).__init__(lexer) 337 self.command_stack = [] 338 339 def enter_command(self, command, token): 340 self.command_stack.append( (command, token.source) ) 341 342 def exit_command(self): 343 self.command_stack.pop() 344 345 def format_source(self, source): 346 return "at %s, line %d" % source 347 348 def extend_nodelist(self, nodelist, node, token): 349 node.source = token.source 350 super(DebugParser, self).extend_nodelist(nodelist, node, token) 351 352 def empty_variable(self, token): 353 raise TemplateSyntaxError, "Empty variable tag %s" % self.format_source(token.source) 354 355 def empty_block_tag(self, token): 356 raise TemplateSyntaxError, "Empty block tag %s" % self.format_source(token.source) 357 358 def invalid_block_tag(self, token, command): 359 raise TemplateSyntaxError, "Invalid block tag: '%s' %s" % (command, self.format_source(token.source)) 360 361 def unclosed_block_tag(self, token): 362 (command, (file,line)) = self.command_stack.pop() 363 msg = "Unclosed tag '%s' starting at %s, line %d. Looking for one of: %s " % \ 364 (command, file, line, ', '.join(parse_until) ) 365 raise TemplateSyntaxError, msg 366 237 367 class FilterParser: 238 368 """Parse a variable token and its optional filters (all as a single string), 239 369 and return a list of tuples of the filter name and arguments.