Skip to content

Commit f428bc7

Browse files
committed
Change PYBIND11_MODULE to use multi-phase init
Use slots to specify advanced init options (currently just Py_GIL_NOT_USED). Adds a new function `initialize_multiphase_module_def` to module_, similar to the existing (unchanged) create_extension_module.
1 parent 655c60d commit f428bc7

File tree

3 files changed

+83
-7
lines changed

3 files changed

+83
-7
lines changed

include/pybind11/detail/common.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,9 @@
372372
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
373373
catch (pybind11::error_already_set & e) { \
374374
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
375-
return nullptr; \
376375
} \
377376
catch (const std::exception &e) { \
378377
::pybind11::set_error(PyExc_ImportError, e.what()); \
379-
return nullptr; \
380378
}
381379

382380
/** \rst
@@ -404,6 +402,7 @@
404402
return pybind11_init(); \
405403
} \
406404
PYBIND11_CATCH_INIT_EXCEPTIONS \
405+
return nullptr; \
407406
} \
408407
PyObject *pybind11_init()
409408

@@ -447,23 +446,33 @@
447446
PYBIND11_WARNING_PUSH
448447
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
449448
#define PYBIND11_MODULE(name, variable, ...) \
450-
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
451-
PYBIND11_MAYBE_UNUSED; \
452-
PYBIND11_MAYBE_UNUSED \
449+
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
450+
static std::vector<PyModuleDef_Slot> PYBIND11_CONCAT(pybind11_module_slots_, name); \
451+
static int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject *); \
453452
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
454453
PYBIND11_PLUGIN_IMPL(name) { \
455454
PYBIND11_CHECK_PYTHON_VERSION \
456455
PYBIND11_ENSURE_INTERNALS_READY \
457-
auto m = ::pybind11::module_::create_extension_module( \
456+
auto &slots = PYBIND11_CONCAT(pybind11_module_slots_, name); \
457+
slots.clear(); \
458+
slots.emplace_back(PyModuleDef_Slot{ \
459+
Py_mod_exec, reinterpret_cast<void *>(&PYBIND11_CONCAT(pybind11_exec_, name))}); \
460+
auto m = ::pybind11::module_::initialize_multiphase_module_def( \
458461
PYBIND11_TOSTRING(name), \
459462
nullptr, \
460463
&PYBIND11_CONCAT(pybind11_module_def_, name), \
464+
slots, \
461465
##__VA_ARGS__); \
466+
return m.ptr(); \
467+
} \
468+
int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject * pm) { \
462469
try { \
470+
auto m = pybind11::reinterpret_borrow<::pybind11::module_>(pm); \
463471
PYBIND11_CONCAT(pybind11_init_, name)(m); \
464-
return m.ptr(); \
472+
return 0; \
465473
} \
466474
PYBIND11_CATCH_INIT_EXCEPTIONS \
475+
return -1; \
467476
} \
468477
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
469478
PYBIND11_WARNING_POP

include/pybind11/embed.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
return m.ptr(); \
5050
} \
5151
PYBIND11_CATCH_INIT_EXCEPTIONS \
52+
return nullptr; \
5253
} \
5354
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
5455
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \

include/pybind11/pybind11.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,22 @@ class mod_gil_not_used {
12551255
bool flag_;
12561256
};
12571257

1258+
PYBIND11_NAMESPACE_BEGIN(detail)
1259+
1260+
inline bool gil_not_used_option() { return false; }
1261+
template <typename F, typename... O>
1262+
bool gil_not_used_option(F &&, O &&...o);
1263+
template <typename... O>
1264+
inline bool gil_not_used_option(mod_gil_not_used f, O &&...o) {
1265+
return f.flag() || gil_not_used_option(o...);
1266+
}
1267+
template <typename F, typename... O>
1268+
inline bool gil_not_used_option(F &&, O &&...o) {
1269+
return gil_not_used_option(o...);
1270+
}
1271+
1272+
PYBIND11_NAMESPACE_END(detail)
1273+
12581274
/// Wrapper for Python extension modules
12591275
class module_ : public object {
12601276
public:
@@ -1389,6 +1405,56 @@ class module_ : public object {
13891405
// For Python 2, reinterpret_borrow was correct.
13901406
return reinterpret_borrow<module_>(m);
13911407
}
1408+
1409+
/** \rst
1410+
Initialized a module def for use with multi-phase module initialization.
1411+
1412+
``def`` should point to a statically allocated module_def.
1413+
``slots`` must point to an initialized array of slots with space for at
1414+
least one additional slot to be populated based on the options.
1415+
\endrst */
1416+
template <typename... Options>
1417+
static object initialize_multiphase_module_def(const char *name,
1418+
const char *doc,
1419+
module_def *def,
1420+
std::vector<PyModuleDef_Slot> &slots,
1421+
Options &&...options) {
1422+
// remove zero end sentinel, if present
1423+
while (!slots.empty() && slots.back().slot == 0) {
1424+
slots.pop_back();
1425+
}
1426+
1427+
bool nogil PYBIND11_MAYBE_UNUSED = detail::gil_not_used_option(options...);
1428+
if (nogil) {
1429+
#if defined(Py_mod_gil) && defined(Py_GIL_DISABLED)
1430+
slots.emplace_back(PyModuleDef_Slot{Py_mod_gil, Py_MOD_GIL_NOT_USED});
1431+
#endif
1432+
}
1433+
1434+
// must have a zero end sentinel
1435+
slots.emplace_back(PyModuleDef_Slot{0, nullptr});
1436+
1437+
// module_def is PyModuleDef
1438+
// Placement new (not an allocation).
1439+
def = new (def)
1440+
PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1441+
/* m_name */ name,
1442+
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
1443+
/* m_size */ 0,
1444+
/* m_methods */ nullptr,
1445+
/* m_slots */ slots.data(),
1446+
/* m_traverse */ nullptr,
1447+
/* m_clear */ nullptr,
1448+
/* m_free */ nullptr};
1449+
auto *m = PyModuleDef_Init(def);
1450+
if (m == nullptr) {
1451+
if (PyErr_Occurred()) {
1452+
throw error_already_set();
1453+
}
1454+
pybind11_fail("Internal error in module_::initialize_multiphase_module_def()");
1455+
}
1456+
return reinterpret_borrow<object>(m);
1457+
}
13921458
};
13931459

13941460
PYBIND11_NAMESPACE_BEGIN(detail)

0 commit comments

Comments
 (0)