Redneck's Debugger. source


  Screen shot of the 175 200 208 248 250  260 300 320 333 lines debugger, written in python and can debug python. Source below. run the debugger in the lib tkinter library, and invoke with:
python name_you_gave_it.py. Make sure you got a copy of the python.org interpreter.





Recent changes, printing correct info on break.

import tkinter,sys,inspect,filedialog
version = "1.485"

def TabCount(s) :
 i =0
 max = len(s)
 while (s[i:i+1].isspace()) & (i < max ): i=i+1
 return i
#TabCount = lambda d,s : s.count(d, 0,len(s))
#define a snippet under test
class Snippet :
 def __init__(self):
  self.root = None
  self.flag = -2
  self.maxline = 1
  self.prevlineno = 1
  self.lineno = 1
  self.source_name = "none"
  self.init_name = 'none'
  self.source_text = "pass"
  self.break_text = "pass"
  self.status_text = "idle"
  self.usertext = "none"
  self.frame = None
  self.packagename = "none" # Import this user defined module on request
  self.globals = None
  self.strvar = None
  self.source_line = 'pass'
  self.breaks = []
  self.lines = []
  self.start = 1
  self.end = 1
  self.trace_option = 3
  self.sourcesize = 1
 def list(self) : print(self.lineno,self.maxline,self.source_name,self.source_text,self.sourcesize)
   # find the char index pointing to given line
 def markline(self,index) :
  i = index
  for i in range( index,self.sourcesize-1) :
   if self.source_text[i] == '\n' : break
   i = i+1
  return i
 def setmsg(self,text) :
  self.status_text=text
  self.strvar.set(self.status_text)
  self.root.update_idletasks()
  self.root.update()
 def run(self,f) :
  self.flag= 1
  try :
   exec(self.source_text,self.globals)
  except(IOError,EOFError,LookupError,RuntimeError, TypeError, NameError,UnboundLocalError) as e:
   self.setmsg("Source error: %s :%3d:  %s" % (e, TestCode.lineno,TestCode.source_line ))
####  Helpers
SIndex = lambda j : ("%d.0")%(j)
DPtr = lambda str,chr : str.find(chr,0,len(str))
Atom = lambda x,y : x[0:DPtr(x,y)-1]
iLine = lambda index : int(index[0:DPtr(index,'.')])
iChar = lambda index : int(index[DPtr(index,'.')-1:3])

 ######## Call backs from interpreter
from inspect import getframeinfo, stack

def WaitLoop(event,frame):
 global TestCode
 global DisplayUpdate
 if(TestCode.flag == 3) :
  exec(TestCode.breakexpression)  # this should set the flag for futher processing)
  if(TestCode.flag != 1) : return
 if TestCode.flag == 2 and TestCode.breaks.count(frame.f_lineno) == 0 : return
 # we are stepping
 TestCode.frame = frame
 TestCode.lineno = frame.f_lineno
 #print(inspect.getsource(frame))
 if TestCode.flag == 0 :
  print("Recurse  error ")
  return
 if frame.f_code.co_filename == '<string>' :
  TestCode.flag = 0
  TestCode.source_line= TestCode.source_text[TestCode.lines[frame.f_lineno-1]:TestCode.lines[frame.f_lineno]]
  TestCode.setmsg("Trace: %4s :%3d:  %s" % (event, TestCode.lineno,TestCode.source_line ))
  if TestCode.lineno != TestCode.prevlineno : DisplayUpdate()
  while True :
   if TestCode.flag != 0 :
    #print("Exit loop")
    break
   TestCode.root.update_idletasks()
   TestCode.root.update()

def getfile() :
 global TestCode
 TestCode.status_text = "File Opened "
 r = tkinter.Tk()
 r.withdraw()
 fd = filedialog.LoadFileDialog(r)
 fileobj = fd.go(key="test")
 return(fileobj)
def inittest() :
 global TestCode,source_text
 filename = TestCode.source_name
 fobj=open(filename,'r')
 TestCode.source_text =  fobj.read()
 fobj.close()
 TestCode.globals = dict()
 TestCode.sourcesize = len(TestCode.source_text)
 # Add line numbersgetfile
 index = -1
 count  = 1
 source_text.delete("0.0",'end')
 while index <  TestCode.sourcesize-1 :
  prev = index
  index = TestCode.markline(prev+1)
  line = ("%3d %s\n") % (count,TestCode.source_text[prev+1:index])
  source_text.insert('end', line)
  TestCode.lines.append(prev)
  count = count + 1
