Debugging wxWidgets From wxPython
So you're on the trail of a bug and you've tracked the big nasty thing down to it's hidey hole, which turns out to be one of the wrapper functions in one of the wxPython extension modules. As your knees begin to wobble you wonder, "What can I do now? Do I dare descend from the wide open spaces of Python into the dark, twisty and pointery subterranean madness that is C++?"
In a flash RobinDunn is on the scene, hitches up his spandex long-johns and tosses his cape behind his back. As he puts his arm around your shoulders he says, "No worries my friend. Anything worth doing is... No, that's not it. Um... Give a man a fish... No that's not it either... With great power comes... These aren't the droids you're looking for... !@$#@ There once was a man from Nan... Er... Ah..." Suddenly in a puff of smoke that smells like something that escaped from a fried CPU Robin's snazzy skin-tight blue and red suit showing off his awesome pecks and huge biceps is replaced by a ratty old t-shirt, jean shorts, birkenstocks, skinny arms, sunken chest, a pot belly and two days worth of stubble on his pale, sullen face. He says, "Hey friend, it sure as hell ain't easy, but it is possible. Here's what I do..."
GDB on Linux or OS X
GDB is the GNU debugger program, which is a fairly powerful but incredibly complex tool. If you don't wave a dead chicken over the keyboard it may dump core all over your shoes, but when it works it works well, that is when you can figure out how to make it do what you want it to do. It just has a textual command line interface, which really sucks, so you'll definitely want to use it via some 3rd-party UI tool that wraps GDB in a bit nicer package. You'll want to make that the tool still gives you access to the gdb command line though, or at least allows you to connect to a running process and to specify breakpoints by function name, (automatic name completion is nice too.) If anybody feels up to it you can make a GDB UI Wrappers page here and list the names of some common tools and their pros and cons. Personally I tend to use the Grand Unified Debugger mode (gud) of Emacs, but that is a whole other bucket of self-flagellation that I won't go into at this time.
First things first: You'll need to have a debug build of wxWidgets and wxPython, and most likely a debug build of Python too.
Next, you'll need to know what the process ID of your program is so you can attach to it in the debugger. The easiest way to do this it to just tell yourself what it is. Add something like this to the beginning of your app, but after you have imported the wx pacakge. (I'll tell you why in a minute):
1 import os; print "My PID is:", os.getpid()
I put it all on one line because I am lazy and this allows me to comment out this code with a single keystroke.
You need to decide when and how to stop your program so you can break into it with the debugger. If you've already tracked the bug down to the C++ interface then you probably have a good idea where it is in the sequence of things. If the problem is happening as the program starts up, then you'll want to be able to break in with the debugger before the app is initialized and the main loop is started. An easy way to do this is to pause before you get that far, and just add a raw_input() on the same line where you printed the PID:
1 import os; print "My PID is:", os.getpid(); raw_input("Press Enter...")
If your problem happens later in the run sequence then you don't need this. Now just start your app normally and get it to the point when you are about to trigger the bug or to the "Press Enter..." message if you are using that. Now you need to run gdb, or whatever UI font-end you are using. Tell it to load the same python binary you used to run your app, and then tell it to attach to your process using the PID that was printed by the line above. At this point your app will be paused and is under the control of gdb.
Now you'll need to set a breakpoint in the function or functions that you are investigating. For all of the methods in all of the wrapped classes in wxPython there is a C function named like _wrap_ClassName_MethodName so you can use a name like that to set a breakpoint where you need it. Once your breakpoints are set, you just need to give the continue command so gdb will start running your app again, and then you can try to trigger your bug.
Now, if all went according to plan your breakpoint should be hit and the debugger will be waiting for you to tell it what to do. You can use the step, next, print, and continue commands like normal to try and track down the source of your problems.
MSVC on Windows
Using the MSVC debugger on windows is a bit easier. You can use the attach to process trick just like the above if you want, or you can create a simple "Makefile" project and in the project settings set the executable to run to the full path name of your debug python executable, set the working directory, and set the command line args so Python runs your app.
The wx.Trap() function is a way to insert a dynamic C++ breakpoint in your code. wx.Trap() calls the platform specific function that generates a break instruction for the debugger. This is good for when you don't want to set the C++ breakpoint right away, or if the problem happens only in certain code paths and you don't want to break in the debugger for other calls to the same function. Simply insert a call to wx.Trap() and run your program in the debugger like normal. Then when the trap is hit you'll land in the debugger where you can set the breakpoint you really need, and then continue.