OLD | NEW |
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 """ | 2 """ |
3 jinja2.debug | 3 jinja2.debug |
4 ~~~~~~~~~~~~ | 4 ~~~~~~~~~~~~ |
5 | 5 |
6 Implements the debug interface for Jinja. This module does some pretty | 6 Implements the debug interface for Jinja. This module does some pretty |
7 ugly stuff with the Python traceback system in order to achieve tracebacks | 7 ugly stuff with the Python traceback system in order to achieve tracebacks |
8 with correct line numbers, locals and contents. | 8 with correct line numbers, locals and contents. |
9 | 9 |
10 :copyright: (c) 2010 by the Jinja Team. | 10 :copyright: (c) 2010 by the Jinja Team. |
11 :license: BSD, see LICENSE for more details. | 11 :license: BSD, see LICENSE for more details. |
12 """ | 12 """ |
13 import sys | 13 import sys |
14 import traceback | 14 import traceback |
15 from types import TracebackType | 15 from types import TracebackType |
16 from jinja2.utils import CodeType, missing, internal_code | 16 from jinja2.utils import missing, internal_code |
17 from jinja2.exceptions import TemplateSyntaxError | 17 from jinja2.exceptions import TemplateSyntaxError |
| 18 from jinja2._compat import iteritems, reraise, code_type |
18 | 19 |
19 # on pypy we can take advantage of transparent proxies | 20 # on pypy we can take advantage of transparent proxies |
20 try: | 21 try: |
21 from __pypy__ import tproxy | 22 from __pypy__ import tproxy |
22 except ImportError: | 23 except ImportError: |
23 tproxy = None | 24 tproxy = None |
24 | 25 |
25 | 26 |
26 # how does the raise helper look like? | 27 # how does the raise helper look like? |
27 try: | 28 try: |
28 exec "raise TypeError, 'foo'" | 29 exec("raise TypeError, 'foo'") |
29 except SyntaxError: | 30 except SyntaxError: |
30 raise_helper = 'raise __jinja_exception__[1]' | 31 raise_helper = 'raise __jinja_exception__[1]' |
31 except TypeError: | 32 except TypeError: |
32 raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' | 33 raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' |
33 | 34 |
34 | 35 |
35 class TracebackFrameProxy(object): | 36 class TracebackFrameProxy(object): |
36 """Proxies a traceback frame.""" | 37 """Proxies a traceback frame.""" |
37 | 38 |
38 def __init__(self, tb): | 39 def __init__(self, tb): |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 if operation in ('__getattribute__', '__getattr__'): | 71 if operation in ('__getattribute__', '__getattr__'): |
71 return getattr(proxy, args[0]) | 72 return getattr(proxy, args[0]) |
72 elif operation == '__setattr__': | 73 elif operation == '__setattr__': |
73 proxy.__setattr__(*args, **kwargs) | 74 proxy.__setattr__(*args, **kwargs) |
74 else: | 75 else: |
75 return getattr(proxy, operation)(*args, **kwargs) | 76 return getattr(proxy, operation)(*args, **kwargs) |
76 return tproxy(TracebackType, operation_handler) | 77 return tproxy(TracebackType, operation_handler) |
77 | 78 |
78 | 79 |
79 class ProcessedTraceback(object): | 80 class ProcessedTraceback(object): |
80 """Holds a Jinja preprocessed traceback for priting or reraising.""" | 81 """Holds a Jinja preprocessed traceback for printing or reraising.""" |
81 | 82 |
82 def __init__(self, exc_type, exc_value, frames): | 83 def __init__(self, exc_type, exc_value, frames): |
83 assert frames, 'no frames for this traceback?' | 84 assert frames, 'no frames for this traceback?' |
84 self.exc_type = exc_type | 85 self.exc_type = exc_type |
85 self.exc_value = exc_value | 86 self.exc_value = exc_value |
86 self.frames = frames | 87 self.frames = frames |
87 | 88 |
88 # newly concatenate the frames (which are proxies) | 89 # newly concatenate the frames (which are proxies) |
89 prev_tb = None | 90 prev_tb = None |
90 for tb in self.frames: | 91 for tb in self.frames: |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 | 152 |
152 | 153 |
153 def translate_exception(exc_info, initial_skip=0): | 154 def translate_exception(exc_info, initial_skip=0): |
154 """If passed an exc_info it will automatically rewrite the exceptions | 155 """If passed an exc_info it will automatically rewrite the exceptions |
155 all the way down to the correct line numbers and frames. | 156 all the way down to the correct line numbers and frames. |
156 """ | 157 """ |
157 tb = exc_info[2] | 158 tb = exc_info[2] |
158 frames = [] | 159 frames = [] |
159 | 160 |
160 # skip some internal frames if wanted | 161 # skip some internal frames if wanted |
161 for x in xrange(initial_skip): | 162 for x in range(initial_skip): |
162 if tb is not None: | 163 if tb is not None: |
163 tb = tb.tb_next | 164 tb = tb.tb_next |
164 initial_tb = tb | 165 initial_tb = tb |
165 | 166 |
166 while tb is not None: | 167 while tb is not None: |
167 # skip frames decorated with @internalcode. These are internal | 168 # skip frames decorated with @internalcode. These are internal |
168 # calls we can't avoid and that are useless in template debugging | 169 # calls we can't avoid and that are useless in template debugging |
169 # output. | 170 # output. |
170 if tb.tb_frame.f_code in internal_code: | 171 if tb.tb_frame.f_code in internal_code: |
171 tb = tb.tb_next | 172 tb = tb.tb_next |
(...skipping 10 matching lines...) Expand all Loading... |
182 tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, | 183 tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, |
183 lineno)[2] | 184 lineno)[2] |
184 | 185 |
185 frames.append(make_frame_proxy(tb)) | 186 frames.append(make_frame_proxy(tb)) |
186 tb = next | 187 tb = next |
187 | 188 |
188 # if we don't have any exceptions in the frames left, we have to | 189 # if we don't have any exceptions in the frames left, we have to |
189 # reraise it unchanged. | 190 # reraise it unchanged. |
190 # XXX: can we backup here? when could this happen? | 191 # XXX: can we backup here? when could this happen? |
191 if not frames: | 192 if not frames: |
192 raise exc_info[0], exc_info[1], exc_info[2] | 193 reraise(exc_info[0], exc_info[1], exc_info[2]) |
193 | 194 |
194 return ProcessedTraceback(exc_info[0], exc_info[1], frames) | 195 return ProcessedTraceback(exc_info[0], exc_info[1], frames) |
195 | 196 |
196 | 197 |
197 def fake_exc_info(exc_info, filename, lineno): | 198 def fake_exc_info(exc_info, filename, lineno): |
198 """Helper for `translate_exception`.""" | 199 """Helper for `translate_exception`.""" |
199 exc_type, exc_value, tb = exc_info | 200 exc_type, exc_value, tb = exc_info |
200 | 201 |
201 # figure the real context out | 202 # figure the real context out |
202 if tb is not None: | 203 if tb is not None: |
203 real_locals = tb.tb_frame.f_locals.copy() | 204 real_locals = tb.tb_frame.f_locals.copy() |
204 ctx = real_locals.get('context') | 205 ctx = real_locals.get('context') |
205 if ctx: | 206 if ctx: |
206 locals = ctx.get_all() | 207 locals = ctx.get_all() |
207 else: | 208 else: |
208 locals = {} | 209 locals = {} |
209 for name, value in real_locals.iteritems(): | 210 for name, value in iteritems(real_locals): |
210 if name.startswith('l_') and value is not missing: | 211 if name.startswith('l_') and value is not missing: |
211 locals[name[2:]] = value | 212 locals[name[2:]] = value |
212 | 213 |
213 # if there is a local called __jinja_exception__, we get | 214 # if there is a local called __jinja_exception__, we get |
214 # rid of it to not break the debug functionality. | 215 # rid of it to not break the debug functionality. |
215 locals.pop('__jinja_exception__', None) | 216 locals.pop('__jinja_exception__', None) |
216 else: | 217 else: |
217 locals = {} | 218 locals = {} |
218 | 219 |
219 # assamble fake globals we need | 220 # assamble fake globals we need |
(...skipping 17 matching lines...) Expand all Loading... |
237 if tb is None: | 238 if tb is None: |
238 location = 'template' | 239 location = 'template' |
239 else: | 240 else: |
240 function = tb.tb_frame.f_code.co_name | 241 function = tb.tb_frame.f_code.co_name |
241 if function == 'root': | 242 if function == 'root': |
242 location = 'top-level template code' | 243 location = 'top-level template code' |
243 elif function.startswith('block_'): | 244 elif function.startswith('block_'): |
244 location = 'block "%s"' % function[6:] | 245 location = 'block "%s"' % function[6:] |
245 else: | 246 else: |
246 location = 'template' | 247 location = 'template' |
247 code = CodeType(0, code.co_nlocals, code.co_stacksize, | 248 code = code_type(0, code.co_nlocals, code.co_stacksize, |
248 code.co_flags, code.co_code, code.co_consts, | 249 code.co_flags, code.co_code, code.co_consts, |
249 code.co_names, code.co_varnames, filename, | 250 code.co_names, code.co_varnames, filename, |
250 location, code.co_firstlineno, | 251 location, code.co_firstlineno, |
251 code.co_lnotab, (), ()) | 252 code.co_lnotab, (), ()) |
252 except: | 253 except: |
253 pass | 254 pass |
254 | 255 |
255 # execute the code and catch the new traceback | 256 # execute the code and catch the new traceback |
256 try: | 257 try: |
257 exec code in globals, locals | 258 exec(code, globals, locals) |
258 except: | 259 except: |
259 exc_info = sys.exc_info() | 260 exc_info = sys.exc_info() |
260 new_tb = exc_info[2].tb_next | 261 new_tb = exc_info[2].tb_next |
261 | 262 |
262 # return without this frame | 263 # return without this frame |
263 return exc_info[:2] + (new_tb,) | 264 return exc_info[:2] + (new_tb,) |
264 | 265 |
265 | 266 |
266 def _init_ugly_crap(): | 267 def _init_ugly_crap(): |
267 """This function implements a few ugly things so that we can patch the | 268 """This function implements a few ugly things so that we can patch the |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
323 obj.tb_next = ctypes.pointer(next) | 324 obj.tb_next = ctypes.pointer(next) |
324 | 325 |
325 return tb_set_next | 326 return tb_set_next |
326 | 327 |
327 | 328 |
328 # try to get a tb_set_next implementation if we don't have transparent | 329 # try to get a tb_set_next implementation if we don't have transparent |
329 # proxies. | 330 # proxies. |
330 tb_set_next = None | 331 tb_set_next = None |
331 if tproxy is None: | 332 if tproxy is None: |
332 try: | 333 try: |
333 from jinja2._debugsupport import tb_set_next | 334 tb_set_next = _init_ugly_crap() |
334 except ImportError: | 335 except: |
335 try: | 336 pass |
336 tb_set_next = _init_ugly_crap() | |
337 except: | |
338 pass | |
339 del _init_ugly_crap | 337 del _init_ugly_crap |
OLD | NEW |