def proffun(frame,event,arg) :
 global TestCode
 if (TestCode.trace_option & 2) and (TestCode.flag > 0 ): WaitLoop(event,frame)
 return(proffun)
def tracefun(frame,event,arg) :
 global TestCode
 if (TestCode.trace_option & 1) and (TestCode.flag > 0 ) > 0 : WaitLoop(event,frame)
 return(tracefun)

#call backs for  widgets.
 ############### Call backs ########
def QuitCallback() :
 global TestCode
 TestCode.flag =-2
 sys.settrace(None)
 sys.setprofile(None)
 TestCode.setmsg("Exit")
 exit()

# Use eval to grab a source svatiable value, return it in a string
def ScriptCallback() :
 TestCode.source_name = getfile()
 inittest()
def InitCallback() :
 filename= getfile()
 TestCode.init_name = filename
 print(filename)
 fobj=open(filename,'r')
 TestCode.usertext =  fobj.read()
 fobj.close()
 print(TestCode.usertext )
 exec(TestCode.usertext)

def ExtractValFromPython(varname) :
 global TestCode
 try :
  result = eval(varname,TestCode.globals)
 except(LookupError,RuntimeError, TypeError, NameError,UnboundLocalError) as e:
  result=e
 return(result)
def DisplayUpdate() :
 global WatchText
 global source_text
 global TestCode
 p = TestCode.prevlineno
 c = TestCode.lineno
 TestCode.prevlineno = TestCode.lineno
 source_text.tag_remove('halt', SIndex(p),SIndex(p+1))
 source_text.tag_add('halt', SIndex(c),SIndex(c+1))
 source_text.see(SIndex(c))
 max = iLine(WatchText.index('end'))
 i = max -2
 while i > 0  : # zero based count and we also skip the last "\n" char
  varName =  WatchText.get(SIndex(i),SIndex(i+1))
  varName.replace("\n"," ")
  j = DPtr(varName," ")
  if j < 0 : j = len(varName)
  ReturnString = ExtractValFromPython(varName[0:j])
  WatchString =  ("%s      %s\n") % (varName[0:j], ReturnString)
  WatchText.replace(SIndex(i),SIndex(i+1),WatchString)
  i = i - 1
  # check userdefined break
 if (TestCode.flag == 3)  and (TestCode.breaktext != "none") :
  TestCode.flag =  eval(TestCode.breaktext)
def RestartMsg() :
  global TestCode
  TestCode.setmsg("End File, Restart:  %3d" % (TestCode.lineno))
def StepCallback(event) :
 global TestCode
 if TestCode.flag < 0 : RestartMsg()
 else : TestCode.flag = 1
# run the whole code
def UntilCallback(event) : # wait for break
 global TestCode
 if TestCode.flag < 0 : RestartMsg() # just restart
 else : TestCode.flag = 2
def StartCallback(event) : # Start fresh
 global TestCode
 # Shut down the call back if waiting
 TestCode.flag = -2
 TestCode.root.update_idletasks()
 TestCode.root.update()
 TestCode.run(1) # just restart
def StopCallback(event) : # wait for break
 global TestCode
 TestCode.flag = -2

def AddWatchCallback(event) :
 global WatchText,source_text
 t = source_text.selection_get()
 t.strip()
 WatchText.insert('end', ("%s      %s\n") % (t,"None" ) )
 DisplayUpdate()
def DelWatchCallback(event) :
 global WatchText
 i = iLine(WatchText.index('insert'))
 WatchText.delete(SIndex(i),SIndex(i+1))
def AddBreakCallback(event) :
 global source_text
 global TestCode
 i = source_text.index('insert')
 l = iLine(i)
 if TestCode.breaks.count(l) == 0  :
  TestCode.breaks.append(l)
  source_text.tag_add('break', SIndex(l),SIndex(l+1))
def DelBreakCallback(event) :
 global source_text
 i = source_text.index('insert')
 l = iLine(i)
 if TestCode.breaks.count(l) > 0 :
  TestCode.breaks.remove(l)
  source_text.tag_remove('break', SIndex(l),SIndex(l+1))
