python-2.5.2/win32/Lib/idlelib/Debugger.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 import os
       
     2 import bdb
       
     3 import types
       
     4 from Tkinter import *
       
     5 from WindowList import ListedToplevel
       
     6 from ScrolledList import ScrolledList
       
     7 import macosxSupport
       
     8 
       
     9 
       
    10 class Idb(bdb.Bdb):
       
    11 
       
    12     def __init__(self, gui):
       
    13         self.gui = gui
       
    14         bdb.Bdb.__init__(self)
       
    15 
       
    16     def user_line(self, frame):
       
    17         if self.in_rpc_code(frame):
       
    18             self.set_step()
       
    19             return
       
    20         message = self.__frame2message(frame)
       
    21         self.gui.interaction(message, frame)
       
    22 
       
    23     def user_exception(self, frame, info):
       
    24         if self.in_rpc_code(frame):
       
    25             self.set_step()
       
    26             return
       
    27         message = self.__frame2message(frame)
       
    28         self.gui.interaction(message, frame, info)
       
    29 
       
    30     def in_rpc_code(self, frame):
       
    31         if frame.f_code.co_filename.count('rpc.py'):
       
    32             return True
       
    33         else:
       
    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)
       
    37                 return False
       
    38             return self.in_rpc_code(prev_frame)
       
    39 
       
    40     def __frame2message(self, frame):
       
    41         code = frame.f_code
       
    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)
       
    48         return message
       
    49 
       
    50 
       
    51 class Debugger:
       
    52 
       
    53     vstack = vsource = vlocals = vglobals = None
       
    54 
       
    55     def __init__(self, pyshell, idb=None):
       
    56         if idb is None:
       
    57             idb = Idb(self)
       
    58         self.pyshell = pyshell
       
    59         self.idb = idb
       
    60         self.frame = None
       
    61         self.make_gui()
       
    62         self.interacting = 0
       
    63 
       
    64     def run(self, *args):
       
    65         try:
       
    66             self.interacting = 1
       
    67             return self.idb.run(*args)
       
    68         finally:
       
    69             self.interacting = 0
       
    70 
       
    71     def close(self, event=None):
       
    72         if self.interacting:
       
    73             self.top.bell()
       
    74             return
       
    75         if self.stackviewer:
       
    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....
       
    82         self.top.destroy()
       
    83 
       
    84     def make_gui(self):
       
    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)
       
    93         #
       
    94         self.bframe = bframe = Frame(top)
       
    95         self.bframe.pack(anchor="w")
       
    96         self.buttons = bl = []
       
    97         #
       
    98         self.bcont = b = Button(bframe, text="Go", command=self.cont)
       
    99         bl.append(b)
       
   100         self.bstep = b = Button(bframe, text="Step", command=self.step)
       
   101         bl.append(b)
       
   102         self.bnext = b = Button(bframe, text="Over", command=self.next)
       
   103         bl.append(b)
       
   104         self.bret = b = Button(bframe, text="Out", command=self.ret)
       
   105         bl.append(b)
       
   106         self.bret = b = Button(bframe, text="Quit", command=self.quit)
       
   107         bl.append(b)
       
   108         #
       
   109         for b in bl:
       
   110             b.configure(state="disabled")
       
   111             b.pack(side="left")
       
   112         #
       
   113         self.cframe = cframe = Frame(bframe)
       
   114         self.cframe.pack(side="left")
       
   115         #
       
   116         if not self.vstack:
       
   117             self.__class__.vstack = BooleanVar(top)
       
   118             self.vstack.set(1)
       
   119         self.bstack = Checkbutton(cframe,
       
   120             text="Stack", command=self.show_stack, variable=self.vstack)
       
   121         self.bstack.grid(row=0, column=0)
       
   122         if not self.vsource:
       
   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)
       
   127         if not self.vlocals:
       
   128             self.__class__.vlocals = BooleanVar(top)
       
   129             self.vlocals.set(1)
       
   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)
       
   138         #
       
   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")
       
   144         #
       
   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")
       
   151         #
       
   152         if self.vstack.get():
       
   153             self.show_stack()
       
   154         if self.vlocals.get():
       
   155             self.show_locals()
       
   156         if self.vglobals.get():
       
   157             self.show_globals()
       
   158 
       
   159     def interaction(self, message, frame, info=None):
       
   160         self.frame = frame
       
   161         self.status.configure(text=message)
       
   162         #
       
   163         if info:
       
   164             type, value, tb = info
       
   165             try:
       
   166                 m1 = type.__name__
       
   167             except AttributeError:
       
   168                 m1 = "%s" % str(type)
       
   169             if value is not None:
       
   170                 try:
       
   171                     m1 = "%s: %s" % (m1, str(value))
       
   172                 except:
       
   173                     pass
       
   174             bg = "yellow"
       
   175         else:
       
   176             m1 = ""
       
   177             tb = None
       
   178             bg = self.errorbg
       
   179         self.error.configure(text=m1, background=bg)
       
   180         #
       
   181         sv = self.stackviewer
       
   182         if sv:
       
   183             stack, i = self.idb.get_stack(self.frame, tb)
       
   184             sv.load_stack(stack, i)
       
   185         #
       
   186         self.show_variables(1)
       
   187         #
       
   188         if self.vsource.get():
       
   189             self.sync_source_line()
       
   190         #
       
   191         for b in self.buttons:
       
   192             b.configure(state="normal")
       
   193         #
       
   194         self.top.wakeup()
       
   195         self.root.mainloop()
       
   196         #
       
   197         for b in self.buttons:
       
   198             b.configure(state="disabled")
       
   199         self.status.configure(text="")
       
   200         self.error.configure(text="", background=self.errorbg)
       
   201         self.frame = None
       
   202 
       
   203     def sync_source_line(self):
       
   204         frame = self.frame
       
   205         if not frame:
       
   206             return
       
   207         filename, lineno = self.__frame2fileline(frame)
       
   208         if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
       
   209             self.flist.gotofileline(filename, lineno)
       
   210 
       
   211     def __frame2fileline(self, frame):
       
   212         code = frame.f_code
       
   213         filename = code.co_filename
       
   214         lineno = frame.f_lineno
       
   215         return filename, lineno
       
   216 
       
   217     def cont(self):
       
   218         self.idb.set_continue()
       
   219         self.root.quit()
       
   220 
       
   221     def step(self):
       
   222         self.idb.set_step()
       
   223         self.root.quit()
       
   224 
       
   225     def next(self):
       
   226         self.idb.set_next(self.frame)
       
   227         self.root.quit()
       
   228 
       
   229     def ret(self):
       
   230         self.idb.set_return(self.frame)
       
   231         self.root.quit()
       
   232 
       
   233     def quit(self):
       
   234         self.idb.set_quit()
       
   235         self.root.quit()
       
   236 
       
   237     stackviewer = None
       
   238 
       
   239     def show_stack(self):
       
   240         if not self.stackviewer and self.vstack.get():
       
   241             self.stackviewer = sv = StackViewer(self.fstack, self.flist, self)
       
   242             if self.frame:
       
   243                 stack, i = self.idb.get_stack(self.frame, None)
       
   244                 sv.load_stack(stack, i)
       
   245         else:
       
   246             sv = self.stackviewer
       
   247             if sv and not self.vstack.get():
       
   248                 self.stackviewer = None
       
   249                 sv.close()
       
   250             self.fstack['height'] = 1
       
   251 
       
   252     def show_source(self):
       
   253         if self.vsource.get():
       
   254             self.sync_source_line()
       
   255 
       
   256     def show_frame(self, (frame, lineno)):
       
   257         self.frame = frame
       
   258         self.show_variables()
       
   259 
       
   260     localsviewer = None
       
   261     globalsviewer = None
       
   262 
       
   263     def show_locals(self):
       
   264         lv = self.localsviewer
       
   265         if self.vlocals.get():
       
   266             if not lv:
       
   267                 self.localsviewer = NamespaceViewer(self.flocals, "Locals")
       
   268         else:
       
   269             if lv:
       
   270                 self.localsviewer = None
       
   271                 lv.close()
       
   272                 self.flocals['height'] = 1
       
   273         self.show_variables()
       
   274 
       
   275     def show_globals(self):
       
   276         gv = self.globalsviewer
       
   277         if self.vglobals.get():
       
   278             if not gv:
       
   279                 self.globalsviewer = NamespaceViewer(self.fglobals, "Globals")
       
   280         else:
       
   281             if gv:
       
   282                 self.globalsviewer = None
       
   283                 gv.close()
       
   284                 self.fglobals['height'] = 1
       
   285         self.show_variables()
       
   286 
       
   287     def show_variables(self, force=0):
       
   288         lv = self.localsviewer
       
   289         gv = self.globalsviewer
       
   290         frame = self.frame
       
   291         if not frame:
       
   292             ldict = gdict = None
       
   293         else:
       
   294             ldict = frame.f_locals
       
   295             gdict = frame.f_globals
       
   296             if lv and gv and ldict is gdict:
       
   297                 ldict = None
       
   298         if lv:
       
   299             lv.load_dict(ldict, force, self.pyshell.interp.rpcclt)
       
   300         if gv:
       
   301             gv.load_dict(gdict, force, self.pyshell.interp.rpcclt)
       
   302 
       
   303     def set_breakpoint_here(self, filename, lineno):
       
   304         self.idb.set_break(filename, lineno)
       
   305 
       
   306     def clear_breakpoint_here(self, filename, lineno):
       
   307         self.idb.clear_break(filename, lineno)
       
   308 
       
   309     def clear_file_breaks(self, filename):
       
   310         self.idb.clear_all_file_breaks(filename)
       
   311 
       
   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
       
   317             try:
       
   318                 for lineno in editwin.breakpoints:
       
   319                     self.set_breakpoint_here(filename, lineno)
       
   320             except AttributeError:
       
   321                 continue
       
   322 
       
   323 class StackViewer(ScrolledList):
       
   324 
       
   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)
       
   331         else:
       
   332             ScrolledList.__init__(self, master, width=80)
       
   333         self.flist = flist
       
   334         self.gui = gui
       
   335         self.stack = []
       
   336 
       
   337     def load_stack(self, stack, index=None):
       
   338         self.stack = stack
       
   339         self.clear()
       
   340         for i in range(len(stack)):
       
   341             frame, lineno = stack[i]
       
   342             try:
       
   343                 modname = frame.f_globals["__name__"]
       
   344             except:
       
   345                 modname = "?"
       
   346             code = frame.f_code
       
   347             filename = code.co_filename
       
   348             funcname = code.co_name
       
   349             import linecache
       
   350             sourceline = linecache.getline(filename, lineno)
       
   351             import string
       
   352             sourceline = string.strip(sourceline)
       
   353             if funcname in ("?", "", None):
       
   354                 item = "%s, line %d: %s" % (modname, lineno, sourceline)
       
   355             else:
       
   356                 item = "%s.%s(), line %d: %s" % (modname, funcname,
       
   357                                                  lineno, sourceline)
       
   358             if i == index:
       
   359                 item = "> " + item
       
   360             self.append(item)
       
   361         if index is not None:
       
   362             self.select(index)
       
   363 
       
   364     def popup_event(self, event):
       
   365         "override base method"
       
   366         if self.stack:
       
   367             return ScrolledList.popup_event(self, event)
       
   368 
       
   369     def fill_menu(self):
       
   370         "override base method"
       
   371         menu = self.menu
       
   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)
       
   376 
       
   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])
       
   381 
       
   382     def on_double(self, index):
       
   383         "override base method"
       
   384         self.show_source(index)
       
   385 
       
   386     def goto_source_line(self):
       
   387         index = self.listbox.index("active")
       
   388         self.show_source(index)
       
   389 
       
   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])
       
   394 
       
   395     def show_source(self, index):
       
   396         if not (0 <= index < len(self.stack)):
       
   397             return
       
   398         frame, lineno = self.stack[index]
       
   399         code = frame.f_code
       
   400         filename = code.co_filename
       
   401         if os.path.isfile(filename):
       
   402             edit = self.flist.open(filename)
       
   403             if edit:
       
   404                 edit.gotoline(lineno)
       
   405 
       
   406 
       
   407 class NamespaceViewer:
       
   408 
       
   409     def __init__(self, master, title, dict=None):
       
   410         width = 0
       
   411         height = 40
       
   412         if dict:
       
   413             height = 20*len(dict) # XXX 20 == observed height of Entry widget
       
   414         self.master = master
       
   415         self.title = title
       
   416         import repr
       
   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")
       
   434         self.load_dict(dict)
       
   435 
       
   436     dict = -1
       
   437 
       
   438     def load_dict(self, dict, force=0, rpc_client=None):
       
   439         if dict is self.dict and not force:
       
   440             return
       
   441         subframe = self.subframe
       
   442         frame = self.frame
       
   443         for c in subframe.children.values():
       
   444             c.destroy()
       
   445         self.dict = None
       
   446         if not dict:
       
   447             l = Label(subframe, text="None")
       
   448             l.grid(row=0, column=0)
       
   449         else:
       
   450             names = dict.keys()
       
   451             names.sort()
       
   452             row = 0
       
   453             for name in names:
       
   454                 value = dict[name]
       
   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:
       
   458                 if rpc_client:
       
   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)
       
   463                 l.insert(0, svalue)
       
   464                 l.grid(row=row, column=1, sticky="nw")
       
   465                 row = row+1
       
   466         self.dict = dict
       
   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()
       
   471         canvas = self.canvas
       
   472         self.canvas["scrollregion"] = (0, 0, width, height)
       
   473         if height > 300:
       
   474             canvas["height"] = 300
       
   475             frame.pack(expand=1)
       
   476         else:
       
   477             canvas["height"] = height
       
   478             frame.pack(expand=0)
       
   479 
       
   480     def close(self):
       
   481         self.frame.destroy()