-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
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 <[email protected]>
Follow-up to pythongh-101962
Why not deprecate it? |
…w normalized before stored (python#102702)
…w normalized before stored (python#102702)
I am just trying to port from PyErr_Fetch. And I would really suggest improving docs of new api
As a general remark, it isn't first such case when deprecation doesn't bring sufficient docs (and sometimes doesn't even bring sufficient new API). Please, don't deprecate without considering whether users of old code get sufficient info of how to port it. When you liquidate function which returned 3 values, describe how to get those 3 values. PS Update. One more doubt which would be worth docs (less related to deprecation as such):
|
@Mekk Would you like to create a PR with improvements to the docs? |
I don't know all the replies yet (not to mention lack of formal/legal framework). In general IMHO such info shouldn't be added as afterthought but should be treated as necessary part of change which deprecates APIs (nothing shows better whether deprecation is well thought out than detailed description how to change code which uses deprecated API). |
It's open source. I understand that you feel this deprecation was insufficiently documented and possibly not thought through sufficiently. Okay. But please give us a break and don't lecture us like we are noobs. We're only human and we sometimes make mistakes. And you get to use the fruits of our efforts for free. |
I comment on issue, and maybe process¹, not on people. Going back to technical aspects, there is also some subtle change with those old/deprecated APIs in 3.12, code like
stopped to work in 3.12 at least in some cases (I tested in context of ZeroDivisionError like above, UnicodeDecodeError triggered by invalid conversion and few more): msg is NULL and TypeError is raised by PyUnicode_AsUTf8. This is caused by the fact that errValue isn't str anymore but exception object instead:
I don't claim the former is better or worth keeping or anything, but simply this is change which breaks some code. ¹ To give some other recent example, deprecation of |
This is mentioned in the release notes:
|
This seems to be doubling down on your communication style, so I don't know if you're open to feedback on it or not. But on the off chance that you are, your messages came across to me like "I don't know enough about cpython dev process to actually contribute a docs improvement PR, and I can't be asked to learn the processes so that I can contribute, but I can tell you how you should be doing your job (twice)". That kind of attitude doesn't go down well in open source. |
Regarding your earlier questions:
I will add that it returns NULL.
I don't understand this question.
The doc says
We document when a function steals references, as that is the untypical case. We don't specify this for functions that do not steal references. |
…NULL when the error indicator is not set
…hen the error indicator is not set (#113369)
…NULL when the error indicator is not set (pythonGH-113369) (cherry picked from commit 2849cbb) Co-authored-by: Irit Katriel <[email protected]>
…NULL when the error indicator is not set (python#113369)
…NULL when the error indicator is not set (python#113369)
I think this is all done. Thanks @iritkatriel and @erlend-aasland |
Co-authored-by: C.A.M. Gerlach <[email protected]>
Co-authored-by: C.A.M. Gerlach <[email protected]>
…NULL when the error indicator is not set (python#113369)
Co-authored-by: C.A.M. Gerlach <[email protected]>
`_PyErr_SetKeyError` is more complex than originally thought, use the provided API when possible See also python/cpython#101578
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: