From 5f8e1803c076e196e7db6c0403c65e9cf2ebd7b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 9 Nov 2020 18:23:57 +0100 Subject: [PATCH] Add handler for Python logging module. --- src/runner/pylogger.cpp | 69 +++++++++++++++++++++++++++++++++++++ src/runner/pylogger.h | 10 ++++++ src/runner/pythonrunner.cpp | 3 ++ 3 files changed, 82 insertions(+) create mode 100644 src/runner/pylogger.cpp create mode 100644 src/runner/pylogger.h diff --git a/src/runner/pylogger.cpp b/src/runner/pylogger.cpp new file mode 100644 index 0000000..111f571 --- /dev/null +++ b/src/runner/pylogger.cpp @@ -0,0 +1,69 @@ +#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(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() +{ + // 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, "MO2Handler", bpy::make_tuple(handler), methods); + + // Call basicConfig() with a new instance of our handler. + auto kwargs = bpy::dict(); + kwargs["handlers"] = bpy::make_tuple(MO2Handler()); + kwargs["level"] = 0; + logging.attr("basicConfig")(*bpy::make_tuple(), **kwargs); +} \ No newline at end of file diff --git a/src/runner/pylogger.h b/src/runner/pylogger.h new file mode 100644 index 0000000..20c1e9c --- /dev/null +++ b/src/runner/pylogger.h @@ -0,0 +1,10 @@ +#ifndef MO2_PYTHON_LOGGER_H +#define MO2_PYTHON_LOGGER_H + +/** + * @brief Configure logging for MO2 python plugin. + * + */ +void configure_python_logging(); + +#endif \ No newline at end of file diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index a500ad9..bb1603f 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(); + PyEval_SaveThread(); return true; } catch (const bpy::error_already_set&) {