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.)
This commit is contained in:
Dean Moldovan 2017-02-08 20:23:56 +01:00 committed by Wenzel Jakob
parent 1eaacd19f6
commit d534bd670e
2 changed files with 8 additions and 2 deletions

View File

@ -189,6 +189,10 @@ extern "C" {
} \ } \
try { \ try { \
return pybind11_init(); \ return pybind11_init(); \
} catch (pybind11::error_already_set &e) { \
e.clear(); \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} catch (const std::exception &e) { \ } catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \ PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \ return nullptr; \
@ -561,6 +565,9 @@ public:
/// Give the error back to Python /// Give the error back to Python
void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; } 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: private:
PyObject *type, *value, *trace; PyObject *type, *value, *trace;
}; };

View File

@ -1884,8 +1884,7 @@ class gil_scoped_release { };
error_already_set::~error_already_set() { error_already_set::~error_already_set() {
if (value) { if (value) {
gil_scoped_acquire gil; gil_scoped_acquire gil;
PyErr_Restore(type, value, trace); clear();
PyErr_Clear();
} }
} }