PyErr_SetObject()
behavior is strange and not as documented. · Issue #101578 · python/cpython (original) (raw)
Briefly:PyErr_SetObject(exc_type, exc_val)
does not create a new exception iff isinstance(exc_val, BaseException)
, but uses exc_val
instead.
Callers of PyErr_SetObject()
need various workarounds to handle this.
The long version:
Internally CPython handles exceptions as a triple (type, value, traceback)
, but the language treats exceptions as a single value.
This a legacy of the olden days before proper exceptions.
To handle adding proper exceptions to Python, various error handling functions, specifically _PyErr_SetObject
still treat exceptions as triples, with the convention that if the value is an exception, then the exception is already normalized.
One other oddity is that if exc_val
is a tuple, it is treated as the *
arguments to exc_type
when calling it. So, if isinstance(exc_val, BaseException)
the desired behavior can be achieved by wrapping exc_val
in a one-tuple.
As a consequence, both _PyErr_SetKeyError
and _PyGen_SetStopIterationValue
are a lot more complex than they should be to workaround this behavior.
We could make PyErr_SetObject
act as documented, but that is likely to break C extensions, given how old this behavior is, and that it is relied on throughout CPython.
Code that does the following is common:
exc = new_foo_exception();
PyErr_SetObject(&PyFooException_Type, exc);
We could just document the current behavior, but the current behavior is strange.
What I suggest is this:
- Create a new API function, PyErr_SetException(exc)` that takes a single exception object.
- Document
PyErr_SetObject()
accurately - Deprecate the old function
This is an old bug going back to the 2 series.
Linked PRs
- GH-101578: Normalize the current exception #101607
- gh-101578: Amend PyErr_{Set,Get}RaisedException docs #101962
- gh-101578: Amend exception docs #102057
- gh-101578: Fixup NEWS and add What's New entry for new exception APIs #102157
- gh-101578: mention in what's new in 3.12 that exceptions are now normalized before stored #102702
- gh-101578: [doc] mention that PyErr_GetRaisedException returns NULL when the error indicator is not set #113369
- [3.12] gh-101578: [doc] mention that PyErr_GetRaisedException returns NULL when the error indicator is not set (GH-113369) #113606
Also relevant: