Skip to content

Commit 957157e

Browse files
committed
Add support for multi-phase module initialization
1 parent f604eb8 commit 957157e

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
@@ -17,6 +17,12 @@ namespace boost { namespace python { namespace detail {
1717

1818
BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)());
1919

20+
# if PY_VERSION_HEX >= 0x03050000
21+
22+
BOOST_PYTHON_DECL int exec_module(PyObject*, void(*)());
23+
24+
# endif
25+
2026
#else
2127

2228
BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
@@ -54,6 +60,46 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
5460
} \
5561
void BOOST_PP_CAT(init_module_, name)()
5662

63+
# if PY_VERSION_HEX >= 0x03050000
64+
65+
# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
66+
int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \
67+
{ \
68+
return boost::python::detail::exec_module( \
69+
module, BOOST_PP_CAT(init_module_, name) ); \
70+
} \
71+
extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \
72+
{ \
73+
static PyModuleDef_Base initial_m_base = { \
74+
PyObject_HEAD_INIT(NULL) \
75+
0, /* m_init */ \
76+
0, /* m_index */ \
77+
0 /* m_copy */ }; \
78+
static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \
79+
\
80+
static PyModuleDef_Slot slots[] = { \
81+
{Py_mod_exec, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(BOOST_PP_CAT(exec_module_, name)))}, \
82+
{0, NULL} \
83+
}; \
84+
\
85+
static struct PyModuleDef moduledef = { \
86+
initial_m_base, \
87+
BOOST_PP_STRINGIZE(name), \
88+
0, /* m_doc */ \
89+
0, /* m_size */ \
90+
initial_methods, \
91+
slots, /* m_slots */ \
92+
0, /* m_traverse */ \
93+
0, /* m_clear */ \
94+
0, /* m_free */ \
95+
}; \
96+
\
97+
return PyModuleDef_Init(&moduledef); \
98+
} \
99+
void BOOST_PP_CAT(init_module_, name)()
100+
101+
# endif
102+
57103
# else
58104

59105
# define _BOOST_PYTHON_MODULE_INIT(name) \
@@ -70,6 +116,14 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
70116
void BOOST_PP_CAT(init_module_,name)(); \
71117
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name)
72118

119+
# if PY_VERSION_HEX >= 0x03050000
120+
121+
# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
122+
void BOOST_PP_CAT(init_module_,name)(); \
123+
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name)
124+
125+
# endif
126+
73127
# endif
74128

75129
#endif // MODULE_INIT_DWA20020722_HPP

src/module.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef, void(*init_funct
4545
init_function);
4646
}
4747

48+
# if PY_VERSION_HEX >= 0x03050000
49+
50+
BOOST_PYTHON_DECL int exec_module(PyObject* module, void(*init_function)())
51+
{
52+
PyObject* retval = init_module_in_scope(
53+
module,
54+
init_function);
55+
return retval ? 0 : -1;
56+
}
57+
58+
# endif
59+
4860
#else
4961

5062
namespace

test/fabscript

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

176+
python_version_major, python_version_minor = map(int, python.instance().version.split('.')[:2])
177+
178+
tests.append(extension_test("module_multi_phase",
179+
condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5)))
180+
176181
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)