diff --git a/src/runner/error.cpp b/src/runner/error.cpp index 62a2346..e17cd14 100644 --- a/src/runner/error.cpp +++ b/src/runner/error.cpp @@ -1,8 +1,9 @@ #ifndef Q_MOC_RUN #include #endif -#include #include +#include +#include "error.h" using namespace MOBase; namespace bpy = boost::python; @@ -10,17 +11,55 @@ namespace bpy = boost::python; void reportPythonError() { if (PyErr_Occurred()) { - // prints to s_ErrIO buffer + ErrWrapper &errWrapper = ErrWrapper::instance(); + + errWrapper.startRecordingExceptionMessage(); PyErr_Print(); - // extract data from python buffer - bpy::object mainModule = bpy::import("__main__"); - bpy::object mainNamespace = mainModule.attr("__dict__"); - bpy::object errMsgObj = bpy::eval("s_ErrIO.getvalue()", mainNamespace); - QString errMsg = bpy::extract(errMsgObj.ptr()); - bpy::eval("s_ErrIO.truncate(0)", mainNamespace); + errWrapper.stopRecordingExceptionMessage(); + + QString errMsg = errWrapper.getLastExceptionMessage(); throw MyException(errMsg); } else { throw MyException("An unexpected C++ exception was thrown in python code"); } } + +ErrWrapper & ErrWrapper::instance() +{ + static ErrWrapper err; + return err; +} + +void ErrWrapper::write(const char * message) +{ + buffer << message; + if (buffer.tellp() != 0 && buffer.str().back() == '\n') + { + // actually put the string in a variable so it doesn't get destroyed as soon as we get a pointer to its data + std::string string = buffer.str().substr(0, buffer.str().length() - 1); + qCritical().nospace().noquote() << string.c_str(); + buffer = std::stringstream(); + } + + if (recordingExceptionMessage) + { + lastException << message; + } +} + +void ErrWrapper::startRecordingExceptionMessage() +{ + recordingExceptionMessage = true; + lastException = std::stringstream(); +} + +void ErrWrapper::stopRecordingExceptionMessage() +{ + recordingExceptionMessage = false; +} + +QString ErrWrapper::getLastExceptionMessage() +{ + return QString::fromStdString(lastException.str()); +} diff --git a/src/runner/error.h b/src/runner/error.h index 5d3d6ff..2cadb57 100644 --- a/src/runner/error.h +++ b/src/runner/error.h @@ -1,7 +1,26 @@ #ifndef ERROR_H #define ERROR_H +#include +#include // turn an error from the python interpreter into an exception void reportPythonError(); +struct ErrWrapper +{ + static ErrWrapper & instance(); + + void write(const char * message); + + void startRecordingExceptionMessage(); + + void stopRecordingExceptionMessage(); + + QString getLastExceptionMessage(); + + std::stringstream buffer; + bool recordingExceptionMessage; + std::stringstream lastException; +}; + #endif // ERROR_H diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 36b45a6..5c8d91f 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -1299,6 +1299,36 @@ PythonRunner::PythonRunner(const MOBase::IOrganizer *moInfo) static const char *argv0 = "ModOrganizer.exe"; +struct PrintWrapper +{ + void write(const char * message) + { + buffer << message; + if (buffer.tellp() != 0 && buffer.str().back() == '\n') + { + // actually put the string in a variable so it doesn't get destroyed as soon as we get a pointer to its data + std::string string = buffer.str().substr(0, buffer.str().length() - 1); + qDebug().nospace().noquote() << string.c_str(); + buffer = std::stringstream(); + } + } + + std::stringstream buffer; +}; + +// ErrWrapper is in error.h + +BOOST_PYTHON_MODULE(moprivate) +{ + bpy::class_("PrintWrapper", bpy::init<>()) + .def("write", &PrintWrapper::write); + bpy::class_("ErrWrapper", bpy::init<>()) + .def("instance", &ErrWrapper::instance, bpy::return_value_policy()).staticmethod("instance") + .def("write", &ErrWrapper::write) + .def("startRecordingExceptionMessage", &ErrWrapper::startRecordingExceptionMessage) + .def("stopRecordingExceptionMessage", &ErrWrapper::stopRecordingExceptionMessage) + .def("getLastExceptionMessage", &ErrWrapper::getLastExceptionMessage); +} bool PythonRunner::initPython(const QString &pythonPath) { @@ -1317,6 +1347,7 @@ bool PythonRunner::initPython(const QString &pythonPath) Py_SetProgramName(argBuffer); PyImport_AppendInittab("mobase", &PyInit_mobase); + PyImport_AppendInittab("moprivate", &PyInit_moprivate); Py_OptimizeFlag = 2; Py_NoSiteFlag = 1; initPath(); @@ -1331,11 +1362,13 @@ bool PythonRunner::initPython(const QString &pythonPath) bpy::object mainModule = bpy::import("__main__"); bpy::object mainNamespace = mainModule.attr("__dict__"); mainNamespace["sys"] = bpy::import("sys"); + mainNamespace["moprivate"] = bpy::import("moprivate"); bpy::import("site"); - mainNamespace["io"] = bpy::import("io"); - bpy::exec("s_ErrIO = io.StringIO()\n" - "sys.stderr = s_ErrIO", + bpy::exec("sys.stdout = moprivate.PrintWrapper()\n" + "sys.stderr = moprivate.ErrWrapper.instance()\n" + "sys.excepthook = lambda x, y, z: sys.__excepthook__(x, y, z)\n", mainNamespace); + return true; } catch (const bpy::error_already_set&) { qDebug("failed to init python");