mirror of
https://github.com/pybind/pybind11.git
synced 2025-03-24 01:26:42 +00:00
Change PYBIND11_MODULE to use multi-phase init (PEP 489) (#5574)
* 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. * Avoid dynamic allocation and non-trivial destruction ... by using an std::array for the slots. * Oops, stray cut and paste character * Remove assignment from placement new, change size fo slots array * style: pre-commit fixes --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
97022f83ce
commit
974eba77a5
@ -372,11 +372,9 @@
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set & e) { \
|
||||
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
|
||||
return nullptr; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
::pybind11::set_error(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
/** \rst
|
||||
@ -404,6 +402,7 @@
|
||||
return pybind11_init(); \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
return nullptr; \
|
||||
} \
|
||||
PyObject *pybind11_init()
|
||||
|
||||
@ -447,23 +446,33 @@
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
|
||||
#define PYBIND11_MODULE(name, variable, ...) \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
|
||||
PYBIND11_MAYBE_UNUSED; \
|
||||
PYBIND11_MAYBE_UNUSED \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||
static ::pybind11::module_::slots_array PYBIND11_CONCAT(pybind11_module_slots_, name); \
|
||||
static int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject *); \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
PYBIND11_CHECK_PYTHON_VERSION \
|
||||
PYBIND11_ENSURE_INTERNALS_READY \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
auto &slots = PYBIND11_CONCAT(pybind11_module_slots_, name); \
|
||||
slots[0] \
|
||||
= {Py_mod_exec, reinterpret_cast<void *>(&PYBIND11_CONCAT(pybind11_exec_, name))}; \
|
||||
slots[1] = {0, nullptr}; \
|
||||
auto m = ::pybind11::module_::initialize_multiphase_module_def( \
|
||||
PYBIND11_TOSTRING(name), \
|
||||
nullptr, \
|
||||
&PYBIND11_CONCAT(pybind11_module_def_, name), \
|
||||
slots, \
|
||||
##__VA_ARGS__); \
|
||||
return m.ptr(); \
|
||||
} \
|
||||
int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject * pm) { \
|
||||
try { \
|
||||
auto m = pybind11::reinterpret_borrow<::pybind11::module_>(pm); \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
return 0; \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
return -1; \
|
||||
} \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
|
||||
PYBIND11_WARNING_POP
|
||||
|
@ -49,6 +49,7 @@
|
||||
return m.ptr(); \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
return nullptr; \
|
||||
} \
|
||||
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
|
||||
|
@ -1255,6 +1255,22 @@ private:
|
||||
bool flag_;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline bool gil_not_used_option() { return false; }
|
||||
template <typename F, typename... O>
|
||||
bool gil_not_used_option(F &&, O &&...o);
|
||||
template <typename... O>
|
||||
inline bool gil_not_used_option(mod_gil_not_used f, O &&...o) {
|
||||
return f.flag() || gil_not_used_option(o...);
|
||||
}
|
||||
template <typename F, typename... O>
|
||||
inline bool gil_not_used_option(F &&, O &&...o) {
|
||||
return gil_not_used_option(o...);
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// Wrapper for Python extension modules
|
||||
class module_ : public object {
|
||||
public:
|
||||
@ -1362,16 +1378,15 @@ public:
|
||||
= mod_gil_not_used(false)) {
|
||||
// module_def is PyModuleDef
|
||||
// Placement new (not an allocation).
|
||||
def = new (def)
|
||||
PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
|
||||
/* m_name */ name,
|
||||
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
|
||||
/* m_size */ -1,
|
||||
/* m_methods */ nullptr,
|
||||
/* m_slots */ nullptr,
|
||||
/* m_traverse */ nullptr,
|
||||
/* m_clear */ nullptr,
|
||||
/* m_free */ nullptr};
|
||||
new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
|
||||
/* m_name */ name,
|
||||
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
|
||||
/* m_size */ -1,
|
||||
/* m_methods */ nullptr,
|
||||
/* m_slots */ nullptr,
|
||||
/* m_traverse */ nullptr,
|
||||
/* m_clear */ nullptr,
|
||||
/* m_free */ nullptr};
|
||||
auto *m = PyModule_Create(def);
|
||||
if (m == nullptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
@ -1389,6 +1404,68 @@ public:
|
||||
// For Python 2, reinterpret_borrow was correct.
|
||||
return reinterpret_borrow<module_>(m);
|
||||
}
|
||||
|
||||
/// Must be a POD type, and must hold enough entries for all of the possible slots PLUS ONE for
|
||||
/// the sentinel (0) end slot.
|
||||
using slots_array = std::array<PyModuleDef_Slot, 3>;
|
||||
|
||||
/** \rst
|
||||
Initialized a module def for use with multi-phase module initialization.
|
||||
|
||||
``def`` should point to a statically allocated module_def.
|
||||
``slots`` must already contain a Py_mod_exec or Py_mod_create slot and will be filled with
|
||||
additional slots from the supplied options (and the empty sentinel slot).
|
||||
\endrst */
|
||||
template <typename... Options>
|
||||
static object initialize_multiphase_module_def(const char *name,
|
||||
const char *doc,
|
||||
module_def *def,
|
||||
slots_array &slots,
|
||||
Options &&...options) {
|
||||
size_t next_slot = 0;
|
||||
size_t term_slot = slots.size() - 1;
|
||||
|
||||
// find the end of the supplied slots
|
||||
while (next_slot < term_slot && slots[next_slot].slot != 0) {
|
||||
++next_slot;
|
||||
}
|
||||
|
||||
bool nogil PYBIND11_MAYBE_UNUSED = detail::gil_not_used_option(options...);
|
||||
if (nogil) {
|
||||
#if defined(Py_mod_gil) && defined(Py_GIL_DISABLED)
|
||||
if (next_slot >= term_slot) {
|
||||
pybind11_fail("initialize_multiphase_module_def: not enough space in slots");
|
||||
}
|
||||
slots[next_slot++] = {Py_mod_gil, Py_MOD_GIL_NOT_USED};
|
||||
#endif
|
||||
}
|
||||
|
||||
// slots must have a zero end sentinel
|
||||
if (next_slot > term_slot) {
|
||||
pybind11_fail("initialize_multiphase_module_def: not enough space in slots");
|
||||
}
|
||||
slots[next_slot++] = {0, nullptr};
|
||||
|
||||
// module_def is PyModuleDef
|
||||
// Placement new (not an allocation).
|
||||
new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
|
||||
/* m_name */ name,
|
||||
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
|
||||
/* m_size */ 0,
|
||||
/* m_methods */ nullptr,
|
||||
/* m_slots */ &slots[0],
|
||||
/* m_traverse */ nullptr,
|
||||
/* m_clear */ nullptr,
|
||||
/* m_free */ nullptr};
|
||||
auto *m = PyModuleDef_Init(def);
|
||||
if (m == nullptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
pybind11_fail("Internal error in module_::initialize_multiphase_module_def()");
|
||||
}
|
||||
return reinterpret_borrow<object>(m);
|
||||
}
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
Loading…
Reference in New Issue
Block a user