This is really gross, and I apologize in advance, but I was able to strip it down to the essentials and still break it. This is wxPython 2.9.2/wxOSX_Cocoa, Mac OS 10.7, Python 2.7.2.
For long-running processes, which do all of the important work in my app, I have this procedure:
-
Create a Thread with a set of methods to call depending on the activity and exit value of the target function. In the context of the GUI, these methods simply create and post an event to the parent window.
-
Create a Pipe which the thread will listen on; the other end is passed to the target function wrapper
-
Create a Process object with the actual target function, wrapped in a try/except to propagate exceptions through the pipe back to the parent process
-
Start the thread, which in turn starts the process, and listens on the pipe for any kind of printed output, intermediate data, errors, or return value, any of which are passed back to the parent window via events.
-
When the target function completes, call join() on the child process, and exit the thread.
For some quick tasks where I don’t need to display printed output or intermediate data, I use a simple dialog with a wx.Gauge to handle the process. This is the attached sample. Normally this works just fine, especially if the target function returns without raising an exception. In the sample, this is the first dialog that pops up. There seems to be an unusually long pause of several seconds after the process finishes and the dialog closes, but at least it continues on. The second dialog is handling a function that immediately raises a RuntimeError. This should be safely intercepted, the thread and process should end, the dialog should finish and close, and the exception should be re-raised in the main thread. Instead, I get a blank dialog (attached), and although it calls self.EndModal(wx.ID_CANCEL), ShowModal() never actually returns. Inspecting the Python process with Apple’s Activity Monitor indicated that it was still running a modal dialog event loop.
So, I’m stuck - this is really dangerous, and it’s not the first time I’ve seen a dialog freeze up like this (I could never reproduce the previous instance in a self-contained script). In practice, when I try deliberately breaking it in the context of my entire app by raising an exception in a target function, it seems to respond okay. But since the minimal sample (which is also my regression test) breaks, I’m very concerned about how this will behave “in the wild”.
Any ideas?
-Nat
debug_process.py (11.7 KB)
