Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/boost/python/module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# define BOOST_PYTHON_MODULE BOOST_PYTHON_MODULE_INIT
# if PY_VERSION_HEX >= 0x03050000
# define BOOST_PYTHON_MODULE_MULTI_PHASE BOOST_PYTHON_MODULE_MULTI_PHASE_INIT
# define BOOST_PYTHON_MODULE_WITH_STATE BOOST_PYTHON_MODULE_WITH_STATE_INIT
# endif

#endif // MODULE_DWA20011221_HPP
88 changes: 88 additions & 0 deletions include/boost/python/module_init.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
# include <boost/preprocessor/cat.hpp>
# include <boost/preprocessor/stringize.hpp>

# if PY_VERSION_HEX >= 0x03050000
# include <boost/static_assert.hpp>
# include <boost/type_traits/is_pod.hpp>
# endif

# ifndef BOOST_PYTHON_MODULE_INIT

namespace boost { namespace python {
Expand Down Expand Up @@ -51,6 +56,8 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)(), bool gil_not_us

BOOST_PYTHON_DECL int exec_module(PyObject*, void(*)());

BOOST_PYTHON_DECL int exec_module_with_state(PyObject*, void(*)(void*));

# endif // PY_VERSION_HEX >= 0x03050000

# else // PY_VERSION_HEX >= 0x03000000
Expand Down Expand Up @@ -160,6 +167,44 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
return PyModuleDef_Init(&moduledef); \
} \
void BOOST_PP_CAT(init_module_, name)()
# define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \
int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \
{ \
return boost::python::detail::exec_module_with_state( \
mod, reinterpret_cast<void(*)(void*)>(BOOST_PP_CAT(init_module_, name)) ); \
} \
extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \
{ \
BOOST_STATIC_ASSERT_MSG(boost::is_pod<StateType>::value, \
"Module State MUST be a plain POD structure!"); \
static PyModuleDef_Base initial_m_base = { \
PyObject_HEAD_INIT(NULL) \
0, /* m_init */ \
0, /* m_index */ \
0 /* m_copy */ }; \
static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \
\
static PyModuleDef_Slot slots[] = { \
{Py_mod_exec, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(BOOST_PP_CAT(exec_module_, name)))}, \
{Py_mod_gil, boost::python::detail::gil_not_used_option(__VA_ARGS__) ? Py_MOD_GIL_NOT_USED : Py_MOD_GIL_USED}, \
{0, NULL} \
}; \
\
static struct PyModuleDef moduledef = { \
initial_m_base, \
BOOST_PP_STRINGIZE(name), \
0, /* m_doc */ \
sizeof(StateType), /* m_size */ \
initial_methods, \
slots, /* m_slots */ \
0, /* m_traverse */ \
0, /* m_clear */ \
0, /* m_free */ \
}; \
\
return PyModuleDef_Init(&moduledef); \
} \
void BOOST_PP_CAT(init_module_, name)(StateType* state)
# else // ! HAS_CXX11 && Python 3.13+
# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \
Expand Down Expand Up @@ -196,6 +241,43 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
return PyModuleDef_Init(&moduledef); \
} \
void BOOST_PP_CAT(init_module_, name)()
# define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) \
int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \
{ \
return boost::python::detail::exec_module_with_state( \
mod, reinterpret_cast<void(*)(void*)>(BOOST_PP_CAT(init_module_, name)) ); \
} \
extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \
{ \
BOOST_STATIC_ASSERT_MSG(boost::is_pod<StateType>::value, \
"Module State MUST be a plain POD structure!"); \
static PyModuleDef_Base initial_m_base = { \
PyObject_HEAD_INIT(NULL) \
0, /* m_init */ \
0, /* m_index */ \
0 /* m_copy */ }; \
static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \
\
static PyModuleDef_Slot slots[] = { \
{Py_mod_exec, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(BOOST_PP_CAT(exec_module_, name)))}, \
{0, NULL} \
}; \
\
static struct PyModuleDef moduledef = { \
initial_m_base, \
BOOST_PP_STRINGIZE(name), \
0, /* m_doc */ \
sizeof(StateType), /* m_size */ \
initial_methods, \
slots, /* m_slots */ \
0, /* m_traverse */ \
0, /* m_clear */ \
0, /* m_free */ \
}; \
\
return PyModuleDef_Init(&moduledef); \
} \
void BOOST_PP_CAT(init_module_, name)(StateType* state)
# endif // HAS_CXX11 && Python 3.13+

# endif // PY_VERSION_HEX >= 0x03050000
Expand Down Expand Up @@ -227,10 +309,16 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name)
# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, ...) \
void BOOST_PP_CAT(init_module_,name)(); \
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, __VA_ARGS__)
# define BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \
void BOOST_PP_CAT(init_module_,name)(StateType* state); \
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, __VA_ARGS__)
# else
# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
void BOOST_PP_CAT(init_module_,name)(); \
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name)
# define BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) \
void BOOST_PP_CAT(init_module_,name)(StateType* state); \
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType)
# endif // HAS_CXX11 && Python 3.13+
# endif // PY_VERSION_HEX >= 0x03050000

Expand Down
36 changes: 36 additions & 0 deletions src/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,34 @@ namespace

return m;
}
# if PY_VERSION_HEX >= 0x03050000
class init_function_with_state {
public:
init_function_with_state(void(*init_function)(void*), void* state)
: init_function_(init_function), state_(state) {}

void operator()() const { init_function_(state_); }
private:
void(*const init_function_)(void*);
void* const state_;
};

PyObject* init_module_in_scope_with_state(PyObject* m, void(*init_function)(void*))
{
if (m != 0)
{
// Create the current module scope
object m_obj(((borrowed_reference_t*)m));
scope current_module(m_obj);

void* state = PyModule_GetState(m);

if (handle_exception(init_function_with_state(init_function, state))) return NULL;
}

return m;
}
# endif
}

BOOST_PYTHON_DECL void scope_setattr_doc(char const* name, object const& x, char const* doc)
Expand Down Expand Up @@ -62,6 +90,14 @@ BOOST_PYTHON_DECL int exec_module(PyObject* mod, void(*init_function)())
return retval ? 0 : -1;
}

BOOST_PYTHON_DECL int exec_module_with_state(PyObject* mod, void(*init_function)(void*))
{
PyObject* retval = init_module_in_scope_with_state(
mod,
init_function);
return retval ? 0 : -1;
}

# endif

#else
Expand Down
2 changes: 2 additions & 0 deletions test/fabscript
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,7 @@ tests.append(extension_test("module_multi_phase",
condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5)))
tests.append(extension_test("module_multi_phase_nogil",
condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5)))
tests.append(extension_test("module_multi_phase_state",
condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5)))

default = report('report', tests, fail_on_failures=True)
30 changes: 30 additions & 0 deletions test/module_multi_phase_state.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <boost/python/def.hpp>
#include <boost/python/import.hpp>
#include <boost/python/module.hpp>

using namespace boost::python;

struct TestState
{
int x;
};

int get_state_x() {
TestState* state = reinterpret_cast<TestState*>(PyModule_GetState(import("module_multi_phase_state_ext").ptr()));
if (state != nullptr) {
return state->x;
}
return -1;
}

BOOST_PYTHON_MODULE_WITH_STATE(module_multi_phase_state_ext, TestState)
{
state->x = 42;
def("x", get_state_x);
}

#include "module_tail.cpp"
23 changes: 23 additions & 0 deletions test/module_multi_phase_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
"""
>>> import module_multi_phase_state_ext
>>> module_multi_phase_state_ext.x()
42
"""

def run(args = None):
import sys
import doctest

if args is not None:
sys.argv = args
return doctest.testmod(sys.modules.get(__name__))

if __name__ == '__main__':
print("running...")
import sys
status = run()[0]
if (status == 0): print("Done.")
sys.exit(status)
Loading