| import sys |
| import bdb |
| import types |
| import os |
| |
| import W |
| import WASTEconst |
| import PyBrowser |
| from Carbon import Qd |
| from Carbon import Evt |
| from Carbon import Lists |
| import MacOS |
| _filenames = {} |
| |
| SIMPLE_TYPES = ( |
| types.NoneType, |
| types.IntType, |
| types.LongType, |
| types.FloatType, |
| types.ComplexType, |
| types.StringType |
| ) |
| |
| |
| class Debugger(bdb.Bdb): |
| |
| def __init__(self, title = 'Debugger'): |
| bdb.Bdb.__init__(self) |
| self.closed = 1 |
| self.title = title |
| self.breaksviewer = None |
| self.reset() |
| self.tracing = 0 |
| self.tracingmonitortime = Evt.TickCount() |
| self.editors = {} |
| |
| prefs = W.getapplication().getprefs() |
| if prefs.debugger: |
| for file, breaks in prefs.debugger.breaks.items(): |
| for b in breaks: |
| self.set_break(file, b) |
| self.bounds, self.horpanes, self.verpanes = prefs.debugger.windowsettings |
| self.tracemagic = prefs.debugger.tracemagic |
| else: |
| self.breaks = {} |
| self.horpanes = (0.4, 0.6) |
| self.verpanes = (0.3, 0.35, 0.35) |
| self.bounds = (600, 400) |
| self.tracemagic = 0 |
| self.laststacksel = None |
| |
| def canonic(self, filename): |
| # override: the provided canonic() method breaks our |
| # file-less Untitled windows |
| return filename |
| |
| def reset(self): |
| self.currentframe = None |
| self.file = None |
| self.laststack = None |
| self.reason = 'Not running' |
| self.continuewithoutdebugger = 0 |
| bdb.Bdb.reset(self) |
| self.forget() |
| |
| def start(self, bottomframe = None, running = 0): |
| W.getapplication().DebuggerQuit = bdb.BdbQuit |
| from Carbon import Menu |
| Menu.HiliteMenu(0) |
| if self.closed: |
| self.setupwidgets(self.title) |
| self.closed = 0 |
| if not self.w.parent.debugger_quitting: |
| self.w.select() |
| raise W.AlertError, 'There is another debugger session busy.' |
| self.reset() |
| self.botframe = bottomframe |
| if running: |
| self.set_continue() |
| self.reason = 'Running\xc9' |
| self.setstate('running') |
| else: |
| self.set_step() |
| self.reason = 'stopped' |
| self.setstate('stopped') |
| sys.settrace(self.trace_dispatch) |
| |
| def stop(self): |
| self.set_quit() |
| if self.w.parent: |
| self.exit_mainloop() |
| self.resetwidgets() |
| |
| def set_continue_without_debugger(self): |
| sys.settrace(None) |
| self.set_quit() |
| self.clear_tracefuncs() |
| self.continuewithoutdebugger = 1 |
| if hasattr(self, "w") and self.w.parent: |
| self.exit_mainloop() |
| self.resetwidgets() |
| |
| def clear_tracefuncs(self): |
| try: |
| raise 'spam' |
| except: |
| pass |
| frame = sys.exc_traceback.tb_frame |
| while frame is not None: |
| del frame.f_trace |
| frame = frame.f_back |
| |
| def postmortem(self, exc_type, exc_value, traceback): |
| if self.closed: |
| self.setupwidgets(self.title) |
| self.closed = 0 |
| if not self.w.parent.debugger_quitting: |
| raise W.AlertError, 'There is another debugger session busy.' |
| self.reset() |
| if traceback: |
| self.botframe = traceback.tb_frame |
| while traceback.tb_next <> None: |
| traceback = traceback.tb_next |
| frame = traceback.tb_frame |
| else: |
| self.botframe = None |
| frame = None |
| self.w.panes.bottom.buttons.killbutton.enable(1) |
| self.reason = '(dead) ' + self.formatexception(exc_type, exc_value) |
| self.w.select() |
| self.setup(frame, traceback) |
| self.setstate('dead') |
| self.showstack(self.curindex) |
| self.showframe(self.curindex) |
| |
| def setupwidgets(self, title): |
| self.w = w = W.Window(self.bounds, title, minsize = (500, 300)) |
| |
| w.panes = W.HorizontalPanes((8, 4, -8, -8), self.horpanes) |
| |
| w.panes.browserpanes = browserpanes = W.VerticalPanes(None, self.verpanes) |
| |
| browserpanes.stacklist = W.Group(None) |
| browserpanes.stacklist.title = W.TextBox((4, 0, 0, 12), 'Stack') |
| browserpanes.stacklist.stack = W.List((0, 16, 0, 0), callback = self.do_stack, flags = Lists.lOnlyOne) |
| |
| browserpanes.locals = W.Group(None) |
| browserpanes.locals.title = W.TextBox((4, 0, 0, 12), 'Local variables') |
| browserpanes.locals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0)) |
| |
| browserpanes.globals = W.Group(None) |
| browserpanes.globals.title = W.TextBox((4, 0, 0, 12), 'Global variables') |
| browserpanes.globals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0)) |
| |
| w.panes.bottom = bottom = W.Group(None) |
| bottom.src = src = W.Group((0, 52, 0, 0)) |
| source = SourceViewer((1, 1, -15, -15), readonly = 1, debugger = self) |
| src.optionsmenu = W.PopupMenu((-16, 0, 16, 16), []) |
| src.optionsmenu.bind('<click>', self.makeoptionsmenu) |
| |
| src._barx = W.Scrollbar((0, -16, -15, 16), source.hscroll, max = 32767) |
| src._bary = W.Scrollbar((-16, 15, 16, -15), source.vscroll, max = 32767) |
| src.source = source |
| src.frame = W.Frame((0, 0, -15, -15)) |
| |
| bottom.tracingmonitor = TracingMonitor((0, 23, 6, 6)) |
| bottom.state = W.TextBox((12, 20, 0, 16), self.reason) |
| |
| bottom.srctitle = W.TextBox((12, 36, 0, 14)) |
| bottom.buttons = buttons = W.Group((12, 0, 0, 16)) |
| |
| buttons.runbutton = W.Button((0, 0, 50, 16), "Run", self.do_run) |
| buttons.stopbutton = W.Button((58, 0, 50, 16), "Stop", self.do_stop) |
| buttons.killbutton = W.Button((116, 0, 50, 16), "Kill", self.do_kill) |
| buttons.line = W.VerticalLine((173, 0, 0, 0)) |
| buttons.stepbutton = W.Button((181, 0, 50, 16), "Step", self.do_step) |
| buttons.stepinbutton = W.Button((239, 0, 50, 16), "Step in", self.do_stepin) |
| buttons.stepoutbutton = W.Button((297, 0, 50, 16), "Step out", self.do_stepout) |
| |
| w.bind('cmdr', buttons.runbutton.push) |
| w.bind('cmd.', buttons.stopbutton.push) |
| w.bind('cmdk', buttons.killbutton.push) |
| w.bind('cmds', buttons.stepbutton.push) |
| w.bind('cmdt', buttons.stepinbutton.push) |
| w.bind('cmdu', buttons.stepoutbutton.push) |
| |
| w.bind('<close>', self.close) |
| |
| w.open() |
| w.xxx___select(w.panes.bottom.src.source) |
| |
| def makeoptionsmenu(self): |
| options = [('Clear breakpoints', self.w.panes.bottom.src.source.clearbreakpoints), |
| ('Clear all breakpoints', self.clear_all_breaks), |
| ('Edit breakpoints\xc9', self.edit_breaks), '-', |
| (self.tracemagic and |
| 'Disable __magic__ tracing' or 'Enable __magic__ tracing', self.togglemagic)] |
| self.w.panes.bottom.src.optionsmenu.set(options) |
| |
| def edit_breaks(self): |
| if self.breaksviewer: |
| self.breaksviewer.select() |
| else: |
| self.breaksviewer = BreakpointsViewer(self) |
| |
| def togglemagic(self): |
| self.tracemagic = not self.tracemagic |
| |
| def setstate(self, state): |
| self.w.panes.bottom.tracingmonitor.reset() |
| self.w.panes.bottom.state.set(self.reason) |
| buttons = self.w.panes.bottom.buttons |
| if state == 'stopped': |
| buttons.runbutton.enable(1) |
| buttons.stopbutton.enable(0) |
| buttons.killbutton.enable(1) |
| buttons.stepbutton.enable(1) |
| buttons.stepinbutton.enable(1) |
| buttons.stepoutbutton.enable(1) |
| elif state == 'running': |
| buttons.runbutton.enable(0) |
| buttons.stopbutton.enable(1) |
| buttons.killbutton.enable(1) |
| buttons.stepbutton.enable(0) |
| buttons.stepinbutton.enable(0) |
| buttons.stepoutbutton.enable(0) |
| elif state == 'idle': |
| buttons.runbutton.enable(0) |
| buttons.stopbutton.enable(0) |
| buttons.killbutton.enable(0) |
| buttons.stepbutton.enable(0) |
| buttons.stepinbutton.enable(0) |
| buttons.stepoutbutton.enable(0) |
| elif state == 'dead': |
| buttons.runbutton.enable(0) |
| buttons.stopbutton.enable(0) |
| buttons.killbutton.enable(1) |
| buttons.stepbutton.enable(0) |
| buttons.stepinbutton.enable(0) |
| buttons.stepoutbutton.enable(0) |
| else: |
| print 'unknown state:', state |
| |
| def resetwidgets(self): |
| self.reason = '' |
| self.w.panes.bottom.srctitle.set('') |
| self.w.panes.bottom.src.source.set('') |
| self.w.panes.browserpanes.stacklist.stack.set([]) |
| self.w.panes.browserpanes.locals.browser.set({}) |
| self.w.panes.browserpanes.globals.browser.set({}) |
| self.setstate('idle') |
| |
| # W callbacks |
| |
| def close(self): |
| self.set_quit() |
| self.exit_mainloop() |
| self.closed = 1 |
| |
| self.unregister_editor(self.w.panes.bottom.src.source, |
| self.w.panes.bottom.src.source.file) |
| self.horpanes = self.w.panes.getpanesizes() |
| self.verpanes = self.w.panes.browserpanes.getpanesizes() |
| self.bounds = self.w.getbounds() |
| prefs = W.getapplication().getprefs() |
| prefs.debugger.breaks = self.breaks |
| prefs.debugger.windowsettings = self.bounds, self.horpanes, self.verpanes |
| prefs.debugger.tracemagic = self.tracemagic |
| prefs.save() |
| |
| # stack list callback |
| |
| def do_stack(self, isdbl): |
| sel = self.w.panes.browserpanes.stacklist.stack.getselection() |
| if isdbl: |
| if sel: |
| frame, lineno = self.stack[sel[0] + 1] |
| filename = frame.f_code.co_filename |
| editor = self.w._parentwindow.parent.openscript(filename, lineno) |
| if self.breaks.has_key(filename): |
| editor.showbreakpoints(1) |
| else: |
| if sel and sel <> self.laststacksel: |
| self.showframe(sel[0] + 1) |
| self.laststacksel = sel |
| |
| def geteditor(self, filename): |
| if filename[:1] == '<' and filename[-1:] == '>': |
| editor = W.getapplication().getscript(filename[1:-1]) |
| else: |
| editor = W.getapplication().getscript(filename) |
| return editor |
| |
| # button callbacks |
| |
| def do_run(self): |
| self.running() |
| self.set_continue() |
| self.exit_mainloop() |
| |
| def do_stop(self): |
| self.set_step() |
| |
| def do_kill(self): |
| self.set_quit() |
| self.exit_mainloop() |
| self.resetwidgets() |
| |
| def do_step(self): |
| self.running() |
| self.set_next(self.curframe) |
| self.exit_mainloop() |
| |
| def do_stepin(self): |
| self.running() |
| self.set_step() |
| self.exit_mainloop() |
| |
| def do_stepout(self): |
| self.running() |
| self.set_return(self.curframe) |
| self.exit_mainloop() |
| |
| def running(self): |
| W.SetCursor('watch') |
| self.reason = 'Running\xc9' |
| self.setstate('running') |
| #self.w.panes.bottom.src.source.set('') |
| #self.w.panes.browserpanes.stacklist.stack.set([]) |
| #self.w.panes.browserpanes.locals.browser.set({}) |
| #self.w.panes.browserpanes.globals.browser.set({}) |
| |
| def exit_mainloop(self): |
| self.w.parent.debugger_quitting = 1 |
| |
| # |
| |
| def showframe(self, stackindex): |
| (frame, lineno) = self.stack[stackindex] |
| W.SetCursor('watch') |
| filename = frame.f_code.co_filename |
| if filename <> self.file: |
| editor = self.geteditor(filename) |
| if editor: |
| self.w.panes.bottom.src.source.set(editor.get(), filename) |
| else: |
| try: |
| f = open(filename, 'rU') |
| data = f.read() |
| f.close() |
| except IOError: |
| if filename[-3:] == '.py': |
| import imp |
| modname = os.path.basename(filename)[:-3] |
| try: |
| f, filename, (suff, mode, dummy) = imp.find_module(modname) |
| except ImportError: |
| self.w.panes.bottom.src.source.set("can't find file") |
| else: |
| if f: |
| f.close() |
| if f and suff == '.py': |
| f = open(filename, 'rU') |
| data = f.read() |
| f.close() |
| self.w.panes.bottom.src.source.set(data, filename) |
| else: |
| self.w.panes.bottom.src.source.set("can't find file") |
| else: |
| self.w.panes.bottom.src.source.set("can't find file") |
| else: |
| data = data.replace('\n', '\r') |
| self.w.panes.bottom.src.source.set(data, filename) |
| self.file = filename |
| self.w.panes.bottom.srctitle.set('Source: ' + filename + ((lineno > 0) and (' (line %d)' % lineno) or ' ')) |
| self.goto_line(lineno) |
| self.lineno = lineno |
| self.showvars((frame, lineno)) |
| |
| def showvars(self, (frame, lineno)): |
| if frame.f_locals is not frame.f_globals: |
| locals = frame.f_locals |
| else: |
| locals = {'Same as Globals':''} |
| filteredlocals = {} |
| for key, value in locals.items(): |
| # empty key is magic for Python 1.4; '.' is magic for 1.5... |
| if not key or key[0] <> '.': |
| filteredlocals[key] = value |
| self.w.panes.browserpanes.locals.browser.set(filteredlocals) |
| self.w.panes.browserpanes.globals.browser.set(frame.f_globals) |
| |
| def showstack(self, stackindex): |
| stack = [] |
| for frame, lineno in self.stack[1:]: |
| filename = frame.f_code.co_filename |
| try: |
| filename = _filenames[filename] |
| except KeyError: |
| if filename[:1] + filename[-1:] <> '<>': |
| filename = os.path.basename(filename) |
| _filenames[frame.f_code.co_filename] = filename |
| funcname = frame.f_code.co_name |
| if funcname == '?': |
| funcname = '<toplevel>' |
| stack.append(filename + ': ' + funcname) |
| if stack <> self.laststack: |
| self.w.panes.browserpanes.stacklist.stack.set(stack) |
| self.laststack = stack |
| sel = [stackindex - 1] |
| self.w.panes.browserpanes.stacklist.stack.setselection(sel) |
| self.laststacksel = sel |
| |
| def goto_line(self, lineno): |
| if lineno > 0: |
| self.w.panes.bottom.src.source.selectline(lineno - 1) |
| else: |
| self.w.panes.bottom.src.source.setselection(0, 0) |
| |
| # bdb entry points |
| |
| # def user_call(self, frame, argument_list): |
| # self.reason = 'Calling' |
| # self.interaction(frame, None) |
| |
| def user_line(self, frame): |
| # This function is called when we stop or break at this line |
| self.reason = 'Stopped' |
| self.interaction(frame, None) |
| |
| def user_return(self, frame, return_value): |
| # This function is called when a return trap is set here |
| fname = frame.f_code.co_name |
| if fname <> '?': |
| self.reason = 'Returning from %s()' % frame.f_code.co_name |
| frame.f_locals['__return__'] = return_value |
| elif frame.f_back is self.botframe: |
| self.reason = 'Done' |
| else: |
| self.reason = 'Returning' |
| self.interaction(frame, None, 1) |
| |
| def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): |
| # This function is called when we stop or break at this line |
| self.reason = self.formatexception(exc_type, exc_value) |
| self.interaction(frame, exc_traceback) |
| |
| def formatexception(self, exc_type, exc_value): |
| if exc_type == SyntaxError: |
| try: |
| value, (filename, lineno, charno, line) = exc_value |
| except: |
| pass |
| else: |
| return str(exc_type) + ': ' + str(value) |
| if type(exc_type) == types.ClassType: |
| nice = exc_type.__name__ |
| else: |
| nice = str(exc_type) |
| value = str(exc_value) |
| if exc_value and value: |
| nice = nice + ": " + value |
| return nice |
| |
| def forget(self): |
| self.stack = [] |
| self.curindex = 0 |
| self.curframe = None |
| |
| def setup(self, f, t, isreturning = 0): |
| self.forget() |
| self.stack, self.curindex = self.get_stack(f, t) |
| self.curframe = self.stack[self.curindex - isreturning][0] |
| |
| def interaction(self, frame, traceback, isreturning = 0): |
| saveport = Qd.GetPort() |
| self.w.select() |
| try: |
| self.setup(frame, traceback, isreturning) |
| self.setstate('stopped') |
| stackindex = self.curindex |
| if isreturning: |
| if frame.f_back is not self.botframe: |
| stackindex = stackindex - 1 |
| self.showstack(stackindex) |
| self.showframe(stackindex) |
| self.w.parent.debugger_mainloop() |
| self.forget() |
| finally: |
| Qd.SetPort(saveport) |
| |
| # bdb customization |
| |
| def trace_dispatch(self, frame, event, arg, TickCount = Evt.TickCount): |
| if TickCount() - self.tracingmonitortime > 15: |
| self.tracingmonitortime = TickCount() |
| self.w.panes.bottom.tracingmonitor.toggle() |
| try: |
| try: |
| if hasattr(MacOS, 'EnableAppswitch'): |
| MacOS.EnableAppswitch(0) |
| if self.quitting: |
| # returning None is not enough, a former BdbQuit exception |
| # might have been eaten by the print statement |
| raise bdb.BdbQuit |
| if event == 'line': |
| return self.dispatch_line(frame) |
| if event == 'call': |
| return self.dispatch_call(frame, arg) |
| if event == 'return': |
| return self.dispatch_return(frame, arg) |
| if event == 'exception': |
| return self.dispatch_exception(frame, arg) |
| print 'bdb.Bdb.dispatch: unknown debugging event:', `event` |
| return self.trace_dispatch |
| finally: |
| if hasattr(MacOS, 'EnableAppswitch'): |
| MacOS.EnableAppswitch(-1) |
| except KeyboardInterrupt: |
| self.set_step() |
| return self.trace_dispatch |
| except bdb.BdbQuit: |
| if self.continuewithoutdebugger: |
| self.clear_tracefuncs() |
| return |
| else: |
| raise bdb.BdbQuit |
| except: |
| print 'XXX Exception during debugger interaction.', \ |
| self.formatexception(sys.exc_type, sys.exc_value) |
| import traceback |
| traceback.print_exc() |
| return self.trace_dispatch |
| |
| def dispatch_call(self, frame, arg): |
| if not self.tracemagic and \ |
| frame.f_code.co_name[:2] == '__' == frame.f_code.co_name[-2:] and \ |
| frame.f_code.co_name <> '__init__': |
| return |
| if self.botframe is None: |
| # First call of dispatch since reset() |
| self.botframe = frame.f_back # xxx !!! added f_back |
| return self.trace_dispatch |
| if not (self.stop_here(frame) or self.break_anywhere(frame)): |
| # No need to trace this function |
| return # None |
| self.user_call(frame, arg) |
| if self.quitting: |
| raise bdb.BdbQuit |
| return self.trace_dispatch |
| |
| def set_continue(self): |
| # Don't stop except at breakpoints or when finished |
| self.stopframe = self.botframe |
| self.returnframe = None |
| self.quitting = 0 |
| # unlike in bdb/pdb, there's a chance that breakpoints change |
| # *while* a program (this program ;-) is running. It's actually quite likely. |
| # So we don't delete frame.f_trace until the bottom frame if there are no breakpoints. |
| |
| def set_break(self, filename, lineno): |
| if not self.breaks.has_key(filename): |
| self.breaks[filename] = [] |
| list = self.breaks[filename] |
| if lineno in list: |
| return 'There is already a breakpoint there!' |
| list.append(lineno) |
| list.sort() # I want to keep them neatly sorted; easier for drawing |
| if hasattr(bdb, "Breakpoint"): |
| # 1.5.2b1 specific |
| bp = bdb.Breakpoint(filename, lineno, 0, None) |
| self.update_breaks(filename) |
| |
| def clear_break(self, filename, lineno): |
| bdb.Bdb.clear_break(self, filename, lineno) |
| self.update_breaks(filename) |
| |
| def clear_all_file_breaks(self, filename): |
| bdb.Bdb.clear_all_file_breaks(self, filename) |
| self.update_breaks(filename) |
| |
| def clear_all_breaks(self): |
| bdb.Bdb.clear_all_breaks(self) |
| for editors in self.editors.values(): |
| for editor in editors: |
| editor.drawbreakpoints() |
| |
| # special |
| |
| def toggle_break(self, filename, lineno): |
| if self.get_break(filename, lineno): |
| self.clear_break(filename, lineno) |
| else: |
| self.set_break(filename, lineno) |
| |
| def clear_breaks_above(self, filename, above): |
| if not self.breaks.has_key(filename): |
| return 'There are no breakpoints in that file!' |
| for lineno in self.breaks[filename][:]: |
| if lineno > above: |
| self.breaks[filename].remove(lineno) |
| if not self.breaks[filename]: |
| del self.breaks[filename] |
| |
| # editor stuff |
| |
| def update_breaks(self, filename): |
| if self.breaksviewer: |
| self.breaksviewer.update() |
| if self.editors.has_key(filename): |
| for editor in self.editors[filename]: |
| if editor._debugger: # XXX |
| editor.drawbreakpoints() |
| else: |
| print 'xxx dead editor!' |
| |
| def update_allbreaks(self): |
| if self.breaksviewer: |
| self.breaksviewer.update() |
| for filename in self.breaks.keys(): |
| if self.editors.has_key(filename): |
| for editor in self.editors[filename]: |
| if editor._debugger: # XXX |
| editor.drawbreakpoints() |
| else: |
| print 'xxx dead editor!' |
| |
| def register_editor(self, editor, filename): |
| if not filename: |
| return |
| if not self.editors.has_key(filename): |
| self.editors[filename] = [editor] |
| elif editor not in self.editors[filename]: |
| self.editors[filename].append(editor) |
| |
| def unregister_editor(self, editor, filename): |
| if not filename: |
| return |
| try: |
| self.editors[filename].remove(editor) |
| if not self.editors[filename]: |
| del self.editors[filename] |
| # if this was an untitled window, clear the breaks. |
| if filename[:1] == '<' and filename[-1:] == '>' and \ |
| self.breaks.has_key(filename): |
| self.clear_all_file_breaks(filename) |
| except (KeyError, ValueError): |
| pass |
| |
| |
| class SourceViewer(W.PyEditor): |
| |
| def __init__(self, *args, **kwargs): |
| apply(W.PyEditor.__init__, (self,) + args, kwargs) |
| self.bind('<click>', self.clickintercept) |
| |
| def clickintercept(self, point, modifiers): |
| if self._parentwindow._currentwidget <> self and not self.pt_in_breaks(point): |
| self._parentwindow.xxx___select(self) |
| return 1 |
| |
| def _getviewrect(self): |
| l, t, r, b = self._bounds |
| if self._debugger: |
| return (l + 12, t + 2, r - 1, b - 2) |
| else: |
| return (l + 5, t + 2, r - 1, b - 2) |
| |
| def select(self, onoff, isclick = 0): |
| if W.SelectableWidget.select(self, onoff): |
| return |
| self.SetPort() |
| #if onoff: |
| # self.ted.WEActivate() |
| #else: |
| # self.ted.WEDeactivate() |
| self.drawselframe(onoff) |
| |
| def drawselframe(self, onoff): |
| pass |
| |
| |
| class BreakpointsViewer: |
| |
| def __init__(self, debugger): |
| self.debugger = debugger |
| self.w = W.Window((300, 250), 'Breakpoints', minsize = (200, 200)) |
| self.w.panes = W.HorizontalPanes((8, 8, -8, -32), (0.3, 0.7)) |
| self.w.panes.files = W.List(None, callback = self.filehit) #, flags = Lists.lOnlyOne) |
| self.w.panes.gr = W.Group(None) |
| self.w.panes.gr.breaks = W.List((0, 0, -130, 0), callback = self.linehit) #, flags = Lists.lOnlyOne) |
| self.w.panes.gr.openbutton = W.Button((-80, 4, 0, 16), 'View\xc9', self.openbuttonhit) |
| self.w.panes.gr.deletebutton = W.Button((-80, 28, 0, 16), 'Delete', self.deletebuttonhit) |
| |
| self.w.bind('<close>', self.close) |
| self.w.bind('backspace', self.w.panes.gr.deletebutton.push) |
| |
| self.setup() |
| self.w.open() |
| self.w.panes.gr.openbutton.enable(0) |
| self.w.panes.gr.deletebutton.enable(0) |
| self.curfile = None |
| |
| def deletebuttonhit(self): |
| if self.w._currentwidget == self.w.panes.files: |
| self.del_filename() |
| else: |
| self.del_number() |
| self.checkbuttons() |
| |
| def del_number(self): |
| if self.curfile is None: |
| return |
| sel = self.w.panes.gr.breaks.getselectedobjects() |
| for lineno in sel: |
| self.debugger.clear_break(self.curfile, lineno) |
| |
| def del_filename(self): |
| sel = self.w.panes.files.getselectedobjects() |
| for filename in sel: |
| self.debugger.clear_all_file_breaks(filename) |
| self.debugger.update_allbreaks() |
| |
| def setup(self): |
| files = self.debugger.breaks.keys() |
| files.sort() |
| self.w.panes.files.set(files) |
| |
| def close(self): |
| self.debugger.breaksviewer = None |
| self.debugger = None |
| |
| def update(self): |
| sel = self.w.panes.files.getselectedobjects() |
| self.setup() |
| self.w.panes.files.setselectedobjects(sel) |
| sel = self.w.panes.files.getselection() |
| if len(sel) == 0 and self.curfile: |
| self.w.panes.files.setselectedobjects([self.curfile]) |
| self.filehit(0) |
| |
| def select(self): |
| self.w.select() |
| |
| def selectfile(self, file): |
| self.w.panes.files.setselectedobjects([file]) |
| self.filehit(0) |
| |
| def openbuttonhit(self): |
| self.filehit(1) |
| |
| def filehit(self, isdbl): |
| sel = self.w.panes.files.getselectedobjects() |
| if isdbl: |
| for filename in sel: |
| lineno = None |
| if filename == self.curfile: |
| linesel = self.w.panes.gr.breaks.getselectedobjects() |
| if linesel: |
| lineno = linesel[-1] |
| elif self.w.panes.gr.breaks: |
| lineno = self.w.panes.gr.breaks[0] |
| editor = self.w._parentwindow.parent.openscript(filename, lineno) |
| editor.showbreakpoints(1) |
| return |
| if len(sel) == 1: |
| file = sel[0] |
| filebreaks = self.debugger.breaks[file][:] |
| if self.curfile == file: |
| linesel = self.w.panes.gr.breaks.getselectedobjects() |
| self.w.panes.gr.breaks.set(filebreaks) |
| if self.curfile == file: |
| self.w.panes.gr.breaks.setselectedobjects(linesel) |
| self.curfile = file |
| else: |
| if len(sel) <> 0: |
| self.curfile = None |
| self.w.panes.gr.breaks.set([]) |
| self.checkbuttons() |
| |
| def linehit(self, isdbl): |
| if isdbl: |
| files = self.w.panes.files.getselectedobjects() |
| if len(files) <> 1: |
| return |
| filename = files[0] |
| linenos = self.w.panes.gr.breaks.getselectedobjects() |
| if not linenos: |
| return |
| lineno = linenos[-1] |
| editor = self.w._parentwindow.parent.openscript(filename, lineno) |
| editor.showbreakpoints(1) |
| self.checkbuttons() |
| |
| def checkbuttons(self): |
| if self.w.panes.files.getselection(): |
| self.w.panes.gr.openbutton.enable(1) |
| self.w._parentwindow.setdefaultbutton(self.w.panes.gr.openbutton) |
| if self.w._currentwidget == self.w.panes.files: |
| if self.w.panes.files.getselection(): |
| self.w.panes.gr.deletebutton.enable(1) |
| else: |
| self.w.panes.gr.deletebutton.enable(0) |
| else: |
| if self.w.panes.gr.breaks.getselection(): |
| self.w.panes.gr.deletebutton.enable(1) |
| else: |
| self.w.panes.gr.deletebutton.enable(0) |
| else: |
| self.w.panes.gr.openbutton.enable(0) |
| self.w.panes.gr.deletebutton.enable(0) |
| |
| |
| class TracingMonitor(W.Widget): |
| |
| def __init__(self, *args, **kwargs): |
| apply(W.Widget.__init__, (self,) + args, kwargs) |
| self.state = 0 |
| |
| def toggle(self): |
| if hasattr(self, "_parentwindow") and self._parentwindow is not None: |
| self.state = self.state % 2 + 1 |
| port = Qd.GetPort() |
| self.SetPort() |
| self.draw() |
| Qd.SetPort(port) |
| |
| def reset(self): |
| if self._parentwindow: |
| self.state = 0 |
| port = Qd.GetPort() |
| self.SetPort() |
| self.draw() |
| Qd.SetPort(port) |
| |
| def draw(self, visRgn = None): |
| if self.state == 2: |
| Qd.PaintOval(self._bounds) |
| else: |
| Qd.EraseOval(self._bounds) |
| |
| |
| # convenience funcs |
| |
| def postmortem(exc_type, exc_value, tb): |
| d = getdebugger() |
| d.postmortem(exc_type, exc_value, tb) |
| |
| def start(bottomframe = None): |
| d = getdebugger() |
| d.start(bottomframe) |
| |
| def startfromhere(): |
| d = getdebugger() |
| try: |
| raise 'spam' |
| except: |
| frame = sys.exc_traceback.tb_frame.f_back |
| d.start(frame) |
| |
| def startfrombottom(): |
| d = getdebugger() |
| d.start(_getbottomframe(), 1) |
| |
| def stop(): |
| d = getdebugger() |
| d.stop() |
| |
| def cont(): |
| sys.settrace(None) |
| d = getdebugger() |
| d.set_continue_without_debugger() |
| |
| def _getbottomframe(): |
| try: |
| raise 'spam' |
| except: |
| pass |
| frame = sys.exc_traceback.tb_frame |
| while 1: |
| if frame.f_code.co_name == 'mainloop' or frame.f_back is None: |
| break |
| frame = frame.f_back |
| return frame |
| |
| _debugger = None |
| |
| def getdebugger(): |
| if not __debug__: |
| raise W.AlertError, "Can't debug in \"Optimize bytecode\" mode.\r(see \"Default startup options\" in EditPythonPreferences)" |
| global _debugger |
| if _debugger is None: |
| _debugger = Debugger() |
| return _debugger |