From d159a563383d10c821ba7b2a71905d1207db6de4 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 26 Sep 2020 22:55:11 -0700 Subject: [PATCH] static allocation for PyModuleDef, to avoid leak check errors. (#2413) * Initializing PyModuleDef object with PyModuleDef_HEAD_INIT. Python 3.8 documentation: m_base - Always initialize this member to PyModuleDef_HEAD_INIT. Long-standing (since first github commit in 2015), inconsequential bug. Also removing inconsequential Py_INCREF(def): PyModule_Create() resets the reference count to 1. * git rebase master * moving static PyModuleDef declaration to global scope, as requested by @wjakob * renaming the two new macros, to start with PYBIND11_DETAIL_MODULE --- include/pybind11/detail/common.h | 15 ++++++++++++++- include/pybind11/pybind11.h | 24 +++++++++++++++--------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 1f8390fba..c18970ddd 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -307,13 +307,26 @@ extern "C" { }); } \endrst */ +#if PY_MAJOR_VERSION >= 3 +#define PYBIND11_DETAIL_MODULE_STATIC_DEF(name) \ + static PyModuleDef PYBIND11_CONCAT(pybind11_module_def_, name); +#define PYBIND11_DETAIL_MODULE_CREATE(name) \ + auto m = pybind11::module( \ + PYBIND11_TOSTRING(name), nullptr, \ + &PYBIND11_CONCAT(pybind11_module_def_, name)); +#else +#define PYBIND11_DETAIL_MODULE_STATIC_DEF(name) +#define PYBIND11_DETAIL_MODULE_CREATE(name) \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); +#endif #define PYBIND11_MODULE(name, variable) \ + PYBIND11_DETAIL_MODULE_STATIC_DEF(name) \ PYBIND11_MAYBE_UNUSED \ 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(PYBIND11_TOSTRING(name)); \ + PYBIND11_DETAIL_MODULE_CREATE(name) \ try { \ PYBIND11_CONCAT(pybind11_init_, name)(m); \ return m.ptr(); \ diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index da3a78c25..0cde4fa95 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -862,18 +862,24 @@ public: PYBIND11_OBJECT_DEFAULT(module_, object, PyModule_Check) /// Create a new top-level Python module with the given name and docstring - explicit module_(const char *name, const char *doc = nullptr) { - if (!options::show_user_defined_docstrings()) doc = nullptr; #if PY_MAJOR_VERSION >= 3 - auto *def = new PyModuleDef(); - std::memset(def, 0, sizeof(PyModuleDef)); - def->m_name = name; - def->m_doc = doc; - def->m_size = -1; - Py_INCREF(def); + explicit module_(const char *name, const char *doc = nullptr, PyModuleDef *def = nullptr) { + if (!def) def = new PyModuleDef(); + def = new (def) PyModuleDef { // Placement new (not an allocation). + /* 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 + }; m_ptr = PyModule_Create(def); #else - m_ptr = Py_InitModule3(name, nullptr, doc); + explicit module_(const char *name, const char *doc = nullptr) { + m_ptr = Py_InitModule3(name, nullptr, options::show_user_defined_docstrings() ? doc : nullptr); #endif if (m_ptr == nullptr) pybind11_fail("Internal error in module_::module_()");