Am reopening this issue as due to work on mod_wsgi I believe I have discovered that problems with this are nothing to do with signal handlers.
The first source of Python exceptions that were seen when process was being shutdown were due to the fact that PyFinalize() was not being called from what the main Python interpreter's 'threading' module saw as being a valid Python thread, ie., it expected it to be the main thread for the interpreter, when in fact it was a foreign thread created outside of Python. The consequence of this was that when Py_Finalize() was called and it triggered sys.exitfunc() to call any atexit registered callbacks, a callback registered by the 'threading' module was called and because the thread ID wasn't in the table of Python threads, it threw a Python exception.
The next problem is with sub interpreters other than the main Python interpreter. The issue here was that Py_Finalize() only calls sys.exitfunc() on the main Python interpreter. As a consequence, any thread cleanup is not done in sub interpreters when they are being destroyed and depending on what a thread was doing, this could cause the process to crash.
What should happen on process shutdown is that it should iterate over all interpreters, create a thread state against each interpreter in turn, call 'threading.currentThread()' to ensure that 'threading' creates a dummy thread object for that thread state, then call sys.exitfunc(). After having done that, then it can call Py_Finalize().
By ensuring that exit funcs are called, means that atexit.register() then becomes an alternate way to register functions to be called on process shutdown, which is a much better option for generic modules than trying to hook into apache.register_cleanup().