def popupmsg(msg):
 popup = tkinter.Tk()
 popup.wm_title("!")
 label = tkinter.Label(popup, text=msg,justify='left')
 label.pack(side="top", fill="x", pady=10)
 B1 = tkinter.Button(popup, text="Okay", command = popup.destroy)
 B1.pack()
 popup.mainloop()

 # constructors for the GUI
def NextButton(w,name,callback) :
 b = tkinter.Button(w,text=name)
 b.pack(side='left')
 b.bind("<Button-1>", callback)
def AddMenu(root) :
 menubar = tkinter.Menu(root)
 menubar.add_command(label="Files", command=ScriptCallback)
 menubar.add_command(label="Init", command=InitCallback)
 menubar.add_command(label="Help", command=HelpCallback)
 menubar.add_command(label="Quit", command=QuitCallback)
 root.config(menu=menubar)
 #menubar.pack(side='left')
def controls(r) :
 global TestCode
 NextButton(r,"Start",StartCallback)
 NextButton(r,"Halt",StopCallback)
 NextButton(r,"Until",UntilCallback)
 NextButton(r,"Step",StepCallback)
 NextButton(r,"Br Del",DelBreakCallback)
 NextButton(r,"Br Add",AddBreakCallback)
 NextButton(r,"Watch",AddWatchCallback)
 NextButton(r,"Drop",DelWatchCallback)
 b = tkinter.Label(r,textvariable=TestCode.strvar)
 b.pack(side='left')
 #Inline help text makes the debugger self contained
helptext = \
"\nThe online help for pbug, " + version + " a lightweight python debugger\
\n Contact:youngsanger at gmail dot com.  \n\nThis app is a light weight debugger written in python and using \
\nthe python.org interpreter The mini debuger is  an entirely self contained debugger, requires these modules:\
\ntkinter, sys and inspect. And has its own inline help.  A single file but aloows user specified\ initialization and analysis source\n\
\n Written 350 lines of code including self contained help,it is designed for easy maintainence and modification.\
\n This is a fully functional. Start the debugger from icon or command line with\n\
\n..\..\python pbug.py        ## this starts pdbug in the  tkinter folder.\n\
\nThese are the buttons:\n " + "\
\nFile - Loads a file and keeps the original copy in a string.  The text box is loaded with a copy having \
\nthe line number inserted.  All parts of  the class Snippet are set up to manage the file under test.\
\nStart - Loads the source string into exec with the run flags set to step.\
\nUntil and Step - Until runs free until a break point, Step goes one step\
\n   Note:  Two trace functions are both working and we twice per line. \
\nWatch and Drop - Adds and deletes watch variable. Select variable on source window for add and put cursor\
\n on watch line for delete. \
\n"

def HelpCallback() :
 global helptext
 popupmsg(helptext)

 # set up the frame and paned windows
global source_text
global WatchText

TestCode = Snippet()
TestCode.list()
TestCode.breaks.append(1)
# Tracers are set to shut
# but the call back is permanent
TestCode.flag = -2 # Traces shut
sys.settrace(tracefun)
sys.setprofile(proffun)

TestCode.root = tkinter.Tk()
TestCode.root.wm_title("Mini debugger for pythn Release " + version)
AddMenu(TestCode.root)
TestCode.strvar = tkinter.StringVar()
TestCode.strvar.set('Initializing')
m1 = tkinter.PanedWindow(TestCode.root,orient = 'vertical',width=1000)
m1.pack( fill = 'both',expand=1)
separator = tkinter.Frame(m1,height=2, bd=1, relief='sunken')

controls(separator)
m1.add(separator)
m2 = tkinter.PanedWindow( TestCode.root,orient='horizontal')

m1.add(m2)
source_text = tkinter.Text(TestCode.root) #left pane")
source_text.pack( padx=5, pady=5)
Scroller = tkinter.Scrollbar(source_text)

source_text.config(yscrollcommand=Scroller.set)
m2.add(source_text)
WatchText = tkinter.Text(TestCode.root)
WatchText.pack( padx=5, pady=5)
Scroller.pack(side='right',fill='y')
Scroller.config(command=source_text.yview) #ScrollCallback)
m2.add(WatchText)
source_text.tag_config('halt', background='red')
source_text.tag_config('break', background='blue')
print("5: watch",WatchText.index('end'),WatchText.index('insert'),WatchText.index('current'))

TestCode.root.mainloop()

No comments: