#@+leo-ver=4-thin
#@+node:ekr.20060516135654.94:@thin leo_Debugger.py
#@<< imports >>
#@+node:ekr.20060516135654.95:<< imports >>
import leoGlobals as g
import leo_FileList
import leo_run
import leo_RemoteDebugger
import leo_Shell
import idlelib.ScrolledList as ScrolledList
import bdb
import os
import socket
import sys
import time
import types
import Tkinter as Tk
import tkMessageBox
import __main__
#@nonl
#@-node:ekr.20060516135654.95:<< imports >>
#@nl
LOCALHOST = '127.0.0.1'
#@+others
#@+node:ekr.20060516142615.4:go
def go (c):
# From Pyshell.ctor
interp = leo_Shell.ModifiedInterpreter(dummyShell(c))
# From Pyshell.begin:
client = interp.start_subprocess()
g.trace('client',client,'interp',interp)
open_debugger(c,interp.rpcclt,interp)
#@nonl
#@-node:ekr.20060516142615.4:go
#@+node:ekr.20060516142615.5:open_debugger
def open_debugger(c,rpcClient,interp):
dbg_gui = leo_RemoteDebugger.start_remote_debugger(c,rpcClient,interp)
interp.setdebugger(dbg_gui)
dbg_gui.load_breakpoints()
if 0: ### old code
if self.interp.rpcclt:
dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,self)
else:
dbg_gui = Debugger.Debugger(self)
self.interp.setdebugger(dbg_gui)
dbg_gui.load_breakpoints()
sys.ps1 = "[DEBUG ON]\n>>> "
self.showprompt()
self.set_debugger_indicator()
#@nonl
#@-node:ekr.20060516142615.5:open_debugger
#@+node:ekr.20060516142615.6:class dummyShell
class dummyShell:
def __init__ (self,c):
self.c = c
self.stdout = sys.__stdout__
self.stderr = sys.__stderr__
self.flist = []
self.closing = False
self.executing = False
self.text = Tk.Text() # Ignored completely
self.pollinterval = 50 # millisec
# g.trace('dummyShell')
#@nonl
#@-node:ekr.20060516142615.6:class dummyShell
#@+node:ekr.20060516135654.163:class Idb (used by remote debugger code)
# Important: this class is used by the remote debugger code.
class Idb(bdb.Bdb):
#@ @+others
#@+node:ekr.20060516135654.164:__init__
def __init__(self, gui):
self.gui = gui
bdb.Bdb.__init__(self)
#@nonl
#@-node:ekr.20060516135654.164:__init__
#@+node:ekr.20060516135654.165:user_line
def user_line(self, frame):
if self.in_rpc_code(frame):
self.set_step()
else:
message = self.__frame2message(frame)
self.gui.interaction(message, frame)
#@nonl
#@-node:ekr.20060516135654.165:user_line
#@+node:ekr.20060516135654.166:user_exception
def user_exception(self, frame, info):
if self.in_rpc_code(frame):
self.set_step()
else:
message = self.__frame2message(frame)
self.gui.interaction(message, frame, info)
#@nonl
#@-node:ekr.20060516135654.166:user_exception
#@+node:ekr.20060516135654.167:in_rpc_code
def in_rpc_code(self, frame):
if frame.f_code.co_filename.count('rpc.py'):
return True
else:
prev_frame = frame.f_back
if prev_frame.f_code.co_filename.count('Debugger.py'):
# (that test will catch both Debugger.py and RemoteDebugger.py)
return False
return self.in_rpc_code(prev_frame)
#@-node:ekr.20060516135654.167:in_rpc_code
#@+node:ekr.20060516135654.168:__frame2message
def __frame2message(self, frame):
code = frame.f_code
filename = code.co_filename
lineno = frame.f_lineno
basename = os.path.basename(filename)
message = "%s:%s" % (basename, lineno)
if code.co_name != "?":
message = "%s: %s()" % (message, code.co_name)
return message
#@nonl
#@-node:ekr.20060516135654.168:__frame2message
#@-others
#@-node:ekr.20060516135654.163:class Idb (used by remote debugger code)
#@+node:ekr.20060516135654.169:class StackViewer
class StackViewer(ScrolledList.ScrolledList):
#@ @+others
#@+node:ekr.20060516135654.170:__init__
def __init__(self, master, flist, gui):
g.trace('StackViewer: flist',flist,g.callers())
ScrolledList.ScrolledList.__init__(self, master, width=80)
self.flist = flist
self.gui = gui
self.stack = []
#@nonl
#@-node:ekr.20060516135654.170:__init__
#@+node:ekr.20060516135654.171:load_stack
def load_stack(self, stack, index=None):
self.stack = stack
self.clear()
for i in range(len(stack)):
frame, lineno = stack[i]
try:
modname = frame.f_globals["__name__"]
except:
modname = "?"
code = frame.f_code
filename = code.co_filename
funcname = code.co_name
import linecache
sourceline = linecache.getline(filename, lineno)
import string
sourceline = string.strip(sourceline)
if funcname in ("?", "", None):
item = "%s, line %d: %s" % (modname, lineno, sourceline)
else:
item = "%s.%s(), line %d: %s" % (modname, funcname,
lineno, sourceline)
if i == index:
item = "> " + item
self.append(item)
if index is not None:
self.select(index)
#@-node:ekr.20060516135654.171:load_stack
#@+node:ekr.20060516135654.172:popup_event
def popup_event(self, event):
"override base method"
if self.stack:
return ScrolledList.ScrolledList.popup_event(self, event)
#@-node:ekr.20060516135654.172:popup_event
#@+node:ekr.20060516135654.173:fill_menu
def fill_menu(self):
"override base method"
menu = self.menu
menu.add_command(label="Go to source line",
command=self.goto_source_line)
menu.add_command(label="Show stack frame",
command=self.show_stack_frame)
#@-node:ekr.20060516135654.173:fill_menu
#@+node:ekr.20060516135654.174:on_select
def on_select(self, index):
"override base method"
if 0 <= index < len(self.stack):
self.gui.show_frame(self.stack[index])
#@-node:ekr.20060516135654.174:on_select
#@+node:ekr.20060516135654.175:on_double
def on_double(self, index):
"override base method"
self.show_source(index)
#@-node:ekr.20060516135654.175:on_double
#@+node:ekr.20060516135654.176:goto_source_line
def goto_source_line(self):
index = self.listbox.index("active")
self.show_source(index)
#@-node:ekr.20060516135654.176:goto_source_line
#@+node:ekr.20060516135654.177:show_stack_frame
def show_stack_frame(self):
index = self.listbox.index("active")
if 0 <= index < len(self.stack):
self.gui.show_frame(self.stack[index])
#@-node:ekr.20060516135654.177:show_stack_frame
#@+node:ekr.20060516135654.178:show_source
def show_source(self, index):
if not (0 <= index < len(self.stack)):
return
frame, lineno = self.stack[index]
code = frame.f_code
filename = code.co_filename
if os.path.isfile(filename):
edit = self.flist.open(filename)
if edit:
edit.gotoline(lineno)
#@-node:ekr.20060516135654.178:show_source
#@-others
#@-node:ekr.20060516135654.169:class StackViewer
#@+node:ekr.20060516135654.179:class NamespaceViewer
class NamespaceViewer:
#@ @+others
#@+node:ekr.20060516135654.180:__init__
def __init__(self, master, title, dict=None):
g.trace('NamespaceViewer','dict',dict,g.callers())
width = 0
height = 40
if dict:
height = 20*len(dict) # XXX 20 == observed height of Entry widget
self.master = master
self.title = title
import repr
self.repr = repr.Repr()
self.repr.maxstring = 60
self.repr.maxother = 60
self.frame = frame = Tk.Frame(master)
self.frame.pack(expand=1, fill="both")
self.label = Tk.Label(frame, text=title, borderwidth=2, relief="groove")
self.label.pack(fill="x")
self.vbar = vbar = Tk.Scrollbar(frame, name="vbar")
vbar.pack(side="right", fill="y")
self.canvas = canvas = Tk.Canvas(frame,
height=min(300, max(40, height)),
scrollregion=(0, 0, width, height))
canvas.pack(side="left", fill="both", expand=1)
vbar["command"] = canvas.yview
canvas["yscrollcommand"] = vbar.set
self.subframe = subframe = Tk.Frame(canvas)
self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
self.load_dict(dict)
#@nonl
#@-node:ekr.20060516135654.180:__init__
#@+node:ekr.20060516135654.181:load_dict
dict = -1
def load_dict(self, dict, force=0, rpc_client=None):
if dict is self.dict and not force:
return
subframe = self.subframe
frame = self.frame
for c in subframe.children.values():
c.destroy()
self.dict = None
if not dict:
l = Tk.Label(subframe, text="None")
l.grid(row=0, column=0)
else:
names = dict.keys()
names.sort()
row = 0
for name in names:
value = dict[name]
svalue = self.repr.repr(value) # repr(value)
# Strip extra quotes caused by calling repr on the (already)
# repr'd value sent across the RPC interface:
if rpc_client:
svalue = svalue[1:-1]
l = Tk.Label(subframe, text=name)
l.grid(row=row, column=0, sticky="nw")
l = Entry(subframe, width=0, borderwidth=0)
l.insert(0, svalue)
l.grid(row=row, column=1, sticky="nw")
row = row+1
self.dict = dict
# XXX Could we use a <Configure> callback for the following?
subframe.update_idletasks() # Alas!
width = subframe.winfo_reqwidth()
height = subframe.winfo_reqheight()
canvas = self.canvas
self.canvas["scrollregion"] = (0, 0, width, height)
if height > 300:
canvas["height"] = 300
frame.pack(expand=1)
else:
canvas["height"] = height
frame.pack(expand=0)
#@-node:ekr.20060516135654.181:load_dict
#@+node:ekr.20060516135654.182:close
def close(self):
self.frame.destroy()
#@-node:ekr.20060516135654.182:close
#@-others
#@-node:ekr.20060516135654.179:class NamespaceViewer
#@+node:ekr.20060516135654.183:class Debugger
class Debugger:
vstack = vsource = vlocals = vglobals = None
#@ @+others
#@+node:ekr.20060516135654.184:Birth
#@+node:ekr.20060516135654.185:__init__
def __init__(self, c, interp, idb=None):
self.c = c
# Were inited as needed.
self.globalsviewer = None
self.interacting = 0
self.localsviewer = None
self.stackviewer = None
###self.pyshell = pyshell
self.interp = interp
self.idb = idb or Idb(self)
self.frame = None
### self.flist = pyshell.flist
self.flist = leo_FileList.FileList(c)
self.make_gui()
g.trace('Debugger',interp,self.idb)
#@nonl
#@-node:ekr.20060516135654.185:__init__
#@+node:ekr.20060516135654.186:make_gui
def make_gui(self):
#@ << create the top level frame >>
#@+node:ekr.20060516135654.187:<< create the top level frame >>
self.root = root = g.app.root
self.top = top = Tk.Toplevel(self.root)
top.title("Leo Debug Control")
top.wm_iconname("Debug")
top.wm_protocol("WM_DELETE_WINDOW", self.close)
# self.top.bind("<Escape>", self.close)
#@nonl
#@-node:ekr.20060516135654.187:<< create the top level frame >>
#@nl
#@ << create the control buttons >>
#@+node:ekr.20060516135654.188:<< create the control buttons >>
self.bframe = bframe = Tk.Frame(top)
self.bframe.pack(anchor="w")
self.buttons = bl = []
#
self.bcont = b = Tk.Button(bframe, text="Go", command=self.cont)
bl.append(b)
self.bstep = b = Tk.Button(bframe, text="Step", command=self.step)
bl.append(b)
self.bnext = b = Tk.Button(bframe, text="Over", command=self.next)
bl.append(b)
self.bret = b = Tk.Button(bframe, text="Out", command=self.ret)
bl.append(b)
self.bret = b = Tk.Button(bframe, text="Quit", command=self.quit)
bl.append(b)
#
for b in bl:
b.configure(state="disabled")
b.pack(side="left")
#@nonl
#@-node:ekr.20060516135654.188:<< create the control buttons >>
#@nl
#@ << create the check boxes >>
#@+node:ekr.20060516135654.189:<< create the check boxes >>
self.cframe = cframe = Tk.Frame(bframe)
self.cframe.pack(side="left")
if not self.vstack:
self.__class__.vstack = Tk.BooleanVar(top)
self.vstack.set(1)
self.bstack = Tk.Checkbutton(cframe,text="Stack",
command = self.show_stack, variable = self.vstack)
self.bstack.grid(row=0,column=0)
if not self.vsource:
self.__class__.vsource = Tk.BooleanVar(top)
self.bsource = Tk.Checkbutton(cframe,text="Source",
command = self.show_source, variable = self.vsource)
self.bsource.grid(row=0,column=1)
if not self.vlocals:
self.__class__.vlocals = Tk.BooleanVar(top)
self.vlocals.set(1)
self.blocals = Tk.Checkbutton(cframe,text="Locals",
command = self.show_locals, variable = self.vlocals)
self.blocals.grid(row=1,column=0)
if not self.vglobals:
self.__class__.vglobals = Tk.BooleanVar(top)
self.bglobals = Tk.Checkbutton(cframe,text="Globals",
command = self.show_globals, variable = self.vglobals)
self.bglobals.grid(row=1,column=1)
#@nonl
#@-node:ekr.20060516135654.189:<< create the check boxes >>
#@nl
#
self.status = Tk.Label(top, anchor="w")
self.status.pack(anchor="w")
self.error = Tk.Label(top, anchor="w")
self.error.pack(anchor="w", fill="x")
self.errorbg = self.error.cget("background")
#
self.fstack = Tk.Frame(top, height=1)
self.fstack.pack(expand=1, fill="both")
self.flocals = Tk.Frame(top)
self.flocals.pack(expand=1, fill="both")
self.fglobals = Tk.Frame(top, height=1)
self.fglobals.pack(expand=1, fill="both")
#
if self.vstack.get():
self.show_stack()
if self.vlocals.get():
self.show_locals()
if self.vglobals.get():
self.show_globals()
#@-node:ekr.20060516135654.186:make_gui
#@-node:ekr.20060516135654.184:Birth
#@+node:ekr.20060516135654.190:run
def run(self, *args):
g.trace('Debugger',args)
try:
self.interacting = 1
return self.idb.run(*args)
finally:
self.interacting = 0
#@nonl
#@-node:ekr.20060516135654.190:run
#@+node:ekr.20060516135654.191:close
def close(self, event=None):
if self.interacting:
self.top.bell()
else:
if self.stackviewer:
self.stackviewer.close()
self.stackviewer = None
# Clean up pyshell if user clicked debugger control close widget.
# (Causes a harmless extra cycle through close_debugger() if user
# toggled debugger from pyshell Debug menu)
### self.pyshell.close_debugger()
self.interp.close_debugger()
# Now close the debugger control window....
self.top.destroy()
#@nonl
#@-node:ekr.20060516135654.191:close
#@+node:ekr.20060516135654.192:interaction
def interaction(self, message, frame, info=None):
g.trace('Debugger',message)
self.frame = frame
self.status.configure(text=message)
if info:
type, value, tb = info
try:
m1 = type.__name__
except AttributeError:
m1 = "%s" % str(type)
if value is not None:
try:
m1 = "%s: %s" % (m1, str(value))
except:
pass
bg = "yellow"
else:
m1 = ""
tb = None
bg = self.errorbg
self.error.configure(text=m1, background=bg)
#
sv = self.stackviewer
if sv:
stack, i = self.idb.get_stack(self.frame, tb)
sv.load_stack(stack, i)
#
self.show_variables(1)
#
if self.vsource.get():
self.sync_source_line()
#
for b in self.buttons:
b.configure(state="normal")
#
self.top.wakeup()
self.root.mainloop()
#
for b in self.buttons:
b.configure(state="disabled")
self.status.configure(text="")
self.error.configure(text="", background=self.errorbg)
self.frame = None
#@nonl
#@-node:ekr.20060516135654.192:interaction
#@+node:ekr.20060516135654.193:sync_source_line (used flist)
def sync_source_line(self):
frame = self.frame
if not frame:
return
filename, lineno = self.__frame2fileline(frame)
if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
g.trace(filename,lineno)
if 0: ### Not yet
f.gotoline(filename,lineno)
# self.flist.gotofileline(filename, lineno)
#@nonl
#@-node:ekr.20060516135654.193:sync_source_line (used flist)
#@+node:ekr.20060516135654.194:__frame2fileline
def __frame2fileline(self, frame):
code = frame.f_code
filename = code.co_filename
lineno = frame.f_lineno
return filename, lineno
#@-node:ekr.20060516135654.194:__frame2fileline
#@+node:ekr.20060516135654.195:Callbacks
#@+node:ekr.20060516135654.196:cont
def cont(self):
self.idb.set_continue()
self.root.quit()
#@-node:ekr.20060516135654.196:cont
#@+node:ekr.20060516135654.197:step
def step(self):
self.idb.set_step()
self.root.quit()
#@-node:ekr.20060516135654.197:step
#@+node:ekr.20060516135654.198:next
def next(self):
self.idb.set_next(self.frame)
self.root.quit()
#@-node:ekr.20060516135654.198:next
#@+node:ekr.20060516135654.199:ret
def ret(self):
self.idb.set_return(self.frame)
self.root.quit()
#@-node:ekr.20060516135654.199:ret
#@+node:ekr.20060516135654.200:quit
def quit(self):
self.idb.set_quit()
self.root.quit()
#@nonl
#@-node:ekr.20060516135654.200:quit
#@-node:ekr.20060516135654.195:Callbacks
#@+node:ekr.20060516135654.201:refresh
#@+node:ekr.20060516135654.202:show_stack
def show_stack(self):
g.trace(self.stackviewer)
if not self.stackviewer and self.vstack.get():
self.stackviewer = sv = StackViewer(self.fstack, self.flist, self)
if self.frame:
stack, i = self.idb.get_stack(self.frame, None)
sv.load_stack(stack, i)
else:
sv = self.stackviewer
if sv and not self.vstack.get():
self.stackviewer = None
sv.close()
self.fstack['height'] = 1
#@-node:ekr.20060516135654.202:show_stack
#@+node:ekr.20060516135654.203:show_source
def show_source(self):
if self.vsource.get():
self.sync_source_line()
#@-node:ekr.20060516135654.203:show_source
#@+node:ekr.20060516135654.204:show_frame
def show_frame(self, (frame, lineno)):
self.frame = frame
self.show_variables()
#@-node:ekr.20060516135654.204:show_frame
#@+node:ekr.20060516135654.205:show_locals
def show_locals(self):
lv = self.localsviewer
if self.vlocals.get():
if not lv:
self.localsviewer = NamespaceViewer(self.flocals, "Locals")
else:
if lv:
self.localsviewer = None
lv.close()
self.flocals['height'] = 1
self.show_variables()
#@-node:ekr.20060516135654.205:show_locals
#@+node:ekr.20060516135654.206:show_globals
def show_globals(self):
gv = self.globalsviewer
if self.vglobals.get():
if not gv:
self.globalsviewer = NamespaceViewer(self.fglobals, "Globals")
else:
if gv:
self.globalsviewer = None
gv.close()
self.fglobals['height'] = 1
self.show_variables()
#@-node:ekr.20060516135654.206:show_globals
#@+node:ekr.20060516135654.207:show_variables
def show_variables(self, force=0):
lv = self.localsviewer
gv = self.globalsviewer
frame = self.frame
if not frame:
ldict = gdict = None
else:
ldict = frame.f_locals
gdict = frame.f_globals
if lv and gv and ldict is gdict:
ldict = None
if 1:
if lv:
### lv.load_dict(ldict, force, self.pyshell.interp.rpcclt)
lv.load_dict(ldict, force, self.interp.rpcclt)
if gv:
### gv.load_dict(gdict, force, self.pyshell.interp.rpcclt)
gv.load_dict(gdict, force, self.interp.rpcclt)
#@-node:ekr.20060516135654.207:show_variables
#@-node:ekr.20060516135654.201:refresh
#@+node:ekr.20060516135654.208:breakpoints
#@+node:ekr.20060516135654.209:set_breakpoint_here
def set_breakpoint_here(self, filename, lineno):
self.idb.set_break(filename, lineno)
#@nonl
#@-node:ekr.20060516135654.209:set_breakpoint_here
#@+node:ekr.20060516135654.210:clear_breakpoint_here
def clear_breakpoint_here(self, filename, lineno):
self.idb.clear_break(filename, lineno)
#@-node:ekr.20060516135654.210:clear_breakpoint_here
#@+node:ekr.20060516135654.211:clear_file_breaks
def clear_file_breaks(self, filename):
self.idb.clear_all_file_breaks(filename)
#@-node:ekr.20060516135654.211:clear_file_breaks
#@+node:ekr.20060516135654.212:load_breakpoints
def load_breakpoints(self):
g.trace()
if 0: # "Load PyShellEditorWindow breakpoints into subprocess debugger"
pyshell_edit_windows = self.pyshell.flist.inversedict.keys()
for editwin in pyshell_edit_windows:
filename = editwin.io.filename
try:
for lineno in editwin.breakpoints:
self.set_breakpoint_here(filename, lineno)
except AttributeError:
continue
#@nonl
#@-node:ekr.20060516135654.212:load_breakpoints
#@-node:ekr.20060516135654.208:breakpoints
#@-others
#@nonl
#@-node:ekr.20060516135654.183:class Debugger
#@-others
#@nonl
#@-node:ekr.20060516135654.94:@thin leo_Debugger.py
#@-leo
|