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

Recommended Answers

All 17 Replies

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 :)

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.

Thanks grib, your tracer mod looks very useful.. I'm just starting to get into decorators.. Thanks for the replies

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.

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'

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.

..... 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.

..... 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)

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()

Awesomely perfect. You're a genious

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

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.

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

hmm, difficult to help without the code which produces the bug...

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

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.

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.