blob: 622f2b3e1a61f10cfe122b4d1dd07781aa54227c [file] [log] [blame]
Armin Ronacherba3757b2008-04-16 19:43:16 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.debug
4 ~~~~~~~~~~~~
5
6 Implements the debug interface for Jinja.
7
8 :copyright: Copyright 2008 by Armin Ronacher.
9 :license: BSD.
10"""
Armin Ronacherba3757b2008-04-16 19:43:16 +020011import sys
Armin Ronacher32a910f2008-04-26 23:21:03 +020012from types import CodeType
Armin Ronacherba3757b2008-04-16 19:43:16 +020013
14
Armin Ronacher8e8d0712008-04-16 23:10:49 +020015def translate_exception(exc_info):
16 """If passed an exc_info it will automatically rewrite the exceptions
17 all the way down to the correct line numbers and frames.
18 """
19 result_tb = prev_tb = None
Armin Ronacher6cc8dd02008-04-16 23:15:15 +020020 initial_tb = tb = exc_info[2].tb_next
Armin Ronacher8e8d0712008-04-16 23:10:49 +020021
22 while tb is not None:
23 template = tb.tb_frame.f_globals.get('__jinja_template__')
24 if template is not None:
25 lineno = template.get_corresponding_lineno(tb.tb_lineno)
26 tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
27 lineno, prev_tb)[2]
28 if result_tb is None:
29 result_tb = tb
30 prev_tb = tb
31 tb = tb.tb_next
32
33 return exc_info[:2] + (result_tb or initial_tb,)
Armin Ronacherba3757b2008-04-16 19:43:16 +020034
35
36def fake_exc_info(exc_info, filename, lineno, tb_back=None):
Armin Ronacher8e8d0712008-04-16 23:10:49 +020037 """Helper for `translate_exception`."""
Armin Ronacherba3757b2008-04-16 19:43:16 +020038 exc_type, exc_value, tb = exc_info
39
40 # figure the real context out
41 real_locals = tb.tb_frame.f_locals.copy()
Armin Ronacher203bfcb2008-04-24 21:54:44 +020042 ctx = real_locals.get('context')
43 if ctx:
44 locals = ctx.get_all()
45 else:
46 locals = {}
Armin Ronacherba3757b2008-04-16 19:43:16 +020047 for name, value in real_locals.iteritems():
48 if name.startswith('l_'):
49 locals[name[2:]] = value
50
Armin Ronacher18c6ca02008-04-17 10:03:29 +020051 # if there is a local called __jinja_exception__, we get
52 # rid of it to not break the debug functionality.
53 locals.pop('__jinja_exception__', None)
54
Armin Ronacherba3757b2008-04-16 19:43:16 +020055 # assamble fake globals we need
56 globals = {
57 '__name__': filename,
58 '__file__': filename,
59 '__jinja_exception__': exc_info[:2]
60 }
61
62 # and fake the exception
63 code = compile('\n' * (lineno - 1) + 'raise __jinja_exception__[0], ' +
64 '__jinja_exception__[1]', filename, 'exec')
Armin Ronacher32a910f2008-04-26 23:21:03 +020065
66 # if it's possible, change the name of the code. This won't work
67 # on some python environments such as google appengine
68 try:
69 function = tb.tb_frame.f_code.co_name
70 if function == 'root':
71 location = 'top-level template code'
72 elif function.startswith('block_'):
73 location = 'block "%s"' % function[6:]
74 else:
75 location = 'template'
76 code = CodeType(0, code.co_nlocals, code.co_stacksize,
77 code.co_flags, code.co_code, code.co_consts,
78 code.co_names, code.co_varnames, filename,
79 location, code.co_firstlineno,
80 code.co_lnotab, (), ())
81 except:
82 pass
83
84 # execute the code and catch the new traceback
Armin Ronacherba3757b2008-04-16 19:43:16 +020085 try:
86 exec code in globals, locals
87 except:
88 exc_info = sys.exc_info()
Armin Ronacher32a910f2008-04-26 23:21:03 +020089 new_tb = exc_info[2].tb_next
Armin Ronacherba3757b2008-04-16 19:43:16 +020090
91 # now we can patch the exc info accordingly
92 if tb_set_next is not None:
93 if tb_back is not None:
Armin Ronacher32a910f2008-04-26 23:21:03 +020094 tb_set_next(tb_back, new_tb)
Armin Ronacherba3757b2008-04-16 19:43:16 +020095 if tb is not None:
Armin Ronacher32a910f2008-04-26 23:21:03 +020096 tb_set_next(new_tb, tb.tb_next)
Armin Ronacher6cc8dd02008-04-16 23:15:15 +020097
98 # return without this frame
Armin Ronacher32a910f2008-04-26 23:21:03 +020099 return exc_info[:2] + (new_tb,)
Armin Ronacherba3757b2008-04-16 19:43:16 +0200100
101
Armin Ronacherba3757b2008-04-16 19:43:16 +0200102def _init_ugly_crap():
103 """This function implements a few ugly things so that we can patch the
104 traceback objects. The function returned allows resetting `tb_next` on
105 any python traceback object.
106 """
107 import ctypes
108 from types import TracebackType
109
110 # figure out side of _Py_ssize_t
111 if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
112 _Py_ssize_t = ctypes.c_int64
113 else:
114 _Py_ssize_t = ctypes.c_int
115
116 # regular python
117 class _PyObject(ctypes.Structure):
118 pass
119 _PyObject._fields_ = [
120 ('ob_refcnt', _Py_ssize_t),
121 ('ob_type', ctypes.POINTER(_PyObject))
122 ]
123
124 # python with trace
125 if object.__basicsize__ != ctypes.sizeof(_PyObject):
126 class _PyObject(ctypes.Structure):
127 pass
128 _PyObject._fields_ = [
129 ('_ob_next', ctypes.POINTER(_PyObject)),
130 ('_ob_prev', ctypes.POINTER(_PyObject)),
131 ('ob_refcnt', _Py_ssize_t),
132 ('ob_type', ctypes.POINTER(_PyObject))
133 ]
134
135 class _Traceback(_PyObject):
136 pass
137 _Traceback._fields_ = [
138 ('tb_next', ctypes.POINTER(_Traceback)),
139 ('tb_frame', ctypes.POINTER(_PyObject)),
140 ('tb_lasti', ctypes.c_int),
141 ('tb_lineno', ctypes.c_int)
142 ]
143
144 def tb_set_next(tb, next):
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200145 """Set the tb_next attribute of a traceback object."""
Armin Ronacherba3757b2008-04-16 19:43:16 +0200146 if not (isinstance(tb, TracebackType) and
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200147 (next is None or isinstance(next, TracebackType))):
Armin Ronacherba3757b2008-04-16 19:43:16 +0200148 raise TypeError('tb_set_next arguments must be traceback objects')
149 obj = _Traceback.from_address(id(tb))
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200150 if tb.tb_next is not None:
151 old = _Traceback.from_address(id(tb.tb_next))
152 old.ob_refcnt -= 1
153 if next is None:
154 obj.tb_next = ctypes.POINTER(_Traceback)()
155 else:
156 next = _Traceback.from_address(id(next))
157 next.ob_refcnt += 1
158 obj.tb_next = ctypes.pointer(next)
Armin Ronacherba3757b2008-04-16 19:43:16 +0200159
160 return tb_set_next
161
162
Armin Ronacherbd33f112008-04-18 09:17:32 +0200163# try to get a tb_set_next implementation
Armin Ronacherba3757b2008-04-16 19:43:16 +0200164try:
Armin Ronacherbd33f112008-04-18 09:17:32 +0200165 from jinja2._speedups import tb_set_next
166except ImportError:
167 try:
168 tb_set_next = _init_ugly_crap()
169 except:
170 tb_set_next = None
Armin Ronacherba3757b2008-04-16 19:43:16 +0200171del _init_ugly_crap