Skip to content

Commit

Permalink
Use 'raise from' in initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
virtuald committed Jul 26, 2021
1 parent ce46778 commit 2d2cfe7
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 1 deletion.
15 changes: 15 additions & 0 deletions include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,19 @@ extern "C" {
} \
}

#if PY_VERSION_HEX >= 0x03030000

#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) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} \

#else

#define PYBIND11_CATCH_INIT_EXCEPTIONS \
catch (pybind11::error_already_set &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
Expand All @@ -285,6 +298,8 @@ extern "C" {
return nullptr; \
} \

#endif

/** \rst
***Deprecated in favor of PYBIND11_MODULE***
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/pytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ class PYBIND11_EXPORT error_already_set : public std::runtime_error {
/// 'raise from' to indicate that the chosen error was caused by the original error
inline void raise_from(PyObject *type, const char *message) {
// from cpython/errors.c _PyErr_FormatVFromCause
PyObject *exc, *val, *val2, *tb;
PyObject *exc = nullptr, *val = nullptr, *val2 = nullptr, *tb = nullptr;
PyErr_Fetch(&exc, &val, &tb);

PyErr_NormalizeException(&exc, &val, &tb);
Expand Down
16 changes: 16 additions & 0 deletions tests/test_embed/test_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,24 @@ TEST_CASE("Import error handling") {
REQUIRE_NOTHROW(py::module_::import("widget_module"));
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"),
"ImportError: C++ Error");
#if PY_VERSION_HEX >= 0x03030000
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: initialization failed"));

auto locals = py::dict("is_keyerror"_a=false, "message"_a="not set");
py::exec(R"(
try:
import throw_error_already_set
except ImportError as e:
is_keyerror = type(e.__cause__) == KeyError
message = str(e.__cause__)
)", py::globals(), locals);
REQUIRE(locals["is_keyerror"].cast<bool>() == true);
REQUIRE(locals["message"].cast<std::string>() == "'missing'");
#else
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: KeyError"));
#endif
}

TEST_CASE("There can be only one interpreter") {
Expand Down

0 comments on commit 2d2cfe7

Please sign in to comment.