Fix handling of Python exceptions during module initialization (#657)
Fixes #656.
Before this commit, the problematic sequence was:
1. `catch (const std::exception &e)` gets a Python exception,
i.e. `error_already_set`.
2. `PyErr_SetString(PyExc_ImportError, e.what())` sets an `ImportError`.
3. `~error_already_set()` now runs, but `gil_scoped_acquire` fails due
to an unhandled `ImportError` (which was just set in step 2).
This commit adds a separate catch block for Python exceptions which just
clears the Python error state a little earlier and replaces it with an
`ImportError`, thus making sure that there is only a single Python
exception in flight at a time. (After step 2 in the sequence above,
there were effectively two Python expections set.)
diff --git a/include/pybind11/common.h b/include/pybind11/common.h
index bbe92df..7299fbf 100644
--- a/include/pybind11/common.h
+++ b/include/pybind11/common.h
@@ -189,6 +189,10 @@
} \
try { \
return pybind11_init(); \
+ } catch (pybind11::error_already_set &e) { \
+ e.clear(); \
+ PyErr_SetString(PyExc_ImportError, e.what()); \
+ return nullptr; \
} catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
@@ -561,6 +565,9 @@
/// Give the error back to Python
void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; }
+ /// Clear the held Python error state (the C++ `what()` message remains intact)
+ void clear() { restore(); PyErr_Clear(); }
+
private:
PyObject *type, *value, *trace;
};
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index 05d6b06..006a4ff 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -1884,8 +1884,7 @@
error_already_set::~error_already_set() {
if (value) {
gil_scoped_acquire gil;
- PyErr_Restore(type, value, trace);
- PyErr_Clear();
+ clear();
}
}