diff --git a/src/runner/pylogger.cpp b/src/runner/pylogger.cpp new file mode 100644 index 0000000..29922c9 --- /dev/null +++ b/src/runner/pylogger.cpp @@ -0,0 +1,74 @@ +#include "pylogger.h" + +#include "log.h" + +#include + +namespace bpy = boost::python; + +// Small structure to hold the levels - There are copy paste from +// my Python version and I assume these will not change soon: +struct PyLogLevel { + static constexpr int CRITICAL = 50; + static constexpr int ERROR = 40; + static constexpr int WARNING = 30; + static constexpr int INFO = 20; + static constexpr int DEBUG = 10; +}; + +// This is the function we are going to use as our Handler .emit +// method. +void emit_function(bpy::object self, bpy::object record) { + + // There are other parameters that could be used, but this is minimal for + // now (filename, line number, etc.). + const int level = bpy::extract(record.attr("levelno")); + const std::wstring msg = bpy::extract(bpy::str(record.attr("msg"))); + + switch (level) { + case PyLogLevel::CRITICAL: + case PyLogLevel::ERROR: + MOBase::log::error("{}", msg); + break; + case PyLogLevel::WARNING: + MOBase::log::warn("{}", msg); + break; + case PyLogLevel::INFO: + MOBase::log::info("{}", msg); + break; + case PyLogLevel::DEBUG: + default: // There is a "NOTSET" level in theory: + MOBase::log::debug("{}", msg); + break; + } +}; + +void configure_python_logging(bpy::object mobase) +{ + // Most of this is dealing with actual Python objects since it is not possible + // to derive from logging.Handler in C++ using Boost.Python, and since a lot of + // this would require extra register only for this. + + // Retrieve the logging module and the Handler class. + auto logging = bpy::import("logging"); + auto Handler = logging.attr("Handler"); + + // This is ugly but that's how it's done in C Python. + auto type = (PyObject*)&PyType_Type; + + // Create the "MO2Handler" python class: + auto methods = bpy::dict(); + methods["emit"] = bpy::make_function(emit_function); + auto MO2Handler = bpy::call(type, "LogHandler", bpy::make_tuple(Handler), methods); + + // Create the default logger: + auto handler = MO2Handler(); + handler.attr("setLevel")(PyLogLevel::DEBUG); + auto logger = logging.attr("getLogger")(bpy::object(mobase.attr("__name__"))); + logger.attr("setLevel")(PyLogLevel::DEBUG); + logger.attr("addHandler")(handler); + + // Set mobase attributes: + mobase.attr("LogHandler") = MO2Handler; + mobase.attr("logger") = logger; +} \ No newline at end of file diff --git a/src/runner/pylogger.h b/src/runner/pylogger.h new file mode 100644 index 0000000..df6cd8f --- /dev/null +++ b/src/runner/pylogger.h @@ -0,0 +1,13 @@ +#ifndef MO2_PYTHON_LOGGER_H +#define MO2_PYTHON_LOGGER_H + +#include + +/** + * @brief Configure logging for MO2 python plugin. + * + * @param mobase The mobase module. + */ +void configure_python_logging(boost::python::object mobase); + +#endif \ No newline at end of file diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 3e75531..7bb5a65 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -39,6 +39,7 @@ #include "tuple_helper.h" #include "variant_helper.h" #include "converters.h" +#include "pylogger.h" using namespace MOBase; @@ -1196,6 +1197,8 @@ bool PythonRunner::initPython(const QString &pythonPath) "sys.excepthook = lambda x, y, z: sys.__excepthook__(x, y, z)\n", mainNamespace); + configure_python_logging(mainNamespace["mobase"]); + PyEval_SaveThread(); return true; } catch (const bpy::error_already_set&) {