You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
6.9KB

  1. extern "C" {
  2. #define PY_SSIZE_T_CLEAN
  3. #include <Python.h>
  4. #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
  5. #include <numpy/arrayobject.h>
  6. }
  7. #include <dlfcn.h>
  8. #include "ScriptEngine.hpp"
  9. #include <thread>
  10. /*
  11. TODO:
  12. - Fix Numpy loading on Linux.
  13. - "undefined symbol: PyExc_RecursionError"
  14. - This worked in Python 3.7, but because some other crash (regarding GIL interpreter objects, I forgot) was fixed on Python's issue tracker in the last month, I switched to Python 3.8 to solve that problem.
  15. - Test build and running on Windows/Mac. (Has not been attempted.)
  16. - Allow multiple instances with GIL.
  17. - Fix destructors.
  18. */
  19. extern rack::Plugin* pluginInstance;
  20. static void initPython() {
  21. if (Py_IsInitialized())
  22. return;
  23. std::string pythonDir = rack::asset::plugin(pluginInstance, "dep/lib/python3.8");
  24. // Set python path
  25. std::string sep = ":";
  26. std::string pythonPath = pythonDir;
  27. pythonPath += sep + pythonDir + "/lib-dynload";
  28. pythonPath += sep + pythonDir + "/site-packages";
  29. wchar_t* pythonPathW = Py_DecodeLocale(pythonPath.c_str(), NULL);
  30. Py_SetPath(pythonPathW);
  31. PyMem_RawFree(pythonPathW);
  32. // Initialize but don't register signal handlers
  33. Py_InitializeEx(0);
  34. assert(Py_IsInitialized());
  35. // PyEval_InitThreads();
  36. // Import numpy
  37. if (_import_array()) {
  38. PyErr_Print();
  39. abort();
  40. }
  41. }
  42. struct PythonEngine : ScriptEngine {
  43. PyObject* mainDict = NULL;
  44. PyObject* processFunc = NULL;
  45. PyObject* blockObj = NULL;
  46. PyInterpreterState* interp = NULL;
  47. ~PythonEngine() {
  48. if (mainDict)
  49. Py_DECREF(mainDict);
  50. if (processFunc)
  51. Py_DECREF(processFunc);
  52. if (blockObj)
  53. Py_DECREF(blockObj);
  54. if (interp)
  55. PyInterpreterState_Delete(interp);
  56. }
  57. std::string getEngineName() override {
  58. return "Python";
  59. }
  60. int run(const std::string& path, const std::string& script) override {
  61. ProcessBlock* block = getProcessBlock();
  62. initPython();
  63. // PyThreadState* tstate = PyThreadState_Get();
  64. // interp = PyInterpreterState_New();
  65. // PyThreadState_Swap(tstate);
  66. // Get globals dictionary
  67. PyObject* mainModule = PyImport_AddModule("__main__");
  68. assert(mainModule);
  69. DEFER({Py_DECREF(mainModule);});
  70. mainDict = PyModule_GetDict(mainModule);
  71. assert(mainDict);
  72. // Set context pointer
  73. PyObject* engineObj = PyCapsule_New(this, NULL, NULL);
  74. PyDict_SetItemString(mainDict, "_engine", engineObj);
  75. // Add functions to globals
  76. static PyMethodDef native_functions[] = {
  77. {"display", nativeDisplay, METH_VARARGS, ""},
  78. {NULL, NULL, 0, NULL},
  79. };
  80. if (PyModule_AddFunctions(mainModule, native_functions)) {
  81. WARN("Could not add global functions");
  82. return -1;
  83. }
  84. // Set config
  85. static PyStructSequence_Field configFields[] = {
  86. {"frameDivider", ""},
  87. {"bufferSize", ""},
  88. {NULL, NULL},
  89. };
  90. static PyStructSequence_Desc configDesc = {"Config", "", configFields, LENGTHOF(configFields) - 1};
  91. PyTypeObject* configType = PyStructSequence_NewType(&configDesc);
  92. assert(configType);
  93. PyObject* configObj = PyStructSequence_New(configType);
  94. assert(configObj);
  95. PyDict_SetItemString(mainDict, "config", configObj);
  96. // frameDivider
  97. PyStructSequence_SetItem(configObj, 0, PyLong_FromLong(32));
  98. // bufferSize
  99. PyStructSequence_SetItem(configObj, 1, PyLong_FromLong(1));
  100. // Compile string
  101. PyObject* code = Py_CompileString(script.c_str(), path.c_str(), Py_file_input);
  102. if (!code) {
  103. PyErr_Print();
  104. return -1;
  105. }
  106. DEFER({Py_DECREF(code);});
  107. // Evaluate string
  108. PyObject* result = PyEval_EvalCode(code, mainDict, mainDict);
  109. if (!result) {
  110. PyErr_Print();
  111. return -1;
  112. }
  113. DEFER({Py_DECREF(result);});
  114. // Create block
  115. static PyStructSequence_Field blockFields[] = {
  116. {"inputs", ""},
  117. {"outputs", ""},
  118. {"knobs", ""},
  119. {"switches", ""},
  120. {"lights", ""},
  121. {"switch_lights", ""},
  122. {NULL, NULL},
  123. };
  124. static PyStructSequence_Desc blockDesc = {"Block", "", blockFields, LENGTHOF(blockFields) - 1};
  125. PyTypeObject* blockType = PyStructSequence_NewType(&blockDesc);
  126. assert(blockType);
  127. DEBUG("ref %d", Py_REFCNT(blockType));
  128. blockObj = PyStructSequence_New(blockType);
  129. assert(blockObj);
  130. DEBUG("ref %d", Py_REFCNT(blockObj));
  131. // inputs
  132. npy_intp inputsDims[] = {NUM_ROWS, MAX_BUFFER_SIZE};
  133. PyObject* inputs = PyArray_SimpleNewFromData(2, inputsDims, NPY_FLOAT32, block->inputs);
  134. PyStructSequence_SetItem(blockObj, 0, inputs);
  135. // outputs
  136. npy_intp outputsDims[] = {NUM_ROWS, MAX_BUFFER_SIZE};
  137. PyObject* outputs = PyArray_SimpleNewFromData(2, outputsDims, NPY_FLOAT32, block->outputs);
  138. PyStructSequence_SetItem(blockObj, 1, outputs);
  139. // knobs
  140. npy_intp knobsDims[] = {NUM_ROWS};
  141. PyObject* knobs = PyArray_SimpleNewFromData(1, knobsDims, NPY_FLOAT32, block->knobs);
  142. PyStructSequence_SetItem(blockObj, 2, knobs);
  143. // switches
  144. npy_intp switchesDims[] = {NUM_ROWS};
  145. PyObject* switches = PyArray_SimpleNewFromData(1, switchesDims, NPY_BOOL, block->switches);
  146. PyStructSequence_SetItem(blockObj, 3, switches);
  147. // lights
  148. npy_intp lightsDims[] = {NUM_ROWS, 3};
  149. PyObject* lights = PyArray_SimpleNewFromData(2, lightsDims, NPY_FLOAT32, block->lights);
  150. PyStructSequence_SetItem(blockObj, 4, lights);
  151. // switchLights
  152. npy_intp switchLightsDims[] = {NUM_ROWS, 3};
  153. PyObject* switchLights = PyArray_SimpleNewFromData(2, switchLightsDims, NPY_FLOAT32, block->switchLights);
  154. PyStructSequence_SetItem(blockObj, 5, switchLights);
  155. // Get process function from globals
  156. processFunc = PyDict_GetItemString(mainDict, "process");
  157. if (!processFunc) {
  158. display("No process() function");
  159. return -1;
  160. }
  161. if (!PyCallable_Check(processFunc)) {
  162. display("process() is not callable");
  163. return -1;
  164. }
  165. return 0;
  166. }
  167. int process() override {
  168. // DEBUG("ref %d", Py_REFCNT(blockObj));
  169. // Call process()
  170. PyObject* args = PyTuple_Pack(1, blockObj);
  171. assert(args);
  172. DEFER({Py_DECREF(args);});
  173. PyObject* processResult = PyObject_CallObject(processFunc, args);
  174. if (!processResult) {
  175. PyErr_Print();
  176. // PyObject *ptype, *pvalue, *ptraceback;
  177. // PyErr_Fetch(&ptype, &pvalue, &ptraceback);
  178. // const char* str = PyUnicode_AsUTF8(pvalue);
  179. // if (!str)
  180. // return -1;
  181. // display(str);
  182. return -1;
  183. }
  184. DEFER({Py_DECREF(processResult);});
  185. // PyThreadState* tstate = PyThreadState_New(interp);
  186. // PyEval_RestoreThread(tstate);
  187. // PyThreadState_Clear(tstate);
  188. // PyThreadState_DeleteCurrent();
  189. return 0;
  190. }
  191. static PyObject* nativeDisplay(PyObject* self, PyObject* args) {
  192. PyObject* mainDict = PyEval_GetGlobals();
  193. assert(mainDict);
  194. PyObject* engineObj = PyDict_GetItemString(mainDict, "_engine");
  195. assert(engineObj);
  196. PythonEngine* engine = (PythonEngine*) PyCapsule_GetPointer(engineObj, NULL);
  197. assert(engine);
  198. PyObject* msgO = PyTuple_GetItem(args, 0);
  199. if (!msgO)
  200. return NULL;
  201. PyObject* msgS = PyObject_Str(msgO);
  202. DEFER({Py_DECREF(msgS);});
  203. const char* msg = PyUnicode_AsUTF8(msgS);
  204. engine->display(msg);
  205. Py_INCREF(Py_None);
  206. return Py_None;
  207. }
  208. };
  209. __attribute__((constructor(1000)))
  210. static void constructor() {
  211. addScriptEngine<PythonEngine>(".py");
  212. }