Skip to content

Commit 1d9483f

Browse files
Added exception translator specific mutex used with try_translate_exceptions (#5362)
* Added exception translator specific mutex used with try_translate_exceptions Fixes #5346 * - Replaced with_internals_for_exception_translator by with_exception_translators - Incremented PYBIND11_INTERNALS_VERSION - Added a test * style: pre-commit fixes * Fixed formatting and added explicit to ctors * Addressed PR review comments --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent a7910be commit 1d9483f

File tree

6 files changed

+96
-21
lines changed

6 files changed

+96
-21
lines changed

include/pybind11/detail/exception_translation.h

+11-11
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,17 @@ inline void try_translate_exceptions() {
5050
- delegate translation to the next translator by throwing a new type of exception.
5151
*/
5252

53-
bool handled = with_internals([&](internals &internals) {
54-
auto &local_exception_translators = get_local_internals().registered_exception_translators;
55-
if (detail::apply_exception_translators(local_exception_translators)) {
56-
return true;
57-
}
58-
auto &exception_translators = internals.registered_exception_translators;
59-
if (detail::apply_exception_translators(exception_translators)) {
60-
return true;
61-
}
62-
return false;
63-
});
53+
bool handled = with_exception_translators(
54+
[&](std::forward_list<ExceptionTranslator> &exception_translators,
55+
std::forward_list<ExceptionTranslator> &local_exception_translators) {
56+
if (detail::apply_exception_translators(local_exception_translators)) {
57+
return true;
58+
}
59+
if (detail::apply_exception_translators(exception_translators)) {
60+
return true;
61+
}
62+
return false;
63+
});
6464

6565
if (!handled) {
6666
set_error(PyExc_SystemError, "Exception escaped from default exception translator!");

include/pybind11/detail/internals.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@
3939
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
4040
// Version bump for Python 3.12+, before first 3.12 beta release.
4141
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
42-
# define PYBIND11_INTERNALS_VERSION 5
42+
# ifdef Py_GIL_DISABLED
43+
# define PYBIND11_INTERNALS_VERSION 6
44+
# else
45+
# define PYBIND11_INTERNALS_VERSION 5
46+
# endif
4347
# else
4448
# define PYBIND11_INTERNALS_VERSION 4
4549
# endif
@@ -177,6 +181,7 @@ static_assert(sizeof(instance_map_shard) % 64 == 0,
177181
struct internals {
178182
#ifdef Py_GIL_DISABLED
179183
pymutex mutex;
184+
pymutex exception_translator_mutex;
180185
#endif
181186
// std::type_index -> pybind11's type information
182187
type_map<type_info *> registered_types_cpp;
@@ -643,6 +648,19 @@ inline auto with_internals(const F &cb) -> decltype(cb(get_internals())) {
643648
return cb(internals);
644649
}
645650

651+
template <typename F>
652+
inline auto with_exception_translators(const F &cb)
653+
-> decltype(cb(get_internals().registered_exception_translators,
654+
get_local_internals().registered_exception_translators)) {
655+
auto &internals = get_internals();
656+
#ifdef Py_GIL_DISABLED
657+
std::unique_lock<pymutex> lock((internals).exception_translator_mutex);
658+
#endif
659+
auto &local_internals = get_local_internals();
660+
return cb(internals.registered_exception_translators,
661+
local_internals.registered_exception_translators);
662+
}
663+
646664
inline std::uint64_t mix64(std::uint64_t z) {
647665
// David Stafford's variant 13 of the MurmurHash3 finalizer popularized
648666
// by the SplitMix PRNG.

include/pybind11/pybind11.h

+12-9
Original file line numberDiff line numberDiff line change
@@ -2586,10 +2586,12 @@ void implicitly_convertible() {
25862586
}
25872587

25882588
inline void register_exception_translator(ExceptionTranslator &&translator) {
2589-
detail::with_internals([&](detail::internals &internals) {
2590-
internals.registered_exception_translators.push_front(
2591-
std::forward<ExceptionTranslator>(translator));
2592-
});
2589+
detail::with_exception_translators(
2590+
[&](std::forward_list<ExceptionTranslator> &exception_translators,
2591+
std::forward_list<ExceptionTranslator> &local_exception_translators) {
2592+
(void) local_exception_translators;
2593+
exception_translators.push_front(std::forward<ExceptionTranslator>(translator));
2594+
});
25932595
}
25942596

25952597
/**
@@ -2599,11 +2601,12 @@ inline void register_exception_translator(ExceptionTranslator &&translator) {
25992601
* the exception.
26002602
*/
26012603
inline void register_local_exception_translator(ExceptionTranslator &&translator) {
2602-
detail::with_internals([&](detail::internals &internals) {
2603-
(void) internals;
2604-
detail::get_local_internals().registered_exception_translators.push_front(
2605-
std::forward<ExceptionTranslator>(translator));
2606-
});
2604+
detail::with_exception_translators(
2605+
[&](std::forward_list<ExceptionTranslator> &exception_translators,
2606+
std::forward_list<ExceptionTranslator> &local_exception_translators) {
2607+
(void) exception_translators;
2608+
local_exception_translators.push_front(std::forward<ExceptionTranslator>(translator));
2609+
});
26072610
}
26082611

26092612
/**

tests/custom_exceptions.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from __future__ import annotations
2+
3+
4+
class PythonMyException7(Exception):
5+
def __init__(self, message):
6+
self.message = message
7+
super().__init__(message)
8+
9+
def __str__(self):
10+
return "[PythonMyException7]: " + self.message.a

tests/test_exceptions.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ struct PythonAlreadySetInDestructor {
111111
py::str s;
112112
};
113113

114+
struct CustomData {
115+
explicit CustomData(const std::string &a) : a(a) {}
116+
std::string a;
117+
};
118+
119+
struct MyException7 {
120+
explicit MyException7(const CustomData &message) : message(message) {}
121+
CustomData message;
122+
};
123+
114124
TEST_SUBMODULE(exceptions, m) {
115125
m.def("throw_std_exception",
116126
[]() { throw std::runtime_error("This exception was intentionally thrown."); });
@@ -385,4 +395,33 @@ TEST_SUBMODULE(exceptions, m) {
385395

386396
// m.def("pass_exception_void", [](const py::exception<void>&) {}); // Does not compile.
387397
m.def("return_exception_void", []() { return py::exception<void>(); });
398+
399+
m.def("throws7", []() {
400+
auto data = CustomData("abc");
401+
throw MyException7(data);
402+
});
403+
404+
py::class_<CustomData>(m, "CustomData", py::module_local())
405+
.def(py::init<const std::string &>())
406+
.def_readwrite("a", &CustomData::a);
407+
408+
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object>
409+
PythonMyException7_storage;
410+
PythonMyException7_storage.call_once_and_store_result([&]() {
411+
auto mod = py::module_::import("custom_exceptions");
412+
py::object obj = mod.attr("PythonMyException7");
413+
return obj;
414+
});
415+
416+
py::register_local_exception_translator([](std::exception_ptr p) {
417+
try {
418+
if (p) {
419+
std::rethrow_exception(p);
420+
}
421+
} catch (const MyException7 &e) {
422+
auto exc_type = PythonMyException7_storage.get_stored();
423+
py::object exc_inst = exc_type(e.message);
424+
PyErr_SetObject(PyExc_Exception, exc_inst.ptr());
425+
}
426+
});
388427
}

tests/test_exceptions.py

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44

55
import pytest
6+
from custom_exceptions import PythonMyException7
67

78
import env
89
import pybind11_cross_module_tests as cm
@@ -195,6 +196,10 @@ def test_custom(msg):
195196
raise RuntimeError("Exception error: caught child from parent") from err
196197
assert msg(excinfo.value) == "this is a helper-defined translated exception"
197198

199+
with pytest.raises(PythonMyException7) as excinfo:
200+
m.throws7()
201+
assert msg(excinfo.value) == "[PythonMyException7]: abc"
202+
198203

199204
def test_nested_throws(capture):
200205
"""Tests nested (e.g. C++ -> Python -> C++) exception handling"""

0 commit comments

Comments
 (0)