# Window-interface-independent part of twit import sys import types import bdb import types import os SIMPLE_TYPES=( types.NoneType, types.IntType, types.LongType, types.FloatType, types.ComplexType, types.StringType ) # XXXX Mac-specific ICON_NORMAL=500 ICON_RETURN=503 ICON_CALL=504 ICON_ZERO=505 ICON_DEAD=506 class DebuggerStuff(bdb.Bdb): def __init__(self, parent): bdb.Bdb.__init__(self) self.parent = parent self.exception_info = (None, None) self.reason = 'Not running' self.icon = ICON_NORMAL self.reset() def reset(self): bdb.Bdb.reset(self) self.forget() def forget(self): self.lineno = None self.stack = [] self.curindex = 0 self.curframe = None def run(self, cmd, locals, globals): self.reason = 'Running' bdb.Bdb.run(self, cmd, locals, globals) print 'RETURN from run' self.reason = 'Not running' def setup(self, f, t): self.forget() self.stack, self.curindex = self.get_stack(f, t) self.curframe = self.stack[self.curindex][0] def interaction(self, frame, traceback): self.setup(frame, traceback) self.parent.interact() self.exception_info = (None, None) # def user_call(self, frame, argument_list): # self.reason = 'Calling' # self.icon = ICON_CALL # self.interaction(frame, None) def user_line(self, frame): self.reason = 'Stopped' self.icon = ICON_NORMAL self.interaction(frame, None) def user_return(self, frame, return_value): self.reason = 'Returning' self.icon = ICON_RETURN self.interaction(frame, None) def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): self.reason = 'Exception occurred' self.icon = ICON_DEAD self.parent.setstate('tb') self.exception_info = (exc_type, exc_value) self.interaction(frame, exc_traceback) def getexception(self): tp, value = self.exception_info if tp <> None and type(tp) <> type(''): tp = tp.__name__ if value <> None and type(value) <> type(''): value = `value` return tp, value def getstacktrace(self): names, locations = [], [] for frame, lineno in self.stack: name = frame.f_code.co_name if not name: name = "" elif name == '?': name = "" else: name = name + '()' names.append(name) if lineno == -1: lineno = getframelineno(frame) modname = getframemodname(frame) if not modname: modname = "" locations.append("%s:%d" % (modname, lineno)) return names, locations def getframe(self, number): if number < 0 or number >= len(self.stack): return None return self.stack[number][0] def getframevars(self, number, show_complex=1, show_system=1): frame = self.getframe(number) if not frame: return [], [] return getvarsfromdict(frame.f_locals, show_complex, show_system) def getframevar(self, number, var): frame = self.getframe(number) return frame.f_locals[var] def getframefilepos(self, frameno): if frameno == None or frameno < 0 or frameno >= len(self.stack): return None, None, None frame, line = self.stack[frameno] if line == -1: line = getframelineno(frame) modname = getframemodname(frame) filename = frame.f_code.co_filename return filename, modname, line def getprogramstate(self): return self.reason class Application: """Base code for the application""" def mi_init(self, sessiontype, arg): self.dbg = DebuggerStuff(self) self.run_dialog = self.new_stack_browser(self) self.run_dialog.open() self.module_dialog = None self.initial_cmd = None self.cur_string_name = None if sessiontype == 'tb': while arg.tb_next <> None: arg = arg.tb_next self.dbg.setup(arg.tb_frame, arg) self.run_dialog.setup() elif sessiontype == 'run': self.initial_cmd = arg def breaks_changed(self, filename): self.run_dialog.breaks_changed(filename) if self.module_dialog: self.module_dialog.breaks_changed(filename) def to_debugger(self): cmd = self.initial_cmd self.initial_cmd = None self.setstate('run') self.switch_to_app() apply(self.dbg.run, cmd) self.setstate('none') self.switch_to_dbg() self.run_dialog.update_views() if self.module_dialog: self.module_dialog.update_views() def interact(self): # Interact with user. First, display correct info self.switch_to_dbg() self.run_dialog.update_views() if self.module_dialog: self.module_dialog.update_views() # Next, go into mainloop self.one_mainloop() # Finally (before we start the debuggee again) show state self.switch_to_app() self.run_dialog.show_it_running() def quit_bdb(self): self.dbg.set_quit() def run(self): cmd = self.AskString('Statement to execute:') self.runstring(cmd) def runfile(self, path): dir, file = os.path.split(path) try: os.chdir(dir) except os.error, arg: self.Message("%s: %s"%(dir, arg)) return ns = {'__name__':'__main__', '__file__':path} cmd = "execfile('%s')"%file self.runstring(cmd, ns, ns) def runstring(self, cmd, globals={}, locals={}): self.cur_string_name = ''%cmd try: cmd = compile(cmd, self.cur_string_name, 'exec') except SyntaxError, arg: self.Message('Syntax error: %s'%`arg`) return self.initial_cmd = (cmd, globals, locals) self.exit_mainloop() def cont(self): self.dbg.set_continue() self.exit_mainloop() def step(self, frame): self.dbg.set_next(frame) self.exit_mainloop() def step_in(self): self.dbg.set_step() self.exit_mainloop() def step_out(self, frame): self.dbg.set_return(frame) self.exit_mainloop() def kill(self): self.dbg.set_quit() self.exit_mainloop() def quit(self): self.do_quit() def browse(self, module): if not self.module_dialog: self.module_dialog = self.new_module_browser(self) self.module_dialog.open(module) else: self.module_dialog.focus(module) def browse_var(self, var): b = self.new_var_browser(self, var) class StackBrowser: """Base code for stack browser""" def mi_open(self): """Setup initial data structures""" self.cur_stackitem = None self.cur_source = None self.cur_modname = None self.cur_line = None self.show_complex = 1 self.show_system = 0 self.setup() # create_items(self) should create self.modules, self.vars and self.source def setup(self): self.parent.SetWatch() """Fill the various widgets with values""" name, value = self.parent.dbg.getexception() self.setexception(name, value) self.setprogramstate(self.parent.dbg.getprogramstate()) names, locations = self.parent.dbg.getstacktrace() self.stack_setcontent(names, locations) self.cur_stackitem = len(names)-1 self.stack_select(self.cur_stackitem) self.setup_frame() def setup_frame(self): """Setup frame-dependent widget data""" self.parent.SetWatch() self.cont_varnames, self.cont_varvalues = \ self.parent.dbg.getframevars(self.cur_stackitem, self.show_complex, self.show_system) self.setvars() self.set_var_buttons() msg = "" if self.cur_stackitem == None: self.cur_source = None self.cur_modname = None self.cur_line = None msg = "No stackframe selected" else: self.cur_source, self.cur_modname, optnextline = \ self.parent.dbg.getframefilepos(self.cur_stackitem) if optnextline >= 0: self.cur_line = optnextline if self.cur_source == '': self.cur_source = None msg = "Executing from unknown " elif type(self.cur_source) == types.StringType and \ self.cur_source[:8] == ' None: frame = self.parent.dbg.getframe(self.cur_stackitem) self.parent.step(frame) else: self.parent.step_in() def click_step_in(self): self.parent.step_in() def click_step_out(self): if self.cur_stackitem <> None: frame = self.parent.dbg.getframe(self.cur_stackitem) self.parent.step_out(frame) else: self.parent.step_in() def click_kill(self): self.parent.kill() def click_browse(self): self.parent.browse(self.cur_modname) def click_edit(self): lino = self.cur_line if not lino: lino = 1 if self.cur_source: self.parent.edit(self.cur_source, lino) class ModuleBrowser: """Base code for a module-browser""" def mi_open(self, module): """Setup initial data structures""" self.cur_module = module self.cur_source = None self.cur_line = None self.cont_modules = [] self.value_windows = [] self.setup() # create_items(self) should create self.modules, self.vars and self.source def setup(self): """Fill the various widgets with values""" self.parent.SetWatch() modnames = getmodulenames() if not self.cur_module in modnames: self.cur_module = None if modnames <> self.cont_modules: self.cont_modules = modnames self.setmodulenames() if self.cur_module: self.module_select(self.cont_modules.index(self.cur_module)) else: self.module_select(None) self.setup_module() def setup_module(self): """Setup module-dependent widget data""" self.parent.SetWatch() if not self.cur_module: self.cont_varnames = [] self.cont_varvalues = [] else: self.cont_varnames, self.cont_varvalues = getmodulevars(self.cur_module) self.setvars() msg = "" if not self.cur_module: self.cur_source = None msg = "No module selected" else: m = sys.modules[self.cur_module] try: self.cur_source = m.__file__ except AttributeError: self.cur_source = None msg = "Not a python module" self.cur_lineno = 0 self.setsource(msg) self.source_select(self.cur_line) self.breaks_changed(self.cur_source) self.parent.SetCursor() # setsource(msg) should display cur_source+content, or msg if None def update_views(self): self.setup_module() def click_module(self, module, *dummy): if not module or module == self.cur_module: return self.focus(module) def focus(self, module): self.cur_module = module self.setup() def click_var(self, var, *dummy): if not var: return m = sys.modules[self.cur_module] dict = m.__dict__ self.parent.browse_var(dict[var]) def click_source(self, lineno, inborder): if not inborder: self.source_select(lineno) self.cur_lineno = lineno if lineno == None or not self.cur_source or not inborder: return if self.parent.dbg.get_break(self.cur_source, lineno): self.parent.dbg.clear_break(self.cur_source, lineno) else: self.parent.dbg.set_break(self.cur_source, lineno) self.parent.breaks_changed(self.cur_source) def breaks_changed(self, filename): if filename == self.cur_source: list = self.parent.dbg.get_file_breaks(filename) self.source_setbreaks(list) def click_edit(self): lino = self.cur_lineno if not lino: lino = 1 if self.cur_source: self.parent.edit(self.cur_source, lino) def getmodulenames(): """Return a list of all current modules, sorted""" list = sys.modules.keys()[:] list.sort() return list def getmodulevars(name): """For given module return lists with names and values""" m = sys.modules[name] try: dict = m.__dict__ except AttributeError: dict = {} return getvarsfromdict(dict) def getvarsfromdict(dict, show_complex=1, show_system=1): allnames = dict.keys()[:] allnames.sort() names = [] for n in allnames: if not show_complex: if not type(dict[n]) in SIMPLE_TYPES: continue if not show_system: if n[:2] == '__' and n[-2:] == '__': continue names.append(n) values = [] for n in names: v = pretty(dict[n]) values.append(v) return names, values def pretty(var): t = type(var) if t == types.FunctionType: return '' if t == types.ClassType: return '' return `var` def getframelineno(frame): """Given a frame return the line number""" return getcodelineno(frame.f_code) def getfunclineno(func): """Given a function return the line number""" return getcodelineno(func.func_code) def getcodelineno(cobj): """Given a code object return the line number""" code = cobj.co_code lineno = -1 if ord(code[0]) == 127: # SET_LINENO instruction lineno = ord(code[1]) | (ord(code[2]) << 8) return lineno def getframemodname(frame): """Given a frame return the module name""" globals = frame.f_globals if globals.has_key('__name__'): return globals['__name__'] return None