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
PyTraceBack_Print() fails if signal received but PyErr_CheckSignals() not called #57882
Comments
If SIGINT arrives while a function implemented in C is executing, then it prevents the function from raising an exception unless the function first calls PyErr_CheckSignals(). (If the function returns an object (instead of NULL) then KeyboardInterrupt is raised as expected.) For example, the following function just spins for 5 seconds before raising RuntimeError: static PyObject *
testsigint_wait(PyObject *self, PyObject *arg)
{
clock_t start = clock();
while (clock() - start < 5 * CLOCKS_PER_SEC) {
/* pass */
}
//PyErr_CheckSignals();
PyErr_SetNone(PyExc_RuntimeError);
return NULL;
} If I call this function and press Ctrl-C before it completes, then I get the following: >>> import testsigint
>>> a = testsigint.wait()
^C>>> print(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined So the call failed, but no exception was raised, and the variable "a" was not set! I would have expected RuntimeError (or KeyboardInterrupt) to be raised. If I uncomment the PyErr_CheckSignals() line then I get RuntimeError as expected: >>> import testsigint
>>> a = testsigint.wait()
^CTraceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError Also, if I wrap the call in try...finally or try...except, I get a sensible "chained" traceback: >>> try:
... testsigint.wait()
... finally:
... print("done")
...
^CTraceback (most recent call last):
File "<stdin>", line 2, in <module>
RuntimeError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
KeyboardInterrupt (Tested under Linux and Windows with the default branch.) |
I have tried the same with Python 2.7.1 on Linux. The problem is the same, but one gets a partial traceback with no exception: >>> import sys, testsigint
>>> testsigint.wait()
^CTraceback (most recent call last):
File "<stdin>", line 1, in <module>
>>> sys.last_value
RuntimeError() Both on 2.7 and 3.3 sys.last_value gives RuntimeError(). |
I think I have found the problem. PyTraceBack_Print() calls PyFile_WriteString(), which calls PyFile_WriteObject(), which calls PyObject_Str() which begins with PyObject_Str(PyObject *v)
{
PyObject *res;
if (PyErr_CheckSignals())
return NULL;
... Since PyErr_CheckSignals() returns -1, PyTraceBack_Print() fails. (Changed title.) |
Attached is a patch for the default branch. Before calling PyFile_WriteString() the patch saves the current exception. Then it calls PyErr_CheckSignals() and clears the current exception if any. After calling PyFile_WriteString() the exception is restored. I am not sure this is an appropriate fix. |
I think calling PyErr_WriteUnraisable would be more appropriate than PyErr_Clear. |
You mean just adding PyErr_CheckSignals();
if (PyErr_Occurred())
PyErr_WriteUnraisable(NULL); before the call to PyFile_WriteString()? That seems to work: >>> from testsigint import *; wait()
^CException KeyboardInterrupt ignored
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError
The ignoring and clearing of exceptions also happens higher (lower?) in the call stack in print_exception() and print_exception_recursive(). For example, print_exception() ends with /* If an error happened here, don't show it.
XXX This is wrong, but too many callers rely on this behavior. */
if (err != 0)
PyErr_Clear();
} |
Trivial 3 lines patch. I guess there is still a race: if Ctrl-C is pressed after PyErr_CheckSignals() is called but before PyObject_Str() then the printing of any exception can still be suppressed. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: