فهرست منبع

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

rdb 3 ماه پیش
والد
کامیت
00b5357b8e
1فایلهای تغییر یافته به همراه21 افزوده شده و 7 حذف شده
  1. 21 7
      panda/src/event/pythonTask.cxx

+ 21 - 7
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<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) {