Mercurial > repos > bcclaywell > argo_navis
comparison venv/lib/python2.7/site-packages/jinja2/debug.py @ 0:d67268158946 draft
planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
| author | bcclaywell |
|---|---|
| date | Mon, 12 Oct 2015 17:43:33 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d67268158946 |
|---|---|
| 1 # -*- coding: utf-8 -*- | |
| 2 """ | |
| 3 jinja2.debug | |
| 4 ~~~~~~~~~~~~ | |
| 5 | |
| 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 | |
| 8 with correct line numbers, locals and contents. | |
| 9 | |
| 10 :copyright: (c) 2010 by the Jinja Team. | |
| 11 :license: BSD, see LICENSE for more details. | |
| 12 """ | |
| 13 import sys | |
| 14 import traceback | |
| 15 from types import TracebackType, CodeType | |
| 16 from jinja2.utils import missing, internal_code | |
| 17 from jinja2.exceptions import TemplateSyntaxError | |
| 18 from jinja2._compat import iteritems, reraise, PY2 | |
| 19 | |
| 20 # on pypy we can take advantage of transparent proxies | |
| 21 try: | |
| 22 from __pypy__ import tproxy | |
| 23 except ImportError: | |
| 24 tproxy = None | |
| 25 | |
| 26 | |
| 27 # how does the raise helper look like? | |
| 28 try: | |
| 29 exec("raise TypeError, 'foo'") | |
| 30 except SyntaxError: | |
| 31 raise_helper = 'raise __jinja_exception__[1]' | |
| 32 except TypeError: | |
| 33 raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' | |
| 34 | |
| 35 | |
| 36 class TracebackFrameProxy(object): | |
| 37 """Proxies a traceback frame.""" | |
| 38 | |
| 39 def __init__(self, tb): | |
| 40 self.tb = tb | |
| 41 self._tb_next = None | |
| 42 | |
| 43 @property | |
| 44 def tb_next(self): | |
| 45 return self._tb_next | |
| 46 | |
| 47 def set_next(self, next): | |
| 48 if tb_set_next is not None: | |
| 49 try: | |
| 50 tb_set_next(self.tb, next and next.tb or None) | |
| 51 except Exception: | |
| 52 # this function can fail due to all the hackery it does | |
| 53 # on various python implementations. We just catch errors | |
| 54 # down and ignore them if necessary. | |
| 55 pass | |
| 56 self._tb_next = next | |
| 57 | |
| 58 @property | |
| 59 def is_jinja_frame(self): | |
| 60 return '__jinja_template__' in self.tb.tb_frame.f_globals | |
| 61 | |
| 62 def __getattr__(self, name): | |
| 63 return getattr(self.tb, name) | |
| 64 | |
| 65 | |
| 66 def make_frame_proxy(frame): | |
| 67 proxy = TracebackFrameProxy(frame) | |
| 68 if tproxy is None: | |
| 69 return proxy | |
| 70 def operation_handler(operation, *args, **kwargs): | |
| 71 if operation in ('__getattribute__', '__getattr__'): | |
| 72 return getattr(proxy, args[0]) | |
| 73 elif operation == '__setattr__': | |
| 74 proxy.__setattr__(*args, **kwargs) | |
| 75 else: | |
| 76 return getattr(proxy, operation)(*args, **kwargs) | |
| 77 return tproxy(TracebackType, operation_handler) | |
| 78 | |
| 79 | |
| 80 class ProcessedTraceback(object): | |
| 81 """Holds a Jinja preprocessed traceback for printing or reraising.""" | |
| 82 | |
| 83 def __init__(self, exc_type, exc_value, frames): | |
| 84 assert frames, 'no frames for this traceback?' | |
| 85 self.exc_type = exc_type | |
| 86 self.exc_value = exc_value | |
| 87 self.frames = frames | |
| 88 | |
| 89 # newly concatenate the frames (which are proxies) | |
| 90 prev_tb = None | |
| 91 for tb in self.frames: | |
| 92 if prev_tb is not None: | |
| 93 prev_tb.set_next(tb) | |
| 94 prev_tb = tb | |
| 95 prev_tb.set_next(None) | |
| 96 | |
| 97 def render_as_text(self, limit=None): | |
| 98 """Return a string with the traceback.""" | |
| 99 lines = traceback.format_exception(self.exc_type, self.exc_value, | |
| 100 self.frames[0], limit=limit) | |
| 101 return ''.join(lines).rstrip() | |
| 102 | |
| 103 def render_as_html(self, full=False): | |
| 104 """Return a unicode string with the traceback as rendered HTML.""" | |
| 105 from jinja2.debugrenderer import render_traceback | |
| 106 return u'%s\n\n<!--\n%s\n-->' % ( | |
| 107 render_traceback(self, full=full), | |
| 108 self.render_as_text().decode('utf-8', 'replace') | |
| 109 ) | |
| 110 | |
| 111 @property | |
| 112 def is_template_syntax_error(self): | |
| 113 """`True` if this is a template syntax error.""" | |
| 114 return isinstance(self.exc_value, TemplateSyntaxError) | |
| 115 | |
| 116 @property | |
| 117 def exc_info(self): | |
| 118 """Exception info tuple with a proxy around the frame objects.""" | |
| 119 return self.exc_type, self.exc_value, self.frames[0] | |
| 120 | |
| 121 @property | |
| 122 def standard_exc_info(self): | |
| 123 """Standard python exc_info for re-raising""" | |
| 124 tb = self.frames[0] | |
| 125 # the frame will be an actual traceback (or transparent proxy) if | |
| 126 # we are on pypy or a python implementation with support for tproxy | |
| 127 if type(tb) is not TracebackType: | |
| 128 tb = tb.tb | |
| 129 return self.exc_type, self.exc_value, tb | |
| 130 | |
| 131 | |
| 132 def make_traceback(exc_info, source_hint=None): | |
| 133 """Creates a processed traceback object from the exc_info.""" | |
| 134 exc_type, exc_value, tb = exc_info | |
| 135 if isinstance(exc_value, TemplateSyntaxError): | |
| 136 exc_info = translate_syntax_error(exc_value, source_hint) | |
| 137 initial_skip = 0 | |
| 138 else: | |
| 139 initial_skip = 1 | |
| 140 return translate_exception(exc_info, initial_skip) | |
| 141 | |
| 142 | |
| 143 def translate_syntax_error(error, source=None): | |
| 144 """Rewrites a syntax error to please traceback systems.""" | |
| 145 error.source = source | |
| 146 error.translated = True | |
| 147 exc_info = (error.__class__, error, None) | |
| 148 filename = error.filename | |
| 149 if filename is None: | |
| 150 filename = '<unknown>' | |
| 151 return fake_exc_info(exc_info, filename, error.lineno) | |
| 152 | |
| 153 | |
| 154 def translate_exception(exc_info, initial_skip=0): | |
| 155 """If passed an exc_info it will automatically rewrite the exceptions | |
| 156 all the way down to the correct line numbers and frames. | |
| 157 """ | |
| 158 tb = exc_info[2] | |
| 159 frames = [] | |
| 160 | |
| 161 # skip some internal frames if wanted | |
| 162 for x in range(initial_skip): | |
| 163 if tb is not None: | |
| 164 tb = tb.tb_next | |
| 165 initial_tb = tb | |
| 166 | |
| 167 while tb is not None: | |
| 168 # skip frames decorated with @internalcode. These are internal | |
| 169 # calls we can't avoid and that are useless in template debugging | |
| 170 # output. | |
| 171 if tb.tb_frame.f_code in internal_code: | |
| 172 tb = tb.tb_next | |
| 173 continue | |
| 174 | |
| 175 # save a reference to the next frame if we override the current | |
| 176 # one with a faked one. | |
| 177 next = tb.tb_next | |
| 178 | |
| 179 # fake template exceptions | |
| 180 template = tb.tb_frame.f_globals.get('__jinja_template__') | |
| 181 if template is not None: | |
| 182 lineno = template.get_corresponding_lineno(tb.tb_lineno) | |
| 183 tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, | |
| 184 lineno)[2] | |
| 185 | |
| 186 frames.append(make_frame_proxy(tb)) | |
| 187 tb = next | |
| 188 | |
| 189 # if we don't have any exceptions in the frames left, we have to | |
| 190 # reraise it unchanged. | |
| 191 # XXX: can we backup here? when could this happen? | |
| 192 if not frames: | |
| 193 reraise(exc_info[0], exc_info[1], exc_info[2]) | |
| 194 | |
| 195 return ProcessedTraceback(exc_info[0], exc_info[1], frames) | |
| 196 | |
| 197 | |
| 198 def fake_exc_info(exc_info, filename, lineno): | |
| 199 """Helper for `translate_exception`.""" | |
| 200 exc_type, exc_value, tb = exc_info | |
| 201 | |
| 202 # figure the real context out | |
| 203 if tb is not None: | |
| 204 real_locals = tb.tb_frame.f_locals.copy() | |
| 205 ctx = real_locals.get('context') | |
| 206 if ctx: | |
| 207 locals = ctx.get_all() | |
| 208 else: | |
| 209 locals = {} | |
| 210 for name, value in iteritems(real_locals): | |
| 211 if name.startswith('l_') and value is not missing: | |
| 212 locals[name[2:]] = value | |
| 213 | |
| 214 # if there is a local called __jinja_exception__, we get | |
| 215 # rid of it to not break the debug functionality. | |
| 216 locals.pop('__jinja_exception__', None) | |
| 217 else: | |
| 218 locals = {} | |
| 219 | |
| 220 # assamble fake globals we need | |
| 221 globals = { | |
| 222 '__name__': filename, | |
| 223 '__file__': filename, | |
| 224 '__jinja_exception__': exc_info[:2], | |
| 225 | |
| 226 # we don't want to keep the reference to the template around | |
| 227 # to not cause circular dependencies, but we mark it as Jinja | |
| 228 # frame for the ProcessedTraceback | |
| 229 '__jinja_template__': None | |
| 230 } | |
| 231 | |
| 232 # and fake the exception | |
| 233 code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec') | |
| 234 | |
| 235 # if it's possible, change the name of the code. This won't work | |
| 236 # on some python environments such as google appengine | |
| 237 try: | |
| 238 if tb is None: | |
| 239 location = 'template' | |
| 240 else: | |
| 241 function = tb.tb_frame.f_code.co_name | |
| 242 if function == 'root': | |
| 243 location = 'top-level template code' | |
| 244 elif function.startswith('block_'): | |
| 245 location = 'block "%s"' % function[6:] | |
| 246 else: | |
| 247 location = 'template' | |
| 248 | |
| 249 if PY2: | |
| 250 code = CodeType(0, code.co_nlocals, code.co_stacksize, | |
| 251 code.co_flags, code.co_code, code.co_consts, | |
| 252 code.co_names, code.co_varnames, filename, | |
| 253 location, code.co_firstlineno, | |
| 254 code.co_lnotab, (), ()) | |
| 255 else: | |
| 256 code = CodeType(0, code.co_kwonlyargcount, | |
| 257 code.co_nlocals, code.co_stacksize, | |
| 258 code.co_flags, code.co_code, code.co_consts, | |
| 259 code.co_names, code.co_varnames, filename, | |
| 260 location, code.co_firstlineno, | |
| 261 code.co_lnotab, (), ()) | |
| 262 except Exception as e: | |
| 263 pass | |
| 264 | |
| 265 # execute the code and catch the new traceback | |
| 266 try: | |
| 267 exec(code, globals, locals) | |
| 268 except: | |
| 269 exc_info = sys.exc_info() | |
| 270 new_tb = exc_info[2].tb_next | |
| 271 | |
| 272 # return without this frame | |
| 273 return exc_info[:2] + (new_tb,) | |
| 274 | |
| 275 | |
| 276 def _init_ugly_crap(): | |
| 277 """This function implements a few ugly things so that we can patch the | |
| 278 traceback objects. The function returned allows resetting `tb_next` on | |
| 279 any python traceback object. Do not attempt to use this on non cpython | |
| 280 interpreters | |
| 281 """ | |
| 282 import ctypes | |
| 283 from types import TracebackType | |
| 284 | |
| 285 if PY2: | |
| 286 # figure out size of _Py_ssize_t for Python 2: | |
| 287 if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): | |
| 288 _Py_ssize_t = ctypes.c_int64 | |
| 289 else: | |
| 290 _Py_ssize_t = ctypes.c_int | |
| 291 else: | |
| 292 # platform ssize_t on Python 3 | |
| 293 _Py_ssize_t = ctypes.c_ssize_t | |
| 294 | |
| 295 # regular python | |
| 296 class _PyObject(ctypes.Structure): | |
| 297 pass | |
| 298 _PyObject._fields_ = [ | |
| 299 ('ob_refcnt', _Py_ssize_t), | |
| 300 ('ob_type', ctypes.POINTER(_PyObject)) | |
| 301 ] | |
| 302 | |
| 303 # python with trace | |
| 304 if hasattr(sys, 'getobjects'): | |
| 305 class _PyObject(ctypes.Structure): | |
| 306 pass | |
| 307 _PyObject._fields_ = [ | |
| 308 ('_ob_next', ctypes.POINTER(_PyObject)), | |
| 309 ('_ob_prev', ctypes.POINTER(_PyObject)), | |
| 310 ('ob_refcnt', _Py_ssize_t), | |
| 311 ('ob_type', ctypes.POINTER(_PyObject)) | |
| 312 ] | |
| 313 | |
| 314 class _Traceback(_PyObject): | |
| 315 pass | |
| 316 _Traceback._fields_ = [ | |
| 317 ('tb_next', ctypes.POINTER(_Traceback)), | |
| 318 ('tb_frame', ctypes.POINTER(_PyObject)), | |
| 319 ('tb_lasti', ctypes.c_int), | |
| 320 ('tb_lineno', ctypes.c_int) | |
| 321 ] | |
| 322 | |
| 323 def tb_set_next(tb, next): | |
| 324 """Set the tb_next attribute of a traceback object.""" | |
| 325 if not (isinstance(tb, TracebackType) and | |
| 326 (next is None or isinstance(next, TracebackType))): | |
| 327 raise TypeError('tb_set_next arguments must be traceback objects') | |
| 328 obj = _Traceback.from_address(id(tb)) | |
| 329 if tb.tb_next is not None: | |
| 330 old = _Traceback.from_address(id(tb.tb_next)) | |
| 331 old.ob_refcnt -= 1 | |
| 332 if next is None: | |
| 333 obj.tb_next = ctypes.POINTER(_Traceback)() | |
| 334 else: | |
| 335 next = _Traceback.from_address(id(next)) | |
| 336 next.ob_refcnt += 1 | |
| 337 obj.tb_next = ctypes.pointer(next) | |
| 338 | |
| 339 return tb_set_next | |
| 340 | |
| 341 | |
| 342 # try to get a tb_set_next implementation if we don't have transparent | |
| 343 # proxies. | |
| 344 tb_set_next = None | |
| 345 if tproxy is None: | |
| 346 try: | |
| 347 tb_set_next = _init_ugly_crap() | |
| 348 except: | |
| 349 pass | |
| 350 del _init_ugly_crap |
