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_SetObject()
behavior is strange and not as documented.
#101578
Comments
The only case I can find of strange behavior leaking out is this: import sys
def wrap_in_sys_exit(ex):
try:
try:
raise ex(-1)
except ex as err:
sys.exit(err)
except BaseException as err:
return err
print(repr(wrap_in_sys_exit(ValueError)))
print(repr(wrap_in_sys_exit(SystemExit))) Which prints
instead of the expected
|
This API goes by to 1997. It is remarkable how simple and clear the code used to be: void
PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
{
PyThreadState *tstate = PyThreadState_GET();
PyObject *oldtype, *oldvalue, *oldtraceback;
if (traceback != NULL && !PyTraceBack_Check(traceback)) {
/* XXX Should never happen -- fatal error instead? */
Py_DECREF(traceback);
traceback = NULL;
}
/* Save these in locals to safeguard against recursive
invocation through Py_XDECREF */
oldtype = tstate->curexc_type;
oldvalue = tstate->curexc_value;
oldtraceback = tstate->curexc_traceback;
tstate->curexc_type = type;
tstate->curexc_value = value;
tstate->curexc_traceback = traceback;
Py_XDECREF(oldtype);
Py_XDECREF(oldvalue);
Py_XDECREF(oldtraceback);
}
void
PyErr_SetObject(PyObject *exception, PyObject *value)
{
Py_XINCREF(exception);
Py_XINCREF(value);
PyErr_Restore(exception, value, (PyObject *)NULL);
} |
The code may have been simple, but it can leave the VM in a precarious state if the value is not a legal argument for calling the type. |
* Make sure that the current exception is always normalized. * Remove redundant type and traceback fields for the current exception. * Add new API functions: PyErr_GetRaisedException, PyErr_SetRaisedException * Add new API functions: PyException_GetArgs, PyException_SetArgs
* main: (82 commits) pythongh-101670: typo fix in PyImport_ExtendInittab() (python#101723) pythonGH-99293: Document that `Py_TPFLAGS_VALID_VERSION_TAG` shouldn't be used. (#pythonGH-101736) no-issue: Add Dong-hee Na as the cjkcodecs codeowner (pythongh-101731) pythongh-101678: Merge math_1_to_whatever() and math_1() (python#101730) pythongh-101678: refactor the math module to use special functions from c11 (pythonGH-101679) pythongh-85984: Remove legacy Lib/pty.py code. (python#92365) pythongh-98831: Use opcode metadata for stack_effect() (python#101704) pythongh-101283: Version was just released, so should be changed in 3.11.3 (pythonGH-101719) pythongh-101283: Fix use of unbound variable (pythonGH-101712) pythongh-101283: Improved fallback logic for subprocess with shell=True on Windows (pythonGH-101286) pythongh-101277: Port more itertools static types to heap types (python#101304) pythongh-98831: Modernize CALL and family (python#101508) pythonGH-101696: invalidate type version tag in `_PyStaticType_Dealloc` (python#101697) pythongh-100221: Fix creating dirs in `make sharedinstall` (pythonGH-100329) pythongh-101670: typo fix in PyImport_AppendInittab() (pythonGH-101672) pythongh-101196: Make isdir/isfile/exists faster on Windows (pythonGH-101324) pythongh-101614: Don't treat python3_d.dll as a Python DLL when checking extension modules for incompatibility (pythonGH-101615) pythongh-100933: Improve `check_element` helper in `test_xml_etree` (python#100934) pythonGH-101578: Normalize the current exception (pythonGH-101607) pythongh-47937: Note that Popen attributes are read-only (python#93070) ...
The new C APIs
This goes against our recommendations for adding new public C APIs. I also find the added documentation inadequate. It is not documented that the former function returns a borrowed reference. The documentation for the latter function is not exactly friendly written, and I'm specifically thinking of the last parenthesised sentence:
Suggesting to considering reopening this issue until at least the docs have been improved. IMO, we should also fix the newly added C APIs to be consistent in their reference handling, which means:
|
The remark "(If you don’t understand this, don’t use this function. I warned you.)" comes directly from the documentation for
How does that differ from the current behavior? |
AFAICS, |
Huh? In that case, I'm reading this completely wrong: Lines 448 to 460 in 81e3aa8
Anyway, I still think it smells to introduce a new API that steals its argument. There should be no performance hit in making the caller instead of the callee having to decref that argument. |
If we introduce a new API, why copy the IMO inadequate docs and the smelly ref count behaviour? It's a new API; we can choose to follow the convention; we can choose to document it properly. |
I'm not saying we shouldn't document it better, just don't blame me for the docs 🙂 |
Sorry, if it came out that way; that was not my intent! Let's just amend the docs; I can propose a patch 🙂 (Regarding the reference counting, I'd still like to see the public APIs following the established conventions.) |
(Re-opening till we've patched the docs) |
Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM>
Follow-up to pythongh-101962
Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM>
We've fixed the docs; I'm fine with closing this now. |
OOI, why are all the version labels applied? |
because the behavior of The fix for 3.12+ is a better API. For older versions, the docs need fixing. |
So let's keep this issue open until we've patched the docs even more.
Furthermore, I still consider that the set-function should not steal a reference to its argument. A better API is a consistent API. |
Before we can close this, we need...:
|
* main: (225 commits) pythongh-102056: Fix a few bugs in error handling of exception printing code (python#102078) pythongh-102011: use sys.exception() instead of sys.exc_info() in docs where possible (python#102012) pythongh-101566: Sync with zipp 3.14. (pythonGH-102018) pythonGH-99818: improve the documentation for zipfile.Path and Traversable (pythonGH-101589) pythongh-88233: zipfile: handle extras after a zip64 extra (pythonGH-96161) pythongh-101981: Apply HOMEBREW related environment variables (pythongh-102074) pythongh-101907: Stop using `_Py_OPCODE` and `_Py_OPARG` macros (pythonGH-101912) pythongh-101819: Adapt _io types to heap types, batch 1 (pythonGH-101949) pythongh-101981: Build macOS as recommended by the devguide (pythonGH-102070) pythongh-97786: Fix compiler warnings in pytime.c (python#101826) pythongh-101578: Amend PyErr_{Set,Get}RaisedException docs (python#101962) Misc improvements to the float tutorial (pythonGH-102052) pythongh-85417: Clarify behaviour on branch cuts in cmath module (python#102046) pythongh-100425: Update tutorial docs related to sum() accuracy (FH-101854) Add missing 'is' to `cmath.log()` docstring (python#102049) pythongh-100210: Correct the comment link for unescaping HTML (python#100212) pythongh-97930: Also include subdirectory in makefile. (python#102030) pythongh-99735: Use required=True in argparse subparsers example (python#100927) Fix incorrectly documented attribute in csv docs (python#101250) pythonGH-84783: Make the slice object hashable (pythonGH-101264) ...
* main: (76 commits) Fix syntax error in struct doc example (python#102160) pythongh-99108: Import MD5 and SHA1 from HACL* (python#102089) pythonGH-101777: `queue.rst`: use 2 spaces after a period to be consistent. (python#102143) Few coverage nitpicks for the cmath module (python#102067) pythonGH-100982: Restrict `FOR_ITER_RANGE` to a single instruction to allow instrumentation. (pythonGH-101985) pythongh-102135: Update turtle docs to rename wikipedia demo to rosette (python#102137) pythongh-99942: python.pc on android/cygwin should link to libpython per configure.ac (pythonGH-100356) pythongh-95672 fix typo SkitTest to SkipTest (pythongh-102119) pythongh-101936: Update the default value of fp from io.StringIO to io.BytesIO (pythongh-102100) pythongh-102008: simplify test_except_star by using sys.exception() instead of sys.exc_info() (python#102009) pythongh-101903: Remove obsolete undefs for previously removed macros Py_EnterRecursiveCall and Py_LeaveRecursiveCall (python#101923) pythongh-100556: Improve clarity of `or` docs (python#100589) pythongh-101777: Make `PriorityQueue` docs slightly clearer (python#102026) pythongh-101965: Fix usage of Py_EnterRecursiveCall return value in _bisectmodule.c (pythonGH-101966) pythongh-101578: Amend exception docs (python#102057) pythongh-101961 fileinput.hookcompressed should not set the encoding value for the binary mode (pythongh-102068) pythongh-102056: Fix a few bugs in error handling of exception printing code (python#102078) pythongh-102011: use sys.exception() instead of sys.exc_info() in docs where possible (python#102012) pythongh-101566: Sync with zipp 3.14. (pythonGH-102018) pythonGH-99818: improve the documentation for zipfile.Path and Traversable (pythonGH-101589) ...
@markshannon, after looking through the implementation again, I agree that this is indeed a better API. I'm very sorry for the noise I've created in trying to push for a change in semantics for Regarding #101578 (comment): I'll add some more tests soon, and I'll also draft a proposal for the |
One more thing: did you deliberately not apply the |
Let's be careful with loud deprecations. The macros aren't particularly dangerous, so pushing users to rewrite their code wouldn't help much: this edge case most likely doesn't affect them, and if it does they “just” get the wrong exception. And they aren't much of a burden to keep in CPython. |
Sure; I don't think anyone is planning to do that. |
So if the plan is to deprecate |
There is no plan to change the semantics of We do seem to have to changed its semantics accidentally, as described in #102594, but the semantics in that case made no sense and was setting up a trap for whatever code tried to normalize the exception. |
…w normalized before stored
From offline conversation with Irit it looks like it's better to change the behavior of PyErr_SetObject (but in an acceptable way) without deprecating it. @markshannon We should discuss this at the Wednesday meeting. |
Why not deprecate it? |
…w normalized before stored (python#102702)
…w normalized before stored (python#102702)
Briefly:
PyErr_SetObject(exc_type, exc_val)
does not create a new exception iffisinstance(exc_val, BaseException)
, but usesexc_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 toexc_type
when calling it. So, ifisinstance(exc_val, BaseException)
the desired behavior can be achieved by wrappingexc_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:
We could just document the current behavior, but the current behavior is strange.
What I suggest is this:
PyErr_SetObject()
accuratelyThis is an old bug going back to the 2 series.
Linked PRs
Also relevant:
The text was updated successfully, but these errors were encountered: