Skip to content

Commit

Permalink
Add SystemExit exception handler
Browse files Browse the repository at this point in the history
If enabled, the signal "systemExitExceptionRaised" will be emitted. It gives
application the opportunity to cleanup and terminate nicely.
  • Loading branch information
jcfr committed Nov 8, 2012
1 parent a386dc6 commit 3c84463
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 15 deletions.
114 changes: 99 additions & 15 deletions src/PythonQt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,7 @@ PythonQtPrivate::PythonQtPrivate()
_currentClassInfoForClassWrapperCreation = NULL;
_profilingCB = NULL;
_ErrorOccured = false;
_SystemExitExceptionHandlerEnabled = false;
}

void PythonQtPrivate::setupSharedLibrarySuffixes()
Expand Down Expand Up @@ -1217,26 +1218,93 @@ void PythonQtPrivate::removeSignalEmitter(QObject* obj)
_signalReceivers.remove(obj);
}

namespace
{
//! adapted from python source file "pythonrun.c", function "handle_system_exit"
//! return the exitcode instead of calling "Py_Exit".
//! it gives the application an opportunity to properly terminate.
int custom_system_exit_exception_handler()
{
PyObject *exception, *value, *tb;
int exitcode = 0;

// if (Py_InspectFlag)
// /* Don't exit if -i flag was given. This flag is set to 0
// * when entering interactive mode for inspecting. */
// return exitcode;

PyErr_Fetch(&exception, &value, &tb);
if (Py_FlushLine())
PyErr_Clear();
fflush(stdout);
if (value == NULL || value == Py_None)
goto done;
if (PyExceptionInstance_Check(value)) {
/* The error code should be in the `code' attribute. */
PyObject *code = PyObject_GetAttrString(value, "code");
if (code) {
Py_DECREF(value);
value = code;
if (value == Py_None)
goto done;
}
/* If we failed to dig out the 'code' attribute,
just let the else clause below print the error. */
}
if (PyInt_Check(value))
exitcode = (int)PyInt_AsLong(value);
else {
PyObject *sys_stderr = PySys_GetObject(const_cast<char*>("stderr"));
if (sys_stderr != NULL && sys_stderr != Py_None) {
PyFile_WriteObject(value, sys_stderr, Py_PRINT_RAW);
} else {
PyObject_Print(value, stderr, Py_PRINT_RAW);
fflush(stderr);
}
PySys_WriteStderr("\n");
exitcode = 1;
}
done:
/* Restore and clear the exception info, in order to properly decref
* the exception, value, and traceback. If we just exit instead,
* these leak, which confuses PYTHONDUMPREFS output, and may prevent
* some finalizers from running.
*/
PyErr_Restore(exception, value, tb);
PyErr_Clear();
return exitcode;
//Py_Exit(exitcode);
}
}

bool PythonQt::handleError()
{
bool flag = false;
if (PyErr_Occurred()) {

// currently we just print the error and the stderr handler parses the errors
PyErr_Print();

/*
// EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above
PyObject *ptype;
PyObject *pvalue;
PyObject *ptraceback;
PyErr_Fetch( &ptype, &pvalue, &ptraceback);
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptraceback);
*/
PyErr_Clear();
if (PythonQt::priv()->_SystemExitExceptionHandlerEnabled &&
PyErr_ExceptionMatches(PyExc_SystemExit)) {
int exitcode = custom_system_exit_exception_handler();
emit PythonQt::self()->systemExitExceptionRaised(exitcode);
}
else
{
// currently we just print the error and the stderr handler parses the errors
PyErr_Print();

/*
// EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above
PyObject *ptype;
PyObject *pvalue;
PyObject *ptraceback;
PyErr_Fetch( &ptype, &pvalue, &ptraceback);
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptraceback);
*/
PyErr_Clear();
}
flag = true;
}
PythonQt::priv()->_ErrorOccured = flag;
Expand All @@ -1256,6 +1324,22 @@ void PythonQt::resetErrorFlag()
}
}

void PythonQt::setSystemExitExceptionHandlerEnabled(bool value)
{
if (PythonQt::self())
{
PythonQt::priv()->_SystemExitExceptionHandlerEnabled = value;
}
}

bool PythonQt::systemExitExceptionHandlerEnabled()const
{
if (PythonQt::self())
{
return PythonQt::priv()->_SystemExitExceptionHandlerEnabled;
}
}

void PythonQt::addSysPath(const QString& path)
{
PythonQtObjectPtr sys;
Expand Down
15 changes: 15 additions & 0 deletions src/PythonQt.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,15 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
//! \sa PythonQt::errorOccured()
void resetErrorFlag();

//! if set to True, signal will be emitted if exception SystemExit is caught
//! \sa PythonQt::handleError(), PythonQt::
void setSystemExitExceptionHandlerEnabled(bool value);

//! return \a True if SystemExit exception is handled by PythonQt
//! \sa setSystemExitExceptionHandlerEnabled()
bool systemExitExceptionHandlerEnabled()const;


//! set a callback that is called when a QObject with parent == NULL is wrapped by pythonqt
void setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb);
//! set a callback that is called when a QObject with parent == NULL is no longer wrapped by pythonqt
Expand Down Expand Up @@ -517,6 +526,11 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
//! emitted when help() is called on a PythonQt object and \c ExternalHelp is enabled
void pythonHelpRequest(const QByteArray& cppClassName);

//! emitted when both custom SystemExit exception handler is enabled and a SystemExit
//! exception is raised.
//! \sa setSystemExitExceptionHandlerEnabled(bool)
void systemExitExceptionRaised(int exitCode);

private:
void initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQtModuleName);

Expand Down Expand Up @@ -716,6 +730,7 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
int _PythonQtObjectPtr_metaId;

bool _ErrorOccured;
bool _SystemExitExceptionHandlerEnabled;

friend class PythonQt;
};
Expand Down

0 comments on commit 3c84463

Please sign in to comment.