Skip to content

Commit 2d2cfe7

Browse files
committed
Use 'raise from' in initialization
1 parent ce46778 commit 2d2cfe7

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

include/pybind11/detail/common.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,19 @@ extern "C" {
276276
} \
277277
}
278278

279+
#if PY_VERSION_HEX >= 0x03030000
280+
281+
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
282+
catch (pybind11::error_already_set &e) { \
283+
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
284+
return nullptr; \
285+
} catch (const std::exception &e) { \
286+
PyErr_SetString(PyExc_ImportError, e.what()); \
287+
return nullptr; \
288+
} \
289+
290+
#else
291+
279292
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
280293
catch (pybind11::error_already_set &e) { \
281294
PyErr_SetString(PyExc_ImportError, e.what()); \
@@ -285,6 +298,8 @@ extern "C" {
285298
return nullptr; \
286299
} \
287300

301+
#endif
302+
288303
/** \rst
289304
***Deprecated in favor of PYBIND11_MODULE***
290305

include/pybind11/pytypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ class PYBIND11_EXPORT error_already_set : public std::runtime_error {
386386
/// 'raise from' to indicate that the chosen error was caused by the original error
387387
inline void raise_from(PyObject *type, const char *message) {
388388
// from cpython/errors.c _PyErr_FormatVFromCause
389-
PyObject *exc, *val, *val2, *tb;
389+
PyObject *exc = nullptr, *val = nullptr, *val2 = nullptr, *tb = nullptr;
390390
PyErr_Fetch(&exc, &val, &tb);
391391

392392
PyErr_NormalizeException(&exc, &val, &tb);

tests/test_embed/test_interpreter.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,24 @@ TEST_CASE("Import error handling") {
7474
REQUIRE_NOTHROW(py::module_::import("widget_module"));
7575
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"),
7676
"ImportError: C++ Error");
77+
#if PY_VERSION_HEX >= 0x03030000
78+
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
79+
Catch::Contains("ImportError: initialization failed"));
80+
81+
auto locals = py::dict("is_keyerror"_a=false, "message"_a="not set");
82+
py::exec(R"(
83+
try:
84+
import throw_error_already_set
85+
except ImportError as e:
86+
is_keyerror = type(e.__cause__) == KeyError
87+
message = str(e.__cause__)
88+
)", py::globals(), locals);
89+
REQUIRE(locals["is_keyerror"].cast<bool>() == true);
90+
REQUIRE(locals["message"].cast<std::string>() == "'missing'");
91+
#else
7792
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
7893
Catch::Contains("ImportError: KeyError"));
94+
#endif
7995
}
8096

8197
TEST_CASE("There can be only one interpreter") {

0 commit comments

Comments
 (0)