Skip to content
Open
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
3 changes: 3 additions & 0 deletions include/boost/python/module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@

# include <boost/python/module_init.hpp>
# 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
# endif

#endif // MODULE_DWA20011221_HPP
111 changes: 104 additions & 7 deletions include/boost/python/module_init.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace boost { namespace python {

#ifdef HAS_CXX11
# ifdef HAS_CXX11
// Use to activate the Py_MOD_GIL_NOT_USED flag.
class mod_gil_not_used {
public:
Expand All @@ -39,19 +39,25 @@ inline bool gil_not_used_option(F &&, O &&...o) {
}

}
#endif // HAS_CXX11
# endif // HAS_CXX11

namespace detail {

# if PY_VERSION_HEX >= 0x03000000

BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)(), bool gil_not_used = false);

#else
# if PY_VERSION_HEX >= 0x03050000

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

# endif // PY_VERSION_HEX >= 0x03050000

# else // PY_VERSION_HEX >= 0x03000000

BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());

#endif
# endif // PY_VERSION_HEX >= 0x03000000

}}}

Expand Down Expand Up @@ -115,7 +121,86 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
void BOOST_PP_CAT(init_module_, name)()
# endif // HAS_CXX11

# else
# if PY_VERSION_HEX >= 0x03050000

# if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000)
# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, ...) \
int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \
{ \
return boost::python::detail::exec_module( \
mod, BOOST_PP_CAT(init_module_, name) ); \
} \
extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \
{ \
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 */ \
0, /* 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)()
# else // ! HAS_CXX11 && Python 3.13+
# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \
{ \
return boost::python::detail::exec_module( \
mod, BOOST_PP_CAT(init_module_, name) ); \
} \
extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \
{ \
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 */ \
0, /* 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)()
# endif // HAS_CXX11 && Python 3.13+

# endif // PY_VERSION_HEX >= 0x03050000

# else // ! PY_VERSION_HEX >= 0x03000000

# define _BOOST_PYTHON_MODULE_INIT(name) \
void BOOST_PP_CAT(init,name)() \
Expand All @@ -125,7 +210,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
} \
void BOOST_PP_CAT(init_module_,name)()

# endif
# endif // PY_VERSION_HEX >= 0x03000000

# if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x03000000)
# define BOOST_PYTHON_MODULE_INIT(name, ...) \
Expand All @@ -137,6 +222,18 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name, __VA_ARGS__)
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name)
# endif // HAS_CXX11 && Python 3

# endif
# if PY_VERSION_HEX >= 0x03050000
# if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000)
# 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__)
# 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)
# endif // HAS_CXX11 && Python 3.13+
# endif // PY_VERSION_HEX >= 0x03050000

# endif // BOOST_PYTHON_MODULE_INIT

#endif // MODULE_INIT_DWA20020722_HPP
12 changes: 12 additions & 0 deletions src/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef,
init_function);
}

# if PY_VERSION_HEX >= 0x03050000

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

# endif

#else

namespace
Expand Down
7 changes: 7 additions & 0 deletions test/fabscript
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,11 @@ for t in ['numpy/dtype',
tests.append(extension_test(t, numpy=True,
condition=set.define.contains('HAS_NUMPY')))

python_version_major, python_version_minor = map(int, python.instance().version.split('.')[:2])

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)))

default = report('report', tests, fail_on_failures=True)
15 changes: 15 additions & 0 deletions test/module_multi_phase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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/module.hpp>
#include <boost/python/scope.hpp>

using namespace boost::python;

BOOST_PYTHON_MODULE_MULTI_PHASE(module_multi_phase_ext)
{
scope().attr("x") = "x";
}

#include "module_tail.cpp"
23 changes: 23 additions & 0 deletions test/module_multi_phase.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_ext
>>> module_multi_phase_ext.x
'x'
"""

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)
25 changes: 25 additions & 0 deletions test/module_multi_phase_nogil.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Test for BOOST_PYTHON_MODULE_MULTI_PHASE with optional mod_gil_not_used argument

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

// Simple function to export
int get_value() {
return 1234;
}

#if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000)
// C++11 build with Python 3.13+: test with mod_gil_not_used option
BOOST_PYTHON_MODULE_MULTI_PHASE(module_multi_phase_nogil_ext, boost::python::mod_gil_not_used())
{
using namespace boost::python;
def("get_value", get_value);
}
#else
// C++98 build or Python 3.12-: test without optional arguments
BOOST_PYTHON_MODULE_MULTI_PHASE(module_multi_phase_nogil_ext)
{
using namespace boost::python;
def("get_value", get_value);
}
#endif
29 changes: 29 additions & 0 deletions test/module_multi_phase_nogil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
>>> from module_multi_phase_nogil_ext import *
>>> get_value()
1234
>>> import sys, sysconfig
>>> Py_GIL_DISABLED = bool(sysconfig.get_config_var('Py_GIL_DISABLED'))
>>> if Py_GIL_DISABLED and sys._is_gil_enabled():
... print('GIL is enabled and should not be')
... else:
... print('okay')
okay
"""

from __future__ import print_function

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