| 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 |