[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