Skip to content

Commit 7d962b6

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 `init_module_def` to module_, similar to the existing (unchanged) create_extension_module.
1 parent 655c60d commit 7d962b6

File tree

3 files changed

+94
-11
lines changed

3 files changed

+94
-11
lines changed

include/pybind11/detail/common.h

+20-11
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 PyModuleDef_Slot PYBIND11_CONCAT(pybind11_module_slots_, name)[5]; \
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( \
458-
PYBIND11_TOSTRING(name), \
459-
nullptr, \
460-
&PYBIND11_CONCAT(pybind11_module_def_, name), \
461-
##__VA_ARGS__); \
456+
PYBIND11_CONCAT(pybind11_module_slots_, name)[0].slot = Py_mod_exec; \
457+
PYBIND11_CONCAT(pybind11_module_slots_, name) \
458+
[0].value = reinterpret_cast<void *>(&PYBIND11_CONCAT(pybind11_exec_, name)); \
459+
PYBIND11_CONCAT(pybind11_module_slots_, name)[1] = {0, nullptr}; \
460+
auto m \
461+
= ::pybind11::module_::init_module_def(PYBIND11_TOSTRING(name), \
462+
nullptr, \
463+
&PYBIND11_CONCAT(pybind11_module_def_, name), \
464+
PYBIND11_CONCAT(pybind11_module_slots_, name), \
465+
##__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

+1
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

+73
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 false || 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,63 @@ 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 init_module_def(const char *name,
1418+
const char *doc,
1419+
module_def *def,
1420+
PyModuleDef_Slot *slots,
1421+
Options &&...options) {
1422+
int i = 0;
1423+
while (slots[i].slot != 0) {
1424+
++i;
1425+
}
1426+
1427+
bool nogil PYBIND11_MAYBE_UNUSED = detail::gil_not_used_option(options...);
1428+
1429+
if (nogil) {
1430+
#ifdef Py_mod_gil
1431+
slots[i].slot = Py_mod_gil;
1432+
# ifdef Py_GIL_DISABLED
1433+
slots[i].value = Py_MOD_GIL_NOT_USED;
1434+
# else
1435+
slots[i].value = Py_MOD_GIL_USED;
1436+
# endif
1437+
++i;
1438+
#endif
1439+
}
1440+
1441+
slots[i].slot = 0;
1442+
slots[i].value = nullptr;
1443+
1444+
// module_def is PyModuleDef
1445+
// Placement new (not an allocation).
1446+
def = new (def)
1447+
PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1448+
/* m_name */ name,
1449+
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
1450+
/* m_size */ 0,
1451+
/* m_methods */ nullptr,
1452+
/* m_slots */ slots,
1453+
/* m_traverse */ nullptr,
1454+
/* m_clear */ nullptr,
1455+
/* m_free */ nullptr};
1456+
auto *m = PyModuleDef_Init(def);
1457+
if (m == nullptr) {
1458+
if (PyErr_Occurred()) {
1459+
throw error_already_set();
1460+
}
1461+
pybind11_fail("Internal error in module_::init_module_def()");
1462+
}
1463+
return reinterpret_borrow<object>(m);
1464+
}
13921465
};
13931466

13941467
PYBIND11_NAMESPACE_BEGIN(detail)

0 commit comments

Comments
 (0)