From 18b3d0b9f985fac4d5915f09bd9978ac896477b6 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 14 Mar 2007 20:41:27 +0100 Subject: [PATCH] [svn] improved jinja debugging system. now handles errors in included templates too --HG-- branch : trunk --- docs/src/objects.txt | 4 ++ jinja/translators/python.py | 91 +++++++++++++++++++---------- jinja/utils.py | 28 +++++---- tests/exception.py | 2 +- tests/templates/error.html | 2 +- tests/templates/included_error.html | 2 + www/static/pygments.css | 2 +- 7 files changed, 88 insertions(+), 43 deletions(-) create mode 100644 tests/templates/included_error.html diff --git a/docs/src/objects.txt b/docs/src/objects.txt index 357a3d6..5d0bddc 100644 --- a/docs/src/objects.txt +++ b/docs/src/objects.txt @@ -87,6 +87,10 @@ access. You can mark both attributes and methods as unsafe: class MyModel(...): # just give access to a and b. Default is all + # note that this also disallows the functions from below. + # if you use jinja_allowed_attributes you don't have to + # do the code below because methods are threaded as attributes + # too. jinja_allowed_attributes = ['a', 'b'] def __init__(self, ...): diff --git a/jinja/translators/python.py b/jinja/translators/python.py index 6e2ef01..b341852 100644 --- a/jinja/translators/python.py +++ b/jinja/translators/python.py @@ -39,11 +39,6 @@ class Template(object): self.translated_source = translated_source self.generate_func = None - def source(self): - """The original sourcecode for this template.""" - return self.environment.loader.get_source(self.code.co_filename) - source = property(source, doc=source.__doc__) - def dump(self, stream=None): """Dump the template into python bytecode.""" if stream is not None: @@ -175,12 +170,16 @@ class PythonTranslator(Translator): def nodeinfo(self, node): """ - Return a comment that helds the node informations. + Return a comment that helds the node informations or None + if there is no need to add a debug comment. """ - return '# DEBUG(filename=%r, lineno=%s)' % ( + rv = '# DEBUG(filename=%s, lineno=%s)' % ( node.filename, node.lineno ) + if rv != self.last_debug_comment: + self.last_debug_comment = rv + return rv def filter(self, s, filter_nodes): """ @@ -235,6 +234,7 @@ class PythonTranslator(Translator): def reset(self): self.indention = 0 self.last_cycle_id = 0 + self.last_debug_comment = None def translate(self): self.reset() @@ -329,8 +329,10 @@ class PythonTranslator(Translator): """ Handle data around nodes. """ - return self.indent(self.nodeinfo(node)) + '\n' + \ - self.indent('yield %r' % node.text) + nodeinfo = self.nodeinfo(node) or '' + if nodeinfo: + nodeinfo = self.indent(nodeinfo) + '\n' + return nodeinfo + self.indent('yield %r' % node.text) def handle_node_list(self, node): """ @@ -339,8 +341,10 @@ class PythonTranslator(Translator): """ buf = [self.handle_node(n) for n in node] if buf: - return '\n'.join([self.indent(self.nodeinfo(node))] + buf) - return '' + nodeinfo = self.nodeinfo(node) + if nodeinfo: + buf = [self.indent(nodeinfo)] + buf + return '\n'.join(buf) def handle_for_loop(self, node): """ @@ -349,7 +353,9 @@ class PythonTranslator(Translator): """ buf = [] write = lambda x: buf.append(self.indent(x)) - write(self.nodeinfo(node)) + nodeinfo = self.nodeinfo(node) + if nodeinfo: + write(nodeinfo) write('ctx_push()') # recursive loops @@ -370,7 +376,9 @@ class PythonTranslator(Translator): # handle real loop code self.indention += 1 - buf.append(self.indent(self.nodeinfo(node.body))) + nodeinfo = self.nodeinfo(node.body) + if nodeinfo: + write(nodeinfo) buf.append(self.handle_node(node.body)) self.indention -= 1 @@ -378,7 +386,9 @@ class PythonTranslator(Translator): if node.else_: write('if not context[\'loop\'].iterated:') self.indention += 1 - buf.append(self.indent(self.nodeinfo(node.else_))) + nodeinfo = self.nodeinfo(node.else_) + if nodeinfo: + write(nodeinfo) buf.append(self.handle_node(node.else_)) self.indention -= 1 @@ -404,20 +414,26 @@ class PythonTranslator(Translator): """ buf = [] write = lambda x: buf.append(self.indent(x)) - write(self.nodeinfo(node)) + nodeinfo = self.nodeinfo(node) + if nodeinfo: + write(nodeinfo) for idx, (test, body) in enumerate(node.tests): write('%sif %s:' % ( idx and 'el' or '', self.handle_node(test) )) self.indention += 1 - write(self.nodeinfo(node)) + nodeinfo = self.nodeinfo(body) + if nodeinfo: + write(nodeinfo) buf.append(self.handle_node(body)) self.indention -= 1 if node.else_ is not None: write('else:') self.indention += 1 - write(self.nodeinfo(node)) + nodeinfo = self.nodeinfo(node.else_) + if nodeinfo: + write(nodeinfo) buf.append(self.handle_node(node.else_)) self.indention -= 1 return '\n'.join(buf) @@ -433,7 +449,9 @@ class PythonTranslator(Translator): write('if not %r in context.current:' % name) self.indention += 1 - write(self.nodeinfo(node)) + nodeinfo = self.nodeinfo(node) + if nodeinfo: + write(nodeinfo) if node.seq.__class__ in (ast.Tuple, ast.List): write('context.current[%r] = CycleContext(%s)' % ( name, @@ -459,9 +477,11 @@ class PythonTranslator(Translator): """ Handle a print statement. """ - return self.indent(self.nodeinfo(node)) + '\n' + \ - self.indent('yield finish_var(%s)' % - self.handle_node(node.variable)) + nodeinfo = self.nodeinfo(node) or '' + if nodeinfo: + nodeinfo = self.indent(nodeinfo) + '\n' + return nodeinfo + self.indent('yield finish_var(%s)' % + self.handle_node(node.variable)) def handle_macro(self, node): """ @@ -472,7 +492,9 @@ class PythonTranslator(Translator): write('def macro(*args):') self.indention += 1 - write(self.nodeinfo(node)) + nodeinfo = self.nodeinfo(node) + if nodeinfo: + write(nodeinfo) if node.arguments: write('argcount = len(args)') @@ -488,7 +510,9 @@ class PythonTranslator(Translator): else: write('ctx_push()') - write(self.nodeinfo(node.body)) + nodeinfo = self.nodeinfo(node.body) + if nodeinfo: + write(nodeinfo) buf.append(self.handle_node(node.body)) write('ctx_pop()') write('if False:') @@ -503,8 +527,10 @@ class PythonTranslator(Translator): """ Handle variable assignments. """ - return self.indent(self.nodeinfo(node)) + '\n' + \ - self.indent('context[%r] = %s' % ( + nodeinfo = self.nodeinfo(node) or '' + if nodeinfo: + nodeinfo = self.indent(nodeinfo) + '\n' + return nodeinfo + self.indent('context[%r] = %s' % ( node.name, self.handle_node(node.expr) )) @@ -517,9 +543,10 @@ class PythonTranslator(Translator): write = lambda x: buf.append(self.indent(x)) write('def filtered():') self.indention += 1 - write(self.nodeinfo(node)) write('ctx_push()') - write(self.nodeinfo(node.body)) + nodeinfo = self.nodeinfo(node.body) + if nodeinfo: + write(nodeinfo) buf.append(self.handle_node(node.body)) write('ctx_pop()') write('if False:') @@ -543,7 +570,9 @@ class PythonTranslator(Translator): write = lambda x: buf.append(self.indent(x)) write('ctx_push()') - write(self.nodeinfo(node.body)) + nodeinfo = self.nodeinfo(node.body) + if nodeinfo: + write(nodebody) buf.append(self.handle_node(node.body)) write('ctx_pop()') return '\n'.join(buf) @@ -571,8 +600,10 @@ class PythonTranslator(Translator): replacements = '{%s}' % ', '.join(replacements) else: replacements = 'None' - return self.indent(self.nodeinfo(node)) + '\n' + \ - self.indent('yield translate(%r, %r, %r, %s)' % ( + nodeinfo = self.nodeinfo(node) or '' + if nodeinfo: + nodeinfo = self.indent(nodeinfo) + '\n' + return nodeinfo + self.indent('yield translate(%r, %r, %r, %s)' % ( node.singular, node.plural, node.indicator, diff --git a/jinja/utils.py b/jinja/utils.py index d0f5706..21e5ead 100644 --- a/jinja/utils.py +++ b/jinja/utils.py @@ -174,7 +174,7 @@ def buffereater(f): return wrapped -def raise_template_exception(template, exception, filename, lineno, context): +def raise_template_exception(exception, filename, lineno, context): """ Raise an exception "in a template". Return a traceback object. @@ -188,7 +188,8 @@ def raise_template_exception(template, exception, filename, lineno, context): globals = { '__name__': filename, '__file__': filename, - '__loader__': TracebackLoader(template), + '__loader__': TracebackLoader(context.environment, + filename), '__exception_to_raise__': exception } try: @@ -204,7 +205,7 @@ def translate_exception(template, exc_type, exc_value, traceback, context): """ sourcelines = template.translated_source.splitlines() startpos = traceback.tb_lineno - 1 - args = None + filename = None # looks like we loaded the template from string. we cannot # do anything here. if startpos > len(sourcelines): @@ -213,15 +214,21 @@ def translate_exception(template, exc_type, exc_value, traceback, context): while startpos > 0: m = _debug_info_re.search(sourcelines[startpos]) if m is not None: - args = m.groups() + filename, lineno = m.groups() + if filename == 'None': + filename = '' + if lineno == 'None': + lineno = 0 + else: + lineno = int(lineno) break startpos -= 1 # no traceback information found, reraise unchanged - if args is None: + if filename is None: return traceback - return raise_template_exception(template, exc_value, args[0], - int(args[1] or 0), context) + return raise_template_exception(exc_value, filename, + lineno, context) class TracebackLoader(object): @@ -229,11 +236,12 @@ class TracebackLoader(object): Fake importer that just returns the source of a template. """ - def __init__(self, template): - self.template = template + def __init__(self, environment, filename): + self.loader = environment.loader + self.filename = filename def get_source(self, impname): - return self.template.source + return self.loader.get_source(self.filename) class CacheDict(object): diff --git a/tests/exception.py b/tests/exception.py index fdcc7e1..61a298d 100644 --- a/tests/exception.py +++ b/tests/exception.py @@ -7,4 +7,4 @@ def test(*args): tmpl = e.loader.load('error.html') tmpl.render(items=range(10)) -make_server("localhost", 7000, DebuggedApplication(test, False)).serve_forever() +make_server("localhost", 7000, DebuggedApplication(test)).serve_forever() diff --git a/tests/templates/error.html b/tests/templates/error.html index ef11dfb..da3f60d 100644 --- a/tests/templates/error.html +++ b/tests/templates/error.html @@ -1,6 +1,6 @@ diff --git a/tests/templates/included_error.html b/tests/templates/included_error.html new file mode 100644 index 0000000..da11ba1 --- /dev/null +++ b/tests/templates/included_error.html @@ -0,0 +1,2 @@ +This breaks the template: +{{ 1 / 0 }} diff --git a/www/static/pygments.css b/www/static/pygments.css index 1f1c6d6..477030d 100644 --- a/www/static/pygments.css +++ b/www/static/pygments.css @@ -1,4 +1,4 @@ -.syntax { background: #ffffff; } +.syntax { background: #ffffff; } .syntax .c { color: #888888 } /* Comment */ .syntax .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .syntax .k { color: #008800; font-weight: bold } /* Keyword */ -- 2.26.2