From 00b5357b8ebfe521851f296b7d555d3ba1cf16f6 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 26 Aug 2025 12:22:47 +0200 Subject: [PATCH] task: More robust handling of iterators without send()/throw() --- panda/src/event/pythonTask.cxx | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index a978b9d613..2e2213eacc 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -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::get_cancelled_error_type()); + PyObject *exc_type = Extension::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) {