Skip to content

Commit a5ccb5f

Browse files
committed
Fix builtin exception handlers to work across modules
The builtin exception handler currently doesn't work across modules under clang/libc++ for builtin pybind exceptions like `pybind11::error_already_set` or `pybind11::stop_iteration`: under RTLD_LOCAL module loading clang considers each module's exception classes distinct types. This then means that the base exception translator fails to catch the exceptions and the fall through to the generic `std::exception` handler, which completely breaks things like `stop_iteration`: only the `stop_iteration` of the first module loaded actually works properly; later modules raise a RuntimeError with no message when trying to invoke their iterators. For example, two modules defined like this exhibit the behaviour under clang++/libc++: z1.cpp: #include <pybind11/pybind11.h> #include <pybind11/stl_bind.h> namespace py = pybind11; PYBIND11_MODULE(z1, m) { py::bind_vector<std::vector<long>>(m, "IntVector"); } z2.cpp: #include <pybind11/pybind11.h> #include <pybind11/stl_bind.h> namespace py = pybind11; PYBIND11_MODULE(z2, m) { py::bind_vector<std::vector<double>>(m, "FloatVector"); } Python: import z1, z2 for i in z2.FloatVector(): pass results in: Traceback (most recent call last): File "zs.py", line 2, in <module> for i in z2.FloatVector(): RuntimeError This commit fixes the issue by adding a new exception translator each time the internals pointer is initialized from python builtins: this generally means the internals data was initialized by some other module. (The extra translator(s) are skipped under libstdc++).
1 parent 60526d4 commit a5ccb5f

File tree

1 file changed

+17
-0
lines changed

1 file changed

+17
-0
lines changed

include/pybind11/cast.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,23 @@ PYBIND11_NOINLINE inline internals &get_internals() {
7575
const char *id = PYBIND11_INTERNALS_ID;
7676
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
7777
internals_ptr = *static_cast<internals **>(capsule(builtins[id]));
78+
79+
// We loaded builtins through python's builtins, which means that our error_already_set and
80+
// builtin_exception may be different local classes than the ones set up in the initial
81+
// exception translator, below, so add another for our local exception classes.
82+
//
83+
// stdlibc++ doesn't require this (types there are identified only by name)
84+
#if !defined(__GLIBCXX__)
85+
internals_ptr->registered_exception_translators.push_front(
86+
[](std::exception_ptr p) -> void {
87+
try {
88+
if (p) std::rethrow_exception(p);
89+
} catch (error_already_set &e) { e.restore(); return;
90+
} catch (const builtin_exception &e) { e.set_error(); return;
91+
}
92+
}
93+
);
94+
#endif
7895
} else {
7996
internals_ptr = new internals();
8097
#if defined(WITH_THREAD)

0 commit comments

Comments
 (0)