- add sources.
[platform/framework/web/crosswalk.git] / src / third_party / jinja2 / debug.py
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
16 from jinja2.utils import missing, internal_code
17 from jinja2.exceptions import TemplateSyntaxError
18 from jinja2._compat import iteritems, reraise, code_type
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         code = code_type(0, code.co_nlocals, code.co_stacksize,
249                          code.co_flags, code.co_code, code.co_consts,
250                          code.co_names, code.co_varnames, filename,
251                          location, code.co_firstlineno,
252                          code.co_lnotab, (), ())
253     except:
254         pass
255
256     # execute the code and catch the new traceback
257     try:
258         exec(code, globals, locals)
259     except:
260         exc_info = sys.exc_info()
261         new_tb = exc_info[2].tb_next
262
263     # return without this frame
264     return exc_info[:2] + (new_tb,)
265
266
267 def _init_ugly_crap():
268     """This function implements a few ugly things so that we can patch the
269     traceback objects.  The function returned allows resetting `tb_next` on
270     any python traceback object.  Do not attempt to use this on non cpython
271     interpreters
272     """
273     import ctypes
274     from types import TracebackType
275
276     # figure out side of _Py_ssize_t
277     if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
278         _Py_ssize_t = ctypes.c_int64
279     else:
280         _Py_ssize_t = ctypes.c_int
281
282     # regular python
283     class _PyObject(ctypes.Structure):
284         pass
285     _PyObject._fields_ = [
286         ('ob_refcnt', _Py_ssize_t),
287         ('ob_type', ctypes.POINTER(_PyObject))
288     ]
289
290     # python with trace
291     if hasattr(sys, 'getobjects'):
292         class _PyObject(ctypes.Structure):
293             pass
294         _PyObject._fields_ = [
295             ('_ob_next', ctypes.POINTER(_PyObject)),
296             ('_ob_prev', ctypes.POINTER(_PyObject)),
297             ('ob_refcnt', _Py_ssize_t),
298             ('ob_type', ctypes.POINTER(_PyObject))
299         ]
300
301     class _Traceback(_PyObject):
302         pass
303     _Traceback._fields_ = [
304         ('tb_next', ctypes.POINTER(_Traceback)),
305         ('tb_frame', ctypes.POINTER(_PyObject)),
306         ('tb_lasti', ctypes.c_int),
307         ('tb_lineno', ctypes.c_int)
308     ]
309
310     def tb_set_next(tb, next):
311         """Set the tb_next attribute of a traceback object."""
312         if not (isinstance(tb, TracebackType) and
313                 (next is None or isinstance(next, TracebackType))):
314             raise TypeError('tb_set_next arguments must be traceback objects')
315         obj = _Traceback.from_address(id(tb))
316         if tb.tb_next is not None:
317             old = _Traceback.from_address(id(tb.tb_next))
318             old.ob_refcnt -= 1
319         if next is None:
320             obj.tb_next = ctypes.POINTER(_Traceback)()
321         else:
322             next = _Traceback.from_address(id(next))
323             next.ob_refcnt += 1
324             obj.tb_next = ctypes.pointer(next)
325
326     return tb_set_next
327
328
329 # try to get a tb_set_next implementation if we don't have transparent
330 # proxies.
331 tb_set_next = None
332 if tproxy is None:
333     try:
334         tb_set_next = _init_ugly_crap()
335     except:
336         pass
337     del _init_ugly_crap