From a776468970e137bbd74abad16aef53aaa81db111 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 5 Feb 2026 21:04:41 +0100 Subject: [PATCH 1/3] Redirect SOFA logs to Python's sys.stdout using a custom PythonMessageHandler. --- bindings/SofaRuntime/CMakeLists.txt | 2 + .../SofaRuntime/Module_SofaRuntime.cpp | 6 +- .../SofaRuntime/PythonMessageHandler.cpp | 93 +++++++++++++++++++ .../SofaRuntime/PythonMessageHandler.h | 41 ++++++++ 4 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp create mode 100644 bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h diff --git a/bindings/SofaRuntime/CMakeLists.txt b/bindings/SofaRuntime/CMakeLists.txt index 196701e1..52c4d80e 100644 --- a/bindings/SofaRuntime/CMakeLists.txt +++ b/bindings/SofaRuntime/CMakeLists.txt @@ -2,10 +2,12 @@ project(Bindings.SofaRuntime) set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Timer/Submodule_Timer.cpp ) set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/PythonMessageHandler.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Timer/Submodule_Timer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Timer/Submodule_Timer_doc.h ) diff --git a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp index c501fdb6..2c663607 100644 --- a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp +++ b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp @@ -62,7 +62,7 @@ using sofapython3::SceneLoaderPY3; #include using sofa::helper::logging::MessageDispatcher; using sofa::helper::logging::MainPerComponentLoggingMessageHandler; -using sofa::helper::logging::MainConsoleMessageHandler; +#include #include #include @@ -155,9 +155,9 @@ PYBIND11_MODULE(SofaRuntime, m) { m.def("init", []() { MessageDispatcher::clearHandlers(); - MessageDispatcher::addHandler(&MainConsoleMessageHandler::getInstance()); + MessageDispatcher::addHandler(&MainPythonMessageHandler::getInstance()); MessageDispatcher::addHandler(&MainPerComponentLoggingMessageHandler::getInstance()); - }); + }, "redirect SOFA messages to Python's sys.stdout"); m.add_object("DataRepository", py::cast(&sofa::helper::system::DataRepository)); m.add_object("PluginRepository", py::cast(&sofa::helper::system::PluginRepository)); diff --git a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp new file mode 100644 index 00000000..38996070 --- /dev/null +++ b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp @@ -0,0 +1,93 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include +#include + +namespace sofapython3 +{ + +namespace +{ + +using namespace std::string_literals; +auto red { "\033[31m"s }; +auto green { "\033[32m"s }; +auto orange { "\033[38;5;214m"s }; +auto magenta { "\033[35m"s }; +auto blue { "\033[34m"s }; +auto reset { "\033[0m"s }; + +std::string format(const std::string& type, const std::string& color) +{ + return color + "[" + type + "] " + reset; +}; + +std::string_view getPrefixText(sofa::helper::logging::Message::Type type) +{ + static std::string advice = format("SUGGESTION", green); + static std::string deprecated = format("DEPRECATED", orange); + static std::string warning = format("WARNING", orange); + static std::string info = format("INFO", green); + static std::string error = format("ERROR", red); + static std::string fatal = format("FATAL", magenta); + static std::string empty = format("EMPTY", reset); + + switch (type) + { + case sofa::helper::logging::Message::Advice : return advice; + case sofa::helper::logging::Message::Deprecated : return deprecated; + case sofa::helper::logging::Message::Warning : return warning; + case sofa::helper::logging::Message::Info : return info; + case sofa::helper::logging::Message::Error : return error; + case sofa::helper::logging::Message::Fatal : return fatal; + case sofa::helper::logging::Message::TEmpty : return empty; + + default: + return ""; + } +} + +} + +namespace py { using namespace pybind11; } + +void PythonMessageHandler::process(sofa::helper::logging::Message &m) +{ + if (!m.sender().empty()) + { + py::print(getPrefixText(m.type()), format(m.sender(), blue), m.messageAsString()); + } + else + { + py::print(getPrefixText(m.type()), m.messageAsString()); + } +} + +PythonMessageHandler& MainPythonMessageHandler::getInstance() +{ + static PythonMessageHandler s_instance; + return s_instance; +} + +} // namespace sofapython3 + diff --git a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h new file mode 100644 index 00000000..fd1636f1 --- /dev/null +++ b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h @@ -0,0 +1,41 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include + +namespace sofapython3 +{ + +class PythonMessageHandler : public sofa::helper::logging::MessageHandler +{ +public: + PythonMessageHandler() = default; + void process(sofa::helper::logging::Message& m) override ; +}; + +class MainPythonMessageHandler +{ +public: + static PythonMessageHandler& getInstance() ; +}; +} // namespace sofapython3 From 60110c332bb5ce1643dbcccfbfed6caa1b85d3e2 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 5 Feb 2026 21:04:41 +0100 Subject: [PATCH 2/3] Redirect SOFA logs to Python's sys.stdout using a custom PythonMessageHandler. --- bindings/SofaRuntime/CMakeLists.txt | 2 + .../SofaRuntime/Module_SofaRuntime.cpp | 6 +- .../SofaRuntime/PythonMessageHandler.cpp | 93 +++++++++++++++++++ .../SofaRuntime/PythonMessageHandler.h | 41 ++++++++ 4 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp create mode 100644 bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h diff --git a/bindings/SofaRuntime/CMakeLists.txt b/bindings/SofaRuntime/CMakeLists.txt index 196701e1..52c4d80e 100644 --- a/bindings/SofaRuntime/CMakeLists.txt +++ b/bindings/SofaRuntime/CMakeLists.txt @@ -2,10 +2,12 @@ project(Bindings.SofaRuntime) set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Timer/Submodule_Timer.cpp ) set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/PythonMessageHandler.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Timer/Submodule_Timer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SofaPython3/SofaRuntime/Timer/Submodule_Timer_doc.h ) diff --git a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp index c501fdb6..2c663607 100644 --- a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp +++ b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/Module_SofaRuntime.cpp @@ -62,7 +62,7 @@ using sofapython3::SceneLoaderPY3; #include using sofa::helper::logging::MessageDispatcher; using sofa::helper::logging::MainPerComponentLoggingMessageHandler; -using sofa::helper::logging::MainConsoleMessageHandler; +#include #include #include @@ -155,9 +155,9 @@ PYBIND11_MODULE(SofaRuntime, m) { m.def("init", []() { MessageDispatcher::clearHandlers(); - MessageDispatcher::addHandler(&MainConsoleMessageHandler::getInstance()); + MessageDispatcher::addHandler(&MainPythonMessageHandler::getInstance()); MessageDispatcher::addHandler(&MainPerComponentLoggingMessageHandler::getInstance()); - }); + }, "redirect SOFA messages to Python's sys.stdout"); m.add_object("DataRepository", py::cast(&sofa::helper::system::DataRepository)); m.add_object("PluginRepository", py::cast(&sofa::helper::system::PluginRepository)); diff --git a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp new file mode 100644 index 00000000..38996070 --- /dev/null +++ b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp @@ -0,0 +1,93 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include +#include + +namespace sofapython3 +{ + +namespace +{ + +using namespace std::string_literals; +auto red { "\033[31m"s }; +auto green { "\033[32m"s }; +auto orange { "\033[38;5;214m"s }; +auto magenta { "\033[35m"s }; +auto blue { "\033[34m"s }; +auto reset { "\033[0m"s }; + +std::string format(const std::string& type, const std::string& color) +{ + return color + "[" + type + "] " + reset; +}; + +std::string_view getPrefixText(sofa::helper::logging::Message::Type type) +{ + static std::string advice = format("SUGGESTION", green); + static std::string deprecated = format("DEPRECATED", orange); + static std::string warning = format("WARNING", orange); + static std::string info = format("INFO", green); + static std::string error = format("ERROR", red); + static std::string fatal = format("FATAL", magenta); + static std::string empty = format("EMPTY", reset); + + switch (type) + { + case sofa::helper::logging::Message::Advice : return advice; + case sofa::helper::logging::Message::Deprecated : return deprecated; + case sofa::helper::logging::Message::Warning : return warning; + case sofa::helper::logging::Message::Info : return info; + case sofa::helper::logging::Message::Error : return error; + case sofa::helper::logging::Message::Fatal : return fatal; + case sofa::helper::logging::Message::TEmpty : return empty; + + default: + return ""; + } +} + +} + +namespace py { using namespace pybind11; } + +void PythonMessageHandler::process(sofa::helper::logging::Message &m) +{ + if (!m.sender().empty()) + { + py::print(getPrefixText(m.type()), format(m.sender(), blue), m.messageAsString()); + } + else + { + py::print(getPrefixText(m.type()), m.messageAsString()); + } +} + +PythonMessageHandler& MainPythonMessageHandler::getInstance() +{ + static PythonMessageHandler s_instance; + return s_instance; +} + +} // namespace sofapython3 + diff --git a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h new file mode 100644 index 00000000..fd1636f1 --- /dev/null +++ b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.h @@ -0,0 +1,41 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include + +namespace sofapython3 +{ + +class PythonMessageHandler : public sofa::helper::logging::MessageHandler +{ +public: + PythonMessageHandler() = default; + void process(sofa::helper::logging::Message& m) override ; +}; + +class MainPythonMessageHandler +{ +public: + static PythonMessageHandler& getInstance() ; +}; +} // namespace sofapython3 From 17e4ae7a8a3ae03523868bd9f2ad62a65d3de436 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 6 Feb 2026 07:27:55 +0100 Subject: [PATCH 3/3] cleaning --- .../SofaRuntime/PythonMessageHandler.cpp | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp index 38996070..3bfc9629 100644 --- a/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp +++ b/bindings/SofaRuntime/src/SofaPython3/SofaRuntime/PythonMessageHandler.cpp @@ -30,27 +30,28 @@ namespace { using namespace std::string_literals; -auto red { "\033[31m"s }; -auto green { "\033[32m"s }; -auto orange { "\033[38;5;214m"s }; -auto magenta { "\033[35m"s }; -auto blue { "\033[34m"s }; -auto reset { "\033[0m"s }; +const std::string red { "\033[31m"s }; +const std::string green { "\033[32m"s }; +const std::string orange { "\033[38;5;214m"s }; +const std::string magenta { "\033[35m"s }; +const std::string blue { "\033[34m"s }; +const std::string reset { "\033[0m"s }; std::string format(const std::string& type, const std::string& color) { return color + "[" + type + "] " + reset; }; -std::string_view getPrefixText(sofa::helper::logging::Message::Type type) +const std::string& getPrefixText(sofa::helper::logging::Message::Type type) { - static std::string advice = format("SUGGESTION", green); - static std::string deprecated = format("DEPRECATED", orange); - static std::string warning = format("WARNING", orange); - static std::string info = format("INFO", green); - static std::string error = format("ERROR", red); - static std::string fatal = format("FATAL", magenta); - static std::string empty = format("EMPTY", reset); + static const std::string advice = format("SUGGESTION", green); + static const std::string deprecated = format("DEPRECATED", orange); + static const std::string warning = format("WARNING", orange); + static const std::string info = format("INFO", green); + static const std::string error = format("ERROR", red); + static const std::string fatal = format("FATAL", magenta); + static const std::string empty = format("EMPTY", reset); + static const std::string nothing{}; switch (type) { @@ -62,8 +63,8 @@ std::string_view getPrefixText(sofa::helper::logging::Message::Type type) case sofa::helper::logging::Message::Fatal : return fatal; case sofa::helper::logging::Message::TEmpty : return empty; - default: - return ""; + case sofa::helper::logging::Message::TypeCount: + return nothing; } }