From fe9c91725b9c0890f592c022ff0eb574e25852a8 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Mon, 19 Jan 2026 11:55:45 +0100 Subject: [PATCH 1/2] Link against `Python3::Module` instead of `Python3::Python`, which according to the documentation is meant for embedding the python interpreter. --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e5fecaa38..f967467a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,7 +77,7 @@ SET(core_headers PODIO_ADD_LIB_AND_DICT(podio "${core_headers}" "${core_sources}" selection.xml) target_compile_options(podio PRIVATE -pthread) -target_link_libraries(podio PRIVATE Python3::Python) +target_link_libraries(podio PRIVATE Python3::Module) # For Frame.h if (ROOT_VERSION VERSION_LESS 6.36) target_compile_definitions(podio PUBLIC PODIO_ROOT_OLDER_6_36=1) From 5da2971ff6f22b5895fd1840806ad60281aa004b Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 19 Jan 2026 20:22:12 +0100 Subject: [PATCH 2/2] Make pythonizations header only again This should avoid linking issues on macOS, while still - keeping things working via cppyy (most likely via JIT) - allowing usage in c++ modules --- include/podio/detail/Pythonizations.h | 52 ++++++++++++++++++++++-- src/CMakeLists.txt | 3 +- src/Pythonizations.cc | 58 --------------------------- 3 files changed, 49 insertions(+), 64 deletions(-) delete mode 100644 src/Pythonizations.cc diff --git a/include/podio/detail/Pythonizations.h b/include/podio/detail/Pythonizations.h index 73b34c6c9..8f867fcd7 100644 --- a/include/podio/detail/Pythonizations.h +++ b/include/podio/detail/Pythonizations.h @@ -1,18 +1,62 @@ #ifndef PODIO_DETAIL_PYTHONIZATIONS_H #define PODIO_DETAIL_PYTHONIZATIONS_H -#include +// NOTE: Python.h must be included before any standard headers and should be preceded by PY_SSIZE_T_CLEAN definition +#define PY_SSIZE_T_CLEAN +#include -typedef struct _object PyObject; +#include +#include +#include namespace podio::detail::pythonizations { // Callback function for the subscript pythonization // Calls the `at` method and change exception type to IndexError if the index is out of range -PyObject* subscript(PyObject* self, PyObject* index); +inline PyObject* subscript(PyObject* self, PyObject* index) { + PyObject* result = PyObject_CallMethod(self, "at", "O", index); + if (!result) { + PyObject* exc = PyErr_Occurred(); + // Check if the exception is `cppyy.gbl.std.out_of_range` + // Since PyImport_ImportModule("cppyy") fails, this workaround checks the exception name + if (exc && PyObject_HasAttrString(exc, "__name__")) { + PyObject* exc_name = PyObject_GetAttrString(exc, "__name__"); + if (exc_name) { + const char* name_cstr = PyUnicode_AsUTF8(exc_name); + if (name_cstr && strcmp(name_cstr, "out_of_range") == 0) { + PyErr_Clear(); + PyErr_SetString(PyExc_IndexError, "Index out of range"); + } + Py_DECREF(exc_name); + } + } + } + return result; +} // Helper to register the subscript pythonization callback as `__getitem__` method -void pythonize_subscript(PyObject* klass, const std::string& name); +inline void pythonize_subscript(PyObject* klass, const std::string& name) { + static PyMethodDef ml = {"subscript_pythonization", subscript, METH_VARARGS, R"( + Raise an `IndexError` exception if an index is invalid. + The `__getitem__` will return immutable datatype objects instead of the mutable ones. + )"}; + auto* func = PyCFunction_New(&ml, klass); + if (!func) { + throw std::runtime_error("Failed to create Python subscript function for class " + name); + } + auto* method = PyInstanceMethod_New(func); + if (!method) { + Py_DECREF(func); + throw std::runtime_error("Failed to create Python instance method for subscript for class " + name); + } + if (0 != PyObject_SetAttrString(klass, "__getitem__", method)) { + Py_DECREF(method); + Py_DECREF(func); + throw std::runtime_error("Failed to set __getitem__ attribute on class " + name); + } + Py_DECREF(func); + Py_DECREF(method); +} } // namespace podio::detail::pythonizations diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f967467a3..67502ca52 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,7 +58,6 @@ SET(core_sources MurmurHash3.cpp SchemaEvolution.cc Glob.cc - Pythonizations.cc ) SET(core_headers @@ -77,7 +76,7 @@ SET(core_headers PODIO_ADD_LIB_AND_DICT(podio "${core_headers}" "${core_sources}" selection.xml) target_compile_options(podio PRIVATE -pthread) -target_link_libraries(podio PRIVATE Python3::Module) +target_link_libraries(podio PUBLIC Python3::Module) # For Frame.h if (ROOT_VERSION VERSION_LESS 6.36) target_compile_definitions(podio PUBLIC PODIO_ROOT_OLDER_6_36=1) diff --git a/src/Pythonizations.cc b/src/Pythonizations.cc deleted file mode 100644 index a74f03592..000000000 --- a/src/Pythonizations.cc +++ /dev/null @@ -1,58 +0,0 @@ -// NOTE: Python.h must be included before any standard headers and should be preceded by PY_SSIZE_T_CLEAN definition -#define PY_SSIZE_T_CLEAN -#include - -#include "podio/detail/Pythonizations.h" - -#include -#include -namespace podio::detail::pythonizations { - -// Callback function for the subscript pythonization -// Calls the `at` method and change exception type to IndexError if the index is out of range -PyObject* subscript(PyObject* self, PyObject* index) { - PyObject* result = PyObject_CallMethod(self, "at", "O", index); - if (!result) { - PyObject* exc = PyErr_Occurred(); - // Check if the exception is `cppyy.gbl.std.out_of_range` - // Since PyImport_ImportModule("cppyy") fails, this workaround checks the exception name - if (exc && PyObject_HasAttrString(exc, "__name__")) { - PyObject* exc_name = PyObject_GetAttrString(exc, "__name__"); - if (exc_name) { - const char* name_cstr = PyUnicode_AsUTF8(exc_name); - if (name_cstr && strcmp(name_cstr, "out_of_range") == 0) { - PyErr_Clear(); - PyErr_SetString(PyExc_IndexError, "Index out of range"); - } - Py_DECREF(exc_name); - } - } - } - return result; -} - -// Helper to register the subscript pythonization callback as `__getitem__` method -void pythonize_subscript(PyObject* klass, const std::string& name) { - static PyMethodDef ml = {"subscript_pythonization", subscript, METH_VARARGS, R"( - Raise an `IndexError` exception if an index is invalid. - The `__getitem__` will return immutable datatype objects instead of the mutable ones. - )"}; - auto* func = PyCFunction_New(&ml, klass); - if (!func) { - throw std::runtime_error("Failed to create Python subscript function for class " + name); - } - auto* method = PyInstanceMethod_New(func); - if (!method) { - Py_DECREF(func); - throw std::runtime_error("Failed to create Python instance method for subscript for class " + name); - } - if (0 != PyObject_SetAttrString(klass, "__getitem__", method)) { - Py_DECREF(method); - Py_DECREF(func); - throw std::runtime_error("Failed to set __getitem__ attribute on class " + name); - } - Py_DECREF(func); - Py_DECREF(method); -} - -} // namespace podio::detail::pythonizations