domoticz/domoticz-python37.patch
2018-11-12 07:57:31 -06:00

569 lines
21 KiB
Diff

From 5f61c5be95bb44f2ff3b913678c7bfdd3196ff69 Mon Sep 17 00:00:00 2001
From: dnpwwo <kendel.boul@gmail.com>
Date: Sun, 1 Jul 2018 13:29:03 +1000
Subject: [PATCH] Initial version working with Python 3.7
---
hardware/plugins/DelayedLink.h | 15 +++++++++++++++
hardware/plugins/PluginManager.cpp | 11 ++++++++++-
hardware/plugins/PluginManager.h | 5 +++--
hardware/plugins/PluginMessages.h | 3 +++
hardware/plugins/Plugins.cpp | 14 +++++++++++++-
hardware/plugins/Plugins.h | 1 +
main/EventsPythonModule.cpp | 24 +++++++++++++++++-------
7 files changed, 62 insertions(+), 11 deletions(-)
diff --git a/hardware/plugins/DelayedLink.h b/hardware/plugins/DelayedLink.h
index ecc7ec579..de9327a13 100644
--- a/hardware/plugins/DelayedLink.h
+++ b/hardware/plugins/DelayedLink.h
@@ -86,8 +86,13 @@ namespace Plugins {
DECLARE_PYTHON_SYMBOL(PyObject*, PyImport_ImportModule, const char*);
DECLARE_PYTHON_SYMBOL(PyObject*, PyObject_CallObject, PyObject* COMMA PyObject*);
DECLARE_PYTHON_SYMBOL(int, PyFrame_GetLineNumber, PyFrameObject*);
+ DECLARE_PYTHON_SYMBOL(void, PyEval_InitThreads, );
+ DECLARE_PYTHON_SYMBOL(PyThreadState*, PyThreadState_Get, );
DECLARE_PYTHON_SYMBOL(PyThreadState*, PyEval_SaveThread, void);
DECLARE_PYTHON_SYMBOL(void, PyEval_RestoreThread, PyThreadState*);
+ DECLARE_PYTHON_SYMBOL(void, PyEval_ReleaseLock, );
+ DECLARE_PYTHON_SYMBOL(PyThreadState*, PyThreadState_Swap, PyThreadState*);
+ DECLARE_PYTHON_SYMBOL(int, PyGILState_Check, );
DECLARE_PYTHON_SYMBOL(void, _Py_NegativeRefcount, const char* COMMA int COMMA PyObject*);
DECLARE_PYTHON_SYMBOL(PyObject*, _PyObject_New, PyTypeObject*);
#ifdef _DEBUG
@@ -197,8 +202,13 @@ namespace Plugins {
RESOLVE_PYTHON_SYMBOL(PyImport_ImportModule);
RESOLVE_PYTHON_SYMBOL(PyObject_CallObject);
RESOLVE_PYTHON_SYMBOL(PyFrame_GetLineNumber);
+ RESOLVE_PYTHON_SYMBOL(PyEval_InitThreads);
+ RESOLVE_PYTHON_SYMBOL(PyThreadState_Get);
RESOLVE_PYTHON_SYMBOL(PyEval_SaveThread);
RESOLVE_PYTHON_SYMBOL(PyEval_RestoreThread);
+ RESOLVE_PYTHON_SYMBOL(PyEval_ReleaseLock);
+ RESOLVE_PYTHON_SYMBOL(PyThreadState_Swap);
+ RESOLVE_PYTHON_SYMBOL(PyGILState_Check);
RESOLVE_PYTHON_SYMBOL(_Py_NegativeRefcount);
RESOLVE_PYTHON_SYMBOL(_PyObject_New);
#ifdef _DEBUG
@@ -365,8 +375,13 @@ extern SharedLibraryProxy* pythonLib;
#define PyImport_ImportModule pythonLib->PyImport_ImportModule
#define PyObject_CallObject pythonLib->PyObject_CallObject
#define PyFrame_GetLineNumber pythonLib->PyFrame_GetLineNumber
+#define PyEval_InitThreads pythonLib->PyEval_InitThreads
+#define PyThreadState_Get pythonLib->PyThreadState_Get
#define PyEval_SaveThread pythonLib->PyEval_SaveThread
#define PyEval_RestoreThread pythonLib->PyEval_RestoreThread
+#define PyEval_ReleaseLock pythonLib->PyEval_ReleaseLock
+#define PyThreadState_Swap pythonLib->PyThreadState_Swap
+#define PyGILState_Check pythonLib->PyGILState_Check
#define _Py_NegativeRefcount pythonLib->_Py_NegativeRefcount
#define _PyObject_New pythonLib->_PyObject_New
#define PyArg_ParseTuple pythonLib->PyArg_ParseTuple
diff --git a/hardware/plugins/PluginManager.cpp b/hardware/plugins/PluginManager.cpp
index 72c04575f..e896b8bcc 100644
--- a/hardware/plugins/PluginManager.cpp
+++ b/hardware/plugins/PluginManager.cpp
@@ -141,6 +141,9 @@ namespace Plugins {
Py_Initialize();
m_InitialPythonThread = PyEval_SaveThread();
+ // Initialise threads. Python 3.7+ does this inside Py_Initialize so done here for compatibility
+ PyEval_InitThreads();
+
m_bEnabled = true;
_log.Log(LOG_STATUS, "PluginSystem: Started, Python version '%s'.", sVersion.c_str());
}
@@ -319,7 +322,13 @@ namespace Plugins {
}
}
// Free the memory for the message
- if (Message) delete Message;
+ if (Message)
+ {
+ CPlugin* pPlugin = (CPlugin*)Message->Plugin();
+ pPlugin->RestoreThread();
+ delete Message;
+ pPlugin->ReleaseThread();
+ }
}
sleep_milliseconds(50);
}
diff --git a/hardware/plugins/PluginManager.h b/hardware/plugins/PluginManager.h
index 99e126e41..df66f63f6 100644
--- a/hardware/plugins/PluginManager.h
+++ b/hardware/plugins/PluginManager.h
@@ -36,10 +36,11 @@ namespace Plugins {
std::map<int, CDomoticzHardwareBase*>* GetHardware() { return &m_pPlugins; };
CDomoticzHardwareBase* RegisterPlugin(const int HwdID, const std::string &Name, const std::string &PluginKey);
void DeregisterPlugin(const int HwdID);
- bool StopPluginSystem();
- void AllPluginsStarted() { m_bAllPluginsStarted = true; };
+ bool StopPluginSystem();
+ void AllPluginsStarted() { m_bAllPluginsStarted = true; };
static void LoadSettings();
void DeviceModified(uint64_t ID);
+ void* PythonThread() { return m_InitialPythonThread; };
};
};
diff --git a/hardware/plugins/PluginMessages.h b/hardware/plugins/PluginMessages.h
index f4e902553..b254c969e 100644
--- a/hardware/plugins/PluginMessages.h
+++ b/hardware/plugins/PluginMessages.h
@@ -73,6 +73,7 @@ namespace Plugins {
boost::lock_guard<boost::mutex> l(PythonMutex);
m_pPlugin->RestoreThread();
ProcessLocked();
+ m_pPlugin->ReleaseThread();
};
virtual const char* PythonName() { return m_Callback.c_str(); };
};
@@ -367,6 +368,7 @@ static std::string get_utf8_from_ansi(const std::string &utf8, int codepage)
boost::lock_guard<boost::mutex> l(PythonMutex);
m_pPlugin->RestoreThread();
ProcessLocked();
+ m_pPlugin->ReleaseThread();
};
};
@@ -454,6 +456,7 @@ static std::string get_utf8_from_ansi(const std::string &utf8, int codepage)
boost::lock_guard<boost::mutex> l(PythonMutex);
m_pPlugin->RestoreThread();
ProcessLocked();
+ m_pPlugin->ReleaseThread();
}
};
diff --git a/hardware/plugins/Plugins.cpp b/hardware/plugins/Plugins.cpp
index 329080b0a..9647a8c91 100644
--- a/hardware/plugins/Plugins.cpp
+++ b/hardware/plugins/Plugins.cpp
@@ -35,6 +35,7 @@ extern std::string szWWWFolder;
extern std::string szAppVersion;
extern std::string szAppHash;
extern std::string szAppDate;
+extern MainWorker m_mainworker;
namespace Plugins {
@@ -867,6 +868,7 @@ namespace Plugins {
try
{
+ PyEval_RestoreThread((PyThreadState*)m_mainworker.m_pluginsystem.PythonThread());
m_PyInterpreter = Py_NewInterpreter();
if (!m_PyInterpreter)
{
@@ -973,6 +975,7 @@ namespace Plugins {
}
_log.Log(LOG_STATUS, "(%s) Initialized %s", Name.c_str(), sExtraDetail.c_str());
+ PyEval_SaveThread();
return true;
}
catch (...)
@@ -981,6 +984,7 @@ namespace Plugins {
}
Error:
+ PyEval_SaveThread();
m_bIsStarting = false;
return false;
}
@@ -1488,7 +1492,14 @@ namespace Plugins {
void CPlugin::RestoreThread()
{
- if (m_PyInterpreter) PyEval_RestoreThread((PyThreadState*)m_PyInterpreter);
+ if (m_PyInterpreter)
+ PyEval_RestoreThread((PyThreadState*)m_PyInterpreter);
+ }
+
+ void CPlugin::ReleaseThread()
+ {
+ if (m_PyInterpreter)
+ PyEval_SaveThread();
}
void CPlugin::Callback(std::string sHandler, void * pParams)
@@ -1537,6 +1548,7 @@ namespace Plugins {
if (m_SettingsDict) Py_XDECREF(m_SettingsDict);
if (m_PyInterpreter) Py_EndInterpreter((PyThreadState*)m_PyInterpreter);
Py_XDECREF(m_PyModule);
+ PyEval_ReleaseLock();
}
catch (std::exception *e)
{
diff --git a/hardware/plugins/Plugins.h b/hardware/plugins/Plugins.h
index 0145747ee..d8808e147 100644
--- a/hardware/plugins/Plugins.h
+++ b/hardware/plugins/Plugins.h
@@ -78,6 +78,7 @@ namespace Plugins {
void DisconnectEvent(CEventBase*);
void Callback(std::string sHandler, void* pParams);
void RestoreThread();
+ void ReleaseThread();
void Stop();
void WriteDebugBuffer(const std::vector<byte>& Buffer, bool Incoming);
diff --git a/main/EventsPythonModule.cpp b/main/EventsPythonModule.cpp
index a839cb546..bdde645a9 100644
--- a/main/EventsPythonModule.cpp
+++ b/main/EventsPythonModule.cpp
@@ -122,6 +122,7 @@
}
boost::lock_guard<boost::mutex> l(PythonMutex);
+ PyEval_RestoreThread((PyThreadState*)m_mainworker.m_pluginsystem.PythonThread());
m_PyInterpreter = Py_NewInterpreter();
if (!m_PyInterpreter)
{
@@ -144,7 +145,8 @@
PythonEventsInitalized = 1;
PyObject* pModule = Plugins::PythonEventsGetModule();
- if (!pModule) {
+ PyEval_SaveThread();
+ if (!pModule) {
_log.Log(LOG_ERROR, "EventSystem - Python: Failed to initialize module.");
return false;
}
@@ -159,7 +161,8 @@
if (Plugins::Py_IsInitialized())
Py_EndInterpreter((PyThreadState*)m_PyInterpreter);
m_PyInterpreter = NULL;
- _log.Log(LOG_STATUS, "EventSystem - Python stopped...");
+ PyEval_ReleaseLock();
+ _log.Log(LOG_STATUS, "EventSystem - Python stopped...");
return true;
} else
return false;
@@ -217,7 +220,8 @@
if (!pModuleDict) {
_log.Log(LOG_ERROR, "Python EventSystem: Failed to open module dictionary.");
- return;
+ PyEval_SaveThread();
+ return;
}
if (Plugins::PyDict_SetItemString(pModuleDict, "changed_device_name", Plugins::PyUnicode_FromString(m_devicestates[DeviceID].deviceName.c_str())) == -1) {
@@ -230,13 +234,15 @@
if (Plugins::PyDict_SetItemString(pModuleDict, "Devices", (PyObject*)m_DeviceDict) == -1)
{
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add Device dictionary.");
- return;
+ PyEval_SaveThread();
+ return;
}
Py_DECREF(m_DeviceDict);
if (Plugins::PyType_Ready(&Plugins::PDeviceType) < 0) {
_log.Log(LOG_ERROR, "Python EventSystem: Unable to ready DeviceType Object.");
- return;
+ PyEval_SaveThread();
+ return;
}
// Mutex
@@ -332,7 +338,8 @@
if (Plugins::PyDict_SetItemString(pModuleDict, "user_variables", (PyObject*)m_uservariablesDict) == -1)
{
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add uservariables dictionary.");
- return;
+ PyEval_SaveThread();
+ return;
}
Py_DECREF(m_uservariablesDict);
@@ -401,7 +408,10 @@
} else {
_log.Log(LOG_ERROR, "Python EventSystem: Module not available to events");
}
- } else {
+
+ PyEval_SaveThread();
+
+ } else {
_log.Log(LOG_ERROR, "EventSystem: Python not initalized");
}
From 64750ee9d6b9388d0d4c48f32679b2fb52bb9931 Mon Sep 17 00:00:00 2001
From: dnpwwo <kendel.boul@gmail.com>
Date: Sun, 1 Jul 2018 20:29:24 +1000
Subject: [PATCH] 1st level Python tracing added
---
hardware/plugins/DelayedLink.h | 6 ++
hardware/plugins/Plugins.cpp | 133 +++++++++++++++++++++++++++++++++
hardware/plugins/Plugins.h | 1 +
3 files changed, 140 insertions(+)
diff --git a/hardware/plugins/DelayedLink.h b/hardware/plugins/DelayedLink.h
index de9327a13..bdb9d0ec3 100644
--- a/hardware/plugins/DelayedLink.h
+++ b/hardware/plugins/DelayedLink.h
@@ -119,6 +119,8 @@ namespace Plugins {
DECLARE_PYTHON_SYMBOL(long, PyLong_AsLong, PyObject*);
DECLARE_PYTHON_SYMBOL(PyObject*, PyUnicode_AsUTF8String, PyObject*);
DECLARE_PYTHON_SYMBOL(PyObject*, PyImport_AddModule, const char*);
+ DECLARE_PYTHON_SYMBOL(void, PyEval_SetProfile, Py_tracefunc COMMA PyObject*);
+ DECLARE_PYTHON_SYMBOL(void, PyEval_SetTrace, Py_tracefunc COMMA PyObject*);
#ifdef _DEBUG
// In a debug build dealloc is a function but for release builds its a macro
@@ -238,6 +240,8 @@ namespace Plugins {
RESOLVE_PYTHON_SYMBOL(PyLong_AsLong);
RESOLVE_PYTHON_SYMBOL(PyUnicode_AsUTF8String);
RESOLVE_PYTHON_SYMBOL(PyImport_AddModule);
+ RESOLVE_PYTHON_SYMBOL(PyEval_SetProfile);
+ RESOLVE_PYTHON_SYMBOL(PyEval_SetTrace);
}
}
_Py_NoneStruct.ob_refcnt = 1;
@@ -414,4 +418,6 @@ extern SharedLibraryProxy* pythonLib;
#define PyLong_AsLong pythonLib->PyLong_AsLong
#define PyUnicode_AsUTF8String pythonLib->PyUnicode_AsUTF8String
#define PyImport_AddModule pythonLib->PyImport_AddModule
+#define PyEval_SetProfile pythonLib->PyEval_SetProfile
+#define PyEval_SetTrace pythonLib->PyEval_SetTrace
}
diff --git a/hardware/plugins/Plugins.cpp b/hardware/plugins/Plugins.cpp
index 9647a8c91..05ef29b97 100644
--- a/hardware/plugins/Plugins.cpp
+++ b/hardware/plugins/Plugins.cpp
@@ -120,6 +120,96 @@ namespace Plugins {
if (pTraceback) Py_XDECREF(pTraceback);
}
+ int PyDomoticz_ProfileFunc(PyObject *self, PyFrameObject *frame, int what, PyObject *arg)
+ {
+ module_state* pModState = ((struct module_state*)PyModule_GetState(self));
+ if (!pModState)
+ {
+ _log.Log(LOG_ERROR, "CPlugin:%s, unable to obtain module state.", __func__);
+ }
+ else if (!pModState->pPlugin)
+ {
+ _log.Log(LOG_ERROR, "CPlugin:%s, illegal operation, Plugin has not started yet.", __func__);
+ }
+ else
+ {
+ int lineno = PyFrame_GetLineNumber(frame);
+ std::string sFuncName = "Unknown";
+ PyCodeObject* pCode = frame->f_code;
+ if (pCode && pCode->co_filename)
+ {
+ PyBytesObject* pFileBytes = (PyBytesObject*)PyUnicode_AsASCIIString(pCode->co_filename);
+ sFuncName = pFileBytes->ob_sval;
+ }
+ if (pCode && pCode->co_name)
+ {
+ if (sFuncName.length()) sFuncName += "\\";
+ PyBytesObject* pFuncBytes = (PyBytesObject*)PyUnicode_AsASCIIString(pCode->co_name);
+ sFuncName = pFuncBytes->ob_sval;
+ }
+
+ switch (what)
+ {
+ case PyTrace_CALL:
+ _log.Log(LOG_NORM, "(%s) Calling function at line %d in '%s'", pModState->pPlugin->Name.c_str(), lineno, sFuncName.c_str());
+ break;
+ case PyTrace_RETURN:
+ _log.Log(LOG_NORM, "(%s) Returning from line %d in '%s'", pModState->pPlugin->Name.c_str(), lineno, sFuncName.c_str());
+ break;
+ case PyTrace_EXCEPTION:
+ _log.Log(LOG_NORM, "(%s) Exception at line %d in '%s'", pModState->pPlugin->Name.c_str(), lineno, sFuncName.c_str());
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ int PyDomoticz_TraceFunc(PyObject *self, PyFrameObject *frame, int what, PyObject *arg)
+ {
+ module_state* pModState = ((struct module_state*)PyModule_GetState(self));
+ if (!pModState)
+ {
+ _log.Log(LOG_ERROR, "CPlugin:%s, unable to obtain module state.", __func__);
+ }
+ else if (!pModState->pPlugin)
+ {
+ _log.Log(LOG_ERROR, "CPlugin:%s, illegal operation, Plugin has not started yet.", __func__);
+ }
+ else
+ {
+ int lineno = PyFrame_GetLineNumber(frame);
+ std::string sFuncName = "Unknown";
+ PyCodeObject* pCode = frame->f_code;
+ if (pCode && pCode->co_filename)
+ {
+ PyBytesObject* pFileBytes = (PyBytesObject*)PyUnicode_AsASCIIString(pCode->co_filename);
+ sFuncName = pFileBytes->ob_sval;
+ }
+ if (pCode && pCode->co_name)
+ {
+ if (sFuncName.length()) sFuncName += "\\";
+ PyBytesObject* pFuncBytes = (PyBytesObject*)PyUnicode_AsASCIIString(pCode->co_name);
+ sFuncName = pFuncBytes->ob_sval;
+ }
+
+ switch (what)
+ {
+ case PyTrace_CALL:
+ _log.Log(LOG_NORM, "(%s) Calling function at line %d in '%s'", pModState->pPlugin->Name.c_str(), lineno, sFuncName.c_str());
+ break;
+ case PyTrace_LINE:
+ _log.Log(LOG_NORM, "(%s) Executing line %d in '%s'", pModState->pPlugin->Name.c_str(), lineno, sFuncName.c_str());
+ break;
+ case PyTrace_EXCEPTION:
+ _log.Log(LOG_NORM, "(%s) Exception at line %d in '%s'", pModState->pPlugin->Name.c_str(), lineno, sFuncName.c_str());
+ break;
+ }
+ }
+
+ return 0;
+ }
+
static PyObject* PyDomoticz_Debug(PyObject *self, PyObject *args)
{
module_state* pModState = ((struct module_state*)PyModule_GetState(self));
@@ -355,6 +445,47 @@ namespace Plugins {
return Py_None;
}
+ static PyObject* PyDomoticz_Trace(PyObject *self, PyObject *args)
+ {
+ module_state* pModState = ((struct module_state*)PyModule_GetState(self));
+ if (!pModState)
+ {
+ _log.Log(LOG_ERROR, "CPlugin:%s, unable to obtain module state.", __func__);
+ }
+ else if (!pModState->pPlugin)
+ {
+ _log.Log(LOG_ERROR, "CPlugin:%s, illegal operation, Plugin has not started yet.", __func__);
+ }
+ else
+ {
+ int bTrace = 0;
+ if (!PyArg_ParseTuple(args, "p", &bTrace))
+ {
+ _log.Log(LOG_ERROR, "(%s) failed to parse parameter, True/False expected.", pModState->pPlugin->Name.c_str());
+ LogPythonException(pModState->pPlugin, std::string(__func__));
+ }
+ else
+ {
+ pModState->pPlugin->m_bTracing = (bool)bTrace;
+ _log.Log(LOG_NORM, "(%s) Low level Python tracing %s.", pModState->pPlugin->Name.c_str(), (pModState->pPlugin->m_bTracing ? "ENABLED" : "DISABLED"));
+
+ if (pModState->pPlugin->m_bTracing)
+ {
+ PyEval_SetProfile(PyDomoticz_ProfileFunc, self);
+ PyEval_SetTrace(PyDomoticz_TraceFunc, self);
+ }
+ else
+ {
+ PyEval_SetProfile(NULL, NULL);
+ PyEval_SetTrace(NULL, NULL);
+ }
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
static PyMethodDef DomoticzMethods[] = {
{ "Debug", PyDomoticz_Debug, METH_VARARGS, "Write a message to Domoticz log only if verbose logging is turned on." },
{ "Log", PyDomoticz_Log, METH_VARARGS, "Write a message to Domoticz log." },
@@ -363,6 +494,7 @@ namespace Plugins {
{ "Debugging", PyDomoticz_Debugging, METH_VARARGS, "Set logging level. 1 set verbose logging, all other values use default level" },
{ "Heartbeat", PyDomoticz_Heartbeat, METH_VARARGS, "Set the heartbeat interval, default 10 seconds." },
{ "Notifier", PyDomoticz_Notifier, METH_VARARGS, "Enable notification handling with supplied name." },
+ { "Trace", PyDomoticz_Trace, METH_VARARGS, "Enable/Disable line level Python tracing." },
{ NULL, NULL, 0, NULL }
};
@@ -439,6 +571,7 @@ namespace Plugins {
Name = sName;
m_bIsStarted = false;
m_bIsStarting = false;
+ m_bTracing = false;
}
CPlugin::~CPlugin(void)
diff --git a/hardware/plugins/Plugins.h b/hardware/plugins/Plugins.h
index d8808e147..d184e2be3 100644
--- a/hardware/plugins/Plugins.h
+++ b/hardware/plugins/Plugins.h
@@ -108,6 +108,7 @@ namespace Plugins {
PluginDebugMask m_bDebug;
bool m_stoprequested;
bool m_bIsStarting;
+ bool m_bTracing;
};
class CPluginNotifier : public CNotificationBase
From 463bb8e28be5ec7a6cfc452a5318f89f2d65a14d Mon Sep 17 00:00:00 2001
From: dnpwwo <kendel.boul@gmail.com>
Date: Sun, 1 Jul 2018 22:56:32 +1000
Subject: [PATCH] 2nd version, Python 3.7 and 3.6
---
hardware/plugins/DelayedLink.h | 3 +++
hardware/plugins/PluginManager.cpp | 8 ++++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/hardware/plugins/DelayedLink.h b/hardware/plugins/DelayedLink.h
index bdb9d0ec3..44bcb118e 100644
--- a/hardware/plugins/DelayedLink.h
+++ b/hardware/plugins/DelayedLink.h
@@ -87,6 +87,7 @@ namespace Plugins {
DECLARE_PYTHON_SYMBOL(PyObject*, PyObject_CallObject, PyObject* COMMA PyObject*);
DECLARE_PYTHON_SYMBOL(int, PyFrame_GetLineNumber, PyFrameObject*);
DECLARE_PYTHON_SYMBOL(void, PyEval_InitThreads, );
+ DECLARE_PYTHON_SYMBOL(int, PyEval_ThreadsInitialized, );
DECLARE_PYTHON_SYMBOL(PyThreadState*, PyThreadState_Get, );
DECLARE_PYTHON_SYMBOL(PyThreadState*, PyEval_SaveThread, void);
DECLARE_PYTHON_SYMBOL(void, PyEval_RestoreThread, PyThreadState*);
@@ -205,6 +206,7 @@ namespace Plugins {
RESOLVE_PYTHON_SYMBOL(PyObject_CallObject);
RESOLVE_PYTHON_SYMBOL(PyFrame_GetLineNumber);
RESOLVE_PYTHON_SYMBOL(PyEval_InitThreads);
+ RESOLVE_PYTHON_SYMBOL(PyEval_ThreadsInitialized);
RESOLVE_PYTHON_SYMBOL(PyThreadState_Get);
RESOLVE_PYTHON_SYMBOL(PyEval_SaveThread);
RESOLVE_PYTHON_SYMBOL(PyEval_RestoreThread);
@@ -380,6 +382,7 @@ extern SharedLibraryProxy* pythonLib;
#define PyObject_CallObject pythonLib->PyObject_CallObject
#define PyFrame_GetLineNumber pythonLib->PyFrame_GetLineNumber
#define PyEval_InitThreads pythonLib->PyEval_InitThreads
+#define PyEval_ThreadsInitialized pythonLib->PyEval_ThreadsInitialized
#define PyThreadState_Get pythonLib->PyThreadState_Get
#define PyEval_SaveThread pythonLib->PyEval_SaveThread
#define PyEval_RestoreThread pythonLib->PyEval_RestoreThread
diff --git a/hardware/plugins/PluginManager.cpp b/hardware/plugins/PluginManager.cpp
index e896b8bcc..32fa2bffd 100644
--- a/hardware/plugins/PluginManager.cpp
+++ b/hardware/plugins/PluginManager.cpp
@@ -139,10 +139,14 @@ namespace Plugins {
}
Py_Initialize();
- m_InitialPythonThread = PyEval_SaveThread();
// Initialise threads. Python 3.7+ does this inside Py_Initialize so done here for compatibility
- PyEval_InitThreads();
+ if (!PyEval_ThreadsInitialized())
+ {
+ PyEval_InitThreads();
+ }
+
+ m_InitialPythonThread = PyEval_SaveThread();
m_bEnabled = true;
_log.Log(LOG_STATUS, "PluginSystem: Started, Python version '%s'.", sVersion.c_str());