Show local variables in report_exception, and allow expressions to be
[rox-lib.git] / python / rox / debug.py
blob08ffc8a4b7a4a6eb727e3103ec8be2db66eef3d0
1 """This module provides features to help with debugging ROX applications."""
3 import sys, os
4 import traceback
5 import gobject
6 import linecache
8 from rox import g, ButtonMixed, TRUE, FALSE, toplevel_ref, toplevel_unref, _
9 from rox import info, alert
10 from saving import StringSaver
12 savebox = None
14 def show_exception(type, value, tb):
15 """Display this exception in an error box. The user has the options
16 of ignoring the error, quitting the application and examining the
17 exception in more detail. See also rox.report_exception()."""
19 QUIT = 1
20 DETAILS = 2
21 SAVE = 3
23 brief = ''.join(traceback.format_exception_only(type, value))
25 toplevel_ref()
26 box = g.MessageDialog(None, 0, g.MESSAGE_ERROR, g.BUTTONS_NONE, brief)
28 button = ButtonMixed(g.STOCK_ZOOM_IN, _('_Details'))
29 button.set_flags(g.CAN_DEFAULT)
30 button.show()
31 box.add_action_widget(button, DETAILS)
33 box.add_button(g.STOCK_OK, g.RESPONSE_OK)
34 box.set_default_response(g.RESPONSE_OK)
36 box.set_position(g.WIN_POS_CENTER)
37 box.set_title(_('Error'))
38 reply = []
39 def response(box, resp):
40 reply.append(resp)
41 g.mainquit()
42 box.connect('response', response)
43 box.show()
45 bug_report = 'Traceback (most recent call last):\n' + \
46 ''.join(traceback.format_stack(tb.tb_frame.f_back) +
47 traceback.format_tb(tb) +
48 traceback.format_exception_only(type, value))
50 while 1:
51 g.mainloop()
52 resp = reply.pop()
53 if resp == g.RESPONSE_OK or resp == g.RESPONSE_DELETE_EVENT:
54 break
55 if resp == SAVE:
56 global savebox
57 if savebox:
58 savebox.destroy()
59 def destroy(box):
60 savebox = None
61 savebox = StringSaver(bug_report, 'BugReport')
62 savebox.connect('destroy', destroy)
63 savebox.show()
64 continue
65 if resp == QUIT:
66 sys.exit(1)
67 assert resp == DETAILS
68 box.set_response_sensitive(DETAILS, FALSE)
69 box.set_has_separator(FALSE)
71 button = ButtonMixed(g.STOCK_SAVE, _('_Bug Report'))
72 button.set_flags(g.CAN_DEFAULT)
73 button.show()
74 box.add_action_widget(button, SAVE)
75 box.action_area.set_child_secondary(button, TRUE)
77 button = ButtonMixed(g.STOCK_QUIT, _('Forced Quit'))
78 button.set_flags(g.CAN_DEFAULT)
79 button.show()
80 box.add_action_widget(button, QUIT)
81 box.action_area.set_child_secondary(button, TRUE)
83 ee = ExceptionExplorer(tb)
84 box.vbox.pack_start(ee)
85 ee.show()
86 box.destroy()
87 toplevel_unref()
89 class ExceptionExplorer(g.Frame):
90 FILE = 0
91 LINE = 1
92 FUNC = 2
93 CODE = 3
94 def __init__(self, tb):
95 g.Frame.__init__(self, _('Stack trace (innermost last)'))
97 vbox = g.VBox(FALSE, 0)
98 self.add(vbox)
100 inner = g.Frame()
101 inner.set_shadow_type(g.SHADOW_IN)
102 vbox.add(inner)
104 self.savebox = None
106 self.tb = tb
108 self.model = g.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT,
109 gobject.TYPE_STRING, gobject.TYPE_STRING)
110 tree = g.TreeView(self.model)
111 inner.add(tree)
113 cell = g.CellRendererText()
115 column = g.TreeViewColumn('File', cell, text = ExceptionExplorer.FILE)
116 cell.set_property('xalign', 1)
117 tree.append_column(column)
119 cell = g.CellRendererText()
120 column = g.TreeViewColumn('Line', cell, text = ExceptionExplorer.LINE)
121 tree.append_column(column)
122 column = g.TreeViewColumn('Func', cell, text = ExceptionExplorer.FUNC)
123 tree.append_column(column)
124 column = g.TreeViewColumn('Code', cell, text = ExceptionExplorer.CODE)
125 tree.append_column(column)
127 inner.set_border_width(5)
129 frames = []
130 while tb is not None:
131 frames.insert(0, (tb.tb_frame, traceback.tb_lineno(tb)))
132 tb = tb.tb_next
133 f = self.tb.tb_frame
134 if f:
135 f = f.f_back # Skip the reporting frame
136 while f is not None:
137 frames.append((f, f.f_lineno))
138 f = f.f_back
140 frames.reverse()
142 new = None
143 for f, lineno in frames:
144 co = f.f_code
145 filename = co.co_filename
146 name = co.co_name
147 line = linecache.getline(filename, lineno).strip()
149 filename = os.path.basename(filename)
151 new = self.model.append()
152 self.model.set(new, ExceptionExplorer.FILE, filename,
153 ExceptionExplorer.LINE, lineno,
154 ExceptionExplorer.FUNC, name,
155 ExceptionExplorer.CODE, line)
157 def selected_frame():
158 selected = sel.get_selected()
159 assert selected
160 model, iter = selected
161 frame, = model.get_path(iter)
162 return frames[frame][0]
164 vars = g.ListStore(str, str)
165 sel = tree.get_selection()
166 sel.set_mode(g.SELECTION_BROWSE)
167 def select_frame(tree):
168 vars.clear()
169 for n, v in selected_frame().f_locals.iteritems():
170 new = vars.append()
171 vars.set(new, 0, str(n), 1, `v`)
172 sel.connect('changed', select_frame)
174 # Area to show the local variables
175 tree = g.TreeView(vars)
177 vbox.pack_start(g.Label(_('Local variables in selected frame:')),
178 FALSE, TRUE, 0)
180 cell = g.CellRendererText()
181 column = g.TreeViewColumn('Name', cell, text = 0)
182 cell.set_property('xalign', 1)
183 tree.append_column(column)
184 cell = g.CellRendererText()
185 column = g.TreeViewColumn('Value', cell, text = 1)
186 tree.append_column(column)
188 inner = g.Frame()
189 inner.set_shadow_type(g.SHADOW_IN)
190 inner.add(tree)
191 inner.set_border_width(5)
192 vbox.pack_start(inner, TRUE, TRUE, 0)
194 if new:
195 sel.select_iter(new)
197 hbox = g.HBox(FALSE, 4)
198 hbox.set_border_width(5)
199 vbox.pack_start(hbox, FALSE, TRUE, 0)
200 hbox.pack_start(g.Label('>>>'), FALSE, TRUE, 0)
202 expr = g.Entry()
203 hbox.pack_start(expr, TRUE, TRUE, 0)
204 def activate(entry):
205 expr = entry.get_text()
206 frame = selected_frame()
207 try:
208 info(`eval(expr, frame.f_locals, frame.f_globals)`)
209 except:
210 type, value = sys.exc_info()[:2]
211 brief = ''.join(traceback.format_exception_only(type, value))
212 alert(brief)
213 entry.grab_focus()
214 expr.connect('activate', activate)
216 vbox.show_all()