0001 import os 0002 import bdb 0003 import types 0004 from Tkinter import * 0005 from WindowList import ListedToplevel 0006 from ScrolledList import ScrolledList 0007 0008 0009 class Idb(bdb.Bdb): 0010 0011 def __init__(self, gui): 0012 self.gui = gui 0013 bdb.Bdb.__init__(self) 0014 0015 def user_line(self, frame): 0016 if self.in_rpc_code(frame): 0017 self.set_step() 0018 return 0019 message = self.__frame2message(frame) 0020 self.gui.interaction(message, frame) 0021 0022 def user_exception(self, frame, info): 0023 if self.in_rpc_code(frame): 0024 self.set_step() 0025 return 0026 message = self.__frame2message(frame) 0027 self.gui.interaction(message, frame, info) 0028 0029 def in_rpc_code(self, frame): 0030 if frame.f_code.co_filename.count('rpc.py'): 0031 return True 0032 else: 0033 prev_frame = frame.f_back 0034 if prev_frame.f_code.co_filename.count('Debugger.py'): 0035 # (that test will catch both Debugger.py and RemoteDebugger.py) 0036 return False 0037 return self.in_rpc_code(prev_frame) 0038 0039 def __frame2message(self, frame): 0040 code = frame.f_code 0041 filename = code.co_filename 0042 lineno = frame.f_lineno 0043 basename = os.path.basename(filename) 0044 message = "%s:%s" % (basename, lineno) 0045 if code.co_name != "?": 0046 message = "%s: %s()" % (message, code.co_name) 0047 return message 0048 0049 0050 class Debugger: 0051 0052 vstack = vsource = vlocals = vglobals = None 0053 0054 def __init__(self, pyshell, idb=None): 0055 if idb is None: 0056 idb = Idb(self) 0057 self.pyshell = pyshell 0058 self.idb = idb 0059 self.frame = None 0060 self.make_gui() 0061 self.interacting = 0 0062 0063 def run(self, *args): 0064 try: 0065 self.interacting = 1 0066 return self.idb.run(*args) 0067 finally: 0068 self.interacting = 0 0069 0070 def close(self, event=None): 0071 if self.interacting: 0072 self.top.bell() 0073 return 0074 if self.stackviewer: 0075 self.stackviewer.close(); self.stackviewer = None 0076 # Clean up pyshell if user clicked debugger control close widget. 0077 # (Causes a harmless extra cycle through close_debugger() if user 0078 # toggled debugger from pyshell Debug menu) 0079 self.pyshell.close_debugger() 0080 # Now close the debugger control window.... 0081 self.top.destroy() 0082 0083 def make_gui(self): 0084 pyshell = self.pyshell 0085 self.flist = pyshell.flist 0086 self.root = root = pyshell.root 0087 self.top = top = ListedToplevel(root) 0088 self.top.wm_title("Debug Control") 0089 self.top.wm_iconname("Debug") 0090 top.wm_protocol("WM_DELETE_WINDOW", self.close) 0091 self.top.bind("<Escape>", self.close) 0092 # 0093 self.bframe = bframe = Frame(top) 0094 self.bframe.pack(anchor="w") 0095 self.buttons = bl = [] 0096 # 0097 self.bcont = b = Button(bframe, text="Go", command=self.cont) 0098 bl.append(b) 0099 self.bstep = b = Button(bframe, text="Step", command=self.step) 0100 bl.append(b) 0101 self.bnext = b = Button(bframe, text="Over", command=self.next) 0102 bl.append(b) 0103 self.bret = b = Button(bframe, text="Out", command=self.ret) 0104 bl.append(b) 0105 self.bret = b = Button(bframe, text="Quit", command=self.quit) 0106 bl.append(b) 0107 # 0108 for b in bl: 0109 b.configure(state="disabled") 0110 b.pack(side="left") 0111 # 0112 self.cframe = cframe = Frame(bframe) 0113 self.cframe.pack(side="left") 0114 # 0115 if not self.vstack: 0116 self.__class__.vstack = BooleanVar(top) 0117 self.vstack.set(1) 0118 self.bstack = Checkbutton(cframe, 0119 text="Stack", command=self.show_stack, variable=self.vstack) 0120 self.bstack.grid(row=0, column=0) 0121 if not self.vsource: 0122 self.__class__.vsource = BooleanVar(top) 0123 self.bsource = Checkbutton(cframe, 0124 text="Source", command=self.show_source, variable=self.vsource) 0125 self.bsource.grid(row=0, column=1) 0126 if not self.vlocals: 0127 self.__class__.vlocals = BooleanVar(top) 0128 self.vlocals.set(1) 0129 self.blocals = Checkbutton(cframe, 0130 text="Locals", command=self.show_locals, variable=self.vlocals) 0131 self.blocals.grid(row=1, column=0) 0132 if not self.vglobals: 0133 self.__class__.vglobals = BooleanVar(top) 0134 self.bglobals = Checkbutton(cframe, 0135 text="Globals", command=self.show_globals, variable=self.vglobals) 0136 self.bglobals.grid(row=1, column=1) 0137 # 0138 self.status = Label(top, anchor="w") 0139 self.status.pack(anchor="w") 0140 self.error = Label(top, anchor="w") 0141 self.error.pack(anchor="w", fill="x") 0142 self.errorbg = self.error.cget("background") 0143 # 0144 self.fstack = Frame(top, height=1) 0145 self.fstack.pack(expand=1, fill="both") 0146 self.flocals = Frame(top) 0147 self.flocals.pack(expand=1, fill="both") 0148 self.fglobals = Frame(top, height=1) 0149 self.fglobals.pack(expand=1, fill="both") 0150 # 0151 if self.vstack.get(): 0152 self.show_stack() 0153 if self.vlocals.get(): 0154 self.show_locals() 0155 if self.vglobals.get(): 0156 self.show_globals() 0157 0158 def interaction(self, message, frame, info=None): 0159 self.frame = frame 0160 self.status.configure(text=message) 0161 # 0162 if info: 0163 type, value, tb = info 0164 try: 0165 m1 = type.__name__ 0166 except AttributeError: 0167 m1 = "%s" % str(type) 0168 if value is not None: 0169 try: 0170 m1 = "%s: %s" % (m1, str(value)) 0171 except: 0172 pass 0173 bg = "yellow" 0174 else: 0175 m1 = "" 0176 tb = None 0177 bg = self.errorbg 0178 self.error.configure(text=m1, background=bg) 0179 # 0180 sv = self.stackviewer 0181 if sv: 0182 stack, i = self.idb.get_stack(self.frame, tb) 0183 sv.load_stack(stack, i) 0184 # 0185 self.show_variables(1) 0186 # 0187 if self.vsource.get(): 0188 self.sync_source_line() 0189 # 0190 for b in self.buttons: 0191 b.configure(state="normal") 0192 # 0193 self.top.wakeup() 0194 self.root.mainloop() 0195 # 0196 for b in self.buttons: 0197 b.configure(state="disabled") 0198 self.status.configure(text="") 0199 self.error.configure(text="", background=self.errorbg) 0200 self.frame = None 0201 0202 def sync_source_line(self): 0203 frame = self.frame 0204 if not frame: 0205 return 0206 filename, lineno = self.__frame2fileline(frame) 0207 if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename): 0208 self.flist.gotofileline(filename, lineno) 0209 0210 def __frame2fileline(self, frame): 0211 code = frame.f_code 0212 filename = code.co_filename 0213 lineno = frame.f_lineno 0214 return filename, lineno 0215 0216 def cont(self): 0217 self.idb.set_continue() 0218 self.root.quit() 0219 0220 def step(self): 0221 self.idb.set_step() 0222 self.root.quit() 0223 0224 def next(self): 0225 self.idb.set_next(self.frame) 0226 self.root.quit() 0227 0228 def ret(self): 0229 self.idb.set_return(self.frame) 0230 self.root.quit() 0231 0232 def quit(self): 0233 self.idb.set_quit() 0234 self.root.quit() 0235 0236 stackviewer = None 0237 0238 def show_stack(self): 0239 if not self.stackviewer and self.vstack.get(): 0240 self.stackviewer = sv = StackViewer(self.fstack, self.flist, self) 0241 if self.frame: 0242 stack, i = self.idb.get_stack(self.frame, None) 0243 sv.load_stack(stack, i) 0244 else: 0245 sv = self.stackviewer 0246 if sv and not self.vstack.get(): 0247 self.stackviewer = None 0248 sv.close() 0249 self.fstack['height'] = 1 0250 0251 def show_source(self): 0252 if self.vsource.get(): 0253 self.sync_source_line() 0254 0255 def show_frame(self, (frame, lineno)): 0256 self.frame = frame 0257 self.show_variables() 0258 0259 localsviewer = None 0260 globalsviewer = None 0261 0262 def show_locals(self): 0263 lv = self.localsviewer 0264 if self.vlocals.get(): 0265 if not lv: 0266 self.localsviewer = NamespaceViewer(self.flocals, "Locals") 0267 else: 0268 if lv: 0269 self.localsviewer = None 0270 lv.close() 0271 self.flocals['height'] = 1 0272 self.show_variables() 0273 0274 def show_globals(self): 0275 gv = self.globalsviewer 0276 if self.vglobals.get(): 0277 if not gv: 0278 self.globalsviewer = NamespaceViewer(self.fglobals, "Globals") 0279 else: 0280 if gv: 0281 self.globalsviewer = None 0282 gv.close() 0283 self.fglobals['height'] = 1 0284 self.show_variables() 0285 0286 def show_variables(self, force=0): 0287 lv = self.localsviewer 0288 gv = self.globalsviewer 0289 frame = self.frame 0290 if not frame: 0291 ldict = gdict = None 0292 else: 0293 ldict = frame.f_locals 0294 gdict = frame.f_globals 0295 if lv and gv and ldict is gdict: 0296 ldict = None 0297 if lv: 0298 lv.load_dict(ldict, force, self.pyshell.interp.rpcclt) 0299 if gv: 0300 gv.load_dict(gdict, force, self.pyshell.interp.rpcclt) 0301 0302 def set_breakpoint_here(self, filename, lineno): 0303 self.idb.set_break(filename, lineno) 0304 0305 def clear_breakpoint_here(self, filename, lineno): 0306 self.idb.clear_break(filename, lineno) 0307 0308 def clear_file_breaks(self, filename): 0309 self.idb.clear_all_file_breaks(filename) 0310 0311 def load_breakpoints(self): 0312 "Load PyShellEditorWindow breakpoints into subprocess debugger" 0313 pyshell_edit_windows = self.pyshell.flist.inversedict.keys() 0314 for editwin in pyshell_edit_windows: 0315 filename = editwin.io.filename 0316 try: 0317 for lineno in editwin.breakpoints: 0318 self.set_breakpoint_here(filename, lineno) 0319 except AttributeError: 0320 continue 0321 0322 class StackViewer(ScrolledList): 0323 0324 def __init__(self, master, flist, gui): 0325 ScrolledList.__init__(self, master, width=80) 0326 self.flist = flist 0327 self.gui = gui 0328 self.stack = [] 0329 0330 def load_stack(self, stack, index=None): 0331 self.stack = stack 0332 self.clear() 0333 for i in range(len(stack)): 0334 frame, lineno = stack[i] 0335 try: 0336 modname = frame.f_globals["__name__"] 0337 except: 0338 modname = "?" 0339 code = frame.f_code 0340 filename = code.co_filename 0341 funcname = code.co_name 0342 import linecache 0343 sourceline = linecache.getline(filename, lineno) 0344 import string 0345 sourceline = string.strip(sourceline) 0346 if funcname in ("?", "", None): 0347 item = "%s, line %d: %s" % (modname, lineno, sourceline) 0348 else: 0349 item = "%s.%s(), line %d: %s" % (modname, funcname, 0350 lineno, sourceline) 0351 if i == index: 0352 item = "> " + item 0353 self.append(item) 0354 if index is not None: 0355 self.select(index) 0356 0357 def popup_event(self, event): 0358 "override base method" 0359 if self.stack: 0360 return ScrolledList.popup_event(self, event) 0361 0362 def fill_menu(self): 0363 "override base method" 0364 menu = self.menu 0365 menu.add_command(label="Go to source line", 0366 command=self.goto_source_line) 0367 menu.add_command(label="Show stack frame", 0368 command=self.show_stack_frame) 0369 0370 def on_select(self, index): 0371 "override base method" 0372 if 0 <= index < len(self.stack): 0373 self.gui.show_frame(self.stack[index]) 0374 0375 def on_double(self, index): 0376 "override base method" 0377 self.show_source(index) 0378 0379 def goto_source_line(self): 0380 index = self.listbox.index("active") 0381 self.show_source(index) 0382 0383 def show_stack_frame(self): 0384 index = self.listbox.index("active") 0385 if 0 <= index < len(self.stack): 0386 self.gui.show_frame(self.stack[index]) 0387 0388 def show_source(self, index): 0389 if not (0 <= index < len(self.stack)): 0390 return 0391 frame, lineno = self.stack[index] 0392 code = frame.f_code 0393 filename = code.co_filename 0394 if os.path.isfile(filename): 0395 edit = self.flist.open(filename) 0396 if edit: 0397 edit.gotoline(lineno) 0398 0399 0400 class NamespaceViewer: 0401 0402 def __init__(self, master, title, dict=None): 0403 width = 0 0404 height = 40 0405 if dict: 0406 height = 20*len(dict) # XXX 20 == observed height of Entry widget 0407 self.master = master 0408 self.title = title 0409 import repr 0410 self.repr = repr.Repr() 0411 self.repr.maxstring = 60 0412 self.repr.maxother = 60 0413 self.frame = frame = Frame(master) 0414 self.frame.pack(expand=1, fill="both") 0415 self.label = Label(frame, text=title, borderwidth=2, relief="groove") 0416 self.label.pack(fill="x") 0417 self.vbar = vbar = Scrollbar(frame, name="vbar") 0418 vbar.pack(side="right", fill="y") 0419 self.canvas = canvas = Canvas(frame, 0420 height=min(300, max(40, height)), 0421 scrollregion=(0, 0, width, height)) 0422 canvas.pack(side="left", fill="both", expand=1) 0423 vbar["command"] = canvas.yview 0424 canvas["yscrollcommand"] = vbar.set 0425 self.subframe = subframe = Frame(canvas) 0426 self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw") 0427 self.load_dict(dict) 0428 0429 dict = -1 0430 0431 def load_dict(self, dict, force=0, rpc_client=None): 0432 if dict is self.dict and not force: 0433 return 0434 subframe = self.subframe 0435 frame = self.frame 0436 for c in subframe.children.values(): 0437 c.destroy() 0438 self.dict = None 0439 if not dict: 0440 l = Label(subframe, text="None") 0441 l.grid(row=0, column=0) 0442 else: 0443 names = dict.keys() 0444 names.sort() 0445 row = 0 0446 for name in names: 0447 value = dict[name] 0448 svalue = self.repr.repr(value) # repr(value) 0449 # Strip extra quotes caused by calling repr on the (already) 0450 # repr'd value sent across the RPC interface: 0451 if rpc_client: 0452 svalue = svalue[1:-1] 0453 l = Label(subframe, text=name) 0454 l.grid(row=row, column=0, sticky="nw") 0455 l = Entry(subframe, width=0, borderwidth=0) 0456 l.insert(0, svalue) 0457 l.grid(row=row, column=1, sticky="nw") 0458 row = row+1 0459 self.dict = dict 0460 # XXX Could we use a <Configure> callback for the following? 0461 subframe.update_idletasks() # Alas! 0462 width = subframe.winfo_reqwidth() 0463 height = subframe.winfo_reqheight() 0464 canvas = self.canvas 0465 self.canvas["scrollregion"] = (0, 0, width, height) 0466 if height > 300: 0467 canvas["height"] = 300 0468 frame.pack(expand=1) 0469 else: 0470 canvas["height"] = height 0471 frame.pack(expand=0) 0472 0473 def close(self): 0474 self.frame.destroy() 0475
Generated by PyXR 0.9.4