I have a wx app that I would like to actively debug (even after it is compiled via py2exe). Is there a way to hook all methods(without having to manually add code to each one) and have it print their names' as they execute. I've looked into both the "inspect" and "pdb" modules and have a very limited understanding of both.
Second question, is it possible to change a functions code "on the fly." ie afunction.func_code.co_code = "some compiled code". I'm just not sure how to compile the source.
-Thx
ihatehippies 11 Junior Poster
Gribouillis 1,391 Programming Explorer Team Colleague
I have a wx app that I would like to actively debug (even after it is compiled via py2exe). Is there a way to hook all methods(without having to manually add code to each one) and have it print their names' as they execute. I've looked into both the "inspect" and "pdb" modules and have a very limited understanding of both.
Second question, is it possible to change a functions code "on the fly." ie afunction.func_code.co_code = "some compiled code". I'm just not sure how to compile the source.
-Thx
For the first question, I once wrote a small module which uses AOP (aspect oriented programming) to trace python code. To use it, you must download the aspects module http://www.cs.tut.fi/~ask/aspects/index.shtml and also save my small module (attached file) as tracepect.py. Then in your wx python program, you can write
def main():
from tracepect import Tracer
import aspects
import sys
output_file = sys.stdout # or open("my_file.txt", "w")
t = Tracer(output_file)
functions = [
# put a list of the functions that you want to trace in this list, for example:
MyFrameClass.__init__,
MyFrameClass.OnFoo,
MyFrameClass.OnBar,
my_function,
# etc
]
aspects.with_wrap(t.wrapper, *functions)
# then run your code, for example
try:
MyApp().mainloop()
finally:
output_file.close()
The module tracepect.py is only a draft, but I already found it useful in many debugging cases. The great advantage of this is that you can trace execution without modifying a single bit of your code :)
This attachment is potentially unsafe to open. It may be an executable that is capable of making changes to your file system, or it may require specific software to open. Use caution and only open this attachment if you are comfortable working with zip files.
Edited by Gribouillis because: n/a
jcao219 18 Posting Pro in Training
You can use decorators, or you can use metaclasses to decorate all the methods in a class.
Then you can make the decorator modify each function on the fly.
ihatehippies 11 Junior Poster
Thanks grib, your tracer mod looks very useful.. I'm just starting to get into decorators.. Thanks for the replies
Gribouillis 1,391 Programming Explorer Team Colleague
I forgot to mention a feature of module tracepect above, you can modify the representation of an object in the trace depending on its type. For example suppose that you have this
def my_function(thing):
return len(thing)
class Thing(object):
def __init__(self, name):
self.name = name
Then we trace my_function, but we don't want the thing argument to appear as <Thing instance at 0xJh54878ff>
, we can define another representation
for Thing objects like this
from tracepect import tracerepr
@tracerepr.register(Thing)
def implem(instance):
return "<Thing '%s'>" % instance.name
Then the trace will show
my_function(<Thing 'Daniweb'>):
return 10
for example.
Edited by Gribouillis because: n/a
ihatehippies 11 Junior Poster
have you tried to wrap methods inside of a class without having to declare them explicitly? I tried the wrap_around_re but it raised an attribute error
aspects.wrap_around_re(MainFrame, '', t.wrapper)
File "C:\Python25\aspects\aspects.py", line 199, in wrap_around_re
File "C:\Python25\aspects\aspects.py", line 445, in wrap_around
File "C:\Python25\aspects\aspects.py", line 637, in _names_mce
AttributeError: 'function' object has no attribute 'im_class'
Gribouillis 1,391 Programming Explorer Team Colleague
If you want to wrap all the methods in a class, you can still write a function like this
import types
def wrap_class(tracer, klass):
methods = []
for name, member in vars(klass).items():
if name.startswith("__"):
continue # ignore special methods
if isinstance(member, types.MethodType):
methods.append(member)
if methods:
aspects.with_wrap(tracer.wrapper, *methods)
You could even add wrap_class as a method in the Tracer class, and add options to select methods with a finer granularity.
Edited by Gribouillis because: n/a
ihatehippies 11 Junior Poster
..... Thanks for all the help... I've been trying to hook methods inside of a wx.Frame. I've wrapped the functions in several ways, it never raises an error, but it never sends anything to the Tracer... Let me know if you've ever dealt with this.
Gribouillis 1,391 Programming Explorer Team Colleague
..... Thanks for all the help... I've been trying to hook methods inside of a wx.Frame. I've wrapped the functions in several ways, it never raises an error, but it never sends anything to the Tracer... Let me know if you've ever dealt with this.
Can you post example wx code that you can't wrap ?
Also there was an error in my previous post, it should be
def wrap_class(tracer, klass):
methods = []
for name in vars(klass):
member = getattr(klass, name)
if name.startswith("__"):
continue # ignore special methods
if isinstance(member, types.MethodType):
methods.append(member)
if methods:
aspects.with_wrap(tracer.wrapper, *methods)
Gribouillis 1,391 Programming Explorer Team Colleague
Here is an example where I trace the methods of Vegaseat's simple wx editor example.
I'm using a modified version of tracepect.py (attached file) where I added methods wrap_classes() and wrap() to the Tracer class
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, wx.ID_ANY, title,
size=(500, 300))
self.edit = wx.TextCtrl(self, 1, style=wx.TE_MULTILINE)
# statusBar at the bottom of the window
self.CreateStatusBar()
# set up the menu
filemenu= wx.Menu()
filemenu.Append(ID_ABOUT, "&About",
" Information about this program")
filemenu.Append(ID_LOAD,"File&Load", " Load a text file")
# the optional & allows you to use alt/s
# " Save a text file" shows in the status bar on mouse over
filemenu.Append(ID_SAVE,"File&Save", " Save a text file")
# put in a separator line
filemenu.AppendSeparator()
filemenu.Append(ID_EXIT,"E&xit"," Terminate the program")
# create the menubar
menuBar = wx.MenuBar()
# adding the "filemenu" to the MenuBar
menuBar.Append(filemenu,"&File")
# adding the MenuBar to the Frame content
self.SetMenuBar(menuBar)
# attach the menu-event ID_ABOUT to the method self.onAbout
wx.EVT_MENU(self, ID_ABOUT, self.onAbout)
# attach the menu-event ID_OPEN to the method self.onOpen
wx.EVT_MENU(self, ID_LOAD, self.onLoad)
# attach the menu-event ID_SAVE to the method self.onSave
wx.EVT_MENU(self, ID_SAVE, self.onSave)
# attach the menu-event ID_EXIT to the method self.onExit
wx.EVT_MENU(self, ID_EXIT, self.onExit)
# display the frame
self.Show(True)
def onAbout(self, event):
""" the about box """
about = wx.MessageDialog( self, " A very simple editor \n"
" using the wxPython GUI toolkit", "About Simple Editor", wx.OK)
about.ShowModal()
about.Destroy()
def onLoad(self, event):
""" open a file """
self.dirname = ''
mask = "Text (.txt)|*.txt|Python (.py)|*.py|All (.*)|*.*"
dlg = wx.FileDialog(self, "Choose a file to load",
self.dirname, "", wildcard=mask, style=wx.OPEN)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
filepath = os.path.join(self.dirname, self.filename)
fin = open(filepath, 'r')
self.edit.SetValue(fin.read())
fin.close()
dlg.Destroy()
def onSave(self, event):
""" save a file """
self.dirname = ''
mask = "Text (.txt)|*.txt|Python (.py)|*.py|All (.*)|*.*"
dlg = wx.FileDialog(self, "Choose or create a file to save to",
self.dirname, "", wildcard=mask,
style=wx.OVERWRITE_PROMPT|wx.SAVE)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
filepath = os.path.join(self.dirname, self.filename)
fout = open(filepath, 'w')
fout.write(self.edit.GetValue())
fout.close()
dlg.Destroy()
def onExit(self, e):
self.Close(True)
from tracepect import Tracer, tracerepr
@tracerepr.register(wx._core.CommandEvent)
def implem(instance):
return "<%s>" % instance.__class__.__name__
def main():
t = Tracer()
t.wrap_classes(MyFrame)
app = wx.App(0)
frame = MyFrame(None, -1, "A Simple Editor (click on File for menu)")
app.MainLoop()
if __name__ == "__main__":
main()
This attachment is potentially unsafe to open. It may be an executable that is capable of making changes to your file system, or it may require specific software to open. Use caution and only open this attachment if you are comfortable working with zip files.
Edited by Gribouillis because: n/a
ihatehippies 11 Junior Poster
Awesomely perfect. You're a genious
ihatehippies 11 Junior Poster
Hey grib the tracepect is working great, just found one anomaly. Occasionally, it reports that certain functions are being called several times with several returns.
27. 19:42:27 MainFrame.UpdTxt():
# call, line 2176 in RR Manager.py
return None
28. 19:42:27 MainFrame.UpdTxt():
# call, line 2176 in RR Manager.py
return None
return None
29. 19:42:27 MainFrame.UpdTxt():
# call, line 2176 in RR Manager.py
return None
return None
return None
30. 19:42:27 MainFrame.UpdTxt():
# call, line 2176 in RR Manager.py
return None
return None
return None
return None
I'm thinking it just tracking the returns from subfunctions that aren't 'wrapped.' It's not always the same function that is repeated, and adding
print "method xxx called"
ruled out that method actually being called 4 and 5 times. I'll keep looking into it, just wondering if you had experienced that before. thx again
Gribouillis 1,391 Programming Explorer Team Colleague
No I have'nt experienced that before, but in principle there should be only one 'return' per function called and the indentation should show which return matches which call. A first thing you could do is running your code with the 'print "method xxx called"' enabled and without the tracer. This would tell you if the tracer has anything to do with it.
Edited by Gribouillis because: n/a
ihatehippies 11 Junior Poster
Well the method is only being called once in reality. And the Tracer shows the returns indenting in reverse order, ie
return
return
return
return
Gribouillis 1,391 Programming Explorer Team Colleague
hmm, difficult to help without the code which produces the bug...
ihatehippies 11 Junior Poster
My last post didnt make much sense, the first return has the most indents (daniweb removed the excess indents)
Here is the offending method:
def UpdTxt(self):
try:
txt = "No Currency Data Loaded"
if IsShowingCurrencies():
txt = "(Last Updated: "+FormatTime(os.stat(con.currencyDB)[8])+")"
elif self.CurrentProfile.IsOccupied:
txt = "(Last Updated: "+FormatTime(self.CurrentProfile.lastupdated)+")"
else:
txt = ''
except:
pass
finally:
self.LastUpdTxt.SetLabel(txt)
This method is called from outside the 'MainFrame' class, but from inside another simple class
def LoadPos(self, pos):
window.ResetFieldColors()
if self.IsLocked:
assert Members.data[pos] == self.DumpValues()
self.pos = pos
else:
self.__init__(position = pos)
SetCurMem()
window.UpdTxt() # here is where it was called
If you want, I can send you all of the code, theres a few modules I'd need to give you as well
ihatehippies 11 Junior Poster
It was my error. I misinterpreted how the output was supposed to look. In your tracepect module I had a wx.TextCtrl as my "fileobj." The "write" method I had created was obscuring the output. I color coded the indents so it all looks very clean. Thanks again for the help.
Gribouillis 1,391 Programming Explorer Team Colleague
It was my error. I misinterpreted how the output was supposed to look. In your tracepect module I had a wx.TextCtrl as my "fileobj." The "write" method I had created was obscuring the output. I color coded the indents so it all looks very clean. Thanks again for the help.
Great, good luck with tracepect.py. If you improve it, don't forget to post your ideas.
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.