home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Mac / Tools / IDE / PyDebugger.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  24.7 KB  |  888 lines

  1. import sys
  2. import bdb
  3. import types
  4. import os
  5.  
  6. import W
  7. import WASTEconst
  8. import PyBrowser
  9. import Qd
  10. import Evt
  11. import Lists
  12. import MacOS
  13. _filenames = {}
  14.  
  15. SIMPLE_TYPES = (
  16.     types.NoneType,
  17.     types.IntType,
  18.     types.LongType,
  19.     types.FloatType,
  20.     types.ComplexType,
  21.     types.StringType
  22. )
  23.  
  24.  
  25. class Debugger(bdb.Bdb):
  26.     
  27.     def __init__(self, title = 'Debugger'):
  28.         bdb.Bdb.__init__(self)
  29.         self.closed = 1
  30.         self.title = title
  31.         self.breaksviewer = None
  32.         self.reset()
  33.         self.tracing = 0
  34.         self.tracingmonitortime = Evt.TickCount()
  35.         self.editors = {}
  36.         
  37.         prefs = W.getapplication().getprefs()
  38.         if prefs.debugger:
  39.             for file, breaks in prefs.debugger.breaks.items():
  40.                 for b in breaks:
  41.                     self.set_break(file, b)
  42.             self.bounds, self.horpanes, self.verpanes = prefs.debugger.windowsettings
  43.             self.tracemagic = prefs.debugger.tracemagic
  44.         else:
  45.             self.breaks = {}
  46.             self.horpanes = (0.4, 0.6)
  47.             self.verpanes = (0.3, 0.35, 0.35)
  48.             self.bounds = (600, 400)
  49.             self.tracemagic = 0
  50.         self.laststacksel = None
  51.     
  52.     def reset(self):
  53.         self.currentframe = None
  54.         self.file = None
  55.         self.laststack = None
  56.         self.reason = 'Not running'
  57.         self.continuewithoutdebugger = 0
  58.         bdb.Bdb.reset(self)
  59.         self.forget()
  60.     
  61.     def start(self, bottomframe = None, running = 0):
  62.         W.getapplication().DebuggerQuit = bdb.BdbQuit
  63.         import Menu
  64.         Menu.HiliteMenu(0)
  65.         if self.closed:
  66.             self.setupwidgets(self.title)
  67.             self.closed = 0
  68.         if not self.w.parent.debugger_quitting:
  69.             self.w.select()
  70.             raise W.AlertError, 'There is another debugger session busy.'
  71.         self.reset()
  72.         self.botframe = bottomframe
  73.         if running:
  74.             self.set_continue()
  75.             self.reason = 'Running…'
  76.             self.setstate('running')
  77.         else:
  78.             self.set_step()
  79.             self.reason = 'stopped'
  80.             self.setstate('stopped')
  81.         sys.settrace(self.trace_dispatch)
  82.     
  83.     def stop(self):
  84.         self.set_quit()
  85.         if self.w.parent:
  86.             self.exit_mainloop()
  87.             self.resetwidgets()
  88.     
  89.     def set_continue_without_debugger(self):
  90.         sys.settrace(None)
  91.         self.set_quit()
  92.         self.clear_tracefuncs()
  93.         self.continuewithoutdebugger = 1
  94.         if hasattr(self, "w") and self.w.parent:
  95.             self.exit_mainloop()
  96.             self.resetwidgets()
  97.     
  98.     def clear_tracefuncs(self):
  99.         try:
  100.             raise 'spam'
  101.         except:
  102.             pass
  103.         frame = sys.exc_traceback.tb_frame
  104.         while frame is not None:
  105.             del frame.f_trace
  106.             frame = frame.f_back
  107.     
  108.     def postmortem(self, exc_type, exc_value, traceback):
  109.         if self.closed:
  110.             self.setupwidgets(self.title)
  111.             self.closed = 0
  112.         if not self.w.parent.debugger_quitting:
  113.             raise W.AlertError, 'There is another debugger session busy.'
  114.         self.reset()
  115.         if traceback:
  116.             self.botframe = traceback.tb_frame
  117.             while traceback.tb_next <> None:
  118.                 traceback = traceback.tb_next
  119.             frame = traceback.tb_frame
  120.         else:
  121.             self.botframe = None
  122.             frame = None
  123.         self.w.panes.bottom.buttons.killbutton.enable(1)
  124.         self.reason = '(dead) ' + self.formatexception(exc_type, exc_value)
  125.         self.w.select()
  126.         self.setup(frame, traceback)
  127.         self.setstate('dead')
  128.         self.showstack(self.curindex)
  129.         self.showframe(self.curindex)
  130.     
  131.     def setupwidgets(self, title):
  132.         self.w = w = W.Window(self.bounds, title, minsize = (500, 300))
  133.         
  134.         w.panes = W.HorizontalPanes((8, 4, -8, -8), self.horpanes)
  135.         
  136.         w.panes.browserpanes = browserpanes = W.VerticalPanes(None, self.verpanes)
  137.         
  138.         browserpanes.stacklist = W.Group(None)
  139.         browserpanes.stacklist.title = W.TextBox((4, 0, 0, 12), 'Stack')
  140.         browserpanes.stacklist.stack = W.List((0, 16, 0, 0), callback = self.do_stack, flags = Lists.lOnlyOne)
  141.         
  142.         browserpanes.locals = W.Group(None)
  143.         browserpanes.locals.title = W.TextBox((4, 0, 0, 12), 'Local variables')
  144.         browserpanes.locals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0))
  145.         
  146.         browserpanes.globals = W.Group(None)
  147.         browserpanes.globals.title = W.TextBox((4, 0, 0, 12), 'Global variables')
  148.         browserpanes.globals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0))
  149.         
  150.         w.panes.bottom = bottom = W.Group(None)
  151.         bottom.src = src = W.Group((0, 52, 0, 0))
  152.         source = SourceViewer((1, 1, -15, -15), readonly = 1, debugger = self)
  153.         src.optionsmenu = W.PopupMenu((-16, 0, 16, 16), [])
  154.         src.optionsmenu.bind('<click>', self.makeoptionsmenu)
  155.         
  156.         src._barx = W.Scrollbar((0, -16, -15, 16), source.hscroll, max = 32767)
  157.         src._bary = W.Scrollbar((-16, 15, 16, -15), source.vscroll, max = 32767)
  158.         src.source = source
  159.         src.frame = W.Frame((0, 0, -15, -15))
  160.         
  161.         bottom.tracingmonitor = TracingMonitor((0, 23, 6, 6))
  162.         bottom.state = W.TextBox((12, 20, 0, 16), self.reason)
  163.         
  164.         bottom.srctitle = W.TextBox((12, 36, 0, 14))
  165.         bottom.buttons = buttons = W.Group((12, 0, 0, 16))
  166.         
  167.         buttons.runbutton = W.Button((0, 0, 50, 16), "Run", self.do_run)
  168.         buttons.stopbutton = W.Button((58, 0, 50, 16), "Stop", self.do_stop)
  169.         buttons.killbutton = W.Button((116, 0, 50, 16), "Kill", self.do_kill)
  170.         buttons.line = W.VerticalLine((173, 0, 0, 0))
  171.         buttons.stepbutton = W.Button((181, 0, 50, 16), "Step", self.do_step)
  172.         buttons.stepinbutton = W.Button((239, 0, 50, 16), "Step in", self.do_stepin)
  173.         buttons.stepoutbutton = W.Button((297, 0, 50, 16), "Step out", self.do_stepout)
  174.         
  175.         w.bind('cmdr', buttons.runbutton.push)
  176.         w.bind('cmd.', buttons.stopbutton.push)
  177.         w.bind('cmdk', buttons.killbutton.push)
  178.         w.bind('cmds', buttons.stepbutton.push)
  179.         w.bind('cmdt', buttons.stepinbutton.push)
  180.         w.bind('cmdu', buttons.stepoutbutton.push)
  181.         
  182.         w.bind('<close>', self.close)
  183.         
  184.         w.open()
  185.         w.xxx___select(w.panes.bottom.src.source)
  186.     
  187.     def makeoptionsmenu(self):
  188.         options = [('Clear breakpoints', self.w.panes.bottom.src.source.clearbreakpoints), 
  189.                 ('Clear all breakpoints', self.clear_all_breaks),
  190.                 ('Edit breakpoints…', self.edit_breaks), '-',
  191.                 (self.tracemagic and 
  192.                     'Disable __magic__ tracing' or 'Enable __magic__ tracing', self.togglemagic)]
  193.         self.w.panes.bottom.src.optionsmenu.set(options)
  194.     
  195.     def edit_breaks(self):
  196.         if self.breaksviewer:
  197.             self.breaksviewer.select()
  198.         else:
  199.             self.breaksviewer = BreakpointsViewer(self)
  200.     
  201.     def togglemagic(self):
  202.         self.tracemagic = not self.tracemagic
  203.     
  204.     def setstate(self, state):
  205.         self.w.panes.bottom.tracingmonitor.reset()
  206.         self.w.panes.bottom.state.set(self.reason)
  207.         buttons = self.w.panes.bottom.buttons
  208.         if state == 'stopped':
  209.             buttons.runbutton.enable(1)
  210.             buttons.stopbutton.enable(0)
  211.             buttons.killbutton.enable(1)
  212.             buttons.stepbutton.enable(1)
  213.             buttons.stepinbutton.enable(1)
  214.             buttons.stepoutbutton.enable(1)
  215.         elif state == 'running':
  216.             buttons.runbutton.enable(0)
  217.             buttons.stopbutton.enable(1)
  218.             buttons.killbutton.enable(1)
  219.             buttons.stepbutton.enable(0)
  220.             buttons.stepinbutton.enable(0)
  221.             buttons.stepoutbutton.enable(0)
  222.         elif state == 'idle':
  223.             buttons.runbutton.enable(0)
  224.             buttons.stopbutton.enable(0)
  225.             buttons.killbutton.enable(0)
  226.             buttons.stepbutton.enable(0)
  227.             buttons.stepinbutton.enable(0)
  228.             buttons.stepoutbutton.enable(0)
  229.         elif state == 'dead':
  230.             buttons.runbutton.enable(0)
  231.             buttons.stopbutton.enable(0)
  232.             buttons.killbutton.enable(1)
  233.             buttons.stepbutton.enable(0)
  234.             buttons.stepinbutton.enable(0)
  235.             buttons.stepoutbutton.enable(0)
  236.         else:
  237.             print 'unknown state:', state
  238.     
  239.     def resetwidgets(self):
  240.         self.reason = ''
  241.         self.w.panes.bottom.srctitle.set('')
  242.         self.w.panes.bottom.src.source.set('')
  243.         self.w.panes.browserpanes.stacklist.stack.set([])
  244.         self.w.panes.browserpanes.locals.browser.set({})
  245.         self.w.panes.browserpanes.globals.browser.set({})
  246.         self.setstate('idle')
  247.     
  248.     # W callbacks
  249.     
  250.     def close(self):
  251.         self.set_quit()
  252.         self.exit_mainloop()
  253.         self.closed = 1
  254.         
  255.         self.unregister_editor(self.w.panes.bottom.src.source, 
  256.                 self.w.panes.bottom.src.source.file)
  257.         self.horpanes = self.w.panes.getpanesizes()
  258.         self.verpanes = self.w.panes.browserpanes.getpanesizes()
  259.         self.bounds = self.w.getbounds()
  260.         prefs = W.getapplication().getprefs()
  261.         prefs.debugger.breaks = self.breaks
  262.         prefs.debugger.windowsettings = self.bounds, self.horpanes, self.verpanes
  263.         prefs.debugger.tracemagic = self.tracemagic
  264.         prefs.save()
  265.     
  266.     # stack list callback
  267.     
  268.     def do_stack(self, isdbl):
  269.         sel = self.w.panes.browserpanes.stacklist.stack.getselection()
  270.         if isdbl:
  271.             if sel:
  272.                 frame, lineno = self.stack[sel[0] + 1]
  273.                 filename = frame.f_code.co_filename
  274.                 editor = self.w._parentwindow.parent.openscript(filename, lineno)
  275.                 if self.breaks.has_key(filename):
  276.                     editor.showbreakpoints(1)
  277.         else:
  278.             if sel and sel <> self.laststacksel:
  279.                 self.showframe(sel[0] + 1)
  280.             self.laststacksel = sel
  281.     
  282.     def geteditor(self, filename):
  283.         if filename[:1] == '<' and filename[-1:] == '>':
  284.             editor = W.getapplication().getscript(filename[1:-1])
  285.         else:
  286.             editor = W.getapplication().getscript(filename)
  287.         return editor
  288.     
  289.     # button callbacks
  290.     
  291.     def do_run(self):
  292.         self.running()
  293.         self.set_continue()
  294.         self.exit_mainloop()
  295.     
  296.     def do_stop(self):
  297.         self.set_step()
  298.     
  299.     def do_kill(self):
  300.         self.set_quit()
  301.         self.exit_mainloop()
  302.         self.resetwidgets()
  303.     
  304.     def do_step(self):
  305.         self.running()
  306.         self.set_next(self.curframe)
  307.         self.exit_mainloop()
  308.     
  309.     def do_stepin(self):
  310.         self.running()
  311.         self.set_step()
  312.         self.exit_mainloop()
  313.     
  314.     def do_stepout(self):
  315.         self.running()
  316.         self.set_return(self.curframe)
  317.         self.exit_mainloop()
  318.     
  319.     def running(self):
  320.         W.SetCursor('watch')
  321.         self.reason = 'Running…'
  322.         self.setstate('running')
  323.         #self.w.panes.bottom.src.source.set('')
  324.         #self.w.panes.browserpanes.stacklist.stack.set([])
  325.         #self.w.panes.browserpanes.locals.browser.set({})
  326.         #self.w.panes.browserpanes.globals.browser.set({})
  327.     
  328.     def exit_mainloop(self):
  329.         self.w.parent.debugger_quitting = 1
  330.     
  331.     #
  332.     
  333.     def showframe(self, stackindex):
  334.         (frame, lineno) = self.stack[stackindex]
  335.         W.SetCursor('watch')
  336.         filename = frame.f_code.co_filename
  337.         if filename <> self.file:
  338.             editor = self.geteditor(filename)
  339.             if editor:
  340.                 self.w.panes.bottom.src.source.set(editor.get(), filename)
  341.             else:
  342.                 try:
  343.                     f = open(filename, 'rb')
  344.                     data = f.read()
  345.                     f.close()
  346.                 except IOError:
  347.                     if filename[-3:] == '.py':
  348.                         import imp
  349.                         modname = os.path.basename(filename)[:-3]
  350.                         try:
  351.                             f, filename, (suff, mode, dummy) = imp.find_module(modname)
  352.                         except ImportError:
  353.                             self.w.panes.bottom.src.source.set('can’t find file')
  354.                         else:
  355.                             if f:
  356.                                 f.close()
  357.                             if f and suff == '.py':
  358.                                 f = open(filename, 'rb')
  359.                                 data = f.read()
  360.                                 f.close()
  361.                                 self.w.panes.bottom.src.source.set(data, filename)
  362.                             else:
  363.                                 self.w.panes.bottom.src.source.set('can’t find file')
  364.                     else:
  365.                         self.w.panes.bottom.src.source.set('can’t find file')
  366.                 else:
  367.                     self.w.panes.bottom.src.source.set(data, filename)
  368.             self.file = filename
  369.         self.w.panes.bottom.srctitle.set('Source: ' + filename + ((lineno > 0) and (' (line %d)' % lineno) or ' '))
  370.         self.goto_line(lineno)
  371.         self.lineno = lineno
  372.         self.showvars((frame, lineno))
  373.     
  374.     def showvars(self, (frame, lineno)):
  375.         if frame.f_locals is not frame.f_globals:
  376.             locals = frame.f_locals
  377.         else:
  378.             locals = {'Same as Globals':''}
  379.         filteredlocals = {}
  380.         for key, value in locals.items():
  381.             # empty key is magic for Python 1.4; '.' is magic for 1.5...
  382.             if not key or key[0] <> '.':
  383.                 filteredlocals[key] = value
  384.         self.w.panes.browserpanes.locals.browser.set(filteredlocals)
  385.         self.w.panes.browserpanes.globals.browser.set(frame.f_globals)
  386.     
  387.     def showstack(self, stackindex):
  388.         stack = []
  389.         for frame, lineno in self.stack[1:]:
  390.             filename = frame.f_code.co_filename
  391.             try:
  392.                 filename = _filenames[filename]
  393.             except KeyError:
  394.                 if filename[:1] + filename[-1:] <> '<>':
  395.                     filename = os.path.basename(filename)
  396.                 _filenames[frame.f_code.co_filename] = filename
  397.             funcname = frame.f_code.co_name
  398.             if funcname == '?':
  399.                 funcname = '<toplevel>'
  400.             stack.append(filename + ': ' + funcname)
  401.         if stack <> self.laststack:
  402.             self.w.panes.browserpanes.stacklist.stack.set(stack)
  403.             self.laststack = stack
  404.         sel = [stackindex - 1]
  405.         self.w.panes.browserpanes.stacklist.stack.setselection(sel)
  406.         self.laststacksel = sel
  407.     
  408.     def goto_line(self, lineno):
  409.         if lineno > 0:
  410.             self.w.panes.bottom.src.source.selectline(lineno - 1)
  411.         else:
  412.             self.w.panes.bottom.src.source.setselection(0, 0)
  413.     
  414.     # bdb entry points
  415.     
  416. #    def user_call(self, frame, argument_list):
  417. #        self.reason = 'Calling'
  418. #        self.interaction(frame, None)
  419.     
  420.     def user_line(self, frame):
  421.         # This function is called when we stop or break at this line
  422.         self.reason = 'Stopped'
  423.         self.interaction(frame, None)
  424.     
  425.     def user_return(self, frame, return_value):
  426.         # This function is called when a return trap is set here
  427.         fname = frame.f_code.co_name
  428.         if fname <> '?':
  429.             self.reason = 'Returning from %s()' % frame.f_code.co_name
  430.             frame.f_locals['__return__'] = return_value
  431.         elif frame.f_back is self.botframe:
  432.             self.reason = 'Done'
  433.         else:
  434.             self.reason = 'Returning'
  435.         self.interaction(frame, None, 1)
  436.     
  437.     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
  438.         # This function is called when we stop or break at this line
  439.         self.reason = self.formatexception(exc_type, exc_value)
  440.         self.interaction(frame, exc_traceback)
  441.     
  442.     def formatexception(self, exc_type, exc_value):
  443.         if exc_type == SyntaxError:
  444.             try:
  445.                 value, (filename, lineno, charno, line) = exc_value
  446.             except:
  447.                 pass
  448.             else:
  449.                 return str(exc_type) + ': ' + str(value)
  450.         if type(exc_type) == types.ClassType:
  451.             nice = exc_type.__name__
  452.         else:
  453.             nice = str(exc_type)
  454.         value = str(exc_value)
  455.         if exc_value and value:
  456.             nice = nice + ": " + value
  457.         return nice
  458.     
  459.     def forget(self):
  460.         self.stack = []
  461.         self.curindex = 0
  462.         self.curframe = None
  463.     
  464.     def setup(self, f, t, isreturning = 0):
  465.         self.forget()
  466.         self.stack, self.curindex = self.get_stack(f, t)
  467.         self.curframe = self.stack[self.curindex - isreturning][0]
  468.     
  469.     def interaction(self, frame, traceback, isreturning = 0):
  470.         saveport = Qd.GetPort()
  471.         self.w.select()
  472.         try:
  473.             self.setup(frame, traceback, isreturning)
  474.             self.setstate('stopped')
  475.             stackindex = self.curindex
  476.             if isreturning:
  477.                 if frame.f_back is not self.botframe:
  478.                     stackindex = stackindex - 1
  479.             self.showstack(stackindex)
  480.             self.showframe(stackindex)
  481.             self.w.parent.debugger_mainloop()
  482.             self.forget()
  483.         finally:
  484.             Qd.SetPort(saveport)
  485.     
  486.     # bdb customization
  487.     
  488.     def trace_dispatch(self, frame, event, arg, TickCount = Evt.TickCount):
  489.         if TickCount() - self.tracingmonitortime > 15:
  490.             self.tracingmonitortime = TickCount()
  491.             self.w.panes.bottom.tracingmonitor.toggle()
  492.         try:
  493.             try:
  494.                 MacOS.EnableAppswitch(0)
  495.                 if self.quitting:
  496.                     # returning None is not enough, a former BdbQuit exception
  497.                     # might have been eaten by the print statement
  498.                     raise bdb.BdbQuit
  499.                 if event == 'line':
  500.                     return self.dispatch_line(frame)
  501.                 if event == 'call':
  502.                     return self.dispatch_call(frame, arg)
  503.                 if event == 'return':
  504.                     return self.dispatch_return(frame, arg)
  505.                 if event == 'exception':
  506.                     return self.dispatch_exception(frame, arg)
  507.                 print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
  508.                 return self.trace_dispatch
  509.             finally:
  510.                 MacOS.EnableAppswitch(-1)
  511.         except KeyboardInterrupt:
  512.             self.set_step()
  513.             return self.trace_dispatch
  514.         except bdb.BdbQuit:
  515.             if self.continuewithoutdebugger:
  516.                 self.clear_tracefuncs()
  517.                 return
  518.             else:
  519.                 raise bdb.BdbQuit
  520.         except:
  521.             print 'XXX Exception during debugger interaction.', \
  522.                     self.formatexception(sys.exc_type, sys.exc_value)
  523.             import traceback
  524.             traceback.print_exc()
  525.             return self.trace_dispatch
  526.     
  527.     def dispatch_call(self, frame, arg):
  528.         if not self.tracemagic and \
  529.                 frame.f_code.co_name[:2] == '__' == frame.f_code.co_name[-2:] and \
  530.                 frame.f_code.co_name <> '__init__':
  531.             return
  532.         if self.botframe is None:
  533.             # First call of dispatch since reset()
  534.             self.botframe = frame.f_back    # xxx !!! added f_back
  535.             return self.trace_dispatch
  536.         if not (self.stop_here(frame) or self.break_anywhere(frame)):
  537.             # No need to trace this function
  538.             return # None
  539.         self.user_call(frame, arg)
  540.         if self.quitting:
  541.             raise bdb.BdbQuit
  542.         return self.trace_dispatch
  543.     
  544.     def set_continue(self):
  545.         # Don't stop except at breakpoints or when finished
  546.         self.stopframe = self.botframe
  547.         self.returnframe = None
  548.         self.quitting = 0
  549.         # unlike in bdb/pdb, there's a chance that breakpoints change 
  550.         # *while* a program (this program ;-) is running. It's actually quite likely.
  551.         # So we don't delete frame.f_trace until the bottom frame if there are no breakpoints.
  552.     
  553.     def set_break(self, filename, lineno):
  554.         if not self.breaks.has_key(filename):
  555.             self.breaks[filename] = []
  556.         list = self.breaks[filename]
  557.         if lineno in list:
  558.             return 'There is already a breakpoint there!'
  559.         list.append(lineno)
  560.         list.sort()    # I want to keep them neatly sorted; easier for drawing
  561.         if hasattr(bdb, "Breakpoint"):
  562.             # 1.5.2b1 specific
  563.             bp = bdb.Breakpoint(filename, lineno, 0, None)
  564.         self.update_breaks(filename)
  565.     
  566.     def clear_break(self, filename, lineno):
  567.         bdb.Bdb.clear_break(self, filename, lineno)
  568.         self.update_breaks(filename)
  569.     
  570.     def clear_all_file_breaks(self, filename):
  571.         bdb.Bdb.clear_all_file_breaks(self, filename)
  572.         self.update_breaks(filename)
  573.     
  574.     def clear_all_breaks(self):
  575.         bdb.Bdb.clear_all_breaks(self)
  576.         for editors in self.editors.values():
  577.             for editor in editors:
  578.                 editor.drawbreakpoints()
  579.     
  580.     # special
  581.     
  582.     def toggle_break(self, filename, lineno):
  583.         if self.get_break(filename, lineno):
  584.             self.clear_break(filename, lineno)
  585.         else:
  586.             self.set_break(filename, lineno)
  587.     
  588.     def clear_breaks_above(self, filename, above):
  589.         if not self.breaks.has_key(filename):
  590.             return 'There are no breakpoints in that file!'
  591.         for lineno in self.breaks[filename][:]:
  592.             if lineno > above:
  593.                 self.breaks[filename].remove(lineno)
  594.         if not self.breaks[filename]:
  595.             del self.breaks[filename]
  596.     
  597.     # editor stuff
  598.     
  599.     def update_breaks(self, filename):
  600.         if self.breaksviewer:
  601.             self.breaksviewer.update()
  602.         if self.editors.has_key(filename):
  603.             for editor in self.editors[filename]:
  604.                 if editor._debugger:    # XXX
  605.                     editor.drawbreakpoints()
  606.                 else:
  607.                     print 'xxx dead editor!'
  608.     
  609.     def update_allbreaks(self):
  610.         if self.breaksviewer:
  611.             self.breaksviewer.update()
  612.         for filename in self.breaks.keys():
  613.             if self.editors.has_key(filename):
  614.                 for editor in self.editors[filename]:
  615.                     if editor._debugger:    # XXX
  616.                         editor.drawbreakpoints()
  617.                     else:
  618.                         print 'xxx dead editor!'
  619.     
  620.     def register_editor(self, editor, filename):
  621.         if not filename:
  622.             return
  623.         if not self.editors.has_key(filename):
  624.             self.editors[filename] = [editor]
  625.         elif editor not in self.editors[filename]:
  626.             self.editors[filename].append(editor)
  627.     
  628.     def unregister_editor(self, editor, filename):
  629.         if not filename:
  630.             return
  631.         try:
  632.             self.editors[filename].remove(editor)
  633.             if not self.editors[filename]:
  634.                 del self.editors[filename]
  635.                 # if this was an untitled window, clear the breaks.
  636.                 if filename[:1] == '<' and filename[-1:] == '>' and \
  637.                         self.breaks.has_key(filename):
  638.                     self.clear_all_file_breaks(filename)
  639.         except (KeyError, ValueError):
  640.             pass
  641.         
  642.  
  643. class SourceViewer(W.PyEditor):
  644.     
  645.     def __init__(self, *args, **kwargs):
  646.         apply(W.PyEditor.__init__, (self,) + args, kwargs)
  647.         self.bind('<click>', self.clickintercept)
  648.     
  649.     def clickintercept(self, point, modifiers):
  650.         if self._parentwindow._currentwidget <> self and not self.pt_in_breaks(point):
  651.             self._parentwindow.xxx___select(self)
  652.             return 1
  653.     
  654.     def _getviewrect(self):
  655.         l, t, r, b = self._bounds
  656.         if self._debugger:
  657.             return (l + 12, t + 2, r - 1, b - 2)
  658.         else:
  659.             return (l + 5, t + 2, r - 1, b - 2)
  660.     
  661.     def select(self, onoff, isclick = 0):
  662.         if W.SelectableWidget.select(self, onoff):
  663.             return
  664.         self.SetPort()
  665.         #if onoff:
  666.         #    self.ted.WEActivate()
  667.         #else:
  668.         #    self.ted.WEDeactivate()
  669.         self.drawselframe(onoff)
  670.     
  671.     def drawselframe(self, onoff):
  672.         pass
  673.  
  674.  
  675. class BreakpointsViewer:
  676.     
  677.     def __init__(self, debugger):
  678.         self.debugger = debugger
  679.         import Lists
  680.         self.w = W.Window((300, 250), 'Breakpoints', minsize = (200, 200))
  681.         self.w.panes = W.HorizontalPanes((8, 8, -8, -32), (0.3, 0.7))
  682.         self.w.panes.files = W.List(None, callback = self.filehit)        #, flags = Lists.lOnlyOne)
  683.         self.w.panes.gr = W.Group(None)
  684.         self.w.panes.gr.breaks = W.List((0, 0, -130, 0), callback = self.linehit)    #, flags = Lists.lOnlyOne)
  685.         self.w.panes.gr.openbutton = W.Button((-80, 4, 0, 16), 'View…', self.openbuttonhit)
  686.         self.w.panes.gr.deletebutton = W.Button((-80, 28, 0, 16), 'Delete', self.deletebuttonhit)
  687.         
  688.         self.w.bind('<close>', self.close)
  689.         self.w.bind('backspace', self.w.panes.gr.deletebutton.push)
  690.         
  691.         self.setup()
  692.         self.w.open()
  693.         self.w.panes.gr.openbutton.enable(0)
  694.         self.w.panes.gr.deletebutton.enable(0)
  695.         self.curfile = None
  696.     
  697.     def deletebuttonhit(self):
  698.         if self.w._currentwidget == self.w.panes.files:
  699.             self.del_filename()
  700.         else:
  701.             self.del_number()
  702.         self.checkbuttons()
  703.     
  704.     def del_number(self):
  705.         if self.curfile is None:
  706.             return
  707.         sel = self.w.panes.gr.breaks.getselectedobjects()
  708.         for lineno in sel:
  709.             self.debugger.clear_break(self.curfile, lineno)
  710.     
  711.     def del_filename(self):
  712.         sel = self.w.panes.files.getselectedobjects()
  713.         for filename in sel:
  714.             self.debugger.clear_all_file_breaks(filename)
  715.         self.debugger.update_allbreaks()
  716.     
  717.     def setup(self):
  718.         files = self.debugger.breaks.keys()
  719.         files.sort()
  720.         self.w.panes.files.set(files)
  721.     
  722.     def close(self):
  723.         self.debugger.breaksviewer = None
  724.         self.debugger = None
  725.     
  726.     def update(self):
  727.         sel = self.w.panes.files.getselectedobjects()
  728.         self.setup()
  729.         self.w.panes.files.setselectedobjects(sel)
  730.         sel = self.w.panes.files.getselection()
  731.         if len(sel) == 0 and self.curfile:
  732.             self.w.panes.files.setselectedobjects([self.curfile])
  733.         self.filehit(0)
  734.     
  735.     def select(self):
  736.         self.w.select()
  737.     
  738.     def selectfile(self, file):
  739.         self.w.panes.files.setselectedobjects([file])
  740.         self.filehit(0)            
  741.     
  742.     def openbuttonhit(self):
  743.         self.filehit(1)
  744.     
  745.     def filehit(self, isdbl):
  746.         sel = self.w.panes.files.getselectedobjects()
  747.         if isdbl:
  748.             for filename in sel:
  749.                 lineno = None
  750.                 if filename == self.curfile:
  751.                     linesel = self.w.panes.gr.breaks.getselectedobjects()
  752.                     if linesel:
  753.                         lineno = linesel[-1]
  754.                     elif self.w.panes.gr.breaks:
  755.                         lineno = self.w.panes.gr.breaks[0]
  756.                 editor = self.w._parentwindow.parent.openscript(filename, lineno)
  757.                 editor.showbreakpoints(1)
  758.             return
  759.         if len(sel) == 1:
  760.             file = sel[0]
  761.             filebreaks = self.debugger.breaks[file][:]
  762.             if self.curfile == file:
  763.                 linesel = self.w.panes.gr.breaks.getselectedobjects()
  764.             self.w.panes.gr.breaks.set(filebreaks)
  765.             if self.curfile == file:
  766.                 self.w.panes.gr.breaks.setselectedobjects(linesel)
  767.             self.curfile = file
  768.         else:
  769.             if len(sel) <> 0:
  770.                 self.curfile = None
  771.             self.w.panes.gr.breaks.set([])
  772.         self.checkbuttons()
  773.     
  774.     def linehit(self, isdbl):
  775.         if isdbl:
  776.             files = self.w.panes.files.getselectedobjects()
  777.             if len(files) <> 1:
  778.                 return
  779.             filename = files[0]
  780.             linenos = self.w.panes.gr.breaks.getselectedobjects()
  781.             if not linenos:
  782.                 return
  783.             lineno = linenos[-1]
  784.             editor = self.w._parentwindow.parent.openscript(filename, lineno)
  785.             editor.showbreakpoints(1)
  786.         self.checkbuttons()
  787.     
  788.     def checkbuttons(self):
  789.         if self.w.panes.files.getselection():
  790.             self.w.panes.gr.openbutton.enable(1)
  791.             self.w._parentwindow.setdefaultbutton(self.w.panes.gr.openbutton)
  792.             if self.w._currentwidget == self.w.panes.files:
  793.                 if self.w.panes.files.getselection():
  794.                     self.w.panes.gr.deletebutton.enable(1)
  795.                 else:
  796.                     self.w.panes.gr.deletebutton.enable(0)
  797.             else:
  798.                 if self.w.panes.gr.breaks.getselection():
  799.                     self.w.panes.gr.deletebutton.enable(1)
  800.                 else:
  801.                     self.w.panes.gr.deletebutton.enable(0)
  802.         else:
  803.             self.w.panes.gr.openbutton.enable(0)
  804.             self.w.panes.gr.deletebutton.enable(0)
  805.  
  806.  
  807. class TracingMonitor(W.Widget):
  808.     
  809.     def __init__(self, *args, **kwargs):
  810.         apply(W.Widget.__init__, (self,) + args, kwargs)
  811.         self.state = 0
  812.     
  813.     def toggle(self):
  814.         if hasattr(self, "_parentwindow") and self._parentwindow is not None:
  815.             self.state = self.state % 2 + 1
  816.             port = Qd.GetPort()
  817.             self.SetPort()
  818.             self.draw()
  819.             Qd.SetPort(port)
  820.     
  821.     def reset(self):
  822.         if self._parentwindow:
  823.             self.state = 0
  824.             port = Qd.GetPort()
  825.             self.SetPort()
  826.             self.draw()
  827.             Qd.SetPort(port)
  828.     
  829.     def draw(self, visRgn = None):
  830.         if self.state == 2:
  831.             Qd.PaintOval(self._bounds)
  832.         else:
  833.             Qd.EraseOval(self._bounds)
  834.  
  835.  
  836. # convenience funcs
  837.  
  838. def postmortem(exc_type, exc_value, tb):
  839.     d = getdebugger()
  840.     d.postmortem(exc_type, exc_value, tb)
  841.  
  842. def start(bottomframe = None):
  843.     d = getdebugger()
  844.     d.start(bottomframe)
  845.  
  846. def startfromhere():
  847.     d = getdebugger()
  848.     try:
  849.         raise 'spam'
  850.     except:
  851.         frame = sys.exc_traceback.tb_frame.f_back
  852.     d.start(frame)
  853.  
  854. def startfrombottom():
  855.     d = getdebugger()
  856.     d.start(_getbottomframe(), 1)
  857.  
  858. def stop():
  859.     d = getdebugger()
  860.     d.stop()
  861.  
  862. def cont():
  863.     sys.settrace(None)
  864.     d = getdebugger()
  865.     d.set_continue_without_debugger()
  866.  
  867. def _getbottomframe():
  868.     try:
  869.         raise 'spam'
  870.     except:
  871.         pass
  872.     frame = sys.exc_traceback.tb_frame
  873.     while 1:
  874.         if frame.f_code.co_name == 'mainloop' or frame.f_back is None:
  875.             break
  876.         frame = frame.f_back
  877.     return frame
  878.  
  879. _debugger = None
  880.  
  881. def getdebugger():
  882.     if not __debug__:
  883.         raise W.AlertError, "Can’t debug in “Optimize bytecode” mode.\r(see “Default startup options” in EditPythonPreferences)"
  884.     global _debugger
  885.     if _debugger is None:
  886.         _debugger = Debugger()
  887.     return _debugger
  888.