task: More robust handling of iterators without send()/throw()

This commit is contained in:
rdb 2025-08-26 12:22:47 +02:00
parent 67e3ccada7
commit 00b5357b8e
1 changed files with 21 additions and 7 deletions

View File

@ -638,17 +638,31 @@ do_python_task() {
// We are calling a generator. Use "send" rather than PyIter_Next since
// we need to be able to read the value from a StopIteration exception.
PyObject *func = PyObject_GetAttrString(_generator, "send");
nassertr(func != nullptr, DS_interrupt);
result = PyObject_CallOneArg(func, Py_None);
Py_DECREF(func);
if (func != nullptr) {
result = PyObject_CallOneArg(func, Py_None);
Py_DECREF(func);
} else {
// It has no send(), just call next() directly.
nassertr(Py_TYPE(_generator)->tp_iternext != nullptr, DS_interrupt);
PyErr_Clear();
result = Py_TYPE(_generator)->tp_iternext(_generator);
}
} else {
// Throw a CancelledError into the generator.
_must_cancel = false;
PyObject *exc = PyObject_CallNoArgs(Extension<AsyncFuture>::get_cancelled_error_type());
PyObject *exc_type = Extension<AsyncFuture>::get_cancelled_error_type();
PyObject *func = PyObject_GetAttrString(_generator, "throw");
result = PyObject_CallFunctionObjArgs(func, exc, nullptr);
Py_DECREF(func);
Py_DECREF(exc);
if (func != nullptr) {
PyObject *exc = PyObject_CallNoArgs(exc_type);
result = PyObject_CallFunctionObjArgs(func, exc, nullptr);
Py_DECREF(exc);
Py_DECREF(func);
} else {
// If it has no throw(), assume it propagated the CancelledException.
PyErr_Clear();
PyErr_SetNone(exc_type);
result = nullptr;
}
}
if (result == nullptr) {