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()
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
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
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
"""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.
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.
', 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()
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(')')
: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
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
]
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
"""
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:
globals = self.make_globals(globals)
return self.loader.load(self, name, globals)
- def from_string(self, source, filename='<string>', 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):
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 {})
raise TemplateNotFound(template)
f = file(filename)
try:
- return f.read().decode(self.encoding)
+ return f.read().decode(self.encoding), filename
finally:
f.close()