Skip to content

Add support for multi-phase module initialization #485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
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
54 changes: 54 additions & 0 deletions include/boost/python/module_init.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ namespace boost { namespace python { namespace detail {

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

# if PY_VERSION_HEX >= 0x03050000

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

# endif

#else

BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
Expand Down Expand Up @@ -54,6 +60,46 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
} \
void BOOST_PP_CAT(init_module_, name)()

# if PY_VERSION_HEX >= 0x03050000

# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \
int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \
{ \
return boost::python::detail::exec_module( \
module, 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

# else

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

# if PY_VERSION_HEX >= 0x03050000

# 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

# endif

#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 @@ -45,6 +45,18 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef, void(*init_funct
init_function);
}

# if PY_VERSION_HEX >= 0x03050000

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

# endif

#else

namespace
Expand Down
5 changes: 5 additions & 0 deletions test/fabscript
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,9 @@ 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)))

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)