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