-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
_PyErr_ChainExceptions1 no longer avallable to extension modules in Python 3.13 alpha headers #116809
Comments
CC @vstinner |
cc @iritkatriel |
I wouldn't just make this function public without thinking through its design. It's not a very common operation so we need think what its semantics and use case are. |
Maybe right now, we can just revert _PyErr_ChainExceptions1. Not sure about _PyErr_ChainExceptions. |
I agree. |
|
First PR: issue gh-116900 restores the private function IMO it's on time to add a public function In Python, there are around 33 calls to this function. In PyPI top 8,000 projects (at 2023-11-15), I found 2 projects using But if I look for
Then if I look for
In that case, I can easily emulate this function in the pythoncapi-compat project to provide a new public function to old Python versions! |
I'm not sure. I think @ronaldoussoren 's point was that it's not fundamental (you can implement it with the current API). My view is that it's not necessarily a common enough operation that we need to provide in the API. |
As long as the documentation says how to chain exceptions I'd be happy. Currently it doesn't tell you. A general web search only turns up PEP490 mentioning The nice thing about an API with |
It's about having a convenient API for this operation: /* Like PyErr_SetRaisedException(), but if an exception is already set,
set the context associated with it.
The caller is responsible for ensuring that this call won't create
any cycles in the exception context chain. */
void
_PyErr_ChainExceptions1(PyObject *exc)
{
if (exc == NULL) {
return;
}
PyThreadState *tstate = _PyThreadState_GET();
if (_PyErr_Occurred(tstate)) {
PyObject *exc2 = _PyErr_GetRaisedException(tstate);
PyException_SetContext(exc2, exc);
_PyErr_SetRaisedException(tstate, exc2);
}
else {
_PyErr_SetRaisedException(tstate, exc);
}
} Having to copy/paste this function in each project doesn't sound great. And I would prefer that users don't consume private APIs. |
Why should there be a convenient API for this operation? What is this operation? What is the use case for it? How common is it? How would you document it as a public API? The name of this function is not right - I could expect ChainExceptions to take two exceptions and chain them. So how would you name this operation?
There are public versions of the private functions you call in this implementation. |
It does in the sense that one is the supplied parameter, and the other is the already existing raised exception. The context in which I need to chain exceptions is having a C level callback from a third party library and on entry there is already a raised exception. I stash that one away ( |
Indeed. The function is easily implemented using public API if you need it.
In the few cases where I use chaining in a C extension I'd prefer a variant of |
I do wonder why they don't always do that. But in the use case described by @rogerbinns you would need to call the new versions of these function in the called code, rather than in the calling code where the chaining occurs (and you already have the exception object you want to chain). |
Well, my PEP 490 "Chain exceptions at C level" got rejected (in 2015) :-) |
About the last bit: The implementation in CPython uses private APIs, but you can implement this without using private APIs (basically all of the consumed private are public APIs in 3.12 when you drop the leading underscore and the state argument). As mentioned in my previous message the shape of this API doesn't cleanly match how I currently set the context or cause in my own code. I always create a new exception that I'd like to chain to the currently active exception (if any). That is (ignoring reference count updates for clarity):
This code isn't simplified by using @rogerbinns, what's your use case for chaining? Do you want to chain to a pre-existing exception, or does your use case match mine? |
In 2021, @pablogsal asked to expose _PyErr_ChainExceptions() in the stable ABI (emphasis is mine) in gh-89101:
|
@ronaldoussoren my use case is dealing with a sequence of callbacks from a C library that I then pass on back to Python code. I don't actually create any of the exceptions.
In pseudo-code:
|
It is important to note that will not work in the general case. The fundamental problem is that many of the CPython APIs behave differently if there is already a pending exception when they are called, and in some cases clear the pending exception. Usually the problem is their implementation can't tell the difference between an exception being present because they caused it as a failure case, or if it was already there. For manual or automatic chaining to work, every CPython API needs to be audited and updated to ensure that it behaves as expected if there is a pending exception on entry. I don't envy trying to update |
@ronaldoussoren I think it may also be the case that you don't really want to chain an exception, but rather want to add a note to the existing exception PEP678 style. There isn't a CPython API for that either. I have my own implementation which is reproduced in edited form below. In order to add the note, you have to create the string, and call the
|
I think if the current exception already has a context, then |
Bug report
Bug description:
The API
_PyErr_ChainExceptions1
has been removed from public headers as part of this change in Python 3.13 alpha. It is the only C API available to chain exceptions replacing_PyErr_ChainExceptions
. That change was publicly documented in the 3.12 whatsnew.In my extemsion module I use
_PyErr_ChainExceptions
for exception chaining for Python <= 3.11 from PEP 490 and_PyErr_ChainExceptions1
for 3.12+.Please provide an approved way for C extensions to continue to chain exceptions. The function is still present and I can duplicate the prototype for all to continue working, but that is not a desirable solution.
CPython versions tested on:
3.13
Operating systems tested on:
Linux
Linked PRs
The text was updated successfully, but these errors were encountered: