PythonInit.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // utils
  2. #include <utils/Logger.h>
  3. #include <python/PythonInit.h>
  4. #include <python/PythonUtils.h>
  5. // qt include
  6. #include <QCoreApplication>
  7. #include <QDir>
  8. #include <QFile>
  9. #include <QVector>
  10. #include <QStringList>
  11. // modules to init
  12. #include <effectengine/EffectModule.h>
  13. // Required to determine the cmake options
  14. #include <HyperionConfig.h>
  15. #ifdef _WIN32
  16. #include <stdexcept>
  17. #endif
  18. #define STRINGIFY2(x) #x
  19. #define STRINGIFY(x) STRINGIFY2(x)
  20. PythonInit::PythonInit()
  21. {
  22. // register modules
  23. EffectModule::registerHyperionExtensionModule();
  24. #if (PY_VERSION_HEX >= 0x03080000)
  25. PyStatus status;
  26. PyConfig config;
  27. PyConfig_InitPythonConfig(&config);
  28. #endif
  29. #if defined(ENABLE_DEPLOY_DEPENDENCIES)
  30. Debug(Logger::getInstance("DAEMON"), "Initializing Python config");
  31. // Set Program name
  32. wchar_t programName[] = L"Hyperion";
  33. #if (PY_VERSION_HEX >= 0x03080000)
  34. status = PyConfig_SetString(&config, &config.program_name, programName);
  35. if (PyStatus_Exception(status)) {
  36. goto exception;
  37. }
  38. else
  39. #else
  40. Py_SetProgramName(programName);
  41. #endif
  42. {
  43. // set Python module path when exists
  44. QString py_path = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
  45. QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + STRINGIFY(PYTHON_VERSION_MINOR) + ".zip");
  46. QString py_framework = QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current/lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
  47. if (QFile(py_file).exists() || QDir(py_path).exists() || QDir(py_framework).exists())
  48. {
  49. Py_NoSiteFlag++;
  50. if (QFile(py_file).exists()) // Windows
  51. {
  52. #if (PY_VERSION_HEX >= 0x03080000)
  53. status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(py_file));
  54. if (PyStatus_Exception(status)) {
  55. goto exception;
  56. }
  57. config.module_search_paths_set = 1;
  58. status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(py_file.toStdWString().c_str()));
  59. if (PyStatus_Exception(status)) {
  60. goto exception;
  61. }
  62. #else
  63. Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
  64. Py_SetPath(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
  65. #endif
  66. }
  67. else if (QDir(py_path).exists()) // Linux
  68. {
  69. #if (PY_VERSION_HEX >= 0x03080000)
  70. status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../")));
  71. if (PyStatus_Exception(status)) {
  72. goto exception;
  73. }
  74. config.module_search_paths_set = 1;
  75. status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path).absolutePath().toStdWString().c_str()));
  76. if (PyStatus_Exception(status)) {
  77. goto exception;
  78. }
  79. status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path + "/lib-dynload").absolutePath().toStdWString().c_str()));
  80. if (PyStatus_Exception(status)) {
  81. goto exception;
  82. }
  83. #else
  84. QStringList python_paths;
  85. python_paths.append(QDir(py_path).absolutePath());
  86. python_paths.append(QDir(py_path + "/lib-dynload").absolutePath());
  87. QVector<wchar_t> joined_paths(python_paths.join(":").size() + 1, 0);
  88. python_paths.join(":").toWCharArray(joined_paths.data());
  89. Py_SetPath(joined_paths.data());
  90. py_path = QDir::cleanPath(qApp->applicationDirPath() + "/../");
  91. Py_SetPythonHome(Py_DecodeLocale(py_path.toLatin1().data(), nullptr));
  92. #endif
  93. }
  94. else if (QDir(py_framework).exists()) // macOS
  95. {
  96. #if (PY_VERSION_HEX >= 0x03080000)
  97. status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current")));
  98. if (PyStatus_Exception(status)) {
  99. goto exception;
  100. }
  101. config.module_search_paths_set = 1;
  102. status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework).absolutePath().toStdWString().c_str()));
  103. if (PyStatus_Exception(status)) {
  104. goto exception;
  105. }
  106. status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework + "/lib-dynload").absolutePath().toStdWString().c_str()));
  107. if (PyStatus_Exception(status)) {
  108. goto exception;
  109. }
  110. #else
  111. QStringList python_paths;
  112. python_paths.append(QDir(py_framework).absolutePath());
  113. python_paths.append(QDir(py_framework + "/lib-dynload").absolutePath());
  114. QVector<wchar_t> joined_paths(python_paths.join(":").size() + 1, 0);
  115. python_paths.join(":").toWCharArray(joined_paths.data());
  116. Py_SetPath(joined_paths.data());
  117. py_framework = QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current");
  118. Py_SetPythonHome(Py_DecodeLocale(py_framework.toLatin1().data(), nullptr));
  119. #endif
  120. }
  121. }
  122. }
  123. #endif
  124. #if (PY_VERSION_HEX >= 0x03080000)
  125. status = Py_InitializeFromConfig(&config);
  126. if (PyStatus_Exception(status)) {
  127. goto exception;
  128. }
  129. PyConfig_Clear(&config);
  130. #endif
  131. // init Python
  132. Debug(Logger::getInstance("DAEMON"), "Initializing Python interpreter");
  133. Py_InitializeEx(0);
  134. if ( !Py_IsInitialized() )
  135. {
  136. throw std::runtime_error("Initializing Python failed!");
  137. }
  138. #if (PY_VERSION_HEX < 0x03090000)
  139. // PyEval_InitThreads became deprecated in Python 3.9 and will be removed in Python 3.11
  140. PyEval_InitThreads(); // Create the GIL
  141. #endif
  142. mainThreadState = PyEval_SaveThread();
  143. return;
  144. #if (PY_VERSION_HEX >= 0x03080000)
  145. exception:
  146. Error(Logger::getInstance("DAEMON"), "Initializing Python config failed with error [%s]", status.err_msg);
  147. PyConfig_Clear(&config);
  148. throw std::runtime_error("Initializing Python failed!");
  149. #endif
  150. }
  151. PythonInit::~PythonInit()
  152. {
  153. Debug(Logger::getInstance("DAEMON"), "Cleaning up Python interpreter");
  154. PyEval_RestoreThread(mainThreadState);
  155. Py_Finalize();
  156. }