panda3d/panda/src/putil/pythonCallbackObject.cxx

137 lines
3.3 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pythonCallbackObject.cxx
* @author drose
* @date 2009-03-13
*/
#include "pythonCallbackObject.h"
#ifdef HAVE_PYTHON
#include "py_panda.h"
#include "pythonThread.h"
#include "callbackData.h"
#include "config_putil.h"
TypeHandle PythonCallbackObject::_type_handle;
Configure(config_pythonCallbackObject);
ConfigureFn(config_pythonCallbackObject) {
PythonCallbackObject::init_type();
}
#ifndef CPPPARSER
extern struct Dtool_PyTypedObject Dtool_TypedObject;
#endif
/**
*
*/
PythonCallbackObject::
PythonCallbackObject(PyObject *function) {
_function = Py_None;
Py_INCREF(_function);
set_function(function);
#if !defined(SIMPLE_THREADS) && defined(WITH_THREAD)
// Ensure that the Python threading system is initialized and ready to go.
// WITH_THREAD symbol defined within Python.h
Py_Initialize();
#if PY_VERSION_HEX < 0x03090000
// PyEval_InitThreads is now a deprecated no-op in Python 3.9+
PyEval_InitThreads();
#endif // PY_VERSION_HEX
#endif // WITH_THREAD
}
/**
*
*/
PythonCallbackObject::
~PythonCallbackObject() {
Py_DECREF(_function);
}
/**
* Replaces the function that is called for the callback. runs. The
* parameter should be a Python callable object.
*/
void PythonCallbackObject::
set_function(PyObject *function) {
Py_DECREF(_function);
_function = function;
Py_INCREF(_function);
if (_function != Py_None && !PyCallable_Check(_function)) {
nassert_raise("Invalid function passed to PythonCallbackObject");
}
}
/**
* Returns the function that is called for the callback.
*/
PyObject *PythonCallbackObject::
get_function() {
Py_INCREF(_function);
return _function;
}
/**
* This method called when the callback is triggered; it *replaces* the
* original function. To continue performing the original function, you must
* call cbdata->upcall() during the callback.
*/
void PythonCallbackObject::
do_callback(CallbackData *cbdata) {
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
// Use PyGILState to protect this asynchronous call.
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#endif
do_python_callback(cbdata);
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_Release(gstate);
#endif
}
/**
* The Python calls that implement do_callback(). This function is separate
* so we can acquire the Python interpretor lock while it runs.
*/
void PythonCallbackObject::
do_python_callback(CallbackData *cbdata) {
nassertv(cbdata != nullptr);
// Wrap the cbdata up in a Python object, then put it in a tuple, for the
// argument list.
PyObject *pycbdata =
DTool_CreatePyInstanceTyped(cbdata, Dtool_TypedObject,
false, false, cbdata->get_type_index());
PyObject *args = Py_BuildValue("(O)", pycbdata);
Py_DECREF(pycbdata);
PyObject *result = PythonThread::call_python_func(_function, args);
Py_DECREF(args);
if (result == nullptr) {
if (PyErr_Occurred() != PyExc_SystemExit) {
util_cat.error()
<< "Exception occurred in " << *this << "\n";
}
} else {
Py_DECREF(result);
}
}
#endif // HAVE_PYTHON