# -*- coding: utf-8 -*- """ jinja.debugger ~~~~~~~~~~~~~~ This module implements helper function Jinja uses to give the users a possibility to develop Jinja templates like they would debug python code. It seamlessly integreates into the python traceback system, in fact it just modifies the trackback stack so that the line numbers are correct and the frame information are bound to the context and not the frame of the template evaluation loop. To achive this it raises the exception it cought before in an isolated namespace at a given line. The locals namespace is set to the current template context. The traceback generated by raising that exception is then either returned or linked with the former traceback if the `jinja._debugger` module is available. Because it's not possible to modify traceback objects from the python space this module is needed for this process. If it's not available it just ignores the other frames. Because this can lead to actually harder to debug code there is a setting on the jinja environment to disable the debugging system. The isolated namespace which is used to raise the exception also contains a `__loader__` name that helds a reference to a PEP 302 compatible loader. Because there are currently some traceback systems (such as the paste evalexception debugger) that do not provide the frame globals when retrieving the source from the linecache module, Jinja injects the source to the linecache module itself and changes the filename to a URL style "virtual filename" so that Jinja doesn't acidentally override other files in the linecache. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys from random import randrange # if we have extended debugger support we should really use it try: from jinja._debugger import * has_extended_debugger = True except ImportError: has_extended_debugger = False # we need the RUNTIME_EXCEPTION_OFFSET to skip the not used frames from jinja.utils import reversed, RUNTIME_EXCEPTION_OFFSET def fake_template_exception(exc_type, exc_value, tb, filename, lineno, source, context_or_env, tb_back=None): """ Raise an exception "in a template". Return a traceback object. This is used for runtime debugging, not compile time. """ # some traceback systems allow to skip frames __traceback_hide__ = True # create the namespace which will be the local namespace # of the new frame then. Some debuggers show local variables # so we better inject the context and not the evaluation loop context. from jinja.datastructure import Context if isinstance(context_or_env, Context): env = context_or_env.environment namespace = context_or_env.to_dict() else: env = context_or_env namespace = {} # no unicode for filenames if isinstance(filename, unicode): filename = filename.encode('utf-8') # generate an jinja unique filename used so that linecache # gets data that doesn't interfere with other modules if filename is None: vfilename = 'jinja://~%d' % randrange(0, 10000) filename = '' else: vfilename = 'jinja://%s' % filename # now create the used loaded and update the linecache loader = TracebackLoader(env, source, filename) loader.update_linecache(vfilename) globals = { '__name__': vfilename, '__file__': vfilename, '__loader__': loader } # use the simple debugger to reraise the exception in the # line where the error originally occoured globals['__exception_to_raise__'] = (exc_type, exc_value) offset = '\n' * (lineno - 1) code = compile(offset + 'raise __exception_to_raise__[0], ' '__exception_to_raise__[1]', vfilename or '