Skip to content

Commit 75ee0e8

Browse files
committed
Add support for multi-phase module initialization
1 parent 32da86d commit 75ee0e8

6 files changed

Lines changed: 112 additions & 0 deletions

File tree

include/boost/python/module.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@
99

1010
# include <boost/python/module_init.hpp>
1111
# define BOOST_PYTHON_MODULE BOOST_PYTHON_MODULE_INIT
12+
# if PY_VERSION_HEX >= 0x03050000
13+
# define BOOST_PYTHON_MODULE_MULTI_PHASE BOOST_PYTHON_MODULE_MULTI_PHASE_INIT
14+
# endif
1215

1316
#endif // MODULE_DWA20011221_HPP

include/boost/python/module_init.hpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ namespace detail {
4747

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

50+
# if PY_VERSION_HEX >= 0x03050000
51+
52+
BOOST_PYTHON_DECL int exec_module(PyObject*, void(*)());
53+
54+
# endif
55+
5056
#else
5157

5258
BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
@@ -115,6 +121,46 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
115121
void BOOST_PP_CAT(init_module_, name)()
116122
# endif // HAS_CXX11
117123

124+
# if PY_VERSION_HEX >= 0x03050000
125+
126+
# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
127+
int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \
128+
{ \
129+
return boost::python::detail::exec_module( \
130+
module, BOOST_PP_CAT(init_module_, name) ); \
131+
} \
132+
extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \
133+
{ \
134+
static PyModuleDef_Base initial_m_base = { \
135+
PyObject_HEAD_INIT(NULL) \
136+
0, /* m_init */ \
137+
0, /* m_index */ \
138+
0 /* m_copy */ }; \
139+
static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \
140+
\
141+
static PyModuleDef_Slot slots[] = { \
142+
{Py_mod_exec, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(BOOST_PP_CAT(exec_module_, name)))}, \
143+
{0, NULL} \
144+
}; \
145+
\
146+
static struct PyModuleDef moduledef = { \
147+
initial_m_base, \
148+
BOOST_PP_STRINGIZE(name), \
149+
0, /* m_doc */ \
150+
0, /* m_size */ \
151+
initial_methods, \
152+
slots, /* m_slots */ \
153+
0, /* m_traverse */ \
154+
0, /* m_clear */ \
155+
0, /* m_free */ \
156+
}; \
157+
\
158+
return PyModuleDef_Init(&moduledef); \
159+
} \
160+
void BOOST_PP_CAT(init_module_, name)()
161+
162+
# endif
163+
118164
# else
119165

120166
# define _BOOST_PYTHON_MODULE_INIT(name) \
@@ -137,6 +183,14 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name, __VA_ARGS__)
137183
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name)
138184
# endif // HAS_CXX11 && Python 3
139185

186+
# if PY_VERSION_HEX >= 0x03050000
187+
188+
# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
189+
void BOOST_PP_CAT(init_module_,name)(); \
190+
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name)
191+
192+
# endif
193+
140194
# endif
141195

142196
#endif // MODULE_INIT_DWA20020722_HPP

src/module.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef,
5252
init_function);
5353
}
5454

55+
# if PY_VERSION_HEX >= 0x03050000
56+
57+
BOOST_PYTHON_DECL int exec_module(PyObject* module, void(*init_function)())
58+
{
59+
PyObject* retval = init_module_in_scope(
60+
module,
61+
init_function);
62+
return retval ? 0 : -1;
63+
}
64+
65+
# endif
66+
5567
#else
5668

5769
namespace

test/fabscript

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,9 @@ for t in ['numpy/dtype',
174174
tests.append(extension_test(t, numpy=True,
175175
condition=set.define.contains('HAS_NUMPY')))
176176

177+
python_version_major, python_version_minor = map(int, python.instance().version.split('.')[:2])
178+
179+
tests.append(extension_test("module_multi_phase",
180+
condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5)))
181+
177182
default = report('report', tests, fail_on_failures=True)

test/module_multi_phase.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Distributed under the Boost Software License, Version 1.0. (See
2+
// accompanying file LICENSE_1_0.txt or copy at
3+
// http://www.boost.org/LICENSE_1_0.txt)
4+
5+
#include <boost/python/module.hpp>
6+
#include <boost/python/scope.hpp>
7+
8+
using namespace boost::python;
9+
10+
BOOST_PYTHON_MODULE_MULTI_PHASE(module_multi_phase_ext)
11+
{
12+
scope().attr("x") = "x";
13+
}
14+
15+
#include "module_tail.cpp"

test/module_multi_phase.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Distributed under the Boost
2+
# Software License, Version 1.0. (See accompanying
3+
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
4+
"""
5+
>>> import module_multi_phase_ext
6+
>>> module_multi_phase_ext.x
7+
'x'
8+
"""
9+
10+
def run(args = None):
11+
import sys
12+
import doctest
13+
14+
if args is not None:
15+
sys.argv = args
16+
return doctest.testmod(sys.modules.get(__name__))
17+
18+
if __name__ == '__main__':
19+
print("running...")
20+
import sys
21+
status = run()[0]
22+
if (status == 0): print("Done.")
23+
sys.exit(status)

0 commit comments

Comments
 (0)