[PyQt] Exceptions in Python Implementations of Virtuals
Matt Newell
newellm at blur.com
Mon Oct 6 22:57:08 BST 2014
On Tuesday, September 30, 2014 05:23:08 PM Phil Thompson wrote:
> Florian asked this question but it didn't result in any discussion, so
> I'd like to have another poke at it.
>
> Currently, if an exception is raised by a Python re-implementation of a
> C++ virtual then the exception is printed and the application continues.
> A default result will be constructed by PyQt and returned to C++.
> Instead, should PyQt print the exception and then terminate (by calling
> qFatal())?
In some cases I wrap the python exception into a c++ exception which gives the
possibility of a generic exception handler in c++ being able to print the
python traceback, and it also allows the exception to be restored further up
in the call stack, for example when the application is written in python using
c++ components that have virtual member functions reimplemented in python.
Then you can properly catch the actual python exception from within python,
assuming that the c++ part of the stack can gracefully handling propagating an
exception.
It might be nice to be able to have this built in to sip.
Matt
.sip:
%Exception PythonException(SIP_Exception)
{
%TypeHeaderCode
#include <pyembed.h>
%End
%RaiseCode
SIP_BLOCK_THREADS
sipExceptionRef.restore();
SIP_UNBLOCK_THREADS
%End
};
.h:
class PythonException : public std::exception
{
public:
PythonException();
virtual ~PythonException() throw();
void restore();
virtual const char * what() const throw();
protected:
QByteArray mCString;
PyObject *type, *value, *traceback;
};
.cpp:
PythonException::PythonException()
: type(0)
, value(0)
, traceback(0)
{
SIP_BLOCK_THREADS
mCString = pythonExceptionTraceback().toUtf8();
PyErr_Fetch(&type, &value, &traceback);
SIP_UNBLOCK_THREADS
}
PythonException::~PythonException() throw()
{
Py_XDECREF(type);
Py_XDECREF(value);
Py_XDECREF(traceback);
}
void PythonException::restore()
{
SIP_BLOCK_THREADS
PyErr_Restore(type, value, traceback);
type = value = traceback = 0;
SIP_UNBLOCK_THREADS
}
const char * PythonException::what() const throw()
{
return mCString.constData();
}
QString pythonStackTrace()
{
QString ret;
SIP_BLOCK_THREADS
PyObject * traceback_module = PyImport_ImportModule("traceback");
if (traceback_module) {
/* Call the traceback module's format_exception function, which
returns a list */
PyObject * stack = PyObject_CallMethod(traceback_module,
"format_stack","");
if( PyList_Check(stack) ) {
PyObject * separator = PyUnicode_FromString("");
if( separator ) {
PyObject * retString = PyUnicode_Join(separator, stack);
if( retString ) {
ret = unwrapQVariant(retString).toString();
Py_DECREF(retString);
} else
ret = "PyUnicode_Join failed";
Py_DECREF(separator);
} else
ret = "PyUnicode_FromString failed";
Py_DECREF(stack);
} else {
LOG_1( "traceback.format_stack returned unexpected value" );
}
Py_XDECREF(stack);
Py_DECREF(traceback_module);
} else
LOG_1( "Unable to load the traceback module, can't get exception
text" );
SIP_UNBLOCK_THREADS
return ret;
}
More information about the PyQt
mailing list