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 1/4] 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&) { From c45bfb1e3595b3f776fbb2cfc4bf63afaae195c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 9 Nov 2020 18:28:58 +0100 Subject: [PATCH 2/4] Cast the record message to str() before extracting. --- src/runner/pylogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runner/pylogger.cpp b/src/runner/pylogger.cpp index 111f571..3eb4c31 100644 --- a/src/runner/pylogger.cpp +++ b/src/runner/pylogger.cpp @@ -23,7 +23,7 @@ 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")); + const std::wstring msg = bpy::extract(bpy::str(record.attr("msg"))); switch (level) { case PyLogLevel::CRITICAL: From ebb566f773ac42b25a39c2a9ef28678078f105c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 9 Nov 2020 19:17:57 +0100 Subject: [PATCH 3/4] Expose LogHandler in mobase. --- src/runner/pylogger.cpp | 6 ++++-- src/runner/pylogger.h | 5 ++++- src/runner/pythonrunner.cpp | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/runner/pylogger.cpp b/src/runner/pylogger.cpp index 3eb4c31..1e085fd 100644 --- a/src/runner/pylogger.cpp +++ b/src/runner/pylogger.cpp @@ -43,7 +43,7 @@ void emit_function(bpy::object self, bpy::object record) { } }; -void configure_python_logging() +bpy::object 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 @@ -59,11 +59,13 @@ void configure_python_logging() // 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); + auto MO2Handler = bpy::call(type, "LogHandler", 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); + + return MO2Handler; } \ No newline at end of file diff --git a/src/runner/pylogger.h b/src/runner/pylogger.h index 20c1e9c..177a2c5 100644 --- a/src/runner/pylogger.h +++ b/src/runner/pylogger.h @@ -1,10 +1,13 @@ #ifndef MO2_PYTHON_LOGGER_H #define MO2_PYTHON_LOGGER_H +#include + /** * @brief Configure logging for MO2 python plugin. * + * @return the log handler class. */ -void configure_python_logging(); +boost::python::object configure_python_logging(); #endif \ No newline at end of file diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index bb1603f..25d338b 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -1197,7 +1197,7 @@ bool PythonRunner::initPython(const QString &pythonPath) "sys.excepthook = lambda x, y, z: sys.__excepthook__(x, y, z)\n", mainNamespace); - configure_python_logging(); + mainNamespace["mobase"].attr("LogHandler") = configure_python_logging(); PyEval_SaveThread(); return true; From 04e1af60a0b1b2ce3883c5451bea8164204c9d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 9 Nov 2020 20:08:29 +0100 Subject: [PATCH 4/4] Do not set basicConfig() but provide a default logger. --- src/runner/pylogger.cpp | 21 ++++++++++++--------- src/runner/pylogger.h | 4 ++-- src/runner/pythonrunner.cpp | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/runner/pylogger.cpp b/src/runner/pylogger.cpp index 1e085fd..29922c9 100644 --- a/src/runner/pylogger.cpp +++ b/src/runner/pylogger.cpp @@ -43,7 +43,7 @@ void emit_function(bpy::object self, bpy::object record) { } }; -bpy::object configure_python_logging() +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 @@ -51,7 +51,7 @@ bpy::object configure_python_logging() // Retrieve the logging module and the Handler class. auto logging = bpy::import("logging"); - auto handler = logging.attr("Handler"); + auto Handler = logging.attr("Handler"); // This is ugly but that's how it's done in C Python. auto type = (PyObject*)&PyType_Type; @@ -59,13 +59,16 @@ bpy::object configure_python_logging() // 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); + auto MO2Handler = bpy::call(type, "LogHandler", 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); + // 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); - return MO2Handler; + // 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 index 177a2c5..df6cd8f 100644 --- a/src/runner/pylogger.h +++ b/src/runner/pylogger.h @@ -6,8 +6,8 @@ /** * @brief Configure logging for MO2 python plugin. * - * @return the log handler class. + * @param mobase The mobase module. */ -boost::python::object configure_python_logging(); +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 25d338b..f3511cc 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -1197,7 +1197,7 @@ bool PythonRunner::initPython(const QString &pythonPath) "sys.excepthook = lambda x, y, z: sys.__excepthook__(x, y, z)\n", mainNamespace); - mainNamespace["mobase"].attr("LogHandler") = configure_python_logging(); + configure_python_logging(mainNamespace["mobase"]); PyEval_SaveThread(); return true;