From 2d2cfe79fe91e2540706f4692c8f228d2cf36680 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 17 Jul 2020 03:45:17 -0400 Subject: [PATCH] Use 'raise from' in initialization --- include/pybind11/detail/common.h | 15 +++++++++++++++ include/pybind11/pytypes.h | 2 +- tests/test_embed/test_interpreter.cpp | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 52b0193350..7c5062d607 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -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()); \ @@ -285,6 +298,8 @@ extern "C" { return nullptr; \ } \ +#endif + /** \rst ***Deprecated in favor of PYBIND11_MODULE*** diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 0b41695328..360a2e5fce 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -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); diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 6925ee9782..8e45254332 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -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() == true); + REQUIRE(locals["message"].cast() == "'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") {