gh-112182: Replace StopIteration with RuntimeError for future (#113220)

When an `StopIteration` raises into `asyncio.Future`, this will cause
a thread to hang. This commit address this by not raising an exception
and silently transforming the `StopIteration` with a `RuntimeError`,
which the caller can reconstruct from `fut.exception().__cause__`
This commit is contained in:
Jamie Phan 2024-01-10 16:21:00 +11:00 committed by GitHub
parent 5d8a3e74b5
commit 4826d52338
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 12 deletions

View file

@ -597,12 +597,27 @@ future_set_exception(asyncio_state *state, FutureObj *fut, PyObject *exc)
PyErr_SetString(PyExc_TypeError, "invalid exception object");
return NULL;
}
if (Py_IS_TYPE(exc_val, (PyTypeObject *)PyExc_StopIteration)) {
if (PyErr_GivenExceptionMatches(exc_val, PyExc_StopIteration)) {
const char *msg = "StopIteration interacts badly with "
"generators and cannot be raised into a "
"Future";
PyObject *message = PyUnicode_FromString(msg);
if (message == NULL) {
Py_DECREF(exc_val);
return NULL;
}
PyObject *err = PyObject_CallOneArg(PyExc_RuntimeError, message);
Py_DECREF(message);
if (err == NULL) {
Py_DECREF(exc_val);
return NULL;
}
assert(PyExceptionInstance_Check(err));
PyException_SetCause(err, Py_NewRef(exc_val));
PyException_SetContext(err, Py_NewRef(exc_val));
Py_DECREF(exc_val);
PyErr_SetString(PyExc_TypeError,
"StopIteration interacts badly with generators "
"and cannot be raised into a Future");
return NULL;
exc_val = err;
}
assert(!fut->fut_exception);