pybind11 icon indicating copy to clipboard operation
pybind11 copied to clipboard

[BUG]: Issue with Re-registering Exceptions in Multiple Interpreter Lifecycles

Open nowtilous opened this issue 2 years ago • 1 comments

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

nowtilous avatar Dec 11 '23 19:12 nowtilous

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.

mhier avatar Sep 05 '24 09:09 mhier