%Module(name=qgis._core, keyword_arguments="All") ${DEFAULTDOCSTRINGSIGNATURE} %ModuleCode #include "qgsexception.h" #include "qgslogger.h" QString getTraceback() { #define TRACEBACK_FETCH_ERROR(what) {errMsg = what; goto done;} // acquire global interpreter lock to ensure we are in a consistent state PyGILState_STATE gstate; gstate = PyGILState_Ensure(); QString errMsg; QString result; PyObject *modStringIO = nullptr; PyObject *modTB = nullptr; PyObject *obStringIO = nullptr; PyObject *obResult = nullptr; PyObject *type, *value, *traceback; PyErr_Fetch( &type, &value, &traceback ); PyErr_NormalizeException( &type, &value, &traceback ); const char *iomod = "io"; modStringIO = PyImport_ImportModule( iomod ); if ( !modStringIO ) TRACEBACK_FETCH_ERROR( QString( "can't import %1" ).arg( iomod ) ); obStringIO = PyObject_CallMethod( modStringIO, ( char * ) "StringIO", nullptr ); /* Construct a cStringIO object */ if ( !obStringIO ) TRACEBACK_FETCH_ERROR( "cStringIO.StringIO() failed" ); modTB = PyImport_ImportModule( "traceback" ); if ( !modTB ) TRACEBACK_FETCH_ERROR( "can't import traceback" ); obResult = PyObject_CallMethod( modTB, ( char * ) "print_exception", ( char * ) "OOOOO", type, value ? value : Py_None, traceback ? traceback : Py_None, Py_None, obStringIO ); if ( !obResult ) TRACEBACK_FETCH_ERROR( "traceback.print_exception() failed" ); Py_DECREF( obResult ); obResult = PyObject_CallMethod( obStringIO, ( char * ) "getvalue", nullptr ); if ( !obResult ) TRACEBACK_FETCH_ERROR( "getvalue() failed." ); /* And it should be a string all ready to go - duplicate it. */ if ( !PyUnicode_Check( obResult ) ) TRACEBACK_FETCH_ERROR( "getvalue() did not return a string" ); result = QString::fromUtf8( PyUnicode_AsUTF8( obResult ) ); done: // All finished - first see if we encountered an error if ( result.isEmpty() && !errMsg.isEmpty() ) { result = errMsg; } Py_XDECREF( modStringIO ); Py_XDECREF( modTB ); Py_XDECREF( obStringIO ); Py_XDECREF( obResult ); Py_XDECREF( value ); Py_XDECREF( traceback ); Py_XDECREF( type ); // we are done calling python API, release global interpreter lock PyGILState_Release( gstate ); return result; } %End %Feature HAVE_GUI %Feature HAVE_QTSERIALPORT %Feature HAVE_QTPRINTER %Feature ANDROID %Feature VECTOR_MAPPED_TYPE %Feature HAVE_WEBENGINE_SIP %Import QtXml/QtXmlmod.sip %Import QtNetwork/QtNetworkmod.sip %Import QtSql/QtSqlmod.sip %Import QtPrintSupport/QtPrintSupportmod.sip %Import QtWidgets/QtWidgetsmod.sip %Import QtPositioning/QtPositioningmod.sip %If (HAVE_QTSERIALPORT) %Import QtSerialPort/QtSerialPortmod.sip %End %Include conversions.sip %Include qgsexception.sip %Include typedefs.sip %Include std.sip %Include core_auto.sip %VirtualErrorHandler processing_exception_handler // if an explicit QgsProcessingException was raised, we don't retrieve // and append the trace. It's too noisy for these "expected" type errors. // For all other exceptions, it's likely a coding error in the algorithm, // so we DO retrieve the full traceback for debugging. PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyTypeObject* err = reinterpret_cast< PyTypeObject* >( PyErr_Occurred() ); const bool isProcessingException = err && QString( err->tp_name ) == QStringLiteral( "QgsProcessingException" ); QString what; if ( isProcessingException ) { PyObject *type, *value, *traceback; PyErr_Fetch( &type, &value, &traceback ); // check whether the object is already a unicode string if ( PyUnicode_Check( value) ) { what = QString::fromUtf8( PyUnicode_AsUTF8( value ) ); } else { PyObject* str = PyObject_Str( value ); what = QString::fromUtf8( PyUnicode_AsUTF8( str ) ); Py_XDECREF( str ); } PyGILState_Release( gstate ); } else { PyGILState_Release( gstate ); QString trace = getTraceback(); QgsLogger::critical( trace ); what = trace; } SIP_RELEASE_GIL( sipGILState ); throw QgsProcessingException( what ); %End