/** * Convert a python error state to a C++ exception * @param withTrace If true then a traceback will be included in the exception * message * @throws std::runtime_error */ void throwRuntimeError(const bool withTrace) { GlobalInterpreterLock gil; if (!PyErr_Occurred()) { throw std::runtime_error( "ErrorHandling::throwRuntimeError - No Python error state set!"); } PyObject *exception(nullptr), *value(nullptr), *traceback(nullptr); PyErr_Fetch(&exception, &value, &traceback); PyErr_NormalizeException(&exception, &value, &traceback); PyErr_Clear(); PyObject *str_repr = PyObject_Str(value); std::stringstream msg; if (value && str_repr) { msg << PyString_AsString(str_repr); } else { msg << "Unknown exception has occurred."; } if (withTrace) { tracebackToMsg(msg, reinterpret_cast<PyTracebackObject *>(traceback)); } // Ensure we decrement the reference count on the traceback and exception // objects as they hold references to local the stack variables that were // present when the exception was raised. This could include child algorithms // with workspaces stored that would not otherwise be cleaned up until the // program exited. Py_XDECREF(traceback); Py_XDECREF(exception); Py_XDECREF(value); // Raise this error as a C++ error throw std::runtime_error(msg.str()); }
/** * Form a traceback * @param msg The reference to the textstream to accumulate the message * @param traceback A traceback object * @param root If true then this is the root of the traceback */ void PythonScript::tracebackToMsg(QTextStream &msgStream, PyTracebackObject *traceback, bool root) { if (traceback == nullptr) return; msgStream << "\n "; if (root) msgStream << "at"; else msgStream << "caused by"; int lineno = traceback->tb_lineno; QString filename = QString::fromAscii(TO_CSTRING(traceback->tb_frame->f_code->co_filename)); if (filename == identifier().c_str()) { lineno = getRealLineNo(lineno); sendLineChangeSignal(lineno, true); } msgStream << " line " << lineno << " in \'" << filename << "\'"; tracebackToMsg(msgStream, traceback->tb_next, false); }
/** * This emits the error signal and resets the error state * of the python interpreter. */ void PythonScript::emit_error() { // gil is necessary so other things don't continue ScopedPythonGIL lock; // return early if nothing happened if (!PyErr_Occurred()) { emit finished(MSG_FINISHED); return; } // get the error information out PyObject *exception(nullptr), *value(nullptr), *traceback(nullptr); PyErr_Fetch(&exception, &value, &traceback); // special check for system exceptions if (bool(exception) && PyErr_GivenExceptionMatches(exception, PyExc_SystemExit) && PyObject_HasAttrString(exception, "code")) { // value is the return code handed to sys.exit long code = 0; if (bool(value) && INT_CHECK(value)) { code = TO_LONG(value); } // if we are returning 0 then cleanup and return if (code == 0) { // maybe shouldn't clear the error, but for now this // is the agreed upon behavior PyErr_Clear(); Py_XDECREF(traceback); Py_XDECREF(exception); Py_XDECREF(value); emit finished(MSG_FINISHED); return; } } // prework on the exception handling PyErr_NormalizeException(&exception, &value, &traceback); PyErr_Clear(); // convert the traceback into something useful int lineNumber = 0; QString filename; if (traceback) { PyTracebackObject *tb = (PyTracebackObject *)traceback; lineNumber = tb->tb_lineno; filename = TO_CSTRING(tb->tb_frame->f_code->co_filename); } // the error message is the full (formated) traceback PyObject *str_repr = PyObject_Str(value); QString message; QTextStream msgStream(&message); if (value && str_repr) { if (exception == PyExc_SyntaxError) { msgStream << constructSyntaxErrorStr(value); } else { QString excTypeName( value->ob_type ->tp_name); // This is fully qualified with the module name excTypeName = excTypeName.section(".", -1); msgStream << excTypeName << ": " << TO_CSTRING(str_repr); } } else { msgStream << "Unknown exception has occurred."; } tracebackToMsg(msgStream, (PyTracebackObject *)(traceback)); msgStream << "\n"; Py_XDECREF(traceback); Py_XDECREF(exception); Py_XDECREF(value); emit error(msgStream.readAll(), filename, lineNumber); }