Description
Bug report
Bug description:
Exception encountered during attempt to upgrade a C++ wrapper using the C API from Python 3.9 to 3.13.3.
Exception trace:
_abc_impl is set to a wrong type
<FrameSummary file python/3.13.3/lib/python3.13/traceback.py, line 155 in format_exception>
<FrameSummary file python/3.13.3/lib/python3.13/traceback.py, line 1406 in format>
<FrameSummary file python/3.13.3/lib/python3.13/traceback.py, line 993 in emit>
<FrameSummary file python/3.13.3/lib/python3.13/traceback.py, line 1259 in format_exception_only>
<FrameSummary file <frozen abc>, line 119 in __instancecheck__>
Details:
Exception is seen on attempt to use format_exception from the traceback module.
Previously we were using PyErr_Fetch to get type, value and traceback of the Exception.
We note the signature change of format_exception requires an Exception object.
We switched to use PyErr_GetRaisedException and pass this to format_exception.
We have a class that stores the exception reference object from PyErr_GetRaisedException which is then used to rethrow the exception to be caught in our code.
The catch then uses a callable function to the imported traceback module to invoke format_exception with the exception object. We then see the above exception stack. format_exception(py::make_tuple(e.getException()));
Where getException returns the Exception object reference stored.
We have been unable to reproduce this in a Python script yet.
Checked the callable and is as expected <function format_exception at 0x7fda88878720>
.
Checked the initial caught exception object and can print the 'what' as Exception('Thrown exception from static init')
.
Checked the initial caught exception is of type <class 'Exception'>
.
Confirmed signature of format_exception using the inspect module and parameters confirmed as exc, value, tb, limit, chain, kwargs
We are running in a sub-interpreter with the GIL taken prior to the try, catch that sees this exception.
We are hoping the above is enough to spot a potential issue or anti-pattern.
CPython versions tested on:
3.13
Operating systems tested on:
Linux
Metadata
Metadata
Assignees
Projects
Status
Activity
picnixz commentedon May 23, 2025
Can you give us the C code that triggers this issue? or at least a prototype and not with just words please? it's hard to track down the flow.
cc @ZeroIntensity
ZeroIntensity commentedon May 23, 2025
Yeah, it'd be nice to see some code.
It's hard to say without seeing code, but my best theory is that you're taking the GIL of the main interpreter and then trying to raise it in a subinterpreter, or vice versa. Are you using
PyGILState_Ensure
, by any chance?matj-sag commentedon May 27, 2025
Hi all, thanks for the advice so far. I would love to have a minimal repro, unfortunately it's being used via a custom C++ shim (whose tests all pass) and then embedded in a much larger program.
That GIL option is definitely plausible - I thought about it myself, but couldn't see anything going wrong in that regard.
We're generally using this code to aquire GILs:
This is being used something like:
Exceptions are being wrapped with:
One thing which does occur to me, is that we are looking up
format_exception
in the default interpreter and storing a reference to that in a singleton, which is then used each time we want to format something, even in subinterpreters (to avoid looking it up each time). Could that be an issue? This definitely worked on 3.9, it's become an issue as we've tried to migrate to the stable ABI on 3.13.ZeroIntensity commentedon May 27, 2025
Yup, that could cause this problem. You can't share objects between subinterpreters, at all. The interpreter will implicitly share some immortal objects for you (e.g.,
Py_True
andPy_False
), but don't worry about that. Objects have a lot of assumptions that aren't portable across interpreters. It might incidentally work on 3.9, but it's still wrong there. Interpreter isolation wasn't fully implemented until 3.12 (see PEP-684).Basically, the exception is because each interpreter has its own copy of
_abc
, and thus each have their own copy of its types. The reference toformat_exception
has a type pointing to the main interpreter's copy, so when called in a subinterpreter, the type check fails, because it's expecting a pointer for its own copy of the type.matj-sag commentedon May 27, 2025
OK, thanks, I'll review all our usage of that. It certainly wasn't clear when we started working on this that wasn't allowed. I think we can move the cache within where we create each interpreter and maintain it per-interpreter instead. We'll give that a try and let you know
matj-sag commentedon May 29, 2025
Right, that's solved the problem. Thanks for the insights all, you can feel free to close the issue
4 remaining items