pythonCallbackObject.cxx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file pythonCallbackObject.cxx
  10. * @author drose
  11. * @date 2009-03-13
  12. */
  13. #include "pythonCallbackObject.h"
  14. #ifdef HAVE_PYTHON
  15. #include "py_panda.h"
  16. #include "pythonThread.h"
  17. #include "callbackData.h"
  18. #include "config_putil.h"
  19. TypeHandle PythonCallbackObject::_type_handle;
  20. Configure(config_pythonCallbackObject);
  21. ConfigureFn(config_pythonCallbackObject) {
  22. PythonCallbackObject::init_type();
  23. }
  24. #ifndef CPPPARSER
  25. extern struct Dtool_PyTypedObject Dtool_TypedObject;
  26. extern struct Dtool_PyTypedObject Dtool_PythonCallbackObject;
  27. #endif
  28. /**
  29. *
  30. */
  31. PythonCallbackObject::
  32. PythonCallbackObject(PyObject *function) {
  33. _function = Py_NewRef(Py_None);
  34. set_function(function);
  35. #if !defined(SIMPLE_THREADS) && defined(WITH_THREAD)
  36. // Ensure that the Python threading system is initialized and ready to go.
  37. // WITH_THREAD symbol defined within Python.h
  38. Py_Initialize();
  39. #if PY_VERSION_HEX < 0x03090000
  40. // PyEval_InitThreads is now a deprecated no-op in Python 3.9+
  41. PyEval_InitThreads();
  42. #endif // PY_VERSION_HEX
  43. #endif // WITH_THREAD
  44. }
  45. /**
  46. *
  47. */
  48. PythonCallbackObject::
  49. ~PythonCallbackObject() {
  50. Py_DECREF(_function);
  51. }
  52. /**
  53. * Replaces the function that is called for the callback. runs. The
  54. * parameter should be a Python callable object.
  55. */
  56. void PythonCallbackObject::
  57. set_function(PyObject *function) {
  58. Py_DECREF(_function);
  59. _function = Py_NewRef(function);
  60. if (_function != Py_None && !PyCallable_Check(_function)) {
  61. nassert_raise("Invalid function passed to PythonCallbackObject");
  62. }
  63. }
  64. /**
  65. * Returns the function that is called for the callback.
  66. */
  67. PyObject *PythonCallbackObject::
  68. get_function() {
  69. return Py_NewRef(_function);
  70. }
  71. /**
  72. * Implements pickle support.
  73. */
  74. PyObject *PythonCallbackObject::
  75. __reduce__() const {
  76. return Py_BuildValue("O(O)", (PyObject *)Dtool_GetPyTypeObject(&Dtool_PythonCallbackObject), _function);
  77. }
  78. /**
  79. * This method called when the callback is triggered; it *replaces* the
  80. * original function. To continue performing the original function, you must
  81. * call cbdata->upcall() during the callback.
  82. */
  83. void PythonCallbackObject::
  84. do_callback(CallbackData *cbdata) {
  85. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  86. // Use PyGILState to protect this asynchronous call.
  87. PyGILState_STATE gstate;
  88. gstate = PyGILState_Ensure();
  89. #endif
  90. do_python_callback(cbdata);
  91. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  92. PyGILState_Release(gstate);
  93. #endif
  94. }
  95. /**
  96. * The Python calls that implement do_callback(). This function is separate
  97. * so we can acquire the Python interpretor lock while it runs.
  98. */
  99. void PythonCallbackObject::
  100. do_python_callback(CallbackData *cbdata) {
  101. nassertv(cbdata != nullptr);
  102. // Wrap the cbdata up in a Python object, then put it in a tuple, for the
  103. // argument list.
  104. PyObject *pycbdata =
  105. DTool_CreatePyInstanceTyped(cbdata, Dtool_TypedObject,
  106. false, false, cbdata->get_type_index());
  107. PyObject *args = Py_BuildValue("(O)", pycbdata);
  108. Py_DECREF(pycbdata);
  109. PyObject *result = PythonThread::call_python_func(_function, args);
  110. Py_DECREF(args);
  111. if (result == nullptr) {
  112. if (PyErr_Occurred() != PyExc_SystemExit) {
  113. util_cat.error()
  114. << "Exception occurred in " << *this << "\n";
  115. }
  116. } else {
  117. Py_DECREF(result);
  118. }
  119. }
  120. #endif // HAVE_PYTHON