[BUG]: Issue with Re-registering Exceptions in Multiple Interpreter Lifecycles
Required prerequisites
- [X] Make sure you've read the documentation. Your issue may be addressed there.
- [X] Search the issue tracker and Discussions to verify that this hasn't already been reported. +1 or comment there if it has.
- [X] Consider asking first in the Gitter chat room or in a Discussion.
What version (or hash if on master) of pybind11 are you using?
latest
Problem description
When registering an exception in a PYBIND11_MODULE scope (with pybind11::register_exception), pybind invokes allows this action only once per exception type. This is due to the usage of gil_safe_call_once_and_store in register_exception_impl.
The issue is, that some programs require to initialize + deinitialize the interpreter multiple times within the program's life time. So, everytime a module is created, the register_exception occurs, however, after the first time it is invoked, it will never be invoked again for the same exception class due to the usage of gil_safe_call_once_and_store (which calls std::call_once).
A simple solution for this is to not use the call_once in gil_safe_call_once_and_store, and simply registering the exception whenever a registration is invoked, regardless of it being already invoked previously.
This is pretty simple, makes me wonder if this was intended.
Reproducible example code
// a definition of MyException...
PYBIND11_EMBEDDED_MODULE(sample, m) {
py::register_exception<MyException>(m, "MyException")
}
int main() {
py::initialize_interpreter();
py::exec("from sample import MyException");
py::finalize_interpreter(); // Here, the exception registration will be destroyed
py::initialize_interpreter();
// The following will fail due to ImportError of MyException!
py::exec("from sample import MyException");
py::finalize_interpreter();
return 0;
}
Is this a regression? Put the last known working version here if it is.
Not a regression
It looks like this problem is still present in the current master (just by looking at the code, which is pretty much unchanged). I was stumbling across this bug in version 2.11, where the code was still different featuring a function detail::get_exception_object() instead of gil_safe_call_once_and_store with the same effect in the end. In 2.11, one can call py::detail::get_exception_object<MyException>().release(); as a workaround, but this does not work any more in newer versions.