Handle cases where binding code immediately throws py::error_already_set

When binding code immediately throws an exception of type
py::error_already_set (e.g. via py::module::import that fails), the
catch block sets an import error as expected. Unfortunately, following
this, the deconstructor of py::error_already_set decides to call
py::detail::get_internals() and set up various internal data structures
of pybind11, which fails given that the error flag is active. The call
stack of this looks as follows:

Py_init_mymodule() -> __cxa_decrement_exception_refcount ->
error_already_set::~error_already_set() ->
gil_scoped_acquire::gil_scoped_acquire() -> detail::get_internals() ->
... -> pybind11::detail::simple_collector() -> uh oh..

The solution is simple: we call detail::get_internals() once before
running any binding code to make sure that the internal data structures
are ready.
This commit is contained in:
Wenzel Jakob 2020-01-02 22:18:01 +01:00
parent 4c206e8c79
commit bf2b031449

View File

@ -218,6 +218,8 @@ extern "C" {
#define PYBIND11_STRINGIFY(x) #x #define PYBIND11_STRINGIFY(x) #x
#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) #define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
#define PYBIND11_CONCAT(first, second) first##second #define PYBIND11_CONCAT(first, second) first##second
#define PYBIND11_ENSURE_INTERNALS_READY \
pybind11::detail::get_internals();
#define PYBIND11_CHECK_PYTHON_VERSION \ #define PYBIND11_CHECK_PYTHON_VERSION \
{ \ { \
@ -264,6 +266,7 @@ extern "C" {
static PyObject *pybind11_init(); \ static PyObject *pybind11_init(); \
PYBIND11_PLUGIN_IMPL(name) { \ PYBIND11_PLUGIN_IMPL(name) { \
PYBIND11_CHECK_PYTHON_VERSION \ PYBIND11_CHECK_PYTHON_VERSION \
PYBIND11_ENSURE_INTERNALS_READY \
try { \ try { \
return pybind11_init(); \ return pybind11_init(); \
} PYBIND11_CATCH_INIT_EXCEPTIONS \ } PYBIND11_CATCH_INIT_EXCEPTIONS \
@ -291,6 +294,7 @@ extern "C" {
static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \
PYBIND11_PLUGIN_IMPL(name) { \ PYBIND11_PLUGIN_IMPL(name) { \
PYBIND11_CHECK_PYTHON_VERSION \ PYBIND11_CHECK_PYTHON_VERSION \
PYBIND11_ENSURE_INTERNALS_READY \
auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ auto m = pybind11::module(PYBIND11_TOSTRING(name)); \
try { \ try { \
PYBIND11_CONCAT(pybind11_init_, name)(m); \ PYBIND11_CONCAT(pybind11_init_, name)(m); \