#!/usr/bin/env python import sys import bdb import repr import string import linecache # for linecache.getlines(filename) import pygtk pygtk.require('2.0') import gtk import dialogs class PyGTKDb(gtk.Window, bdb.Bdb): ui_string = """ """ def __init__(self): gtk.Window.__init__(self) bdb.Bdb.__init__(self) self.realize() self.set_title("PyGTKDb") self.connect("destroy", self.do_quit) self.connect("delete_event", self.do_quit) self.box = gtk.VBox() self.add(self.box) self.box.show() self.add_stock_ids() actions = [ ('Next', 'pyide-next', None, None, "Next statement", self.do_next), ('Step', 'pyide-step', None, None, "Step into function", self.do_step), ('Return', 'pyide-return', None, None, "Continue execution to end of function", self.do_return), ('Continue', 'pyide-continue', None, None, "Continue execution to next break point", self.do_continue), ('Break', 'pyide-break', None, None, "Toggle break point at selected line", self.do_break), ('Edit', 'pyide-edit', None, None, "Edit the value of the selected variable", self.do_edit), ('Run', 'pyide-run', None, None, "Execute some code in the current stack context", self.do_run), ('Quit', 'pyide-quit', None, None, "Quit the debugger", self.do_quit), ] self.ag = gtk.ActionGroup('PyIDE Actions') self.ag.add_actions(actions) self.ui = gtk.UIManager() self.ui.insert_action_group(self.ag, 0) self.ui.add_ui_from_string(self.ui_string) self.add_accel_group(self.ui.get_accel_group()) self.box.pack_start(self.ui.get_widget('/Toolbar'), expand=False) sep = gtk.HSeparator() self.box.pack_start(sep, expand=False) sep.show() vpane = gtk.VPaned() self.box.pack_start(vpane) vpane.show() hpane = gtk.HPaned() vpane.add1(hpane) hpane.show() swin = gtk.ScrolledWindow() swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) hpane.add1(swin) swin.show() ls = gtk.ListStore(str) self.stackdisp = gtk.TreeView(ls) tvc = gtk.TreeViewColumn('Stack Frame', gtk.CellRendererText(), text=0) self.stackdisp.append_column(tvc) self.stackdisp.set_size_request(280, 125) selection = self.stackdisp.get_selection() selection.set_mode(gtk.SELECTION_BROWSE) selection.connect("changed", self.update_curstack) self.stackdisp.set_border_width(2) swin.add(self.stackdisp) self.stackdisp.show() swin = gtk.ScrolledWindow() swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) hpane.add2(swin) swin.show() ls = gtk.ListStore(str, str, str) self.vardisp = gtk.TreeView(ls) titles = ['local var', 'type', 'value'] for n in range(len(titles)): tvc = gtk.TreeViewColumn(titles[n], gtk.CellRendererText(), text=n) self.vardisp.append_column(tvc) selection = self.vardisp.get_selection() selection.set_mode(gtk.SELECTION_BROWSE) selection.connect("changed", self.update_selectedvar) self.vardisp.set_border_width(2) self.vardisp.set_border_width(2) swin.add(self.vardisp) self.vardisp.show() self.vardisp.selected = 0 self.vardisp.varnames = [] swin = gtk.ScrolledWindow() swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) vpane.add2(swin) swin.show() self.minibreak = gtk.Image() self.minibreak.set_from_file("minibreak.xpm") self.minibreak.show() ls = gtk.ListStore(gtk.gdk.Pixbuf, str, str) self.filedisp = gtk.TreeView(ls) titles = ['break', 'lineno', 'line'] cell = gtk.CellRendererPixbuf() tvc = gtk.TreeViewColumn(None, cell, pixbuf=0) tvc.set_min_width(14) tvc.set_widget(self.minibreak) self.filedisp.append_column(tvc) cell = gtk.CellRendererText() cell.set_property('xalign', 1.0) tvc = gtk.TreeViewColumn(titles[1], cell, text=1) self.filedisp.append_column(tvc) cell = gtk.CellRendererText() tvc = gtk.TreeViewColumn(titles[2], cell, text=2) self.filedisp.append_column(tvc) self.minibreak = self.minibreak.get_pixbuf() selection = self.filedisp.get_selection() selection.set_mode(gtk.SELECTION_BROWSE) selection.connect("changed", self.update_selection) self.filedisp.connect('row-activated', lambda t,p,c: self.do_break()) self.filedisp.set_border_width(2) self.filedisp.set_size_request(600, 200) swin.add(self.filedisp) self.filedisp.show() separator = gtk.HSeparator() self.box.pack_start(separator, expand=False) separator.show() align = gtk.Alignment(0.0, 0.5, 0.0, 0.0) self.box.pack_start(align, expand=False) align.show() self.status = gtk.Label() self.status.set_padding(4, 1) align.add(self.status) self.status.show() self.filename = None self.selected = 0 self.blockupdate = 0 return def add_stock_ids(self): ids = [ ('pyide-next', '_Next', gtk.gdk.CONTROL_MASK, gtk.keysyms.N, 'pyide'), ('pyide-step', '_Step', gtk.gdk.CONTROL_MASK, gtk.keysyms.S, 'pyide'), ('pyide-return', '_Return', gtk.gdk.CONTROL_MASK, gtk.keysyms.R, 'pyide'), ('pyide-continue', '_Continue', gtk.gdk.CONTROL_MASK, gtk.keysyms.C, 'pyide'), ('pyide-break', '_Break', gtk.gdk.CONTROL_MASK, gtk.keysyms.B, 'pyide'), ('pyide-edit', '_Edit', gtk.gdk.CONTROL_MASK, gtk.keysyms.E, 'pyide'), ('pyide-run', 'R_un', gtk.gdk.CONTROL_MASK, gtk.keysyms.U, 'pyide'), ('pyide-quit', '_Quit', gtk.gdk.CONTROL_MASK, gtk.keysyms.Q, 'pyide'), ] gtk.stock_add(ids) names = ['next', 'step', 'return', 'continue', 'break', 'edit', 'run', 'quit'] self.iconfactory = gtk.IconFactory() for name in names: iconset = gtk.IconSet(gtk.gdk.pixbuf_new_from_file(name+'.xpm')) self.iconfactory.add('pyide-'+name, iconset) self.iconfactory.add_default() return def set_status(self, str): self.status.set_text(str) return def update_selection(self, sel): if self.blockupdate: return model, iter = sel.get_selected() r = model.get_path(iter)[0] self.selected = r + 1 return def update_curstack(self, sel): if self.blockupdate: return model, iter = sel.get_selected() r = model.get_path(iter)[0] self.curindex = r self.curframe = self.stack[self.curindex][0] self.lineno = None self.update_code_listing() self.update_var_listing() return def update_selectedvar(self, sel): model, iter = sel.get_selected() if iter: r = model.get_path(iter)[0] self.vardisp.selected = r return def set_quit(self): self.hide() self.destroy() bdb.Bdb.set_quit(self) def reset(self): bdb.Bdb.reset(self) self.forget() def forget(self): self.lineno = None self.stack = [] self.curindex = 0 self.curframe = None def setup(self, f, t): self.forget() self.stack, self.curindex = self.get_stack(f, t) self.curframe = self.stack[self.curindex][0] return # interaction functions -- overriden from bdb def user_line(self, frame): # called when we stop or break at this line self.interaction(frame, None) def user_return(self, frame, return_value): # called when a return trap is set here frame.f_locals['__return__'] = return_value if frame.f_code.co_name: func = frame.f_code.co_name else: func = "" self.set_status(func + " returned " + repr.repr(return_value)) self.interaction(frame, None) def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): frame.f_locals['__exception__'] = exc_type, exc_value if type(exc_type) == type(''): exc_type_name = exc_type else: exc_type_name = exc_type.__name__ self.set_status(exc_type_name + ':' + repr.repr(exc_value)) self.interaction(frame, exc_traceback) def interaction(self, frame, traceback): self.setup(frame, traceback) self.update_stack_listing(self.curindex) gtk.main() self.forget() def update_stack_listing(self, curindex): self.blockupdate = 1 model = self.stackdisp.get_model() model.clear() for i in range(len(self.stack)): frame_lineno = self.stack[i] row = self.format_stack_entry(frame_lineno, "##!##") row = string.split(row, "##!##")[0] model.append([row]) self.blockupdate = 0 self.stackdisp.scroll_to_cell(curindex, None, True, 1.0, 0.0) self.stackdisp.get_selection().select_path(curindex) return def update_var_listing(self): model = self.vardisp.get_model() model.clear() locals = self.curframe.f_locals self.vardisp.varnames = locals.keys() self.vardisp.varnames.sort() for var in self.vardisp.varnames: row = [var, type(locals[var]).__name__, repr.repr(locals[var])] model.append(row) self.vardisp.get_selection().select_path(0) return def update_code_listing(self): frame = self.curframe newfile = frame.f_code.co_filename if newfile != self.filename: lines = linecache.getlines(newfile) self.filename = newfile self.blockupdate = 1 model = self.filedisp.get_model() model.clear() breaks = self.get_file_breaks(newfile) for line in range(len(lines)): if line+1 in breaks: model.append([self.minibreak, line+1, lines[line].rstrip()]) else: model.append([None, line+1, lines[line].rstrip()]) self.blockupdate = 0 self.selected = frame.f_lineno lineno = self.selected if newfile != '': self.filedisp.scroll_to_cell(lineno - 1, None, True, 1.0, 0.0) self.filedisp.get_selection().select_path(lineno - 1) return def do_next(self, _b=None): self.set_next(self.curframe) gtk.main_quit() def do_step(self, _b=None): self.set_step() gtk.main_quit() def do_return(self, _b=None): self.set_return(self.curframe) gtk.main_quit() def do_continue(self, _b=None): self.set_continue() gtk.main_quit() def do_quit(self, _b=None, _e=None): self.set_quit() gtk.main_quit() def do_break(self, _b=None): breaks = self.get_file_breaks(self.filename) if self.selected in breaks: err = self.clear_break(self.filename, self.selected) if err: self.set_status(err) return self.filedisp.get_model()[self.selected-1][0] = None else: err = self.set_break(self.filename, self.selected) if err: self.set_status(err) return self.filedisp.get_model()[self.selected-1][0] = self.minibreak return def do_run(self, _b=None): line = dialogs.InputBox("Execute Code", "Enter code to execute:", self) if line == None: return locals = self.curframe.f_locals globals = self.curframe.f_globals globals['__privileged__'] = 1 try: code = compile(line + '\n', '', 'single') exec code in globals, locals except: if type(sys.exc_type) == type(''): exc_type_name = sys.exc_type else: exc_type_name = sys.exc_type.__name__ self.set_status('*** ' + exc_type_name + ': ' + str(sys.exc_value)) return self.update_var_listing() return def do_edit(self, _b=None): locals = self.curframe.f_locals varname = self.vardisp.varnames[self.vardisp.selected] val = repr.repr(locals[varname]) value = dialogs.InputBox("Edit Variable", "Enter new value for " + varname + ":", self, val) if value == None: return globals = self.curframe.f_globals globals['__privileged__'] = 1 try: val = eval(value, globals, locals) self.curframe.f_locals[varname] = val except: if type(sys.exc_type) == type(''): exc_type_name = sys.exc_type else: exc_type_name = sys.exc_type.__name__ self.set_status('*** ' + exc_type_name + ': ' + str(sys.exc_value)) return row = self.vardisp.selected model = self.vardisp.get_model() model[row][1] = type(val).__name__ model[row][2] = repr.repr(val) # this makes up the interface that is compatible with pdb. def run(statement, globals=None, locals=None): win = PyGTKDb() win.show() win.run(statement, globals, locals) def runeval(expression, globals=None, locals=None): win = PyGTKDb() win.show() return win.runeval(expression, globals, locals) def runcall(*args): win = PyGTKDb() win.show() return apply(win.runcall, args) def set_trace(): win = PyGTKDb() win.show() win.set_trace() def post_mortem(traceback): win = PyGTKDb() win.show() win.reset() win.interaction(None, traceback) def pm(): post_mortem(sys.last_traceback) if __name__ == '__main__': import os if not sys.argv[1:]: print "usage: gtkdb.py scriptfile [args ...]" sys.exit(2) filename = sys.argv[1] del sys.argv[0] # delete gtkdb.py sys.path.insert(0, os.path.dirname(filename)) run('execfile("' + filename + '")', {'__name__': '__main__'})