27 extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
28 extern struct Dtool_PyTypedObject Dtool_AsyncFuture;
29 extern struct Dtool_PyTypedObject Dtool_PythonTask;
36 PythonTask(PyObject *func_or_coro,
const std::string &name) :
42 _registered_to_owner(false),
45 _exc_traceback(nullptr),
47 _future_done(nullptr),
48 _ignore_return(false),
49 _retrieved_exception(false) {
51 nassertv(func_or_coro !=
nullptr);
52 if (func_or_coro == Py_None || PyCallable_Check(func_or_coro)) {
53 _function = func_or_coro;
55 #if PY_VERSION_HEX >= 0x03050000
56 }
else if (PyCoro_CheckExact(func_or_coro)) {
58 _generator = func_or_coro;
59 Py_INCREF(_generator);
61 }
else if (PyGen_CheckExact(func_or_coro)) {
63 _generator = func_or_coro;
64 Py_INCREF(_generator);
66 nassert_raise(
"Invalid function passed to PythonTask");
69 set_args(Py_None,
true);
70 set_upon_death(Py_None);
73 __dict__ = PyDict_New();
75 #ifndef SIMPLE_THREADS
77 #ifdef WITH_THREAD // This symbol defined within Python.h
91 if (_exception !=
nullptr && !_retrieved_exception) {
93 << *
this <<
" exception was never retrieved:\n";
94 PyErr_Restore(_exception, _exc_value, _exc_traceback);
96 PyErr_Restore(
nullptr,
nullptr,
nullptr);
99 _exc_traceback =
nullptr;
103 Py_XDECREF(_function);
106 Py_XDECREF(_exception);
107 Py_XDECREF(_exc_value);
108 Py_XDECREF(_exc_traceback);
109 Py_XDECREF(_generator);
111 Py_XDECREF(_upon_death);
119 set_function(PyObject *
function) {
120 Py_XDECREF(_function);
122 _function =
function;
123 Py_INCREF(_function);
124 if (_function != Py_None && !PyCallable_Check(_function)) {
125 nassert_raise(
"Invalid function passed to PythonTask");
135 set_args(PyObject *args,
bool append_task) {
139 if (args == Py_None) {
141 _args = PyTuple_New(0);
143 if (PySequence_Check(args)) {
144 _args = PySequence_Tuple(args);
148 if (_args ==
nullptr) {
149 nassert_raise(
"Invalid args passed to PythonTask");
150 _args = PyTuple_New(0);
153 _append_task = append_task;
159 PyObject *PythonTask::
167 int num_args = PyTuple_GET_SIZE(_args);
168 PyObject *with_task = PyTuple_New(num_args + 1);
169 for (
int i = 0; i < num_args; ++i) {
170 PyObject *item = PyTuple_GET_ITEM(_args, i);
172 PyTuple_SET_ITEM(with_task, i, item);
177 PyTuple_SET_ITEM(with_task, num_args,
self);
191 set_upon_death(PyObject *upon_death) {
192 Py_XDECREF(_upon_death);
194 _upon_death = upon_death;
195 Py_INCREF(_upon_death);
196 if (_upon_death != Py_None && !PyCallable_Check(_upon_death)) {
197 nassert_raise(
"Invalid upon_death function passed to PythonTask");
210 set_owner(PyObject *owner) {
212 if (owner != Py_None) {
213 PyObject *add = PyObject_GetAttrString(owner,
"_addTask");
214 PyObject *clear = PyObject_GetAttrString(owner,
"_clearTask");
216 if (add ==
nullptr || !PyCallable_Check(add) ||
217 clear ==
nullptr || !PyCallable_Check(clear)) {
218 Dtool_Raise_TypeError(
"owner object should have _addTask and _clearTask methods");
224 if (_owner !=
nullptr && _owner != Py_None && _state != S_inactive) {
225 unregister_from_owner();
232 if (_owner != Py_None && _state != S_inactive) {
242 PyObject *PythonTask::
244 nassertr(done(),
nullptr);
246 if (_exception ==
nullptr) {
248 Py_XINCREF(_exc_value);
251 _retrieved_exception =
true;
252 Py_INCREF(_exception);
253 Py_XINCREF(_exc_value);
254 Py_XINCREF(_exc_traceback);
255 PyErr_Restore(_exception, _exc_value, _exc_traceback);
285 __setattr__(PyObject *
self, PyObject *attr, PyObject *v) {
286 if (PyObject_GenericSetAttr(
self, attr, v) == 0) {
290 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
296 if (task_cat.is_debug()) {
297 PyObject *str = PyObject_Repr(v);
299 << *
this <<
": task."
300 #if PY_MAJOR_VERSION >= 3
301 << PyUnicode_AsUTF8(attr) <<
" = "
302 << PyUnicode_AsUTF8(str) <<
"\n";
304 << PyString_AsString(attr) <<
" = "
305 << PyString_AsString(str) <<
"\n";
310 return PyDict_SetItem(__dict__, attr, v);
320 __delattr__(PyObject *
self, PyObject *attr) {
321 if (PyObject_GenericSetAttr(
self, attr,
nullptr) == 0) {
325 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
331 if (PyDict_DelItem(__dict__, attr) == -1) {
333 #if PY_MAJOR_VERSION < 3
334 PyErr_Format(PyExc_AttributeError,
335 "'PythonTask' object has no attribute '%.400s'",
336 PyString_AS_STRING(attr));
338 PyErr_Format(PyExc_AttributeError,
339 "'PythonTask' object has no attribute '%U'",
354 PyObject *PythonTask::
355 __getattr__(PyObject *attr)
const {
361 PyObject *item = PyDict_GetItem(__dict__, attr);
363 if (item ==
nullptr) {
365 #if PY_MAJOR_VERSION < 3
366 PyErr_Format(PyExc_AttributeError,
367 "'PythonTask' object has no attribute '%.400s'",
368 PyString_AS_STRING(attr));
370 PyErr_Format(PyExc_AttributeError,
371 "'PythonTask' object has no attribute '%U'",
386 __traverse__(visitproc visit,
void *arg) {
423 return _function != Py_None;
431 AsyncTask::DoneStatus PythonTask::
433 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
435 PyGILState_STATE gstate;
436 gstate = PyGILState_Ensure();
439 DoneStatus result = do_python_task();
441 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
442 PyGILState_Release(gstate);
452 AsyncTask::DoneStatus PythonTask::
454 PyObject *result =
nullptr;
457 if (_future_done !=
nullptr) {
458 PyObject *is_done = PyObject_CallObject(_future_done,
nullptr);
459 if (!PyObject_IsTrue(is_done)) {
465 Py_DECREF(_future_done);
466 _future_done =
nullptr;
469 if (_generator ==
nullptr) {
471 nassertr(_function !=
nullptr, DS_interrupt);
473 PyObject *args = get_args();
474 result = PythonThread::call_python_func(_function, args);
477 if (result !=
nullptr && PyGen_Check(result)) {
480 if (task_cat.is_debug()) {
481 #if PY_MAJOR_VERSION >= 3
482 PyObject *str = PyObject_ASCII(_function);
484 << PyUnicode_AsUTF8(str) <<
" in " << *
this
485 <<
" yielded a generator.\n";
487 PyObject *str = PyObject_Repr(_function);
489 << PyString_AsString(str) <<
" in " << *
this
490 <<
" yielded a generator.\n";
497 #if PY_VERSION_HEX >= 0x03050000
498 }
else if (result !=
nullptr && Py_TYPE(result)->tp_as_async !=
nullptr) {
500 if (task_cat.is_debug()) {
501 PyObject *str = PyObject_ASCII(_function);
502 PyObject *str2 = PyObject_ASCII(result);
504 << PyUnicode_AsUTF8(str) <<
" in " << *
this
505 <<
" yielded an awaitable: " << PyUnicode_AsUTF8(str2) <<
"\n";
509 if (PyCoro_CheckExact(result)) {
514 unaryfunc await = Py_TYPE(result)->tp_as_async->am_await;
515 _generator = await(result);
523 if (_generator !=
nullptr) {
526 PyObject *func = PyObject_GetAttrString(_generator,
"send");
527 nassertr(func !=
nullptr, DS_interrupt);
528 result = PyObject_CallFunctionObjArgs(func, Py_None,
nullptr);
531 if (result ==
nullptr) {
535 Py_DECREF(_generator);
536 _generator =
nullptr;
538 #if PY_VERSION_HEX >= 0x03030000
539 if (_PyGen_FetchStopIterationValue(&result) == 0) {
541 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
545 PyErr_Restore(
nullptr,
nullptr,
nullptr);
552 if (_function ==
nullptr) {
553 if (task_cat.is_debug()) {
555 << *
this <<
" received StopIteration from coroutine.\n";
558 Py_XDECREF(_exc_value);
562 }
else if (_function ==
nullptr) {
566 Py_XDECREF(_exception);
567 Py_XDECREF(_exc_value);
568 Py_XDECREF(_exc_traceback);
569 PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback);
570 _retrieved_exception =
false;
572 if (task_cat.is_debug()) {
573 if (_exception !=
nullptr && Py_TYPE(_exception) == &PyType_Type) {
575 << *
this <<
" received " << ((PyTypeObject *)_exception)->tp_name <<
" from coroutine.\n";
578 << *
this <<
" received exception from coroutine.\n";
588 }
else if (DtoolInstance_Check(result)) {
591 if (fut !=
nullptr) {
594 if (fut->is_task()) {
606 if (task_cat.is_debug()) {
608 << *
this <<
" is now awaiting <" << *fut <<
">.\n";
612 if (task_cat.is_debug()) {
614 << *
this <<
" would await <" << *fut <<
">, were it not already done.\n";
623 << *
this <<
" cannot await itself\n";
631 PyObject *check = PyObject_GetAttrString(result,
"_asyncio_future_blocking");
632 if (check !=
nullptr && check != Py_None) {
635 _future_done = PyObject_GetAttrString(result,
"done");
636 if (_future_done ==
nullptr || !PyCallable_Check(_future_done)) {
638 <<
"future.done is not callable\n";
641 #if PY_MAJOR_VERSION >= 3
642 if (task_cat.is_debug()) {
643 PyObject *str = PyObject_ASCII(result);
645 << *
this <<
" is now polling " << PyUnicode_AsUTF8(str) <<
".done()\n";
657 if (result ==
nullptr) {
658 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
661 if (task_cat.is_debug()) {
663 <<
"SystemExit occurred in " << *
this <<
"\n";
667 <<
"Exception occurred in " << *
this <<
"\n";
672 if (result == Py_None || _ignore_return) {
677 #if PY_MAJOR_VERSION >= 3
678 if (PyLong_Check(result)) {
679 long retval = PyLong_AS_LONG(result);
681 if (PyInt_Check(result)) {
682 long retval = PyInt_AS_LONG(result);
687 Py_XDECREF(_generator);
688 _generator =
nullptr;
698 return (DoneStatus) retval;
713 PyMethodDef *meth =
nullptr;
714 if (PyCFunction_Check(result)) {
715 meth = ((PyCFunctionObject *)result)->m_ml;
716 #if PY_MAJOR_VERSION >= 3
717 }
else if (Py_TYPE(result) == &PyMethodDescr_Type) {
719 }
else if (strcmp(Py_TYPE(result)->tp_name,
"method_descriptor") == 0) {
721 meth = ((PyMethodDescrObject *)result)->d_method;
724 if (meth !=
nullptr && strcmp(meth->ml_name,
"done") == 0) {
729 std::ostringstream strm;
730 #if PY_MAJOR_VERSION >= 3
731 PyObject *str = PyObject_ASCII(result);
732 if (str ==
nullptr) {
733 str = PyUnicode_FromString(
"<repr error>");
736 << *
this <<
" returned " << PyUnicode_AsUTF8(str);
738 PyObject *str = PyObject_Repr(result);
739 if (str ==
nullptr) {
740 str = PyString_FromString(
"<repr error>");
743 << *
this <<
" returned " << PyString_AsString(str);
747 std::string message = strm.str();
748 nassert_raise(message);
761 AsyncTask::upon_birth(manager);
780 AsyncTask::upon_death(manager, clean_exit);
782 if (_upon_death != Py_None) {
783 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
785 PyGILState_STATE gstate;
786 gstate = PyGILState_Ensure();
789 call_function(_upon_death);
791 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
792 PyGILState_Release(gstate);
795 unregister_from_owner();
802 register_to_owner() {
803 if (_owner != Py_None && !_registered_to_owner) {
804 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
806 PyGILState_STATE gstate;
807 gstate = PyGILState_Ensure();
810 _registered_to_owner =
true;
811 call_owner_method(
"_addTask");
813 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
814 PyGILState_Release(gstate);
823 unregister_from_owner() {
825 if (_owner != Py_None && _registered_to_owner) {
826 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
828 PyGILState_STATE gstate;
829 gstate = PyGILState_Ensure();
832 _registered_to_owner =
false;
833 call_owner_method(
"_clearTask");
835 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
836 PyGILState_Release(gstate);
846 call_owner_method(
const char *method_name) {
847 if (_owner != Py_None) {
848 PyObject *func = PyObject_GetAttrString(_owner, (
char *)method_name);
849 if (func ==
nullptr) {
851 <<
"Owner object added to " << *
this <<
" has no method "
852 << method_name <<
"().\n";
866 call_function(PyObject *
function) {
867 if (
function != Py_None) {
870 PyObject *result = PyObject_CallFunctionObjArgs(
function,
self,
nullptr);
876 #endif // HAVE_PYTHON