Added first version of IR importer/exporter
[libfirm] / scripts / 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: Copyright 2008 by Armin Ronacher.
11     :license: BSD.
12 """
13 import sys
14 from jinja2.utils import CodeType
15
16
17 def translate_exception(exc_info):
18     """If passed an exc_info it will automatically rewrite the exceptions
19     all the way down to the correct line numbers and frames.
20     """
21     result_tb = prev_tb = None
22     initial_tb = tb = exc_info[2].tb_next
23
24     while tb is not None:
25         template = tb.tb_frame.f_globals.get('__jinja_template__')
26         if template is not None:
27             lineno = template.get_corresponding_lineno(tb.tb_lineno)
28             tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
29                                lineno, prev_tb)[2]
30         if result_tb is None:
31             result_tb = tb
32         prev_tb = tb
33         tb = tb.tb_next
34
35     return exc_info[:2] + (result_tb or initial_tb,)
36
37
38 def fake_exc_info(exc_info, filename, lineno, tb_back=None):
39     """Helper for `translate_exception`."""
40     exc_type, exc_value, tb = exc_info
41
42     # figure the real context out
43     real_locals = tb.tb_frame.f_locals.copy()
44     ctx = real_locals.get('context')
45     if ctx:
46         locals = ctx.get_all()
47     else:
48         locals = {}
49     for name, value in real_locals.iteritems():
50         if name.startswith('l_'):
51             locals[name[2:]] = value
52
53     # if there is a local called __jinja_exception__, we get
54     # rid of it to not break the debug functionality.
55     locals.pop('__jinja_exception__', None)
56
57     # assamble fake globals we need
58     globals = {
59         '__name__':             filename,
60         '__file__':             filename,
61         '__jinja_exception__':  exc_info[:2]
62     }
63
64     # and fake the exception
65     code = compile('\n' * (lineno - 1) + 'raise __jinja_exception__[0], ' +
66                    '__jinja_exception__[1]', filename, 'exec')
67
68     # if it's possible, change the name of the code.  This won't work
69     # on some python environments such as google appengine
70     try:
71         function = tb.tb_frame.f_code.co_name
72         if function == 'root':
73             location = 'top-level template code'
74         elif function.startswith('block_'):
75             location = 'block "%s"' % function[6:]
76         else:
77             location = 'template'
78         code = CodeType(0, code.co_nlocals, code.co_stacksize,
79                         code.co_flags, code.co_code, code.co_consts,
80                         code.co_names, code.co_varnames, filename,
81                         location, code.co_firstlineno,
82                         code.co_lnotab, (), ())
83     except:
84         pass
85
86     # execute the code and catch the new traceback
87     try:
88         exec code in globals, locals
89     except:
90         exc_info = sys.exc_info()
91         new_tb = exc_info[2].tb_next
92
93     # now we can patch the exc info accordingly
94     if tb_set_next is not None:
95         if tb_back is not None:
96             tb_set_next(tb_back, new_tb)
97         if tb is not None:
98             tb_set_next(new_tb, tb.tb_next)
99
100     # return without this frame
101     return exc_info[:2] + (new_tb,)
102
103
104 def _init_ugly_crap():
105     """This function implements a few ugly things so that we can patch the
106     traceback objects.  The function returned allows resetting `tb_next` on
107     any python traceback object.
108     """
109     import ctypes
110     from types import TracebackType
111
112     # figure out side of _Py_ssize_t
113     if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
114         _Py_ssize_t = ctypes.c_int64
115     else:
116         _Py_ssize_t = ctypes.c_int
117
118     # regular python
119     class _PyObject(ctypes.Structure):
120         pass
121     _PyObject._fields_ = [
122         ('ob_refcnt', _Py_ssize_t),
123         ('ob_type', ctypes.POINTER(_PyObject))
124     ]
125
126     # python with trace
127     if object.__basicsize__ != ctypes.sizeof(_PyObject):
128         class _PyObject(ctypes.Structure):
129             pass
130         _PyObject._fields_ = [
131             ('_ob_next', ctypes.POINTER(_PyObject)),
132             ('_ob_prev', ctypes.POINTER(_PyObject)),
133             ('ob_refcnt', _Py_ssize_t),
134             ('ob_type', ctypes.POINTER(_PyObject))
135         ]
136
137     class _Traceback(_PyObject):
138         pass
139     _Traceback._fields_ = [
140         ('tb_next', ctypes.POINTER(_Traceback)),
141         ('tb_frame', ctypes.POINTER(_PyObject)),
142         ('tb_lasti', ctypes.c_int),
143         ('tb_lineno', ctypes.c_int)
144     ]
145
146     def tb_set_next(tb, next):
147         """Set the tb_next attribute of a traceback object."""
148         if not (isinstance(tb, TracebackType) and
149                 (next is None or isinstance(next, TracebackType))):
150             raise TypeError('tb_set_next arguments must be traceback objects')
151         obj = _Traceback.from_address(id(tb))
152         if tb.tb_next is not None:
153             old = _Traceback.from_address(id(tb.tb_next))
154             old.ob_refcnt -= 1
155         if next is None:
156             obj.tb_next = ctypes.POINTER(_Traceback)()
157         else:
158             next = _Traceback.from_address(id(next))
159             next.ob_refcnt += 1
160             obj.tb_next = ctypes.pointer(next)
161
162     return tb_set_next
163
164
165 # try to get a tb_set_next implementation
166 try:
167     from jinja2._speedups import tb_set_next
168 except ImportError:
169     try:
170         tb_set_next = _init_ugly_crap()
171     except:
172         tb_set_next = None
173 del _init_ugly_crap