event: use the persistent python wrapper when appending task object (#1681)

This commit is contained in:
John C. Allwein 2024-09-14 12:25:08 -06:00 committed by GitHub
parent fc394e4c59
commit cfe3885c0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 8 deletions

View File

@ -174,9 +174,15 @@ get_args() {
PyTuple_SET_ITEM(with_task, i, Py_NewRef(item));
}
this->ref();
PyObject *self = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
PyTuple_SET_ITEM(with_task, num_args, self);
// Check whether we have a Python wrapper. This is not the case if the
// object has been created by C++ and never been exposed to Python code.
if (__self__ == nullptr) {
// A __self__ instance does not exist, let's create one now.
this->ref();
__self__ = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
}
PyTuple_SET_ITEM(with_task, num_args, Py_NewRef(__self__));
return with_task;
}
else {
@ -1004,11 +1010,16 @@ call_owner_method(const char *method_name) {
void PythonTask::
call_function(PyObject *function) {
if (function != Py_None) {
this->ref();
PyObject *self = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
PyObject *result = PyObject_CallOneArg(function, self);
// Check whether we have a Python wrapper. This is not the case if the
// object has been created by C++ and never been exposed to Python code.
if (__self__ == nullptr) {
// A __self__ instance does not exist, let's create one now.
this->ref();
__self__ = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
}
PyObject *result = PyObject_CallOneArg(function, __self__);
Py_XDECREF(result);
Py_DECREF(self);
}
}

View File

@ -1,4 +1,4 @@
from panda3d.core import PythonTask
from panda3d.core import AsyncTaskManager, PythonTask
from contextlib import contextmanager
import pytest
import types
@ -115,3 +115,39 @@ def test_pythontask_cycle():
break
else:
pytest.fail('not found in garbage')
def test_task_persistent_wrapper():
task_mgr = AsyncTaskManager.get_global_ptr()
task_chain = task_mgr.make_task_chain("test_task_persistent_wrapper")
# Create a subclass of PythonTask
class PythonTaskSubclassTest(PythonTask):
pass
def task_main(task):
# Set our result to be the input task we got into this function
task.set_result(task)
# Verify we got the subclass
assert isinstance(task, PythonTaskSubclassTest)
return task.done
done_callback_reached = False
def done_callback(task):
# Verify we got the subclass
assert isinstance(task, PythonTaskSubclassTest)
nonlocal done_callback_reached
done_callback_reached = True
task = PythonTaskSubclassTest(task_main)
task.set_task_chain(task_chain.name)
task.set_upon_death(done_callback)
task_mgr.add(task)
task_chain.wait_for_tasks()
assert task.done()
assert not task.cancelled()
# Verify the task passed into the function is the same task we created
assert task.result() is task
# Verify the done callback worked and was tested
assert done_callback_reached