diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 5e225f8c1..bacc5a60d 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -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(&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 diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 0af777033..1b95c32a7 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -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)( \ diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 82c758af6..9499fa704 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1255,6 +1255,22 @@ private: bool flag_; }; +PYBIND11_NAMESPACE_BEGIN(detail) + +inline bool gil_not_used_option() { return false; } +template +bool gil_not_used_option(F &&, O &&...o); +template +inline bool gil_not_used_option(mod_gil_not_used f, O &&...o) { + return f.flag() || gil_not_used_option(o...); +} +template +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(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; + + /** \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 + 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(m); + } }; PYBIND11_NAMESPACE_BEGIN(detail)