From 8e8d071eab02e391e9025b0c8429912b9d98327b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 16 Apr 2008 23:10:49 +0200 Subject: [PATCH] better debugging information. compiler knows about name and filename now (the first one is the load name, the second the estimated filename on the file system if such a name exists) --HG-- branch : trunk --- jinja2/compiler.py | 33 ++++++++++++++++---------- jinja2/debug.py | 54 +++++++++++++++++++++++++------------------ jinja2/environment.py | 11 +++++---- jinja2/loaders.py | 4 ++-- 4 files changed, 60 insertions(+), 42 deletions(-) diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 06017f8..d1025d4 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -37,9 +37,9 @@ else: have_condexpr = True -def generate(node, environment, filename, stream=None): +def generate(node, environment, name, filename, stream=None): """Generate the python source for a node tree.""" - generator = CodeGenerator(environment, filename, stream) + generator = CodeGenerator(environment, name, filename, stream) generator.visit(node) if stream is None: return generator.stream.getvalue() @@ -228,10 +228,11 @@ class CompilerExit(Exception): class CodeGenerator(NodeVisitor): - def __init__(self, environment, filename, stream=None): + def __init__(self, environment, name, filename, stream=None): if stream is None: stream = StringIO() self.environment = environment + self.name = name self.filename = filename self.stream = stream @@ -248,10 +249,11 @@ class CodeGenerator(NodeVisitor): self.has_known_extends = False # the current line number - self.lineno = 1 + self.code_lineno = 1 # the debug information self.debug_info = [] + self._write_debug_info = None # the number of new lines before the next write() self._new_lines = 0 @@ -306,7 +308,11 @@ class CodeGenerator(NodeVisitor): if self._new_lines: if not self._first_write: self.stream.write('\n' * self._new_lines) - self.lineno += self._new_lines + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, + self.code_lineno)) + self._write_debug_info = None self._first_write = False self.stream.write(' ' * self._indentation) self._new_lines = 0 @@ -321,7 +327,8 @@ class CodeGenerator(NodeVisitor): """Add one or more newlines before the next write.""" self._new_lines = max(self._new_lines, 1 + extra) if node is not None and node.lineno != self._last_line: - self.debug_info.append((node.lineno, self.lineno)) + self._write_debug_info = node.lineno + self._last_line = node.lineno def signature(self, node, frame, have_comma=True, extra_kwargs=None): """Writes a function call to the stream for the current node. @@ -444,7 +451,7 @@ class CodeGenerator(NodeVisitor): def visit_Template(self, node, frame=None): assert frame is None, 'no root frame allowed' self.writeline('from jinja2.runtime import *') - self.writeline('name = %r' % self.filename) + self.writeline('name = %r' % self.name) # do we have an extends tag at all? If not, we can save some # overhead by just not processing any inheritance code. @@ -463,7 +470,7 @@ class CodeGenerator(NodeVisitor): ', standalone=False):', extra=1) self.indent() self.writeline('context = TemplateContext(environment, globals, %r, ' - 'blocks, standalone)' % self.filename) + 'blocks, standalone)' % self.name) if have_extends: self.writeline('parent_root = None') self.outdent() @@ -835,15 +842,17 @@ class CodeGenerator(NodeVisitor): self.writeline('%s.append(' % frame.buffer) self.write(repr(u''.join(format)) + ' % (') idx = -1 - for idx, argument in enumerate(arguments): - if idx: - self.write(', ') + self.indent() + for argument in arguments: + self.newline(argument) if have_finalizer: self.write('(') self.visit(argument, frame) if have_finalizer: self.write(')') - self.write(idx == 0 and ',)' or ')') + self.write(',') + self.outdent() + self.writeline(')') if frame.buffer is not None: self.write(')') diff --git a/jinja2/debug.py b/jinja2/debug.py index 909a852..02b6177 100644 --- a/jinja2/debug.py +++ b/jinja2/debug.py @@ -8,15 +8,33 @@ :copyright: Copyright 2008 by Armin Ronacher. :license: BSD. """ -import re import sys from jinja2.exceptions import TemplateNotFound -_line_re = re.compile(r'^\s*# line: (\d+)\s*$') +def translate_exception(exc_info): + """If passed an exc_info it will automatically rewrite the exceptions + all the way down to the correct line numbers and frames. + """ + result_tb = prev_tb = None + initial_tb = tb = exc_info[2] + + while tb is not None: + template = tb.tb_frame.f_globals.get('__jinja_template__') + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, + lineno, prev_tb)[2] + if result_tb is None: + result_tb = tb + prev_tb = tb + tb = tb.tb_next + + return exc_info[:2] + (result_tb or initial_tb,) def fake_exc_info(exc_info, filename, lineno, tb_back=None): + """Helper for `translate_exception`.""" exc_type, exc_value, tb = exc_info # figure the real context out @@ -50,25 +68,6 @@ def fake_exc_info(exc_info, filename, lineno, tb_back=None): return exc_info -def translate_exception(exc_info): - result_tb = prev_tb = None - initial_tb = tb = exc_info[2] - - while tb is not None: - template = tb.tb_frame.f_globals.get('__jinja_template__') - if template is not None: - # TODO: inject faked exception with correct line number - lineno = template.get_corresponding_lineno(tb.tb_lineno) - tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, - lineno, prev_tb)[2] - if result_tb is None: - result_tb = tb - prev_tb = tb - tb = tb.tb_next - - return exc_info[:2] + (result_tb or initial_tb,) - - def _init_ugly_crap(): """This function implements a few ugly things so that we can patch the traceback objects. The function returned allows resetting `tb_next` on @@ -112,11 +111,20 @@ def _init_ugly_crap(): ] def tb_set_next(tb, next): + """Set the tb_next attribute of a traceback object.""" if not (isinstance(tb, TracebackType) and - isinstance(next, TracebackType)): + (next is None or isinstance(next, TracebackType))): raise TypeError('tb_set_next arguments must be traceback objects') obj = _Traceback.from_address(id(tb)) - obj.tb_next = ctypes.pointer(_Traceback.from_address(id(next))) + if tb.tb_next is not None: + old = _Traceback.from_address(id(tb.tb_next)) + old.ob_refcnt -= 1 + if next is None: + obj.tb_next = ctypes.POINTER(_Traceback)() + else: + next = _Traceback.from_address(id(next)) + next.ob_refcnt += 1 + obj.tb_next = ctypes.pointer(next) return tb_set_next diff --git a/jinja2/environment.py b/jinja2/environment.py index 47d99e3..d0bbb1d 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -134,13 +134,14 @@ class Environment(object): """ return self.lexer.tokeniter(source, name) - def compile(self, source, filename=None, raw=False, globals=None): + def compile(self, source, name=None, filename=None, raw=False, + globals=None): """Compile a node or source.""" if isinstance(source, basestring): - source = self.parse(source, filename) + source = self.parse(source, name) if self.optimized: node = optimize(source, self, globals or {}) - source = generate(node, self, filename) + source = generate(node, self, name, filename) if raw: return source if filename is None: @@ -164,10 +165,10 @@ class Environment(object): globals = self.make_globals(globals) return self.loader.load(self, name, globals) - def from_string(self, source, filename='', globals=None): + def from_string(self, source, globals=None): """Load a template from a string.""" globals = self.make_globals(globals) - return Template(self, self.compile(source, filename, globals=globals), + return Template(self, self.compile(source, globals=globals), globals) def make_globals(self, d): diff --git a/jinja2/loaders.py b/jinja2/loaders.py index 6c03ad3..37c34e1 100644 --- a/jinja2/loaders.py +++ b/jinja2/loaders.py @@ -21,7 +21,7 @@ class BaseLoader(object): def load(self, environment, name, globals=None): source, filename = self.get_source(environment, name) - code = environment.compile(source, filename, globals=globals) + code = environment.compile(source, name, filename, globals=globals) return Template(environment, code, globals or {}) @@ -43,7 +43,7 @@ class FileSystemLoader(BaseLoader): raise TemplateNotFound(template) f = file(filename) try: - return f.read().decode(self.encoding) + return f.read().decode(self.encoding), filename finally: f.close() -- 2.26.2