gh-146480: Override the exception in _PyErr_SetKeyError()#146486
gh-146480: Override the exception in _PyErr_SetKeyError()#146486vstinner merged 4 commits intopython:mainfrom
Conversation
If _PyErr_SetKeyError() is called with an exception set, it now replaces the current exception with KeyError (as expected), instead of setting a SystemError or failing with a fatal error (in debug mode).
Lib/test/test_capi/test_misc.py
Outdated
| with self.assertRaises(KeyError) as cm: | ||
| # Test _PyErr_SetKeyError() with an exception set to check | ||
| # that the function overrides the current exception | ||
| _pyerr_setkeyerror("key") |
There was a problem hiding this comment.
I think it is worth to test also the case when the argument is a tuple and when the argument is a KeyError instance. I think this was the reason of introducing _PyErr_SetKeyError() instead of simply calling PyErr_SetObject().
There was a problem hiding this comment.
Ok, I added tests on a tuple and a KeyError.
I think this was the reason of introducing _PyErr_SetKeyError() instead of simply calling PyErr_SetObject().
Ah right, PyErr_SetObject(exc_type, value) has an unsual API: if value is a tuple, it unpacks arguments by calling exc_type(*value). Otherwise, it calls exc_type(value).
There was a problem hiding this comment.
And if the value is already an instance of the specified exception type, it just uses it.
Python/errors.c
Outdated
| @@ -248,11 +248,19 @@ PyErr_SetObject(PyObject *exception, PyObject *value) | |||
|
|
|||
| /* Set a key error with the specified argument, wrapping it in a | |||
| * tuple automatically so that tuple keys are not unpacked as the | |||
There was a problem hiding this comment.
This comment is outdated, because the code no longer explicitly wraps the key in a tuple. But it has roughtly the same effect, the reasons why it does not simply use PyErr_SetObject() is still valid. If we keep the current code, we need other wording here.
I wonder if partial reversion of aa36f83 will fix the issue. Would it have performance impact? This is just a curiosity, not suggestion.
There was a problem hiding this comment.
I updated the comment by removing the part about wrapping into a tuple.
I wonder if partial reversion of aa36f83 will fix the issue.
The old implementation can be called with an exception set:
void
_PyErr_SetKeyError(PyObject *arg)
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *tup = PyTuple_Pack(1, arg);
if (!tup) {
/* caller will expect error to be set anyway */
return;
}
_PyErr_SetObject(tstate, PyExc_KeyError, tup);
Py_DECREF(tup);
}Would it have performance impact? This is just a curiosity, not suggestion.
According to my comment, I rewrote _PyErr_SetKeyError() to be able to reuse the faster BaseException_vectorcall(). I don't know if it has a significant impact on performance. This vectorcall function has to create a tuple of arguments using _PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf)).
Update also outdated comment.
|
@serhiy-storchaka: I updated the PR to address your review. |
|
IMO we should backport the fix to the 3.14 stable branch which is also affected by the issue. Python 3.13 is not affected, it uses the old |
serhiy-storchaka
left a comment
There was a problem hiding this comment.
LGTM. 👍
But could you add other explanation why do we need _PyErr_SetKeyError() and cannot simply use PyErr_SetObject() in the comment?
It is worth to backport the test to 3.13.
Lib/test/test_capi/test_misc.py
Outdated
| with self.assertRaises(KeyError) as cm: | ||
| # Test _PyErr_SetKeyError() with an exception set to check | ||
| # that the function overrides the current exception | ||
| _pyerr_setkeyerror("key") |
There was a problem hiding this comment.
And if the value is already an instance of the specified exception type, it just uses it.
* Elaborate _PyErr_SetKeyError() comment to explain the usage of this
function.
* Explain the purpopose of the KeyError(KeyError('arg')) test.
* Move the test to test_capi.test_exceptions.
Ok, done. |
|
I was lazy and didn't run the test suite, and I got catched by the CI: I forgot |
|
Thanks @vstinner for the PR 🌮🎉.. I'm working now to backport this PR to: 3.14. |
…onGH-146486) If _PyErr_SetKeyError() is called with an exception set, it now replaces the current exception with KeyError (as expected), instead of setting a SystemError or failing with a fatal error (in debug mode). (cherry picked from commit d4153a9f76736128306c4af01776729da846d926) Co-authored-by: Victor Stinner <[email protected]>
|
GH-146511 is a backport of this pull request to the 3.14 branch. |
|
GH-146512 is a backport of this pull request to the 3.13 branch. |
Done in #146512. |
…146486) (#146511) gh-146480: Override the exception in _PyErr_SetKeyError() (GH-146486) If _PyErr_SetKeyError() is called with an exception set, it now replaces the current exception with KeyError (as expected), instead of setting a SystemError or failing with a fatal error (in debug mode). (cherry picked from commit d4153a9) Co-authored-by: Victor Stinner <[email protected]>
If _PyErr_SetKeyError() is called with an exception set, it now replaces the current exception with KeyError (as expected), instead of setting a SystemError or failing with a fatal error (in debug mode).