5 from WindowList
import ListedToplevel
6 from ScrolledList
import ScrolledList
12 def __init__(self
, gui
):
14 bdb
.Bdb
.__init
__(self
)
16 def user_line(self
, frame
):
17 if self
.in_rpc_code(frame
):
20 message
= self
.__frame
2message
(frame
)
21 self
.gui
.interaction(message
, frame
)
23 def user_exception(self
, frame
, info
):
24 if self
.in_rpc_code(frame
):
27 message
= self
.__frame
2message
(frame
)
28 self
.gui
.interaction(message
, frame
, info
)
30 def in_rpc_code(self
, frame
):
31 if frame
.f_code
.co_filename
.count('rpc.py'):
34 prev_frame
= frame
.f_back
35 if prev_frame
.f_code
.co_filename
.count('Debugger.py'):
36 # (that test will catch both Debugger.py and RemoteDebugger.py)
38 return self
.in_rpc_code(prev_frame
)
40 def __frame2message(self
, frame
):
42 filename
= code
.co_filename
43 lineno
= frame
.f_lineno
44 basename
= os
.path
.basename(filename
)
45 message
= "%s:%s" % (basename
, lineno
)
46 if code
.co_name
!= "?":
47 message
= "%s: %s()" % (message
, code
.co_name
)
53 vstack
= vsource
= vlocals
= vglobals
= None
55 def __init__(self
, pyshell
, idb
=None):
58 self
.pyshell
= pyshell
67 return self
.idb
.run(*args
)
71 def close(self
, event
=None):
76 self
.stackviewer
.close(); self
.stackviewer
= None
77 # Clean up pyshell if user clicked debugger control close widget.
78 # (Causes a harmless extra cycle through close_debugger() if user
79 # toggled debugger from pyshell Debug menu)
80 self
.pyshell
.close_debugger()
81 # Now close the debugger control window....
85 pyshell
= self
.pyshell
86 self
.flist
= pyshell
.flist
87 self
.root
= root
= pyshell
.root
88 self
.top
= top
= ListedToplevel(root
)
89 self
.top
.wm_title("Debug Control")
90 self
.top
.wm_iconname("Debug")
91 top
.wm_protocol("WM_DELETE_WINDOW", self
.close
)
92 self
.top
.bind("<Escape>", self
.close
)
94 self
.bframe
= bframe
= Frame(top
)
95 self
.bframe
.pack(anchor
="w")
96 self
.buttons
= bl
= []
98 self
.bcont
= b
= Button(bframe
, text
="Go", command
=self
.cont
)
100 self
.bstep
= b
= Button(bframe
, text
="Step", command
=self
.step
)
102 self
.bnext
= b
= Button(bframe
, text
="Over", command
=self
.next
)
104 self
.bret
= b
= Button(bframe
, text
="Out", command
=self
.ret
)
106 self
.bret
= b
= Button(bframe
, text
="Quit", command
=self
.quit
)
110 b
.configure(state
="disabled")
113 self
.cframe
= cframe
= Frame(bframe
)
114 self
.cframe
.pack(side
="left")
117 self
.__class
__.vstack
= BooleanVar(top
)
119 self
.bstack
= Checkbutton(cframe
,
120 text
="Stack", command
=self
.show_stack
, variable
=self
.vstack
)
121 self
.bstack
.grid(row
=0, column
=0)
123 self
.__class
__.vsource
= BooleanVar(top
)
124 self
.bsource
= Checkbutton(cframe
,
125 text
="Source", command
=self
.show_source
, variable
=self
.vsource
)
126 self
.bsource
.grid(row
=0, column
=1)
128 self
.__class
__.vlocals
= BooleanVar(top
)
130 self
.blocals
= Checkbutton(cframe
,
131 text
="Locals", command
=self
.show_locals
, variable
=self
.vlocals
)
132 self
.blocals
.grid(row
=1, column
=0)
133 if not self
.vglobals
:
134 self
.__class
__.vglobals
= BooleanVar(top
)
135 self
.bglobals
= Checkbutton(cframe
,
136 text
="Globals", command
=self
.show_globals
, variable
=self
.vglobals
)
137 self
.bglobals
.grid(row
=1, column
=1)
139 self
.status
= Label(top
, anchor
="w")
140 self
.status
.pack(anchor
="w")
141 self
.error
= Label(top
, anchor
="w")
142 self
.error
.pack(anchor
="w", fill
="x")
143 self
.errorbg
= self
.error
.cget("background")
145 self
.fstack
= Frame(top
, height
=1)
146 self
.fstack
.pack(expand
=1, fill
="both")
147 self
.flocals
= Frame(top
)
148 self
.flocals
.pack(expand
=1, fill
="both")
149 self
.fglobals
= Frame(top
, height
=1)
150 self
.fglobals
.pack(expand
=1, fill
="both")
152 if self
.vstack
.get():
154 if self
.vlocals
.get():
156 if self
.vglobals
.get():
159 def interaction(self
, message
, frame
, info
=None):
161 self
.status
.configure(text
=message
)
164 type, value
, tb
= info
167 except AttributeError:
168 m1
= "%s" % str(type)
169 if value
is not None:
171 m1
= "%s: %s" % (m1
, str(value
))
179 self
.error
.configure(text
=m1
, background
=bg
)
181 sv
= self
.stackviewer
183 stack
, i
= self
.idb
.get_stack(self
.frame
, tb
)
184 sv
.load_stack(stack
, i
)
186 self
.show_variables(1)
188 if self
.vsource
.get():
189 self
.sync_source_line()
191 for b
in self
.buttons
:
192 b
.configure(state
="normal")
197 for b
in self
.buttons
:
198 b
.configure(state
="disabled")
199 self
.status
.configure(text
="")
200 self
.error
.configure(text
="", background
=self
.errorbg
)
203 def sync_source_line(self
):
207 filename
, lineno
= self
.__frame
2fileline
(frame
)
208 if filename
[:1] + filename
[-1:] != "<>" and os
.path
.exists(filename
):
209 self
.flist
.gotofileline(filename
, lineno
)
211 def __frame2fileline(self
, frame
):
213 filename
= code
.co_filename
214 lineno
= frame
.f_lineno
215 return filename
, lineno
218 self
.idb
.set_continue()
226 self
.idb
.set_next(self
.frame
)
230 self
.idb
.set_return(self
.frame
)
239 def show_stack(self
):
240 if not self
.stackviewer
and self
.vstack
.get():
241 self
.stackviewer
= sv
= StackViewer(self
.fstack
, self
.flist
, self
)
243 stack
, i
= self
.idb
.get_stack(self
.frame
, None)
244 sv
.load_stack(stack
, i
)
246 sv
= self
.stackviewer
247 if sv
and not self
.vstack
.get():
248 self
.stackviewer
= None
250 self
.fstack
['height'] = 1
252 def show_source(self
):
253 if self
.vsource
.get():
254 self
.sync_source_line()
256 def show_frame(self
, (frame
, lineno
)):
258 self
.show_variables()
263 def show_locals(self
):
264 lv
= self
.localsviewer
265 if self
.vlocals
.get():
267 self
.localsviewer
= NamespaceViewer(self
.flocals
, "Locals")
270 self
.localsviewer
= None
272 self
.flocals
['height'] = 1
273 self
.show_variables()
275 def show_globals(self
):
276 gv
= self
.globalsviewer
277 if self
.vglobals
.get():
279 self
.globalsviewer
= NamespaceViewer(self
.fglobals
, "Globals")
282 self
.globalsviewer
= None
284 self
.fglobals
['height'] = 1
285 self
.show_variables()
287 def show_variables(self
, force
=0):
288 lv
= self
.localsviewer
289 gv
= self
.globalsviewer
294 ldict
= frame
.f_locals
295 gdict
= frame
.f_globals
296 if lv
and gv
and ldict
is gdict
:
299 lv
.load_dict(ldict
, force
, self
.pyshell
.interp
.rpcclt
)
301 gv
.load_dict(gdict
, force
, self
.pyshell
.interp
.rpcclt
)
303 def set_breakpoint_here(self
, filename
, lineno
):
304 self
.idb
.set_break(filename
, lineno
)
306 def clear_breakpoint_here(self
, filename
, lineno
):
307 self
.idb
.clear_break(filename
, lineno
)
309 def clear_file_breaks(self
, filename
):
310 self
.idb
.clear_all_file_breaks(filename
)
312 def load_breakpoints(self
):
313 "Load PyShellEditorWindow breakpoints into subprocess debugger"
314 pyshell_edit_windows
= self
.pyshell
.flist
.inversedict
.keys()
315 for editwin
in pyshell_edit_windows
:
316 filename
= editwin
.io
.filename
318 for lineno
in editwin
.breakpoints
:
319 self
.set_breakpoint_here(filename
, lineno
)
320 except AttributeError:
323 class StackViewer(ScrolledList
):
325 def __init__(self
, master
, flist
, gui
):
326 if macosxSupport
.runningAsOSXApp():
327 # At least on with the stock AquaTk version on OSX 10.4 you'll
328 # get an shaking GUI that eventually kills IDLE if the width
329 # argument is specified.
330 ScrolledList
.__init
__(self
, master
)
332 ScrolledList
.__init
__(self
, master
, width
=80)
337 def load_stack(self
, stack
, index
=None):
340 for i
in range(len(stack
)):
341 frame
, lineno
= stack
[i
]
343 modname
= frame
.f_globals
["__name__"]
347 filename
= code
.co_filename
348 funcname
= code
.co_name
350 sourceline
= linecache
.getline(filename
, lineno
)
352 sourceline
= string
.strip(sourceline
)
353 if funcname
in ("?", "", None):
354 item
= "%s, line %d: %s" % (modname
, lineno
, sourceline
)
356 item
= "%s.%s(), line %d: %s" % (modname
, funcname
,
361 if index
is not None:
364 def popup_event(self
, event
):
365 "override base method"
367 return ScrolledList
.popup_event(self
, event
)
370 "override base method"
372 menu
.add_command(label
="Go to source line",
373 command
=self
.goto_source_line
)
374 menu
.add_command(label
="Show stack frame",
375 command
=self
.show_stack_frame
)
377 def on_select(self
, index
):
378 "override base method"
379 if 0 <= index
< len(self
.stack
):
380 self
.gui
.show_frame(self
.stack
[index
])
382 def on_double(self
, index
):
383 "override base method"
384 self
.show_source(index
)
386 def goto_source_line(self
):
387 index
= self
.listbox
.index("active")
388 self
.show_source(index
)
390 def show_stack_frame(self
):
391 index
= self
.listbox
.index("active")
392 if 0 <= index
< len(self
.stack
):
393 self
.gui
.show_frame(self
.stack
[index
])
395 def show_source(self
, index
):
396 if not (0 <= index
< len(self
.stack
)):
398 frame
, lineno
= self
.stack
[index
]
400 filename
= code
.co_filename
401 if os
.path
.isfile(filename
):
402 edit
= self
.flist
.open(filename
)
404 edit
.gotoline(lineno
)
407 class NamespaceViewer
:
409 def __init__(self
, master
, title
, dict=None):
413 height
= 20*len(dict) # XXX 20 == observed height of Entry widget
417 self
.repr = repr.Repr()
418 self
.repr.maxstring
= 60
419 self
.repr.maxother
= 60
420 self
.frame
= frame
= Frame(master
)
421 self
.frame
.pack(expand
=1, fill
="both")
422 self
.label
= Label(frame
, text
=title
, borderwidth
=2, relief
="groove")
423 self
.label
.pack(fill
="x")
424 self
.vbar
= vbar
= Scrollbar(frame
, name
="vbar")
425 vbar
.pack(side
="right", fill
="y")
426 self
.canvas
= canvas
= Canvas(frame
,
427 height
=min(300, max(40, height
)),
428 scrollregion
=(0, 0, width
, height
))
429 canvas
.pack(side
="left", fill
="both", expand
=1)
430 vbar
["command"] = canvas
.yview
431 canvas
["yscrollcommand"] = vbar
.set
432 self
.subframe
= subframe
= Frame(canvas
)
433 self
.sfid
= canvas
.create_window(0, 0, window
=subframe
, anchor
="nw")
438 def load_dict(self
, dict, force
=0, rpc_client
=None):
439 if dict is self
.dict and not force
:
441 subframe
= self
.subframe
443 for c
in subframe
.children
.values():
447 l
= Label(subframe
, text
="None")
448 l
.grid(row
=0, column
=0)
455 svalue
= self
.repr.repr(value
) # repr(value)
456 # Strip extra quotes caused by calling repr on the (already)
457 # repr'd value sent across the RPC interface:
459 svalue
= svalue
[1:-1]
460 l
= Label(subframe
, text
=name
)
461 l
.grid(row
=row
, column
=0, sticky
="nw")
462 l
= Entry(subframe
, width
=0, borderwidth
=0)
464 l
.grid(row
=row
, column
=1, sticky
="nw")
467 # XXX Could we use a <Configure> callback for the following?
468 subframe
.update_idletasks() # Alas!
469 width
= subframe
.winfo_reqwidth()
470 height
= subframe
.winfo_reqheight()
472 self
.canvas
["scrollregion"] = (0, 0, width
, height
)
474 canvas
["height"] = 300
477 canvas
["height"] = height