From d4533d40247c3f7de76b817810dba33368fbcc2a Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 25 Feb 2026 17:10:51 +0100 Subject: [PATCH 1/4] Add binding for `GetVersion` and associated unit tests Introduce a static method `GetVersion` in `Sofa.Helper.Utils` to retrieve the SOFA version in `vMM.mm` format. Added unit tests in `Helper/Version.py` to verify existence and format adherence. Updated CMakeLists accordingly. --- .../SofaPython3/Sofa/Helper/Binding_Utils.cpp | 19 +++++++++++++- bindings/Sofa/tests/CMakeLists.txt | 1 + bindings/Sofa/tests/Helper/Version.py | 26 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 bindings/Sofa/tests/Helper/Version.py diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp index 6996b56c6..a8e68edaf 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp @@ -20,8 +20,11 @@ #include -#include #include +#include + +#include +#include #include @@ -44,6 +47,20 @@ void moduleAddUtils(py::module &m) { Get the directory where is stored the sofa output data such as screenshots. )doc"; utils.def_static("GetSofaDataDirectory", &sofa::helper::Utils::getSofaDataDirectory, GetSofaDataDirectoryDoc); + + utils.def_static("GetVersion", + []() + { + std::stringstream version; + constexpr auto major = SOFA_VERSION / 10000; + constexpr auto minor = SOFA_VERSION / 100 % 100; + version << 'v' + << std::setfill('0') << std::setw(2) << major + << "." + << std::setfill('0') << std::setw(2) << minor; + return version.str(); + }, + "Returns the version of SOFA as a string in the format 'vMM.mm', where MM is the major version and mm is the minor version."); } diff --git a/bindings/Sofa/tests/CMakeLists.txt b/bindings/Sofa/tests/CMakeLists.txt index 038c1cf0f..f4dde4270 100644 --- a/bindings/Sofa/tests/CMakeLists.txt +++ b/bindings/Sofa/tests/CMakeLists.txt @@ -22,6 +22,7 @@ set(PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Core/Mass.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/MyRestShapeForceField.py ${CMAKE_CURRENT_SOURCE_DIR}/Helper/Message.py + ${CMAKE_CURRENT_SOURCE_DIR}/Helper/Version.py ${CMAKE_CURRENT_SOURCE_DIR}/Simulation/Node.py ${CMAKE_CURRENT_SOURCE_DIR}/Simulation/Simulation.py ${CMAKE_CURRENT_SOURCE_DIR}/Types/BoundingBox.py diff --git a/bindings/Sofa/tests/Helper/Version.py b/bindings/Sofa/tests/Helper/Version.py new file mode 100644 index 000000000..e6a1a347c --- /dev/null +++ b/bindings/Sofa/tests/Helper/Version.py @@ -0,0 +1,26 @@ +import Sofa +import Sofa.Helper +import unittest + +class GetVersionTest(unittest.TestCase): + """ + Contains unit tests for verifying the existence and format of the `GetVersion` method + in the `Sofa.Helper.Utils` module. + + Includes tests to assert that the version is correctly formatted and meets the expected + structure. + """ + def test_exist(self): + self.assertTrue(hasattr(Sofa.Helper.Utils, "GetVersion")) + + def test_version_format(self): + version = Sofa.Helper.Utils.GetVersion() + print(f"SOFA Version: {version}") + self.assertTrue(version.startswith('v')) + version = version.replace('v', '') + self.assertTrue(version.count('.') == 1) + major, minor = version.split('.') + self.assertTrue(major.isdigit()) + self.assertTrue(minor.isdigit()) + self.assertEqual(len(major), 2) + self.assertEqual(len(minor), 2) From c3acae5873abca5b9bea34f164b13b09dabb5059 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 3 Mar 2026 14:11:34 +0100 Subject: [PATCH 2/4] make the version string static --- .../SofaPython3/Sofa/Helper/Binding_Utils.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp index a8e68edaf..768b40713 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp @@ -51,14 +51,16 @@ void moduleAddUtils(py::module &m) { utils.def_static("GetVersion", []() { - std::stringstream version; - constexpr auto major = SOFA_VERSION / 10000; - constexpr auto minor = SOFA_VERSION / 100 % 100; - version << 'v' - << std::setfill('0') << std::setw(2) << major - << "." - << std::setfill('0') << std::setw(2) << minor; - return version.str(); + static const std::string sofaVersion = []() { + std::stringstream version; + constexpr auto major = SOFA_VERSION / 10000; + constexpr auto minor = SOFA_VERSION / 100 % 100; + version << 'v' + << std::setfill('0') << std::setw(2) << major + << "." + << std::setfill('0') << std::setw(2) << minor; + return version.str(); + }(); }, "Returns the version of SOFA as a string in the format 'vMM.mm', where MM is the major version and mm is the minor version."); } From 3b54b40857cbfa2ab8928db1772e396c2532c373 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 3 Mar 2026 14:29:16 +0100 Subject: [PATCH 3/4] move GetVersion from Utils to Version --- .../SofaPython3/Sofa/Helper/Binding_Utils.cpp | 21 +------ .../Sofa/Helper/Binding_Version.cpp | 57 +++++++++++++++++++ .../SofaPython3/Sofa/Helper/Binding_Version.h | 30 ++++++++++ .../SofaPython3/Sofa/Helper/CMakeLists.txt | 2 + .../Sofa/Helper/Submodule_Helper.cpp | 2 + bindings/Sofa/tests/Helper/Version.py | 4 +- 6 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.cpp create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.h diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp index 768b40713..6996b56c6 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Utils.cpp @@ -20,11 +20,8 @@ #include -#include #include - -#include -#include +#include #include @@ -47,22 +44,6 @@ void moduleAddUtils(py::module &m) { Get the directory where is stored the sofa output data such as screenshots. )doc"; utils.def_static("GetSofaDataDirectory", &sofa::helper::Utils::getSofaDataDirectory, GetSofaDataDirectoryDoc); - - utils.def_static("GetVersion", - []() - { - static const std::string sofaVersion = []() { - std::stringstream version; - constexpr auto major = SOFA_VERSION / 10000; - constexpr auto minor = SOFA_VERSION / 100 % 100; - version << 'v' - << std::setfill('0') << std::setw(2) << major - << "." - << std::setfill('0') << std::setw(2) << minor; - return version.str(); - }(); - }, - "Returns the version of SOFA as a string in the format 'vMM.mm', where MM is the major version and mm is the minor version."); } diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.cpp new file mode 100644 index 000000000..d3973be25 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.cpp @@ -0,0 +1,57 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* 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 . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include + +#include +#include + +#include +#include + + +/// Makes an alias for the pybind11 namespace to increase readability. +namespace py { using namespace pybind11; } + +namespace sofapython3 +{ + +void moduleAddVersion(py::module &m) +{ + m.def("GetVersion", + []() + { + static const std::string sofaVersion = []() { + std::stringstream version; + constexpr auto major = SOFA_VERSION / 10000; + constexpr auto minor = SOFA_VERSION / 100 % 100; + version << 'v' + << std::setfill('0') << std::setw(2) << major + << "." + << std::setfill('0') << std::setw(2) << minor; + return version.str(); + }(); + return sofaVersion; + }, + "Returns the version of SOFA as a string in the format 'vMM.mm', where MM is the major version and mm is the minor version."); +} + + +} diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.h b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.h new file mode 100644 index 000000000..178ac422a --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Binding_Version.h @@ -0,0 +1,30 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* 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 . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once + +#include + +namespace sofapython3 +{ + +void moduleAddVersion(pybind11::module &m); + +} diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Helper/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Helper/CMakeLists.txt index b65f18044..1283bc540 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Helper/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Helper/CMakeLists.txt @@ -3,6 +3,7 @@ project(Bindings.Sofa.Helper) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Utils.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Version.h ${CMAKE_CURRENT_SOURCE_DIR}/System/Submodule_System.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MessageHandler.h ${CMAKE_CURRENT_SOURCE_DIR}/System/Binding_FileRepository.h @@ -13,6 +14,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MessageHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Vector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Version.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/Submodule_System.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/Binding_FileRepository.cpp ) diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Submodule_Helper.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Submodule_Helper.cpp index 4c80cb148..1fb3690fa 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Helper/Submodule_Helper.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Helper/Submodule_Helper.cpp @@ -26,6 +26,7 @@ #include #include #include +#include /// Makes an alias for the pybind11 namespace to increase readability. namespace py { using namespace pybind11; } @@ -153,6 +154,7 @@ PYBIND11_MODULE(Helper, helper) moduleAddVector(helper); moduleAddSystem(helper); moduleAddUtils(helper); + moduleAddVersion(helper); auto atexit = py::module_::import("atexit"); atexit.attr("register")(py::cpp_function([]() { diff --git a/bindings/Sofa/tests/Helper/Version.py b/bindings/Sofa/tests/Helper/Version.py index e6a1a347c..dc62c5c34 100644 --- a/bindings/Sofa/tests/Helper/Version.py +++ b/bindings/Sofa/tests/Helper/Version.py @@ -11,10 +11,10 @@ class GetVersionTest(unittest.TestCase): structure. """ def test_exist(self): - self.assertTrue(hasattr(Sofa.Helper.Utils, "GetVersion")) + self.assertTrue(hasattr(Sofa.Helper, "GetVersion")) def test_version_format(self): - version = Sofa.Helper.Utils.GetVersion() + version = Sofa.Helper.GetVersion() print(f"SOFA Version: {version}") self.assertTrue(version.startswith('v')) version = version.replace('v', '') From dad5e8197d54bd99898ce5eeefb6e7f8b2a24ab3 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 3 Mar 2026 14:39:19 +0100 Subject: [PATCH 4/4] add GetVersion() to the Sofa package --- bindings/Sofa/package/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bindings/Sofa/package/__init__.py b/bindings/Sofa/package/__init__.py index 8926e8cee..5b73b863a 100644 --- a/bindings/Sofa/package/__init__.py +++ b/bindings/Sofa/package/__init__.py @@ -195,6 +195,11 @@ def unloadModules(): for name in toremove: del(sys.modules[name]) # unload it +def GetVersion(): + """Returns the version of SOFA as a string in the format 'vMM.mm', where MM is the major version and mm is the + minor version. + """ + return Sofa.Helper.GetVersion() def formatStackForSofa(o): """ format the stack trace provided as a parameter into a string like that: