change to codeload fix python 3.10 plugin loading and usage Signed-off-by: Lucian Cristian <lucian.cristian@gmail.com>
4364 lines
153 KiB
Diff
4364 lines
153 KiB
Diff
From 8f01ed77d5831090f34ad59d22ef1f7cd4d740f2 Mon Sep 17 00:00:00 2001
|
|
From: dnpwwo <kendel.boul@gmail.com>
|
|
Date: Mon, 21 Feb 2022 10:27:06 +1100
|
|
Subject: [PATCH] Convert Python implementation to use Python's stable ABI
|
|
|
|
---
|
|
hardware/plugins/DelayedLink.h | 199 +++---
|
|
hardware/plugins/PluginManager.cpp | 17 +-
|
|
hardware/plugins/PluginMessages.h | 1 -
|
|
hardware/plugins/PluginProtocols.cpp | 356 ++++++-----
|
|
hardware/plugins/PluginTransports.cpp | 64 +-
|
|
hardware/plugins/Plugins.cpp | 883 +++++++++-----------------
|
|
hardware/plugins/Plugins.h | 37 +-
|
|
hardware/plugins/PythonObjectEx.cpp | 60 +-
|
|
hardware/plugins/PythonObjectEx.h | 83 +--
|
|
hardware/plugins/PythonObjects.cpp | 147 +++--
|
|
hardware/plugins/PythonObjects.h | 119 ----
|
|
main/EventSystem.cpp | 3 +-
|
|
main/EventsPythonDevice.cpp | 12 +-
|
|
main/EventsPythonDevice.h | 42 +-
|
|
main/EventsPythonModule.cpp | 321 ++++++----
|
|
main/SQLHelper.cpp | 2 +-
|
|
16 files changed, 980 insertions(+), 1366 deletions(-)
|
|
|
|
--- a/hardware/plugins/DelayedLink.h
|
|
+++ b/hardware/plugins/DelayedLink.h
|
|
@@ -9,10 +9,19 @@
|
|
#ifdef WITH_THREAD
|
|
# undefine WITH_THREAD
|
|
#endif
|
|
+
|
|
+#pragma push_macro("_DEBUG")
|
|
+#ifdef _DEBUG
|
|
+# undef _DEBUG // Not compatible with Py_LIMITED_API
|
|
+#endif
|
|
+#define Py_LIMITED_API 0x03040000
|
|
#include <Python.h>
|
|
#include <structmember.h>
|
|
-#include <frameobject.h>
|
|
-#include "../../main/Helper.h"
|
|
+#include "../../main/Logger.h"
|
|
+
|
|
+#ifndef WIN32
|
|
+# include "../../main/Helper.h"
|
|
+#endif
|
|
|
|
namespace Plugins {
|
|
|
|
@@ -29,6 +38,8 @@ namespace Plugins {
|
|
#define DECLARE_PYTHON_SYMBOL(type, symbol, params) typedef type (PYTHON_CALL symbol##_t)(params); symbol##_t symbol
|
|
#define RESOLVE_PYTHON_SYMBOL(symbol) symbol = (symbol##_t)RESOLVE_SYMBOL(shared_lib_, #symbol)
|
|
|
|
+#undef Py_None
|
|
+
|
|
struct SharedLibraryProxy
|
|
{
|
|
#ifdef WIN32
|
|
@@ -36,6 +47,8 @@ namespace Plugins {
|
|
#else
|
|
void* shared_lib_;
|
|
#endif
|
|
+ PyObject* Py_None;
|
|
+
|
|
// Shared library interface begin.
|
|
DECLARE_PYTHON_SYMBOL(const char*, Py_GetVersion, );
|
|
DECLARE_PYTHON_SYMBOL(int, Py_IsInitialized, );
|
|
@@ -50,6 +63,9 @@ namespace Plugins {
|
|
DECLARE_PYTHON_SYMBOL(wchar_t*, Py_GetProgramFullPath, );
|
|
DECLARE_PYTHON_SYMBOL(int, PyImport_AppendInittab, const char *COMMA PyObject *(*initfunc)());
|
|
DECLARE_PYTHON_SYMBOL(int, PyType_Ready, PyTypeObject*);
|
|
+ DECLARE_PYTHON_SYMBOL(PyObject*, PyObject_Type, PyObject*);
|
|
+ DECLARE_PYTHON_SYMBOL(PyObject*, PyType_FromSpec, PyType_Spec*);
|
|
+ DECLARE_PYTHON_SYMBOL(void*, PyType_GetSlot, PyTypeObject* COMMA int);
|
|
DECLARE_PYTHON_SYMBOL(int, PyCallable_Check, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyObject_GetAttrString, PyObject* pObj COMMA const char*);
|
|
DECLARE_PYTHON_SYMBOL(int, PyObject_HasAttrString, PyObject* COMMA const char *);
|
|
@@ -60,7 +76,6 @@ namespace Plugins {
|
|
DECLARE_PYTHON_SYMBOL(wchar_t*, PyUnicode_AsWideCharString, PyObject* COMMA Py_ssize_t*);
|
|
DECLARE_PYTHON_SYMBOL(const char*, PyUnicode_AsUTF8, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(char*, PyByteArray_AsString, PyObject*);
|
|
- DECLARE_PYTHON_SYMBOL(PyObject*, PyUnicode_FromKindAndData, int COMMA const void* COMMA Py_ssize_t);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyLong_FromLong, long);
|
|
DECLARE_PYTHON_SYMBOL(PY_LONG_LONG, PyLong_AsLongLong, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyModule_GetDict, PyObject*);
|
|
@@ -91,8 +106,6 @@ namespace Plugins {
|
|
DECLARE_PYTHON_SYMBOL(PyObject *, PyImport_ImportModule, const char *);
|
|
DECLARE_PYTHON_SYMBOL(int, PyObject_RichCompareBool, PyObject* COMMA PyObject* COMMA int);
|
|
DECLARE_PYTHON_SYMBOL(PyObject *, PyObject_CallObject, PyObject *COMMA PyObject *);
|
|
- DECLARE_PYTHON_SYMBOL(PyObject *, PyObject_CallNoArgs, PyObject *); // Python 3.9 !!!!
|
|
- 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, );
|
|
@@ -102,17 +115,12 @@ namespace Plugins {
|
|
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 *);
|
|
DECLARE_PYTHON_SYMBOL(int, PyObject_IsInstance, PyObject* COMMA PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(int, PyObject_IsSubclass, PyObject *COMMA PyObject *);
|
|
DECLARE_PYTHON_SYMBOL(PyObject *, PyObject_Dir, PyObject *);
|
|
-#ifdef _DEBUG
|
|
- DECLARE_PYTHON_SYMBOL(PyObject*, PyModule_Create2TraceRefs, struct PyModuleDef* COMMA int);
|
|
-#else
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyModule_Create2, struct PyModuleDef* COMMA int);
|
|
-#endif
|
|
DECLARE_PYTHON_SYMBOL(int, PyModule_AddObject, PyObject* COMMA const char* COMMA PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(int, PyArg_ParseTuple, PyObject* COMMA const char* COMMA ...);
|
|
DECLARE_PYTHON_SYMBOL(int, PyArg_ParseTupleAndKeywords, PyObject* COMMA PyObject* COMMA const char* COMMA char*[] COMMA ...);
|
|
@@ -120,8 +128,6 @@ namespace Plugins {
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, Py_BuildValue, const char* COMMA ...);
|
|
DECLARE_PYTHON_SYMBOL(void, PyMem_Free, void*);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyBool_FromLong, long);
|
|
- DECLARE_PYTHON_SYMBOL(int, PyRun_SimpleStringFlags, const char* COMMA PyCompilerFlags*);
|
|
- DECLARE_PYTHON_SYMBOL(int, PyRun_SimpleFileExFlags, FILE* COMMA const char* COMMA int COMMA PyCompilerFlags*);
|
|
DECLARE_PYTHON_SYMBOL(void*, PyCapsule_Import, const char *name COMMA int);
|
|
DECLARE_PYTHON_SYMBOL(void*, PyType_GenericAlloc, const PyTypeObject * COMMA Py_ssize_t);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyUnicode_DecodeUTF8, const char * COMMA Py_ssize_t COMMA const char *);
|
|
@@ -132,44 +138,32 @@ 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*);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyObject_Str, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(int, PyObject_IsTrue, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(double, PyFloat_AsDouble, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyObject_GetIter, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(PyObject*, PyIter_Next, PyObject*);
|
|
DECLARE_PYTHON_SYMBOL(void, PyErr_SetString, PyObject* COMMA const char*);
|
|
-
|
|
-#ifdef _DEBUG
|
|
- // In a debug build dealloc is a function but for release builds its a macro
|
|
+ DECLARE_PYTHON_SYMBOL(PyObject*, PyObject_CallFunctionObjArgs, PyObject* COMMA ...);
|
|
+ DECLARE_PYTHON_SYMBOL(PyObject*, Py_CompileString, const char* COMMA const char* COMMA int);
|
|
+ DECLARE_PYTHON_SYMBOL(PyObject*, PyEval_EvalCode, PyObject* COMMA PyObject* COMMA PyObject*);
|
|
+ DECLARE_PYTHON_SYMBOL(long, PyType_GetFlags, PyTypeObject*);
|
|
DECLARE_PYTHON_SYMBOL(void, _Py_Dealloc, PyObject*);
|
|
-#endif
|
|
- Py_ssize_t _Py_RefTotal;
|
|
- PyObject _Py_NoneStruct;
|
|
- PyObject * dzPy_None;
|
|
|
|
SharedLibraryProxy() {
|
|
+ Py_None = nullptr;
|
|
shared_lib_ = nullptr;
|
|
- _Py_RefTotal = 0;
|
|
if (!shared_lib_) {
|
|
#ifdef WIN32
|
|
-# ifdef _DEBUG
|
|
- if (!shared_lib_) shared_lib_ = LoadLibrary("python39_d.dll");
|
|
- if (!shared_lib_) shared_lib_ = LoadLibrary("python38_d.dll");
|
|
- if (!shared_lib_) shared_lib_ = LoadLibrary("python37_d.dll");
|
|
- if (!shared_lib_) shared_lib_ = LoadLibrary("python36_d.dll");
|
|
- if (!shared_lib_) shared_lib_ = LoadLibrary("python35_d.dll");
|
|
- if (!shared_lib_) shared_lib_ = LoadLibrary("python34_d.dll");
|
|
-# else
|
|
+ if (!shared_lib_) shared_lib_ = LoadLibrary("python310.dll");
|
|
if (!shared_lib_) shared_lib_ = LoadLibrary("python39.dll");
|
|
if (!shared_lib_) shared_lib_ = LoadLibrary("python38.dll");
|
|
if (!shared_lib_) shared_lib_ = LoadLibrary("python37.dll");
|
|
if (!shared_lib_) shared_lib_ = LoadLibrary("python36.dll");
|
|
if (!shared_lib_) shared_lib_ = LoadLibrary("python35.dll");
|
|
if (!shared_lib_) shared_lib_ = LoadLibrary("python34.dll");
|
|
-# endif
|
|
#else
|
|
+ if (!shared_lib_) FindLibrary("python3.10", true);
|
|
if (!shared_lib_) FindLibrary("python3.9", true);
|
|
if (!shared_lib_) FindLibrary("python3.8", true);
|
|
if (!shared_lib_) FindLibrary("python3.7", true);
|
|
@@ -198,6 +192,9 @@ namespace Plugins {
|
|
RESOLVE_PYTHON_SYMBOL(Py_GetProgramFullPath);
|
|
RESOLVE_PYTHON_SYMBOL(PyImport_AppendInittab);
|
|
RESOLVE_PYTHON_SYMBOL(PyType_Ready);
|
|
+ RESOLVE_PYTHON_SYMBOL(PyObject_Type);
|
|
+ RESOLVE_PYTHON_SYMBOL(PyType_FromSpec);
|
|
+ RESOLVE_PYTHON_SYMBOL(PyType_GetSlot);
|
|
RESOLVE_PYTHON_SYMBOL(PyCallable_Check);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_GetAttrString);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_HasAttrString);
|
|
@@ -208,7 +205,6 @@ namespace Plugins {
|
|
RESOLVE_PYTHON_SYMBOL(PyUnicode_AsWideCharString);
|
|
RESOLVE_PYTHON_SYMBOL(PyUnicode_AsUTF8);
|
|
RESOLVE_PYTHON_SYMBOL(PyByteArray_AsString);
|
|
- RESOLVE_PYTHON_SYMBOL(PyUnicode_FromKindAndData);
|
|
RESOLVE_PYTHON_SYMBOL(PyLong_FromLong);
|
|
RESOLVE_PYTHON_SYMBOL(PyLong_AsLongLong);
|
|
RESOLVE_PYTHON_SYMBOL(PyModule_GetDict);
|
|
@@ -239,8 +235,6 @@ namespace Plugins {
|
|
RESOLVE_PYTHON_SYMBOL(PyImport_ImportModule);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_RichCompareBool);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_CallObject);
|
|
- RESOLVE_PYTHON_SYMBOL(PyObject_CallNoArgs);
|
|
- RESOLVE_PYTHON_SYMBOL(PyFrame_GetLineNumber);
|
|
RESOLVE_PYTHON_SYMBOL(PyEval_InitThreads);
|
|
RESOLVE_PYTHON_SYMBOL(PyEval_ThreadsInitialized);
|
|
RESOLVE_PYTHON_SYMBOL(PyThreadState_Get);
|
|
@@ -250,28 +244,18 @@ namespace Plugins {
|
|
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);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_IsInstance);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_IsSubclass);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_Dir);
|
|
-#ifdef _DEBUG
|
|
- RESOLVE_PYTHON_SYMBOL(PyModule_Create2TraceRefs);
|
|
-#else
|
|
RESOLVE_PYTHON_SYMBOL(PyModule_Create2);
|
|
-#endif
|
|
RESOLVE_PYTHON_SYMBOL(PyModule_AddObject);
|
|
RESOLVE_PYTHON_SYMBOL(PyArg_ParseTuple);
|
|
RESOLVE_PYTHON_SYMBOL(PyArg_ParseTupleAndKeywords);
|
|
RESOLVE_PYTHON_SYMBOL(PyUnicode_FromFormat);
|
|
RESOLVE_PYTHON_SYMBOL(Py_BuildValue);
|
|
RESOLVE_PYTHON_SYMBOL(PyMem_Free);
|
|
-#ifdef _DEBUG
|
|
- RESOLVE_PYTHON_SYMBOL(_Py_Dealloc);
|
|
-#endif
|
|
- RESOLVE_PYTHON_SYMBOL(PyRun_SimpleFileExFlags);
|
|
- RESOLVE_PYTHON_SYMBOL(PyRun_SimpleStringFlags);
|
|
RESOLVE_PYTHON_SYMBOL(PyBool_FromLong);
|
|
RESOLVE_PYTHON_SYMBOL(PyCapsule_Import);
|
|
RESOLVE_PYTHON_SYMBOL(PyType_GenericAlloc);
|
|
@@ -283,17 +267,19 @@ 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);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_Str);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_IsTrue);
|
|
RESOLVE_PYTHON_SYMBOL(PyFloat_AsDouble);
|
|
RESOLVE_PYTHON_SYMBOL(PyObject_GetIter);
|
|
RESOLVE_PYTHON_SYMBOL(PyIter_Next);
|
|
RESOLVE_PYTHON_SYMBOL(PyErr_SetString);
|
|
+ RESOLVE_PYTHON_SYMBOL(PyObject_CallFunctionObjArgs);
|
|
+ RESOLVE_PYTHON_SYMBOL(Py_CompileString);
|
|
+ RESOLVE_PYTHON_SYMBOL(PyEval_EvalCode);
|
|
+ RESOLVE_PYTHON_SYMBOL(PyType_GetFlags);
|
|
+ RESOLVE_PYTHON_SYMBOL(_Py_Dealloc);
|
|
}
|
|
}
|
|
- _Py_NoneStruct.ob_refcnt = 1;
|
|
};
|
|
~SharedLibraryProxy() = default;
|
|
|
|
@@ -412,15 +398,7 @@ namespace Plugins {
|
|
|
|
extern SharedLibraryProxy* pythonLib;
|
|
|
|
-// Create local pointer to Py_None, required to work around build complaints
|
|
-#ifdef Py_None
|
|
- #undef Py_None
|
|
-#endif
|
|
-#define Py_None pythonLib->dzPy_None
|
|
-#ifdef Py_RETURN_NONE
|
|
- #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
|
|
-#endif
|
|
-#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
|
|
+#define Py_None pythonLib->Py_None
|
|
#define Py_LoadLibrary pythonLib->Py_LoadLibrary
|
|
#define Py_GetVersion pythonLib->Py_GetVersion
|
|
#define Py_IsInitialized pythonLib->Py_IsInitialized
|
|
@@ -435,6 +413,9 @@ extern SharedLibraryProxy* pythonLib;
|
|
#define Py_GetProgramFullPath pythonLib->Py_GetProgramFullPath
|
|
#define PyImport_AppendInittab pythonLib->PyImport_AppendInittab
|
|
#define PyType_Ready pythonLib->PyType_Ready
|
|
+#define PyObject_Type pythonLib->PyObject_Type
|
|
+#define PyType_FromSpec pythonLib->PyType_FromSpec
|
|
+#define PyType_GetSlot pythonLib->PyType_GetSlot
|
|
#define PyCallable_Check pythonLib->PyCallable_Check
|
|
#define PyObject_GetAttrString pythonLib->PyObject_GetAttrString
|
|
#define PyObject_HasAttrString pythonLib->PyObject_HasAttrString
|
|
@@ -446,7 +427,6 @@ extern SharedLibraryProxy* pythonLib;
|
|
#define PyUnicode_AsWideCharString pythonLib->PyUnicode_AsWideCharString
|
|
#define PyUnicode_AsUTF8 pythonLib->PyUnicode_AsUTF8
|
|
#define PyByteArray_AsString pythonLib->PyByteArray_AsString
|
|
-#define PyUnicode_FromKindAndData pythonLib->PyUnicode_FromKindAndData
|
|
#define PyLong_FromLong pythonLib->PyLong_FromLong
|
|
#define PyLong_AsLongLong pythonLib->PyLong_AsLongLong
|
|
#define PyModule_GetDict pythonLib->PyModule_GetDict
|
|
@@ -460,7 +440,7 @@ extern SharedLibraryProxy* pythonLib;
|
|
#define PyDict_SetItem pythonLib->PyDict_SetItem
|
|
#define PyDict_DelItem pythonLib->PyDict_DelItem
|
|
#define PyDict_DelItemString pythonLib->PyDict_DelItemString
|
|
-#define PyDict_Next pythonLib->PyDict_Next
|
|
+#define PyDict_Next pythonLib->PyDict_Next
|
|
#define PyDict_Items pythonLib->PyDict_Items
|
|
#define PyList_New pythonLib->PyList_New
|
|
#define PyList_Size pythonLib->PyList_Size
|
|
@@ -477,8 +457,6 @@ extern SharedLibraryProxy* pythonLib;
|
|
#define PyImport_ImportModule pythonLib->PyImport_ImportModule
|
|
#define PyObject_RichCompareBool pythonLib->PyObject_RichCompareBool
|
|
#define PyObject_CallObject pythonLib->PyObject_CallObject
|
|
-#define PyObject_CallNoArgs pythonLib->PyObject_CallNoArgs
|
|
-#define PyFrame_GetLineNumber pythonLib->PyFrame_GetLineNumber
|
|
#define PyEval_InitThreads pythonLib->PyEval_InitThreads
|
|
#define PyEval_ThreadsInitialized pythonLib->PyEval_ThreadsInitialized
|
|
#define PyThreadState_Get pythonLib->PyThreadState_Get
|
|
@@ -488,7 +466,6 @@ extern SharedLibraryProxy* pythonLib;
|
|
#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 PyObject_IsInstance pythonLib->PyObject_IsInstance
|
|
@@ -497,22 +474,10 @@ extern SharedLibraryProxy* pythonLib;
|
|
#define PyArg_ParseTuple pythonLib->PyArg_ParseTuple
|
|
#define Py_BuildValue pythonLib->Py_BuildValue
|
|
#define PyMem_Free pythonLib->PyMem_Free
|
|
-#ifdef _DEBUG
|
|
-# define PyModule_Create2TraceRefs pythonLib->PyModule_Create2TraceRefs
|
|
-#else
|
|
-# define PyModule_Create2 pythonLib->PyModule_Create2
|
|
-#endif
|
|
+#define PyModule_Create2 pythonLib->PyModule_Create2
|
|
#define PyModule_AddObject pythonLib->PyModule_AddObject
|
|
#define PyArg_ParseTupleAndKeywords pythonLib->PyArg_ParseTupleAndKeywords
|
|
-
|
|
-#ifdef _DEBUG
|
|
-# define _Py_Dealloc pythonLib->_Py_Dealloc
|
|
-#endif
|
|
-
|
|
#define _Py_RefTotal pythonLib->_Py_RefTotal
|
|
-#define _Py_NoneStruct pythonLib->_Py_NoneStruct
|
|
-#define PyRun_SimpleStringFlags pythonLib->PyRun_SimpleStringFlags
|
|
-#define PyRun_SimpleFileExFlags pythonLib->PyRun_SimpleFileExFlags
|
|
#define PyBool_FromLong pythonLib->PyBool_FromLong
|
|
#define PyCapsule_Import pythonLib->PyCapsule_Import
|
|
#define PyType_GenericAlloc pythonLib->PyType_GenericAlloc
|
|
@@ -524,80 +489,88 @@ 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
|
|
#define PyObject_Str pythonLib->PyObject_Str
|
|
#define PyObject_IsTrue pythonLib->PyObject_IsTrue
|
|
#define PyFloat_AsDouble pythonLib->PyFloat_AsDouble
|
|
#define PyObject_GetIter pythonLib->PyObject_GetIter
|
|
#define PyIter_Next pythonLib->PyIter_Next
|
|
#define PyErr_SetString pythonLib->PyErr_SetString
|
|
-
|
|
-#ifndef _Py_DEC_REFTOTAL
|
|
-/* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by: https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924 */
|
|
-#ifdef Py_REF_DEBUG
|
|
-#define _Py_DEC_REFTOTAL _Py_RefTotal--
|
|
+#define PyObject_CallFunctionObjArgs pythonLib->PyObject_CallFunctionObjArgs
|
|
+#define Py_CompileString pythonLib->Py_CompileString
|
|
+#define PyEval_EvalCode pythonLib->PyEval_EvalCode
|
|
+#define PyType_GetFlags pythonLib->PyType_GetFlags
|
|
+#ifdef WIN32
|
|
+# define _Py_Dealloc pythonLib->_Py_Dealloc // Builds against a low Python version
|
|
+#elif PY_VERSION_HEX < 0x03090000
|
|
+# define _Py_Dealloc pythonLib->_Py_Dealloc
|
|
#else
|
|
-#define _Py_DEC_REFTOTAL
|
|
-#define _Py_Dealloc
|
|
-#endif
|
|
+# ifndef _Py_DEC_REFTOTAL
|
|
+ /* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by: https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924 */
|
|
+# ifdef Py_REF_DEBUG
|
|
+# define _Py_DEC_REFTOTAL _Py_RefTotal--
|
|
+# else
|
|
+# define _Py_DEC_REFTOTAL
|
|
+# define _Py_Dealloc
|
|
+# endif
|
|
+# endif
|
|
#endif
|
|
|
|
#if PY_VERSION_HEX >= 0x030800f0
|
|
- static inline void py3__Py_INCREF(PyObject *op)
|
|
- {
|
|
+static inline void py3__Py_INCREF(PyObject* op)
|
|
+{
|
|
#ifdef Py_REF_DEBUG
|
|
- _Py_RefTotal++;
|
|
+ _Py_RefTotal++;
|
|
#endif
|
|
- op->ob_refcnt++;
|
|
- }
|
|
+ op->ob_refcnt++;
|
|
+}
|
|
|
|
#undef Py_INCREF
|
|
#define Py_INCREF(op) py3__Py_INCREF(_PyObject_CAST(op))
|
|
|
|
- static inline void py3__Py_XINCREF(PyObject *op)
|
|
+static inline void py3__Py_XINCREF(PyObject* op)
|
|
+{
|
|
+ if (op != NULL)
|
|
{
|
|
- if (op != NULL)
|
|
- {
|
|
- Py_INCREF(op);
|
|
- }
|
|
+ Py_INCREF(op);
|
|
}
|
|
+}
|
|
|
|
#undef Py_XINCREF
|
|
#define Py_XINCREF(op) py3__Py_XINCREF(_PyObject_CAST(op))
|
|
|
|
- static inline void py3__Py_DECREF(const char *filename, int lineno, PyObject *op)
|
|
+static inline void py3__Py_DECREF(const char* filename, int lineno, PyObject* op)
|
|
+{
|
|
+ (void)filename; /* may be unused, shut up -Wunused-parameter */
|
|
+ (void)lineno; /* may be unused, shut up -Wunused-parameter */
|
|
+ _Py_DEC_REFTOTAL;
|
|
+ if (--op->ob_refcnt != 0)
|
|
{
|
|
- (void)filename; /* may be unused, shut up -Wunused-parameter */
|
|
- (void)lineno; /* may be unused, shut up -Wunused-parameter */
|
|
- _Py_DEC_REFTOTAL;
|
|
- if (--op->ob_refcnt != 0)
|
|
- {
|
|
#ifdef Py_REF_DEBUG
|
|
- if (op->ob_refcnt < 0)
|
|
- {
|
|
- _Py_NegativeRefcount(filename, lineno, op);
|
|
- }
|
|
-#endif
|
|
- }
|
|
- else
|
|
+ if (op->ob_refcnt < 0)
|
|
{
|
|
- _Py_Dealloc(op);
|
|
+ _Py_NegativeRefcount(filename, lineno, op);
|
|
}
|
|
+#endif
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _Py_Dealloc(op);
|
|
}
|
|
+}
|
|
|
|
#undef Py_DECREF
|
|
#define Py_DECREF(op) py3__Py_DECREF(__FILE__, __LINE__, _PyObject_CAST(op))
|
|
|
|
- static inline void py3__Py_XDECREF(PyObject *op)
|
|
+static inline void py3__Py_XDECREF(PyObject* op)
|
|
+{
|
|
+ if (op != nullptr)
|
|
{
|
|
- if (op != nullptr)
|
|
- {
|
|
- Py_DECREF(op);
|
|
- }
|
|
+ Py_DECREF(op);
|
|
}
|
|
+}
|
|
|
|
#undef Py_XDECREF
|
|
#define Py_XDECREF(op) py3__Py_XDECREF(_PyObject_CAST(op))
|
|
#endif
|
|
+#pragma pop_macro("_DEBUG")
|
|
} // namespace Plugins
|
|
--- a/hardware/plugins/PluginManager.cpp
|
|
+++ b/hardware/plugins/PluginManager.cpp
|
|
@@ -31,7 +31,9 @@
|
|
#include "DelayedLink.h"
|
|
#include "../../main/EventsPythonModule.h"
|
|
|
|
-#define MINIMUM_PYTHON_VERSION "3.4.0"
|
|
+// Python version constants
|
|
+#define MINIMUM_MAJOR_VERSION 3
|
|
+#define MINIMUM_MINOR_VERSION 4
|
|
|
|
#define ATTRIBUTE_VALUE(pElement, Name, Value) \
|
|
{ \
|
|
@@ -105,9 +107,18 @@ namespace Plugins {
|
|
}
|
|
|
|
std::string sVersion = szPyVersion.substr(0, szPyVersion.find_first_of(' '));
|
|
- if (sVersion < MINIMUM_PYTHON_VERSION)
|
|
+
|
|
+ std::string sMajorVersion = sVersion.substr(0, sVersion.find_first_of('.'));
|
|
+ if (std::stoi(sMajorVersion) < MINIMUM_MAJOR_VERSION)
|
|
+ {
|
|
+ _log.Log(LOG_STATUS, "PluginSystem: Invalid Python version '%s' found, Major version '%d' or above required.", sVersion.c_str(), MINIMUM_MAJOR_VERSION);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ std::string sMinorVersion = sVersion.substr(sMajorVersion.length()+1);
|
|
+ if (std::stoi(sMinorVersion) < MINIMUM_MINOR_VERSION)
|
|
{
|
|
- _log.Log(LOG_STATUS, "PluginSystem: Invalid Python version '%s' found, '%s' or above required.", sVersion.c_str(), MINIMUM_PYTHON_VERSION);
|
|
+ _log.Log(LOG_STATUS, "PluginSystem: Invalid Python version '%s' found, Minor version '%d.%d' or above required.", sVersion.c_str(), MINIMUM_MAJOR_VERSION, MINIMUM_MINOR_VERSION);
|
|
return false;
|
|
}
|
|
|
|
--- a/hardware/plugins/PluginMessages.h
|
|
+++ b/hardware/plugins/PluginMessages.h
|
|
@@ -60,7 +60,6 @@ namespace Plugins {
|
|
InitializeMessage() : CPluginMessageBase() { m_Name = __func__; };
|
|
void Process(CPlugin* pPlugin) override
|
|
{
|
|
- //std::lock_guard<std::mutex> l(PythonMutex);
|
|
pPlugin->Initialise();
|
|
};
|
|
void ProcessLocked(CPlugin* pPlugin) override{};
|
|
--- a/hardware/plugins/PluginProtocols.cpp
|
|
+++ b/hardware/plugins/PluginProtocols.cpp
|
|
@@ -5,6 +5,7 @@
|
|
//
|
|
#ifdef ENABLE_PYTHON
|
|
|
|
+#include "../../main/Helper.h"
|
|
#include "PluginMessages.h"
|
|
#include "PluginProtocols.h"
|
|
#include "../../main/Helper.h"
|
|
@@ -52,32 +53,37 @@ namespace Plugins {
|
|
std::vector<byte> CPluginProtocol::ProcessOutbound(const WriteDirective* WriteMessage)
|
|
{
|
|
std::vector<byte> retVal;
|
|
+ PyBorrowedRef pObject(WriteMessage->m_Object);
|
|
|
|
// Handle Bytes objects
|
|
- if ((((PyObject*)WriteMessage->m_Object)->ob_type->tp_flags & (Py_TPFLAGS_BYTES_SUBCLASS)) != 0)
|
|
+ if (pObject.IsBytes())
|
|
{
|
|
- const char* pData = PyBytes_AsString(WriteMessage->m_Object);
|
|
- int iSize = PyBytes_Size(WriteMessage->m_Object);
|
|
+ const char* pData = PyBytes_AsString(pObject);
|
|
+ int iSize = PyBytes_Size(pObject);
|
|
retVal.reserve((size_t)iSize);
|
|
retVal.assign(pData, pData + iSize);
|
|
}
|
|
// Handle ByteArray objects
|
|
- else if ((((PyObject*)WriteMessage->m_Object)->ob_type->tp_name == std::string("bytearray")))
|
|
+ else if (pObject.IsByteArray())
|
|
{
|
|
- size_t len = PyByteArray_Size(WriteMessage->m_Object);
|
|
- char* data = PyByteArray_AsString(WriteMessage->m_Object);
|
|
+ size_t len = PyByteArray_Size(pObject);
|
|
+ char* data = PyByteArray_AsString(pObject);
|
|
retVal.reserve(len);
|
|
retVal.assign((const byte*)data, (const byte*)data + len);
|
|
}
|
|
- // Handle String objects
|
|
- else if ((((PyObject*)WriteMessage->m_Object)->ob_type->tp_flags & (Py_TPFLAGS_UNICODE_SUBCLASS)) != 0)
|
|
+ // Try forcing a String conversion
|
|
+ else
|
|
{
|
|
- std::string sData = PyUnicode_AsUTF8(WriteMessage->m_Object);
|
|
- retVal.reserve((size_t)sData.length());
|
|
- retVal.assign((const byte*)sData.c_str(), (const byte*)sData.c_str() + sData.length());
|
|
+ PyNewRef pStr = PyObject_Str(pObject);
|
|
+ if (pStr)
|
|
+ {
|
|
+ std::string sData = PyUnicode_AsUTF8(pStr);
|
|
+ retVal.reserve((size_t)sData.length());
|
|
+ retVal.assign((const byte*)sData.c_str(), (const byte*)sData.c_str() + sData.length());
|
|
+ }
|
|
+ else
|
|
+ _log.Log(LOG_ERROR, "(%s) Unable to convert data (%s) to string representation, ignored.", __func__, pObject.Type().c_str());
|
|
}
|
|
- else
|
|
- _log.Log(LOG_ERROR, "(%s) Send request Python object parameter was not of type Unicode or Byte, ignored.", __func__);
|
|
|
|
return retVal;
|
|
}
|
|
@@ -120,7 +126,7 @@ namespace Plugins {
|
|
if (PyDict_SetItemString(pDict, key, pObj) == -1)
|
|
_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, key, value.c_str());
|
|
}
|
|
-
|
|
+
|
|
static void AddStringToDict(PyObject* pDict, const char* key, const std::string& value)
|
|
{
|
|
PyNewRef pObj = Py_BuildValue("s#", value.c_str(), value.length());
|
|
@@ -166,7 +172,7 @@ namespace Plugins {
|
|
Py_ssize_t Index = 0;
|
|
for (auto &pRef : *pJSON)
|
|
{
|
|
- // PyList_SetItem 'steal' a reference so use PyBorrowedRef instead of PyNewRef
|
|
+ // PyList_SetItem 'steals' a reference so use PyBorrowedRef instead of PyNewRef
|
|
if (pRef.isArray() || pRef.isObject())
|
|
{
|
|
PyBorrowedRef pObj = JSONtoPython(&pRef);
|
|
@@ -239,7 +245,7 @@ namespace Plugins {
|
|
bool bRet = ParseJSon(sData, root);
|
|
if ((!bRet) || (!root.isObject()))
|
|
{
|
|
- _log.Log(LOG_ERROR, "JSON Protocol: Parse Error on '%s'", sData.c_str());
|
|
+ _log.Log(LOG_ERROR, "(%s) Parse Error on '%s'", __func__, sData.c_str());
|
|
Py_RETURN_NONE;
|
|
}
|
|
else
|
|
@@ -253,66 +259,77 @@ namespace Plugins {
|
|
std::string CPluginProtocolJSON::PythontoJSON(PyObject* pObject)
|
|
{
|
|
std::string sJson;
|
|
+ PyBorrowedRef pObj(pObject);
|
|
|
|
- if (PyUnicode_Check(pObject))
|
|
- {
|
|
- sJson += '"' + std::string(PyUnicode_AsUTF8(pObject)) + '"';
|
|
- }
|
|
- else if (pObject->ob_type->tp_name == std::string("bool"))
|
|
- {
|
|
- sJson += (PyObject_IsTrue(pObject) ? "true" : "false");
|
|
- }
|
|
- else if (PyLong_Check(pObject))
|
|
- {
|
|
- sJson += std::to_string(PyLong_AsLong(pObject));
|
|
- }
|
|
- else if (PyBytes_Check(pObject))
|
|
- {
|
|
- sJson += '"' + std::string(PyBytes_AsString(pObject)) + '"';
|
|
- }
|
|
- else if (pObject->ob_type->tp_name == std::string("bytearray"))
|
|
- {
|
|
- sJson += '"' + std::string(PyByteArray_AsString(pObject)) + '"';
|
|
- }
|
|
- else if (pObject->ob_type->tp_name == std::string("float"))
|
|
- {
|
|
- sJson += std::to_string(PyFloat_AsDouble(pObject));
|
|
- }
|
|
- else if (PyDict_Check(pObject))
|
|
+ if (pObj.IsDict())
|
|
{
|
|
sJson += "{ ";
|
|
PyObject* key, * value;
|
|
Py_ssize_t pos = 0;
|
|
- while (PyDict_Next(pObject, &pos, &key, &value))
|
|
+ while (PyDict_Next(pObj, &pos, &key, &value))
|
|
{
|
|
sJson += PythontoJSON(key) + ':' + PythontoJSON(value) + ',';
|
|
}
|
|
sJson[sJson.length() - 1] = '}';
|
|
}
|
|
- else if (PyList_Check(pObject))
|
|
+ else if (pObj.IsList())
|
|
{
|
|
sJson += "[ ";
|
|
- for (Py_ssize_t i = 0; i < PyList_Size(pObject); i++)
|
|
+ for (Py_ssize_t i = 0; i < PyList_Size(pObj); i++)
|
|
{
|
|
- sJson += PythontoJSON(PyList_GetItem(pObject, i)) + ',';
|
|
+ sJson += PythontoJSON(PyList_GetItem(pObj, i)) + ',';
|
|
}
|
|
sJson[sJson.length() - 1] = ']';
|
|
}
|
|
- else if (PyTuple_Check(pObject))
|
|
+ else if (pObj.IsTuple())
|
|
{
|
|
sJson += "[ ";
|
|
- for (Py_ssize_t i = 0; i < PyTuple_Size(pObject); i++)
|
|
+ for (Py_ssize_t i = 0; i < PyTuple_Size(pObj); i++)
|
|
{
|
|
- sJson += PythontoJSON(PyTuple_GetItem(pObject, i)) + ',';
|
|
+ sJson += PythontoJSON(PyTuple_GetItem(pObj, i)) + ',';
|
|
}
|
|
sJson[sJson.length() - 1] = ']';
|
|
}
|
|
+ else if (pObj.IsBool())
|
|
+ {
|
|
+ sJson += (PyObject_IsTrue(pObj) ? "true" : "false");
|
|
+ }
|
|
+ else if (pObj.IsLong())
|
|
+ {
|
|
+ sJson += std::to_string(PyLong_AsLong(pObj));
|
|
+ }
|
|
+ else if (pObj.IsFloat())
|
|
+ {
|
|
+ sJson += std::to_string(PyFloat_AsDouble(pObj));
|
|
+ }
|
|
+ else if (pObj.IsBytes())
|
|
+ {
|
|
+ sJson += '"' + std::string(PyBytes_AsString(pObj)) + '"';
|
|
+ }
|
|
+ else if (pObj.IsByteArray())
|
|
+ {
|
|
+ sJson += '"' + std::string(PyByteArray_AsString(pObj)) + '"';
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Try forcing a String conversion
|
|
+ PyNewRef pStr = PyObject_Str(pObject);
|
|
+ if (pStr)
|
|
+ {
|
|
+ sJson += '"' + std::string(PyUnicode_AsUTF8(pStr)) + '"';
|
|
+ }
|
|
+ else
|
|
+ _log.Log(LOG_ERROR, "(%s) Unable to convert data type (%s) to string representation, ignored.", __func__, pObj.Type().c_str());
|
|
+ }
|
|
|
|
return sJson;
|
|
}
|
|
|
|
void CPluginProtocolJSON::ProcessInbound(const ReadEvent* Message)
|
|
{
|
|
+ CConnection* pConnection = Message->m_pConnection;
|
|
+ CPlugin* pPlugin = pConnection->pPlugin;
|
|
+
|
|
//
|
|
// Handles the cases where a read contains a partial message or multiple messages
|
|
//
|
|
@@ -332,13 +349,13 @@ namespace Plugins {
|
|
bool bRet = ParseJSon(sData, root);
|
|
if ((!bRet) || (!root.isObject()))
|
|
{
|
|
- _log.Log(LOG_ERROR, "JSON Protocol: Parse Error on '%s'", sData.c_str());
|
|
- Message->m_pConnection->pPlugin->MessagePlugin(new onMessageCallback(Message->m_pConnection, sData));
|
|
+ pPlugin->Log(LOG_ERROR, "(%s) Parse Error on '%s'", __func__, sData.c_str());
|
|
+ pPlugin->MessagePlugin(new onMessageCallback(pConnection, sData));
|
|
}
|
|
else
|
|
{
|
|
PyObject* pMessage = JSONtoPython(&root);
|
|
- Message->m_pConnection->pPlugin->MessagePlugin(new onMessageCallback(Message->m_pConnection, pMessage));
|
|
+ pPlugin->MessagePlugin(new onMessageCallback(pConnection, pMessage));
|
|
}
|
|
sData.clear();
|
|
}
|
|
@@ -350,13 +367,13 @@ namespace Plugins {
|
|
bool bRet = ParseJSon(sMessage, root);
|
|
if ((!bRet) || (!root.isObject()))
|
|
{
|
|
- _log.Log(LOG_ERROR, "JSON Protocol: Parse Error on '%s'", sData.c_str());
|
|
- Message->m_pConnection->pPlugin->MessagePlugin(new onMessageCallback(Message->m_pConnection, sMessage));
|
|
+ pPlugin->Log(LOG_ERROR, "(%s) Parse Error on '%s'", __func__, sData.c_str());
|
|
+ pPlugin->MessagePlugin(new onMessageCallback(pConnection, sMessage));
|
|
}
|
|
else
|
|
{
|
|
PyObject* pMessage = JSONtoPython(&root);
|
|
- Message->m_pConnection->pPlugin->MessagePlugin(new onMessageCallback(Message->m_pConnection, pMessage));
|
|
+ pPlugin->MessagePlugin(new onMessageCallback(pConnection, pMessage));
|
|
}
|
|
}
|
|
}
|
|
@@ -467,7 +484,7 @@ namespace Plugins {
|
|
{
|
|
PyObject* pListObj = pPrevObj;
|
|
// First duplicate? Create a list and add previous value
|
|
- if (!PyList_Check(pListObj))
|
|
+ if (!pPrevObj.IsList())
|
|
{
|
|
pListObj = PyList_New(1);
|
|
if (!pListObj)
|
|
@@ -732,7 +749,7 @@ namespace Plugins {
|
|
std::string sHttp;
|
|
|
|
// Sanity check input
|
|
- if (!WriteMessage->m_Object || !PyDict_Check(WriteMessage->m_Object))
|
|
+ if (PyBorrowedRef(WriteMessage->m_Object).Type() != "dict")
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) HTTP Send parameter was not a dictionary, ignored. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
@@ -763,7 +780,7 @@ namespace Plugins {
|
|
//
|
|
// param1=value¶m2=other+value
|
|
|
|
- if (!PyUnicode_Check(pVerb))
|
|
+ if (!pVerb.IsString())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) HTTP 'Verb' dictionary entry not a string, ignored. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
@@ -774,7 +791,7 @@ namespace Plugins {
|
|
|
|
PyBorrowedRef pURL = PyDict_GetItemString(WriteMessage->m_Object, "URL");
|
|
std::string sHttpURL = "/";
|
|
- if (pURL && PyUnicode_Check(pURL))
|
|
+ if (pURL.IsString())
|
|
{
|
|
sHttpURL = PyUnicode_AsUTF8(pURL);
|
|
}
|
|
@@ -840,7 +857,7 @@ namespace Plugins {
|
|
// </body>
|
|
// </html>
|
|
|
|
- if (!PyUnicode_Check(pStatus))
|
|
+ if (!pStatus.IsString())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) HTTP 'Status' dictionary entry was not a string, ignored. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
@@ -886,53 +903,53 @@ namespace Plugins {
|
|
// Did we get headers to send?
|
|
if (pHeaders)
|
|
{
|
|
- if (PyDict_Check(pHeaders))
|
|
+ if (pHeaders.IsDict())
|
|
{
|
|
PyObject* key, * value;
|
|
Py_ssize_t pos = 0;
|
|
while (PyDict_Next(pHeaders, &pos, &key, &value))
|
|
{
|
|
std::string sKey = PyUnicode_AsUTF8(key);
|
|
- if (PyUnicode_Check(value))
|
|
+ PyBorrowedRef pValue(value);
|
|
+ if (pValue.IsString())
|
|
{
|
|
std::string sValue = PyUnicode_AsUTF8(value);
|
|
sHttp += sKey + ": " + sValue + "\r\n";
|
|
}
|
|
- else if (PyBytes_Check(value))
|
|
+ else if (pValue.IsBytes())
|
|
{
|
|
const char* pBytes = PyBytes_AsString(value);
|
|
sHttp += sKey + ": " + pBytes + "\r\n";
|
|
}
|
|
- else if (value->ob_type->tp_name == std::string("bytearray"))
|
|
+ else if (pValue.IsByteArray())
|
|
{
|
|
const char* pByteArray = PyByteArray_AsString(value);
|
|
sHttp += sKey + ": " + pByteArray + "\r\n";
|
|
}
|
|
- else if (PyList_Check(value))
|
|
+ else if (pValue.IsList())
|
|
{
|
|
- PyObject* iterator = PyObject_GetIter(value);
|
|
- PyObject* item;
|
|
- while ((item = PyIter_Next(iterator)))
|
|
+ PyNewRef iterator = PyObject_GetIter(value);
|
|
+ PyObject* item;
|
|
+ while (item = PyIter_Next(iterator))
|
|
{
|
|
- if (PyUnicode_Check(item))
|
|
+ PyBorrowedRef pItem(item);
|
|
+ if (pItem.IsString())
|
|
{
|
|
std::string sValue = PyUnicode_AsUTF8(item);
|
|
sHttp += sKey + ": " + sValue + "\r\n";
|
|
}
|
|
- else if (PyBytes_Check(item))
|
|
+ else if (pItem.IsBytes())
|
|
{
|
|
const char* pBytes = PyBytes_AsString(item);
|
|
sHttp += sKey + ": " + pBytes + "\r\n";
|
|
}
|
|
- else if (item->ob_type->tp_name == std::string("bytearray"))
|
|
+ else if (pItem.IsByteArray())
|
|
{
|
|
const char* pByteArray = PyByteArray_AsString(item);
|
|
sHttp += sKey + ": " + pByteArray + "\r\n";
|
|
}
|
|
Py_DECREF(item);
|
|
}
|
|
-
|
|
- Py_DECREF(iterator);
|
|
}
|
|
}
|
|
}
|
|
@@ -949,11 +966,11 @@ namespace Plugins {
|
|
if (!pLength && pData && !pChunk)
|
|
{
|
|
Py_ssize_t iLength = 0;
|
|
- if (PyUnicode_Check(pData))
|
|
+ if (pData.IsString())
|
|
iLength = PyUnicode_GetLength(pData);
|
|
- else if (pData->ob_type->tp_name == std::string("bytearray"))
|
|
+ else if (pData.IsByteArray())
|
|
iLength = PyByteArray_Size(pData);
|
|
- else if (PyBytes_Check(pData))
|
|
+ else if (pData.IsBytes())
|
|
iLength = PyBytes_Size(pData);
|
|
sHttp += "Content-Length: " + std::to_string(iLength) + "\r\n";
|
|
}
|
|
@@ -977,15 +994,12 @@ namespace Plugins {
|
|
if (pChunk)
|
|
{
|
|
long lChunkLength = 0;
|
|
- if (pData)
|
|
- {
|
|
- if (PyUnicode_Check(pData))
|
|
- lChunkLength = PyUnicode_GetLength(pData);
|
|
- else if (pData->ob_type->tp_name == std::string("bytearray"))
|
|
- lChunkLength = PyByteArray_Size(pData);
|
|
- else if (PyBytes_Check(pData))
|
|
- lChunkLength = PyBytes_Size(pData);
|
|
- }
|
|
+ if (pData.IsString())
|
|
+ lChunkLength = PyUnicode_GetLength(pData);
|
|
+ else if (pData.IsByteArray())
|
|
+ lChunkLength = PyByteArray_Size(pData);
|
|
+ else if (pData.IsBytes())
|
|
+ lChunkLength = PyBytes_Size(pData);
|
|
std::stringstream stream;
|
|
stream << std::hex << lChunkLength;
|
|
sHttp += std::string(stream.str());
|
|
@@ -993,13 +1007,13 @@ namespace Plugins {
|
|
}
|
|
|
|
// Append data if supplied (for POST) or Response
|
|
- if (pData && PyUnicode_Check(pData))
|
|
+ if (pData.IsString())
|
|
{
|
|
sHttp += PyUnicode_AsUTF8(pData);
|
|
retVal.reserve(sHttp.length() + 2);
|
|
retVal.assign(sHttp.c_str(), sHttp.c_str() + sHttp.length());
|
|
}
|
|
- else if (pData && (pData->ob_type->tp_name == std::string("bytearray")))
|
|
+ else if (pData.IsByteArray())
|
|
{
|
|
retVal.reserve(sHttp.length() + PyByteArray_Size(pData) + 2);
|
|
retVal.assign(sHttp.c_str(), sHttp.c_str() + sHttp.length());
|
|
@@ -1010,7 +1024,7 @@ namespace Plugins {
|
|
retVal.push_back(pByteArray[i]);
|
|
}
|
|
}
|
|
- else if (pData && PyBytes_Check(pData))
|
|
+ else if (pData.IsBytes())
|
|
{
|
|
retVal.reserve(sHttp.length() + PyBytes_Size(pData) + 2);
|
|
retVal.assign(sHttp.c_str(), sHttp.c_str() + sHttp.length());
|
|
@@ -1700,7 +1714,7 @@ namespace Plugins {
|
|
std::vector<byte> retVal;
|
|
|
|
// Sanity check input
|
|
- if (!WriteMessage->m_Object || !PyDict_Check(WriteMessage->m_Object))
|
|
+ if (!PyBorrowedRef(WriteMessage->m_Object).IsDict())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) MQTT Send parameter was not a dictionary, ignored. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
@@ -1710,7 +1724,7 @@ namespace Plugins {
|
|
PyBorrowedRef pVerb = PyDict_GetItemString(WriteMessage->m_Object, "Verb");
|
|
if (pVerb)
|
|
{
|
|
- if (!PyUnicode_Check(pVerb))
|
|
+ if (!pVerb.IsString())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) MQTT 'Verb' dictionary entry not a string, ignored. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
@@ -1726,7 +1740,7 @@ namespace Plugins {
|
|
|
|
// Client Identifier
|
|
PyBorrowedRef pID = PyDict_GetItemString(WriteMessage->m_Object, "ID");
|
|
- if (pID && PyUnicode_Check(pID))
|
|
+ if (pID.IsString())
|
|
{
|
|
MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pID)), vPayload);
|
|
}
|
|
@@ -1735,7 +1749,7 @@ namespace Plugins {
|
|
|
|
byte bCleanSession = 1;
|
|
PyBorrowedRef pCleanSession = PyDict_GetItemString(WriteMessage->m_Object, "CleanSession");
|
|
- if (pCleanSession && PyLong_Check(pCleanSession))
|
|
+ if (pCleanSession.IsLong())
|
|
{
|
|
bCleanSession = (byte)PyLong_AsLong(pCleanSession);
|
|
}
|
|
@@ -1743,7 +1757,7 @@ namespace Plugins {
|
|
|
|
// Will topic
|
|
PyBorrowedRef pTopic = PyDict_GetItemString(WriteMessage->m_Object, "WillTopic");
|
|
- if (pTopic && PyUnicode_Check(pTopic))
|
|
+ if (pTopic.IsString())
|
|
{
|
|
MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vPayload);
|
|
bControlFlags |= 4;
|
|
@@ -1753,14 +1767,14 @@ namespace Plugins {
|
|
if (bControlFlags & 4)
|
|
{
|
|
PyBorrowedRef pQoS = PyDict_GetItemString(WriteMessage->m_Object, "WillQoS");
|
|
- if (pQoS && PyLong_Check(pQoS))
|
|
+ if (pQoS.IsLong())
|
|
{
|
|
byte bQoS = (byte)PyLong_AsLong(pQoS);
|
|
bControlFlags |= (bQoS & 3) << 3; // Set QoS flag
|
|
}
|
|
|
|
PyBorrowedRef pRetain = PyDict_GetItemString(WriteMessage->m_Object, "WillRetain");
|
|
- if (pRetain && PyLong_Check(pRetain))
|
|
+ if (pRetain.IsLong())
|
|
{
|
|
byte bRetain = (byte)PyLong_AsLong(pRetain);
|
|
bControlFlags |= (bRetain & 1) << 5; // Set retain flag
|
|
@@ -1770,11 +1784,11 @@ namespace Plugins {
|
|
PyBorrowedRef pPayload = PyDict_GetItemString(WriteMessage->m_Object, "WillPayload");
|
|
// Support both string and bytes
|
|
//if (pPayload && PyByteArray_Check(pPayload)) // Gives linker error, why?
|
|
- if (pPayload && pPayload->ob_type->tp_name == std::string("bytearray"))
|
|
+ if (pPayload.IsByteArray())
|
|
{
|
|
sPayload = std::string(PyByteArray_AsString(pPayload), PyByteArray_Size(pPayload));
|
|
}
|
|
- else if (pPayload && PyUnicode_Check(pPayload))
|
|
+ else if (pPayload.IsString())
|
|
{
|
|
sPayload = std::string(PyUnicode_AsUTF8(pPayload));
|
|
}
|
|
@@ -1786,7 +1800,7 @@ namespace Plugins {
|
|
std::string Pass;
|
|
PyObject* pModule = (PyObject*)WriteMessage->m_pConnection->pPlugin->PythonModule();
|
|
PyNewRef pDict = PyObject_GetAttrString(pModule, "Parameters");
|
|
- if (pDict)
|
|
+ if (pDict.IsDict())
|
|
{
|
|
PyBorrowedRef pUser = PyDict_GetItemString(pDict, "Username");
|
|
if (pUser) User = PyUnicode_AsUTF8(pUser);
|
|
@@ -1829,7 +1843,7 @@ namespace Plugins {
|
|
// Connect Reason Code
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "ReasonCode");
|
|
byteValue = 0;
|
|
- if (pDictEntry && PyLong_Check(pDictEntry))
|
|
+ if (pDictEntry.IsLong())
|
|
{
|
|
byteValue = PyLong_AsLong(pDictEntry) & 0xFF;
|
|
}
|
|
@@ -1838,35 +1852,35 @@ namespace Plugins {
|
|
// CONNACK Properties
|
|
std::vector<byte> vProperties;
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "SessionExpiryInterval");
|
|
- if (pDictEntry && PyLong_Check(pDictEntry))
|
|
+ if (pDictEntry.IsLong())
|
|
{
|
|
vProperties.push_back(17);
|
|
MQTTPushBackLong(PyLong_AsLong(pDictEntry), vProperties);
|
|
}
|
|
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "MaximumQoS");
|
|
- if (pDictEntry && PyLong_Check(pDictEntry))
|
|
+ if (pDictEntry.IsLong())
|
|
{
|
|
vProperties.push_back(36);
|
|
vProperties.push_back((byte)PyLong_AsLong(pDictEntry));
|
|
}
|
|
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "RetainAvailable");
|
|
- if (pDictEntry && PyLong_Check(pDictEntry))
|
|
+ if (pDictEntry.IsLong())
|
|
{
|
|
vProperties.push_back(37);
|
|
vProperties.push_back((byte)PyLong_AsLong(pDictEntry));
|
|
}
|
|
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "MaximumPacketSize");
|
|
- if (pDictEntry && PyLong_Check(pDictEntry))
|
|
+ if (pDictEntry.IsLong())
|
|
{
|
|
vProperties.push_back(39);
|
|
MQTTPushBackLong(PyLong_AsLong(pDictEntry), vProperties);
|
|
}
|
|
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "AssignedClientID");
|
|
- if (pDictEntry && (pDictEntry != Py_None))
|
|
+ if (pDictEntry && !pDictEntry.IsNone())
|
|
{
|
|
PyNewRef pStr = PyObject_Str(pDictEntry);
|
|
vProperties.push_back(18);
|
|
@@ -1874,7 +1888,7 @@ namespace Plugins {
|
|
}
|
|
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "ReasonString");
|
|
- if (pDictEntry && (pDictEntry != Py_None))
|
|
+ if (pDictEntry && !pDictEntry.IsNone())
|
|
{
|
|
PyNewRef pStr = PyObject_Str(pDictEntry);
|
|
vProperties.push_back(26);
|
|
@@ -1882,7 +1896,7 @@ namespace Plugins {
|
|
}
|
|
|
|
pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "ResponseInformation");
|
|
- if (pDictEntry && (pDictEntry != Py_None))
|
|
+ if (pDictEntry && !pDictEntry.IsNone())
|
|
{
|
|
PyNewRef pStr = PyObject_Str(pDictEntry);
|
|
vProperties.push_back(18);
|
|
@@ -1904,7 +1918,7 @@ namespace Plugins {
|
|
// If supplied then use it otherwise create one
|
|
PyBorrowedRef pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
|
|
long iPacketIdentifier = 0;
|
|
- if (pID && PyLong_Check(pID))
|
|
+ if (pID.IsLong())
|
|
{
|
|
iPacketIdentifier = PyLong_AsLong(pID);
|
|
}
|
|
@@ -1913,25 +1927,25 @@ namespace Plugins {
|
|
|
|
// Payload is list of topics and QoS numbers
|
|
PyBorrowedRef pTopicList = PyDict_GetItemString(WriteMessage->m_Object, "Topics");
|
|
- if (!pTopicList || !PyList_Check(pTopicList))
|
|
+ if (!pTopicList.IsList())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) MQTT Subscribe: No 'Topics' list present, nothing to subscribe to. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
}
|
|
for (Py_ssize_t i = 0; i < PyList_Size(pTopicList); i++)
|
|
{
|
|
- PyObject* pTopicDict = PyList_GetItem(pTopicList, i);
|
|
- if (!pTopicDict || !PyDict_Check(pTopicDict))
|
|
+ PyBorrowedRef pTopicDict = PyList_GetItem(pTopicList, i);
|
|
+ if (!pTopicDict.IsDict())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) MQTT Subscribe: Topics list entry is not a dictionary (Topic, QoS), nothing to subscribe to. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
}
|
|
PyBorrowedRef pTopic = PyDict_GetItemString(pTopicDict, "Topic");
|
|
- if (pTopic && PyUnicode_Check(pTopic))
|
|
+ if (pTopic.IsString())
|
|
{
|
|
MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vPayload);
|
|
PyBorrowedRef pQoS = PyDict_GetItemString(pTopicDict, "QoS");
|
|
- if (pQoS && PyLong_Check(pQoS))
|
|
+ if (pQoS.IsLong())
|
|
{
|
|
vPayload.push_back((byte)PyLong_AsLong(pQoS));
|
|
}
|
|
@@ -1949,7 +1963,7 @@ namespace Plugins {
|
|
// Variable Header
|
|
PyBorrowedRef pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
|
|
long iPacketIdentifier = 0;
|
|
- if (pID && PyLong_Check(pID))
|
|
+ if (pID.IsLong())
|
|
{
|
|
iPacketIdentifier = PyLong_AsLong(pID);
|
|
MQTTPushBackNumber((int)iPacketIdentifier, vVariableHeader);
|
|
@@ -1961,7 +1975,7 @@ namespace Plugins {
|
|
}
|
|
|
|
PyBorrowedRef pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "QoS");
|
|
- if (pDictEntry && PyLong_Check(pDictEntry))
|
|
+ if (pDictEntry.IsLong())
|
|
{
|
|
vPayload.push_back((byte)PyLong_AsLong(pDictEntry));
|
|
}
|
|
@@ -1978,7 +1992,7 @@ namespace Plugins {
|
|
// Variable Header
|
|
PyBorrowedRef pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
|
|
long iPacketIdentifier = 0;
|
|
- if (pID && PyLong_Check(pID))
|
|
+ if (pID.IsLong())
|
|
{
|
|
iPacketIdentifier = PyLong_AsLong(pID);
|
|
}
|
|
@@ -1987,15 +2001,15 @@ namespace Plugins {
|
|
|
|
// Payload is a Python list of topics
|
|
PyBorrowedRef pTopicList = PyDict_GetItemString(WriteMessage->m_Object, "Topics");
|
|
- if (!pTopicList || !PyList_Check(pTopicList))
|
|
+ if (!pTopicList.IsList())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) MQTT Subscribe: No 'Topics' list present, nothing to unsubscribe from. See Python Plugin wiki page for help.", __func__);
|
|
return retVal;
|
|
}
|
|
for (Py_ssize_t i = 0; i < PyList_Size(pTopicList); i++)
|
|
{
|
|
- PyObject* pTopic = PyList_GetItem(pTopicList, i);
|
|
- if (pTopic && PyUnicode_Check(pTopic))
|
|
+ PyBorrowedRef pTopic = PyList_GetItem(pTopicList, i);
|
|
+ if (pTopic.IsString())
|
|
{
|
|
MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vPayload);
|
|
}
|
|
@@ -2009,7 +2023,7 @@ namespace Plugins {
|
|
|
|
// Fixed Header
|
|
PyBorrowedRef pDUP = PyDict_GetItemString(WriteMessage->m_Object, "Duplicate");
|
|
- if (pDUP && PyLong_Check(pDUP))
|
|
+ if (pDUP.IsLong())
|
|
{
|
|
long bDUP = PyLong_AsLong(pDUP);
|
|
if (bDUP) bByte0 |= 0x08; // Set duplicate flag
|
|
@@ -2017,14 +2031,14 @@ namespace Plugins {
|
|
|
|
PyBorrowedRef pQoS = PyDict_GetItemString(WriteMessage->m_Object, "QoS");
|
|
long iQoS = 0;
|
|
- if (pQoS && PyLong_Check(pQoS))
|
|
+ if (pQoS.IsLong())
|
|
{
|
|
iQoS = PyLong_AsLong(pQoS);
|
|
bByte0 |= ((iQoS & 3) << 1); // Set QoS flag
|
|
}
|
|
|
|
PyBorrowedRef pRetain = PyDict_GetItemString(WriteMessage->m_Object, "Retain");
|
|
- if (pRetain && PyLong_Check(pRetain))
|
|
+ if (pRetain.IsLong())
|
|
{
|
|
long bRetain = PyLong_AsLong(pRetain);
|
|
bByte0 |= (bRetain & 1); // Set retain flag
|
|
@@ -2032,7 +2046,7 @@ namespace Plugins {
|
|
|
|
// Variable Header
|
|
PyBorrowedRef pTopic = PyDict_GetItemString(WriteMessage->m_Object, "Topic");
|
|
- if (pTopic && PyUnicode_Check(pTopic))
|
|
+ if (pTopic && pTopic.IsString())
|
|
{
|
|
MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vVariableHeader);
|
|
}
|
|
@@ -2046,7 +2060,7 @@ namespace Plugins {
|
|
if (iQoS)
|
|
{
|
|
long iPacketIdentifier = 0;
|
|
- if (pID && PyLong_Check(pID))
|
|
+ if (pID.IsLong())
|
|
{
|
|
iPacketIdentifier = PyLong_AsLong(pID);
|
|
}
|
|
@@ -2062,20 +2076,22 @@ namespace Plugins {
|
|
PyBorrowedRef pPayload = PyDict_GetItemString(WriteMessage->m_Object, "Payload");
|
|
// Support both string and bytes
|
|
//if (pPayload && PyByteArray_Check(pPayload)) // Gives linker error, why?
|
|
- if (pPayload) {
|
|
- _log.Debug(DEBUG_NORM, "(%s) MQTT Publish: payload %p (%s)", __func__, (PyObject*)pPayload, pPayload->ob_type->tp_name);
|
|
+ if (pPayload)
|
|
+ {
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)pPayload->ob_type, "__name__");
|
|
+ _log.Debug(DEBUG_NORM, "(%s) MQTT Publish: payload %p (%s)", __func__, (PyObject*)pPayload, ((std::string)pName).c_str());
|
|
}
|
|
- if (pPayload && pPayload->ob_type->tp_name == std::string("bytearray"))
|
|
+ if (pPayload.IsByteArray())
|
|
{
|
|
std::string sPayload = std::string(PyByteArray_AsString(pPayload), PyByteArray_Size(pPayload));
|
|
MQTTPushBackString(sPayload, vPayload);
|
|
}
|
|
- else if (pPayload && PyUnicode_Check(pPayload))
|
|
+ else if (pPayload.IsString())
|
|
{
|
|
std::string sPayload = std::string(PyUnicode_AsUTF8(pPayload));
|
|
MQTTPushBackString(sPayload, vPayload);
|
|
}
|
|
- else if (pPayload && PyLong_Check(pPayload))
|
|
+ else if (pPayload.IsLong())
|
|
{
|
|
MQTTPushBackLong(PyLong_AsLong(pPayload), vPayload);
|
|
}
|
|
@@ -2086,7 +2102,7 @@ namespace Plugins {
|
|
// Variable Header
|
|
PyBorrowedRef pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
|
|
long iPacketIdentifier = 0;
|
|
- if (pID && PyLong_Check(pID))
|
|
+ if (pID.IsLong())
|
|
{
|
|
iPacketIdentifier = PyLong_AsLong(pID);
|
|
MQTTPushBackNumber((int)iPacketIdentifier, vVariableHeader);
|
|
@@ -2104,7 +2120,7 @@ namespace Plugins {
|
|
// Variable Header
|
|
PyBorrowedRef pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
|
|
long iPacketIdentifier = 0;
|
|
- if (pID && PyLong_Check(pID))
|
|
+ if (pID.IsLong())
|
|
{
|
|
iPacketIdentifier = PyLong_AsLong(pID);
|
|
MQTTPushBackNumber((int)iPacketIdentifier, vVariableHeader);
|
|
@@ -2117,7 +2133,7 @@ namespace Plugins {
|
|
|
|
// Connect Reason Code
|
|
PyBorrowedRef pDictEntry = PyDict_GetItemString(WriteMessage->m_Object, "ReasonCode");
|
|
- if (pDictEntry && PyLong_Check(pDictEntry))
|
|
+ if (pDictEntry.IsLong())
|
|
{
|
|
vVariableHeader.push_back((byte)PyLong_AsLong(pDictEntry));
|
|
}
|
|
@@ -2381,7 +2397,7 @@ namespace Plugins {
|
|
// Parameters need to be in a dictionary.
|
|
// if a 'URL' key is found message is assumed to be HTTP otherwise WebSocket is assumed
|
|
//
|
|
- if (!WriteMessage->m_Object || !PyDict_Check(WriteMessage->m_Object))
|
|
+ if (!PyBorrowedRef(WriteMessage->m_Object).IsDict())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) Dictionary parameter expected.", __func__);
|
|
}
|
|
@@ -2444,7 +2460,7 @@ namespace Plugins {
|
|
|
|
if (pOperation)
|
|
{
|
|
- if (!PyUnicode_Check(pOperation))
|
|
+ if (!pOperation.IsString())
|
|
{
|
|
_log.Log(LOG_ERROR, "(%s) Expected dictionary 'Operation' key to have a string value.", __func__);
|
|
return retVal;
|
|
@@ -2466,36 +2482,33 @@ namespace Plugins {
|
|
}
|
|
|
|
// If there is no specific OpCode then set it from the payload datatype
|
|
- if (pPayload)
|
|
+ if (pPayload.IsString())
|
|
{
|
|
- if (PyUnicode_Check(pPayload))
|
|
- {
|
|
- lPayloadLength = PyUnicode_GetLength(pPayload);
|
|
- if (!iOpCode)
|
|
- iOpCode = 0x01; // Text message
|
|
- }
|
|
- else if (PyBytes_Check(pPayload))
|
|
- {
|
|
- lPayloadLength = PyBytes_Size(pPayload);
|
|
- if (!iOpCode)
|
|
- iOpCode = 0x02; // Binary message
|
|
- }
|
|
- else if (pPayload->ob_type->tp_name == std::string("bytearray"))
|
|
- {
|
|
- lPayloadLength = PyByteArray_Size(pPayload);
|
|
- if (!iOpCode)
|
|
- iOpCode = 0x02; // Binary message
|
|
- }
|
|
+ lPayloadLength = PyUnicode_GetLength(pPayload);
|
|
+ if (!iOpCode)
|
|
+ iOpCode = 0x01; // Text message
|
|
+ }
|
|
+ else if (pPayload.IsBytes())
|
|
+ {
|
|
+ lPayloadLength = PyBytes_Size(pPayload);
|
|
+ if (!iOpCode)
|
|
+ iOpCode = 0x02; // Binary message
|
|
+ }
|
|
+ else if (pPayload.IsByteArray())
|
|
+ {
|
|
+ lPayloadLength = PyByteArray_Size(pPayload);
|
|
+ if (!iOpCode)
|
|
+ iOpCode = 0x02; // Binary message
|
|
}
|
|
|
|
if (pMask)
|
|
{
|
|
- if (PyLong_Check(pMask))
|
|
+ if (pMask.IsLong())
|
|
{
|
|
lMaskingKey = PyLong_AsLong(pMask);
|
|
bMaskBit = 0x80; // Set mask bit in header
|
|
}
|
|
- else if (PyUnicode_Check(pMask))
|
|
+ else if (pMask.IsString())
|
|
{
|
|
std::string sMask = PyUnicode_AsUTF8(pMask);
|
|
lMaskingKey = atoi(sMask.c_str());
|
|
@@ -2503,7 +2516,7 @@ namespace Plugins {
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Invalid mask, expected number (integer or string).", __func__);
|
|
+ _log.Log(LOG_ERROR, "(%s) Invalid mask, expected number (integer or string) but got '%s'.", __func__, pMask.Type().c_str());
|
|
return retVal;
|
|
}
|
|
}
|
|
@@ -2534,31 +2547,28 @@ namespace Plugins {
|
|
retVal.push_back(lMaskingKey & 0xFF); // Encode mask
|
|
}
|
|
|
|
- if (pPayload)
|
|
+ if (pPayload.IsString())
|
|
{
|
|
- if (PyUnicode_Check(pPayload))
|
|
+ std::string sPayload = PyUnicode_AsUTF8(pPayload);
|
|
+ for (int i = 0; i < lPayloadLength; i++)
|
|
{
|
|
- std::string sPayload = PyUnicode_AsUTF8(pPayload);
|
|
- for (int i = 0; i < lPayloadLength; i++)
|
|
- {
|
|
- retVal.push_back(sPayload[i] ^ pbMask[i % 4]);
|
|
- }
|
|
+ retVal.push_back(sPayload[i] ^ pbMask[i % 4]);
|
|
}
|
|
- else if (PyBytes_Check(pPayload))
|
|
+ }
|
|
+ else if (pPayload.IsBytes())
|
|
+ {
|
|
+ byte *pByte = (byte *)PyBytes_AsString(pPayload);
|
|
+ for (int i = 0; i < lPayloadLength; i++)
|
|
{
|
|
- byte *pByte = (byte *)PyBytes_AsString(pPayload);
|
|
- for (int i = 0; i < lPayloadLength; i++)
|
|
- {
|
|
- retVal.push_back(pByte[i] ^ pbMask[i % 4]);
|
|
- }
|
|
+ retVal.push_back(pByte[i] ^ pbMask[i % 4]);
|
|
}
|
|
- else if (pPayload->ob_type->tp_name == std::string("bytearray"))
|
|
+ }
|
|
+ else if (pPayload.IsByteArray())
|
|
+ {
|
|
+ byte *pByte = (byte *)PyByteArray_AsString(pPayload);
|
|
+ for (int i = 0; i < lPayloadLength; i++)
|
|
{
|
|
- byte *pByte = (byte *)PyByteArray_AsString(pPayload);
|
|
- for (int i = 0; i < lPayloadLength; i++)
|
|
- {
|
|
- retVal.push_back(pByte[i] ^ pbMask[i % 4]);
|
|
- }
|
|
+ retVal.push_back(pByte[i] ^ pbMask[i % 4]);
|
|
}
|
|
}
|
|
}
|
|
--- a/hardware/plugins/PluginTransports.cpp
|
|
+++ b/hardware/plugins/PluginTransports.cpp
|
|
@@ -15,6 +15,8 @@
|
|
|
|
namespace Plugins {
|
|
|
|
+ extern PyTypeObject* CConnectionType;
|
|
+
|
|
void CPluginTransport::configureTimeout()
|
|
{
|
|
if (m_pConnection->Timeout)
|
|
@@ -198,8 +200,6 @@ namespace Plugins {
|
|
{
|
|
try
|
|
{
|
|
- PyType_Ready(&CConnectionType);
|
|
-
|
|
if (!m_Socket)
|
|
{
|
|
if (!m_Acceptor)
|
|
@@ -239,8 +239,21 @@ namespace Plugins {
|
|
std::string sAddress = remote_ep.address().to_string();
|
|
std::string sPort = std::to_string(remote_ep.port());
|
|
|
|
- CConnection *pConnection
|
|
- = (CConnection *)CConnection_new(&CConnectionType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
+ PyNewRef nrArgList = Py_BuildValue("(sssss)",
|
|
+ std::string(sAddress+":"+sPort).c_str(),
|
|
+ PyUnicode_AsUTF8(((CConnection*)m_pConnection)->Transport),
|
|
+ PyUnicode_AsUTF8(((CConnection*)m_pConnection)->Protocol),
|
|
+ sAddress.c_str(),
|
|
+ sPort.c_str());
|
|
+ if (!nrArgList)
|
|
+ {
|
|
+ pPlugin->Log(LOG_ERROR, "Building connection argument list failed for TCP %s:%s.", sAddress.c_str(), sPort.c_str());
|
|
+ }
|
|
+ CConnection* pConnection = (CConnection*)PyObject_CallObject((PyObject*)CConnectionType, nrArgList);
|
|
+ if (!pConnection)
|
|
+ {
|
|
+ pPlugin->Log(LOG_ERROR, "Connection object creation failed for TCP %s:%s.", sAddress.c_str(), sPort.c_str());
|
|
+ }
|
|
CPluginTransportTCP* pTcpTransport = new CPluginTransportTCP(m_HwdID, pConnection, sAddress, sPort);
|
|
Py_DECREF(pConnection);
|
|
|
|
@@ -252,20 +265,10 @@ namespace Plugins {
|
|
|
|
// Configure Python Connection object
|
|
pConnection->pTransport = pTcpTransport;
|
|
- Py_XDECREF(pConnection->Name);
|
|
- pConnection->Name = PyUnicode_FromString(std::string(sAddress+":"+sPort).c_str());
|
|
- Py_XDECREF(pConnection->Address);
|
|
- pConnection->Address = PyUnicode_FromString(sAddress.c_str());
|
|
- Py_XDECREF(pConnection->Port);
|
|
- pConnection->Port = PyUnicode_FromString(sPort.c_str());
|
|
|
|
Py_XDECREF(pConnection->Parent);
|
|
pConnection->Parent = (PyObject*)m_pConnection;
|
|
Py_INCREF(m_pConnection);
|
|
- pConnection->Transport = ((CConnection*)m_pConnection)->Transport;
|
|
- Py_INCREF(pConnection->Transport);
|
|
- pConnection->Protocol = ((CConnection*)m_pConnection)->Protocol;
|
|
- Py_INCREF(pConnection->Protocol);
|
|
pConnection->Target = ((CConnection *)m_pConnection)->Target;
|
|
if (pConnection->Target)
|
|
Py_INCREF(pConnection->Target);
|
|
@@ -626,8 +629,6 @@ namespace Plugins {
|
|
{
|
|
try
|
|
{
|
|
- PyType_Ready(&CConnectionType);
|
|
-
|
|
if (!m_Socket)
|
|
{
|
|
boost::system::error_code ec;
|
|
@@ -680,21 +681,22 @@ namespace Plugins {
|
|
std::string sAddress = m_remote_endpoint.address().to_string();
|
|
std::string sPort = std::to_string(m_remote_endpoint.port());
|
|
|
|
- CConnection *pConnection
|
|
- = (CConnection *)CConnection_new(&CConnectionType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
+ PyNewRef nrArgList = Py_BuildValue("(sssss)",
|
|
+ PyUnicode_AsUTF8(((CConnection*)m_pConnection)->Name),
|
|
+ PyUnicode_AsUTF8(((CConnection*)m_pConnection)->Transport),
|
|
+ PyUnicode_AsUTF8(((CConnection*)m_pConnection)->Protocol),
|
|
+ sAddress.c_str(),
|
|
+ sPort.c_str());
|
|
+ if (!nrArgList)
|
|
+ {
|
|
+ pPlugin->Log(LOG_ERROR, "Building connection argument list failed for UDP %s:%s.", sAddress.c_str(), sPort.c_str());
|
|
+ }
|
|
+ CConnection* pConnection = (CConnection*)PyObject_CallObject((PyObject*)CConnectionType, nrArgList);
|
|
+ if (!pConnection)
|
|
+ {
|
|
+ pPlugin->Log(LOG_ERROR, "Connection object creation failed for UDP %s:%s.", sAddress.c_str(), sPort.c_str());
|
|
+ }
|
|
|
|
- // Configure temporary Python Connection object
|
|
- Py_XDECREF(pConnection->Name);
|
|
- pConnection->Name = ((CConnection*)m_pConnection)->Name;
|
|
- Py_INCREF(pConnection->Name);
|
|
- Py_XDECREF(pConnection->Address);
|
|
- pConnection->Address = PyUnicode_FromString(sAddress.c_str());
|
|
- Py_XDECREF(pConnection->Port);
|
|
- pConnection->Port = PyUnicode_FromString(sPort.c_str());
|
|
- pConnection->Transport = ((CConnection*)m_pConnection)->Transport;
|
|
- Py_INCREF(pConnection->Transport);
|
|
- pConnection->Protocol = ((CConnection*)m_pConnection)->Protocol;
|
|
- Py_INCREF(pConnection->Protocol);
|
|
pConnection->Target = ((CConnection *)m_pConnection)->Target;
|
|
if (pConnection->Target)
|
|
Py_INCREF(pConnection->Target);
|
|
--- a/hardware/plugins/Plugins.cpp
|
|
+++ b/hardware/plugins/Plugins.cpp
|
|
@@ -5,6 +5,8 @@
|
|
//
|
|
#ifdef ENABLE_PYTHON
|
|
|
|
+#include "../../main/Helper.h"
|
|
+
|
|
#include "Plugins.h"
|
|
#include "PluginMessages.h"
|
|
#include "PluginProtocols.h"
|
|
@@ -41,44 +43,22 @@ extern MainWorker m_mainworker;
|
|
|
|
namespace Plugins
|
|
{
|
|
- std::mutex AccessPython::PythonMutex;
|
|
- volatile bool AccessPython::m_bHasThreadState = false;
|
|
+ extern PyTypeObject* CDeviceType;
|
|
+ extern PyTypeObject* CConnectionType;
|
|
+ extern PyTypeObject* CImageType;
|
|
|
|
- AccessPython::AccessPython(CPlugin* pPlugin, const char* sWhat) : m_Python(NULL)
|
|
+ AccessPython::AccessPython(CPlugin* pPlugin, const char* sWhat)
|
|
{
|
|
m_pPlugin = pPlugin;
|
|
m_Text = sWhat;
|
|
|
|
- m_Lock = new std::unique_lock<std::mutex>(PythonMutex, std::defer_lock);
|
|
- if (!m_Lock->try_lock())
|
|
- {
|
|
- if (m_pPlugin)
|
|
- {
|
|
- if (m_pPlugin->m_bDebug & PDM_LOCKING)
|
|
- {
|
|
- _log.Log(LOG_NORM, "(%s) Requesting lock for '%s', waiting...", m_pPlugin->m_Name.c_str(), m_Text);
|
|
- }
|
|
- }
|
|
- else _log.Log(LOG_NORM, "Python lock requested for '%s' in use, will wait.", m_Text);
|
|
- m_Lock->lock();
|
|
- }
|
|
-
|
|
- if (pPlugin)
|
|
+ if (m_pPlugin)
|
|
{
|
|
- if (pPlugin->m_bDebug & PDM_LOCKING)
|
|
- {
|
|
- _log.Log(LOG_NORM, "(%s) Acquiring lock for '%s'", pPlugin->m_Name.c_str(), m_Text);
|
|
- }
|
|
- m_Python = pPlugin->PythonInterpreter();
|
|
- if (m_Python)
|
|
+ if (m_pPlugin->m_bDebug & PDM_LOCKING)
|
|
{
|
|
- PyEval_RestoreThread(m_Python);
|
|
- m_bHasThreadState = true;
|
|
- }
|
|
- else
|
|
- {
|
|
- _log.Log(LOG_ERROR, "Attempt to aquire the GIL with NULL Interpreter details.");
|
|
+ m_pPlugin->Log(LOG_NORM, "Acquiring GIL for '%s'", m_Text.c_str());
|
|
}
|
|
+ m_pPlugin->RestoreThread();
|
|
}
|
|
else
|
|
{
|
|
@@ -88,215 +68,39 @@ namespace Plugins
|
|
|
|
AccessPython::~AccessPython()
|
|
{
|
|
- if (m_Python && m_pPlugin)
|
|
+ if (m_pPlugin)
|
|
{
|
|
if (PyErr_Occurred())
|
|
{
|
|
- _log.Log(LOG_NORM, "(%s) Python error was set during unlock for '%s'", m_pPlugin->m_Name.c_str(), m_Text);
|
|
+ m_pPlugin->Log(LOG_NORM, "Python error was set during unlock for '%s'", m_Text.c_str());
|
|
m_pPlugin->LogPythonException();
|
|
PyErr_Clear();
|
|
}
|
|
-
|
|
- m_bHasThreadState = false;
|
|
- if (m_pPlugin->PythonInterpreter() && !PyEval_SaveThread())
|
|
- {
|
|
- _log.Log(LOG_ERROR, "(%s) Python Save state returned NULL value for '%s'", m_pPlugin->m_Name.c_str(), m_Text);
|
|
- }
|
|
- }
|
|
- if (m_Lock)
|
|
- {
|
|
- if (m_pPlugin && m_pPlugin->m_bDebug & PDM_LOCKING)
|
|
- {
|
|
- _log.Log(LOG_NORM, "(%s) Releasing lock for '%s'", m_pPlugin->m_Name.c_str(), m_Text);
|
|
- }
|
|
- delete m_Lock;
|
|
- }
|
|
- }
|
|
-
|
|
- void LogPythonException(CPlugin *pPlugin, const std::string &sHandler)
|
|
- {
|
|
- PyTracebackObject *pTraceback;
|
|
- PyNewRef pExcept;
|
|
- PyNewRef pValue;
|
|
- PyTypeObject *TypeName;
|
|
- PyBytesObject *pErrBytes = nullptr;
|
|
- const char *pTypeText = nullptr;
|
|
- std::string Name = "Unknown";
|
|
-
|
|
- if (pPlugin)
|
|
- Name = pPlugin->m_Name;
|
|
-
|
|
- PyErr_Fetch(&pExcept, &pValue, (PyObject **)&pTraceback);
|
|
-
|
|
- if (pExcept)
|
|
- {
|
|
- TypeName = (PyTypeObject *)pExcept;
|
|
- pTypeText = TypeName->tp_name;
|
|
- }
|
|
- if (pValue)
|
|
- {
|
|
- pErrBytes = (PyBytesObject *)PyUnicode_AsASCIIString(pValue);
|
|
- }
|
|
- if (pTypeText && pErrBytes)
|
|
- {
|
|
- if (pPlugin)
|
|
- pPlugin->Log(LOG_ERROR, "'%s' failed '%s':'%s'.", sHandler.c_str(), pTypeText, pErrBytes->ob_sval);
|
|
- else
|
|
- _log.Log(LOG_ERROR, "'%s' failed '%s':'%s'.", sHandler.c_str(), pTypeText, pErrBytes->ob_sval);
|
|
- }
|
|
- if (pTypeText && !pErrBytes)
|
|
- {
|
|
- if (pPlugin)
|
|
- pPlugin->Log(LOG_ERROR, "'%s' failed '%s'.", sHandler.c_str(), pTypeText);
|
|
- else
|
|
- _log.Log(LOG_ERROR, "'%s' failed '%s'.", sHandler.c_str(), pTypeText);
|
|
- }
|
|
- if (!pTypeText && pErrBytes)
|
|
- {
|
|
- if (pPlugin)
|
|
- pPlugin->Log(LOG_ERROR, "'%s' failed '%s'.", sHandler.c_str(), pErrBytes->ob_sval);
|
|
- else
|
|
- _log.Log(LOG_ERROR, "'%s' failed '%s'.", sHandler.c_str(), pErrBytes->ob_sval);
|
|
- }
|
|
- if (!pTypeText && !pErrBytes)
|
|
- {
|
|
- if (pPlugin)
|
|
- pPlugin->Log(LOG_ERROR, "'%s' failed, unable to determine error.", sHandler.c_str());
|
|
- else
|
|
- _log.Log(LOG_ERROR, "'%s' failed, unable to determine error.", sHandler.c_str());
|
|
- }
|
|
- if (pErrBytes)
|
|
- Py_XDECREF(pErrBytes);
|
|
-
|
|
- // Log a stack trace if there is one
|
|
- if (pPlugin && pTraceback)
|
|
- pPlugin->LogTraceback(pTraceback);
|
|
-
|
|
- if (!pExcept && !pValue && !pTraceback)
|
|
- {
|
|
- if (pPlugin)
|
|
- pPlugin->Log(LOG_ERROR, "Call to message handler '%s' failed, unable to decode exception.", sHandler.c_str());
|
|
- else
|
|
- _log.Log(LOG_ERROR, "Call to message handler '%s' failed, unable to decode exception.", sHandler.c_str());
|
|
- }
|
|
-
|
|
- if (pTraceback)
|
|
- Py_XDECREF(pTraceback);
|
|
- }
|
|
-
|
|
- int PyDomoticz_ProfileFunc(PyObject *self, PyFrameObject *frame, int what, PyObject *arg)
|
|
- {
|
|
- module_state *pModState = CPlugin::FindModule();
|
|
- if (!pModState)
|
|
- {
|
|
- return 0;
|
|
- }
|
|
- 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)
|
|
- {
|
|
- sFuncName = (std::string)PyBorrowedRef(pCode->co_filename);
|
|
- }
|
|
- if (pCode && pCode->co_name)
|
|
- {
|
|
- if (!sFuncName.empty())
|
|
- sFuncName += "\\";
|
|
- sFuncName += (std::string)PyBorrowedRef(pCode->co_name);
|
|
- }
|
|
-
|
|
- switch (what)
|
|
- {
|
|
- case PyTrace_CALL:
|
|
- pModState->pPlugin->Log(LOG_NORM, "Calling function at line %d in '%s'", lineno, sFuncName.c_str());
|
|
- break;
|
|
- case PyTrace_RETURN:
|
|
- pModState->pPlugin->Log(LOG_NORM, "Returning from line %d in '%s'", lineno, sFuncName.c_str());
|
|
- break;
|
|
- case PyTrace_EXCEPTION:
|
|
- pModState->pPlugin->Log(LOG_NORM, "Exception at line %d in '%s'", lineno, sFuncName.c_str());
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- return 0;
|
|
- }
|
|
-
|
|
- int PyDomoticz_TraceFunc(PyObject *self, PyFrameObject *frame, int what, PyObject *arg)
|
|
- {
|
|
- module_state *pModState = CPlugin::FindModule();
|
|
- if (!pModState)
|
|
- {
|
|
- return 0;
|
|
- }
|
|
- 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)
|
|
- {
|
|
- sFuncName = (std::string)PyBorrowedRef(pCode->co_filename);
|
|
- }
|
|
- if (pCode && pCode->co_name)
|
|
- {
|
|
- if (!sFuncName.empty())
|
|
- sFuncName += "\\";
|
|
- sFuncName += (std::string)PyBorrowedRef(pCode->co_name);
|
|
- }
|
|
-
|
|
- switch (what)
|
|
- {
|
|
- case PyTrace_CALL:
|
|
- pModState->pPlugin->Log(LOG_NORM, "Calling function at line %d in '%s'", lineno, sFuncName.c_str());
|
|
- break;
|
|
- case PyTrace_LINE:
|
|
- pModState->pPlugin->Log(LOG_NORM, "Executing line %d in '%s'", lineno, sFuncName.c_str());
|
|
- break;
|
|
- case PyTrace_EXCEPTION:
|
|
- pModState->pPlugin->Log(LOG_NORM, "Exception at line %d in '%s'", lineno, sFuncName.c_str());
|
|
- break;
|
|
- }
|
|
+ m_pPlugin->ReleaseThread();
|
|
}
|
|
-
|
|
- return 0;
|
|
}
|
|
|
|
static PyObject *PyDomoticz_Debug(PyObject *self, PyObject *args)
|
|
{
|
|
- module_state *pModState = CPlugin::FindModule();
|
|
- if (!pModState)
|
|
+ CPlugin* pPlugin = CPlugin::FindPlugin();
|
|
+ if (!pPlugin)
|
|
{
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- else if (!pModState->pPlugin)
|
|
- {
|
|
- _log.Log(LOG_ERROR, "CPlugin:%s, illegal operation, Plugin has not started yet.", __func__);
|
|
+ _log.Log(LOG_ERROR, "%s, illegal operation, Plugin has not started yet.", __func__);
|
|
}
|
|
else
|
|
{
|
|
- if (pModState->pPlugin->m_bDebug & PDM_PYTHON)
|
|
+ if (pPlugin->m_bDebug & PDM_PYTHON)
|
|
{
|
|
char *msg;
|
|
if (!PyArg_ParseTuple(args, "s", &msg))
|
|
{
|
|
// TODO: Dump data to aid debugging
|
|
- pModState->pPlugin->Log(LOG_ERROR, "PyDomoticz_Debug failed to parse parameters: string expected.");
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pPlugin->Log(LOG_ERROR, "%s failed to parse parameters: string expected.", __func__);
|
|
+ pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
- pModState->pPlugin->Log(LOG_NORM, (std::string)msg);
|
|
+ pPlugin->Log(LOG_NORM, (std::string)msg);
|
|
}
|
|
}
|
|
}
|
|
@@ -306,12 +110,8 @@ namespace Plugins
|
|
|
|
static PyObject *PyDomoticz_Log(PyObject *self, PyObject *args)
|
|
{
|
|
- module_state *pModState = CPlugin::FindModule();
|
|
- if (!pModState)
|
|
- {
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- else if (!pModState->pPlugin)
|
|
+ CPlugin* pPlugin = CPlugin::FindPlugin();
|
|
+ if (!pPlugin)
|
|
{
|
|
_log.Log(LOG_ERROR, "CPlugin:%s, illegal operation, Plugin has not started yet.", __func__);
|
|
}
|
|
@@ -320,12 +120,12 @@ namespace Plugins
|
|
char *msg;
|
|
if (!PyArg_ParseTuple(args, "s", &msg))
|
|
{
|
|
- pModState->pPlugin->Log(LOG_ERROR, "PyDomoticz_Log failed to parse parameters: string expected.");
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pPlugin->Log(LOG_ERROR, "%s failed to parse parameters: string expected.", __func__);
|
|
+ pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
- pModState->pPlugin->Log(LOG_NORM, (std::string)msg);
|
|
+ pPlugin->Log(LOG_NORM, (std::string)msg);
|
|
}
|
|
}
|
|
|
|
@@ -334,26 +134,22 @@ namespace Plugins
|
|
|
|
static PyObject *PyDomoticz_Status(PyObject *self, PyObject *args)
|
|
{
|
|
- module_state *pModState = CPlugin::FindModule();
|
|
- if (!pModState)
|
|
+ CPlugin* pPlugin = CPlugin::FindPlugin();
|
|
+ if (!pPlugin)
|
|
{
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- else if (!pModState->pPlugin)
|
|
- {
|
|
- _log.Log(LOG_ERROR, "CPlugin:%s, illegal operation, Plugin has not started yet.", __func__);
|
|
+ _log.Log(LOG_ERROR, "%s, illegal operation, Plugin has not started yet.", __func__);
|
|
}
|
|
else
|
|
{
|
|
char *msg;
|
|
if (!PyArg_ParseTuple(args, "s", &msg))
|
|
{
|
|
- pModState->pPlugin->Log(LOG_ERROR, "%s failed to parse parameters: string expected.", std::string(__func__).c_str());
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pPlugin->Log(LOG_ERROR, "%s failed to parse parameters: string expected.", __func__);
|
|
+ pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
- pModState->pPlugin->Log(LOG_STATUS, (std::string)msg);
|
|
+ pPlugin->Log(LOG_STATUS, (std::string)msg);
|
|
}
|
|
}
|
|
|
|
@@ -362,14 +158,10 @@ namespace Plugins
|
|
|
|
static PyObject *PyDomoticz_Error(PyObject *self, PyObject *args)
|
|
{
|
|
- module_state *pModState = CPlugin::FindModule();
|
|
- if (!pModState)
|
|
- {
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- else if (!pModState->pPlugin)
|
|
+ CPlugin* pPlugin = CPlugin::FindPlugin();
|
|
+ if (!pPlugin)
|
|
{
|
|
- _log.Log(LOG_ERROR, "CPlugin:%s, illegal operation, Plugin has not started yet.", __func__);
|
|
+ _log.Log(LOG_ERROR, "%s, illegal operation, Plugin has not started yet.", __func__);
|
|
}
|
|
else
|
|
{
|
|
@@ -377,12 +169,12 @@ namespace Plugins
|
|
if ((PyTuple_Size(args) != 1) || !PyArg_ParseTuple(args, "s", &msg))
|
|
{
|
|
// TODO: Dump data to aid debugging
|
|
- pModState->pPlugin->Log(LOG_ERROR, "PyDomoticz_Error failed to parse parameters: string expected.");
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pPlugin->Log(LOG_ERROR, "%s failed to parse parameters: string expected.", __func__);
|
|
+ pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
- pModState->pPlugin->Log(LOG_ERROR, (std::string)msg);
|
|
+ pPlugin->Log(LOG_ERROR, (std::string)msg);
|
|
}
|
|
}
|
|
|
|
@@ -406,7 +198,7 @@ namespace Plugins
|
|
if (!PyArg_ParseTuple(args, "i", &type))
|
|
{
|
|
pModState->pPlugin->Log(LOG_ERROR, "Failed to parse parameters, integer expected.");
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pModState->pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
@@ -440,12 +232,12 @@ namespace Plugins
|
|
else
|
|
{
|
|
iPollinterval = pModState->pPlugin->PollInterval(0);
|
|
- if (PyTuple_Check(args) && PyTuple_Size(args))
|
|
+ if (PyBorrowedRef(args).IsTuple() && PyTuple_Size(args))
|
|
{
|
|
if (!PyArg_ParseTuple(args, "i", &iPollinterval))
|
|
{
|
|
pModState->pPlugin->Log(LOG_ERROR, "failed to parse parameters, integer expected.");
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pModState->pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
@@ -475,7 +267,7 @@ namespace Plugins
|
|
if (!PyArg_ParseTuple(args, "s", &szNotifier))
|
|
{
|
|
pModState->pPlugin->Log(LOG_ERROR, "Failed to parse parameters, Notifier Name expected.");
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pModState->pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
@@ -508,28 +300,7 @@ namespace Plugins
|
|
}
|
|
else
|
|
{
|
|
- int bTrace = 0;
|
|
- if (!PyArg_ParseTuple(args, "p", &bTrace))
|
|
- {
|
|
- pModState->pPlugin->Log(LOG_ERROR, "Failed to parse parameter, True/False expected.");
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
- }
|
|
- else
|
|
- {
|
|
- pModState->pPlugin->m_bTracing = (bool)bTrace;
|
|
- pModState->pPlugin->Log(LOG_NORM, "Low level Python tracing %s.", (pModState->pPlugin->m_bTracing ? "ENABLED" : "DISABLED"));
|
|
-
|
|
- if (pModState->pPlugin->m_bTracing)
|
|
- {
|
|
- PyEval_SetProfile(PyDomoticz_ProfileFunc, self);
|
|
- PyEval_SetTrace(PyDomoticz_TraceFunc, self);
|
|
- }
|
|
- else
|
|
- {
|
|
- PyEval_SetProfile(nullptr, nullptr);
|
|
- PyEval_SetTrace(nullptr, nullptr);
|
|
- }
|
|
- }
|
|
+ pModState->pPlugin->Log(LOG_ERROR, "CPlugin:%s, Low level trace functions have been removed.", __func__);
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
@@ -554,7 +325,7 @@ namespace Plugins
|
|
if (PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &pNewConfig))
|
|
{
|
|
// Python object supplied if it is not a dictionary
|
|
- if (!PyDict_Check(pNewConfig))
|
|
+ if (!PyBorrowedRef(pNewConfig).IsDict())
|
|
{
|
|
pModState->pPlugin->Log(LOG_ERROR, "CPlugin:%s, Function expects no parameter or a Dictionary.", __func__);
|
|
Py_RETURN_NONE;
|
|
@@ -603,46 +374,26 @@ namespace Plugins
|
|
{
|
|
if (pDeviceClass)
|
|
{
|
|
- PyTypeObject *pBaseClass = pDeviceClass->tp_base;
|
|
- while (pBaseClass)
|
|
+ if (!PyType_IsSubtype(pDeviceClass, pModState->pDeviceClass))
|
|
{
|
|
- if (pBaseClass->tp_name == pModState->pDeviceClass->tp_name)
|
|
- {
|
|
- //_log.Log((_eLogLevel)LOG_NORM, "Class '%s' registered to override '%s'.", pDeviceClass->tp_name, pModState->pDeviceClass->tp_name);
|
|
- pModState->pDeviceClass = pDeviceClass;
|
|
- break;
|
|
- }
|
|
- pBaseClass = pBaseClass->tp_base;
|
|
+ pModState->pPlugin->Log(LOG_ERROR, "Device class registration failed, Supplied class is not derived from 'DomoticzEx.Device'");
|
|
}
|
|
- if (pDeviceClass->tp_name != pModState->pDeviceClass->tp_name)
|
|
+ else
|
|
{
|
|
- pModState->pPlugin->Log(LOG_ERROR, "Class '%s' registration failed, Device is not derived from '%s'", pDeviceClass->tp_name, pModState->pDeviceClass->tp_name);
|
|
+ pModState->pDeviceClass = pDeviceClass;
|
|
+ PyType_Ready(pModState->pDeviceClass);
|
|
}
|
|
}
|
|
if (pUnitClass)
|
|
{
|
|
- if (pModState->pUnitClass)
|
|
+ if (!PyType_IsSubtype(pUnitClass, pModState->pUnitClass))
|
|
{
|
|
- PyTypeObject *pBaseClass = pUnitClass->tp_base;
|
|
- while (pBaseClass)
|
|
- {
|
|
- if (pBaseClass->tp_name == pModState->pUnitClass->tp_name)
|
|
- {
|
|
- //_log.Log((_eLogLevel)LOG_NORM, "Class '%s' registered to override '%s'.", pDeviceClass->tp_name, pModState->pUnitClass->tp_name);
|
|
- pModState->pUnitClass = pUnitClass;
|
|
- break;
|
|
- }
|
|
- pBaseClass = pBaseClass->tp_base;
|
|
- }
|
|
- if (pUnitClass->tp_name != pModState->pUnitClass->tp_name)
|
|
- {
|
|
- pModState->pPlugin->Log(LOG_ERROR, "Class '%s' registration failed, Unit is not derived from '%s'", pUnitClass->tp_name,
|
|
- pModState->pDeviceClass->tp_name);
|
|
- }
|
|
+ pModState->pPlugin->Log(LOG_ERROR, "Unit class registration failed, Supplied class is not derived from 'DomoticzEx.Unit'");
|
|
}
|
|
else
|
|
{
|
|
- pModState->pPlugin->Log(LOG_ERROR, "Class '%s' registration failed, imported Domoticz module does not support Unit objects", pUnitClass->tp_name);
|
|
+ pModState->pUnitClass = pUnitClass;
|
|
+ PyType_Ready(pModState->pUnitClass);
|
|
}
|
|
}
|
|
}
|
|
@@ -669,12 +420,12 @@ namespace Plugins
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &pTarget))
|
|
{
|
|
pModState->pPlugin->Log(LOG_ERROR, "%s failed to parse parameters: Object expected (Optional).", __func__);
|
|
- LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
+ pModState->pPlugin->LogPythonException(std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
PyNewRef pLocals = PyObject_Dir(pModState->lastCallback);
|
|
- if (PyList_Check(pLocals)) // && PyIter_Check(pLocals)) // Check fails but iteration works??!?
|
|
+ if (pLocals.IsList()) // && PyIter_Check(pLocals)) // Check fails but iteration works??!?
|
|
{
|
|
pModState->pPlugin->Log(LOG_NORM, "Context dump:");
|
|
PyNewRef pIter = PyObject_GetIter(pLocals);
|
|
@@ -702,7 +453,7 @@ namespace Plugins
|
|
}
|
|
}
|
|
PyBorrowedRef pLocalVars = PyEval_GetLocals();
|
|
- if (PyDict_Check(pLocalVars))
|
|
+ if (pLocalVars.IsDict())
|
|
{
|
|
pModState->pPlugin->Log(LOG_NORM, "Locals dump:");
|
|
PyBorrowedRef key;
|
|
@@ -717,7 +468,7 @@ namespace Plugins
|
|
}
|
|
}
|
|
PyBorrowedRef pGlobalVars = PyEval_GetGlobals();
|
|
- if (PyDict_Check(pGlobalVars))
|
|
+ if (pGlobalVars.IsDict())
|
|
{
|
|
pModState->pPlugin->Log(LOG_NORM, "Globals dump:");
|
|
PyBorrowedRef key;
|
|
@@ -753,6 +504,30 @@ namespace Plugins
|
|
{ "Dump", (PyCFunction)PyDomoticz_Dump, METH_VARARGS | METH_KEYWORDS, "Dump string values of an object or all locals to the log." },
|
|
{ nullptr, nullptr, 0, nullptr } };
|
|
|
|
+ PyType_Slot ConnectionSlots[] = {
|
|
+ { Py_tp_doc, (void*)"Domoticz Connection" },
|
|
+ { Py_tp_new, (void*)CConnection_new },
|
|
+ { Py_tp_init, (void*)CConnection_init },
|
|
+ { Py_tp_dealloc, (void*)CConnection_dealloc },
|
|
+ { Py_tp_members, CConnection_members },
|
|
+ { Py_tp_methods, CConnection_methods },
|
|
+ { Py_tp_str, (void*)CConnection_str },
|
|
+ { 0 },
|
|
+ };
|
|
+ PyType_Spec ConnectionSpec = { "Domoticz.Connection", sizeof(CConnection), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE, ConnectionSlots };
|
|
+
|
|
+ PyType_Slot ImageSlots[] = {
|
|
+ { Py_tp_doc, (void*)"Domoticz Image" },
|
|
+ { Py_tp_new, (void*)CImage_new },
|
|
+ { Py_tp_init, (void*)CImage_init },
|
|
+ { Py_tp_dealloc, (void*)CImage_dealloc },
|
|
+ { Py_tp_members, CImage_members },
|
|
+ { Py_tp_methods, CImage_methods },
|
|
+ { Py_tp_str, (void*)CImage_str },
|
|
+ { 0 },
|
|
+ };
|
|
+ PyType_Spec ImageSpec = { "Domoticz.Image", sizeof(CImage), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE, ImageSlots };
|
|
+
|
|
static int DomoticzTraverse(PyObject *m, visitproc visit, void *arg)
|
|
{
|
|
Py_VISIT(GETSTATE(m)->error);
|
|
@@ -769,37 +544,46 @@ namespace Plugins
|
|
|
|
PyMODINIT_FUNC PyInit_Domoticz(void)
|
|
{
|
|
-
|
|
// This is called during the import of the plugin module
|
|
// triggered by the "import Domoticz" statement
|
|
PyObject *pModule = PyModule_Create2(&DomoticzModuleDef, PYTHON_API_VERSION);
|
|
module_state *pModState = ((struct module_state *)PyModule_GetState(pModule));
|
|
|
|
- if (PyType_Ready(&CDeviceType) < 0)
|
|
+ if (!CDeviceType)
|
|
{
|
|
- _log.Log(LOG_ERROR, "%s, Device Type not ready.", __func__);
|
|
- return pModule;
|
|
+ PyType_Slot DeviceSlots[] = {
|
|
+ { Py_tp_doc, (void*)"Domoticz Device" },
|
|
+ { Py_tp_new, (void*)CDevice_new },
|
|
+ { Py_tp_init, (void*)CDevice_init },
|
|
+ { Py_tp_dealloc, (void*)CDevice_dealloc },
|
|
+ { Py_tp_members, CDevice_members },
|
|
+ { Py_tp_methods, CDevice_methods },
|
|
+ { Py_tp_str, (void*)CDevice_str },
|
|
+ { 0 },
|
|
+ };
|
|
+ PyType_Spec DeviceSpec = { "Domoticz.Device", sizeof(CDevice), 0,
|
|
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE, DeviceSlots };
|
|
+
|
|
+ CDeviceType = (PyTypeObject*)PyType_FromSpec(&DeviceSpec);
|
|
+ PyType_Ready(CDeviceType);
|
|
}
|
|
- Py_INCREF((PyObject *)&CDeviceType);
|
|
- PyModule_AddObject(pModule, "Device", (PyObject *)&CDeviceType);
|
|
- pModState->pDeviceClass = &CDeviceType;
|
|
+ pModState->pDeviceClass = CDeviceType;
|
|
pModState->pUnitClass = nullptr;
|
|
+ PyModule_AddObject(pModule, "Device", (PyObject*)CDeviceType);
|
|
|
|
- if (PyType_Ready(&CConnectionType) < 0)
|
|
+ if (!CConnectionType)
|
|
{
|
|
- _log.Log(LOG_ERROR, "%s, Connection Type not ready.", __func__);
|
|
- return pModule;
|
|
+ CConnectionType = (PyTypeObject*)PyType_FromSpec(&ConnectionSpec);
|
|
+ PyType_Ready(CConnectionType);
|
|
}
|
|
- Py_INCREF((PyObject *)&CConnectionType);
|
|
- PyModule_AddObject(pModule, "Connection", (PyObject *)&CConnectionType);
|
|
+ PyModule_AddObject(pModule, "Connection", (PyObject*)CConnectionType);
|
|
|
|
- if (PyType_Ready(&CImageType) < 0)
|
|
+ if (!CImageType)
|
|
{
|
|
- _log.Log(LOG_ERROR, "%s, Image Type not ready.", __func__);
|
|
- return pModule;
|
|
+ CImageType = (PyTypeObject*)PyType_FromSpec(&ImageSpec);
|
|
+ PyType_Ready(CImageType);
|
|
}
|
|
- Py_INCREF((PyObject *)&CImageType);
|
|
- PyModule_AddObject(pModule, "Image", (PyObject *)&CImageType);
|
|
+ PyModule_AddObject(pModule, "Image", (PyObject*)CImageType);
|
|
|
|
return pModule;
|
|
}
|
|
@@ -808,45 +592,58 @@ namespace Plugins
|
|
|
|
PyMODINIT_FUNC PyInit_DomoticzEx(void)
|
|
{
|
|
-
|
|
// This is called during the import of the plugin module
|
|
- // triggered by the "import Domoticz" statement
|
|
+ // triggered by the "import DomoticzEx" statement
|
|
PyObject *pModule = PyModule_Create2(&DomoticzExModuleDef, PYTHON_API_VERSION);
|
|
module_state *pModState = ((struct module_state *)PyModule_GetState(pModule));
|
|
|
|
- if (PyType_Ready(&CDeviceExType) < 0)
|
|
- {
|
|
- _log.Log(LOG_ERROR, "%s, Device Type not ready.", __func__);
|
|
- return pModule;
|
|
- }
|
|
- Py_INCREF((PyObject *)&CDeviceExType);
|
|
- PyModule_AddObject(pModule, "Device", (PyObject *)&CDeviceExType);
|
|
- pModState->pDeviceClass = &CDeviceExType;
|
|
-
|
|
- if (PyType_Ready(&CUnitExType) < 0)
|
|
- {
|
|
- _log.Log(LOG_ERROR, "%s, Unit Type not ready.", __func__);
|
|
- return pModule;
|
|
- }
|
|
- Py_INCREF((PyObject *)&CUnitExType);
|
|
- PyModule_AddObject(pModule, "Unit", (PyObject *)&CUnitExType);
|
|
- pModState->pUnitClass = &CUnitExType;
|
|
-
|
|
- if (PyType_Ready(&CConnectionType) < 0)
|
|
- {
|
|
- _log.Log(LOG_ERROR, "%s, Connection Type not ready.", __func__);
|
|
- return pModule;
|
|
+ PyType_Slot DeviceExSlots[] = {
|
|
+ { Py_tp_doc, (void*)"DomoticzEx Device" },
|
|
+ { Py_tp_new, (void*)CDeviceEx_new },
|
|
+ { Py_tp_init, (void*)CDeviceEx_init },
|
|
+ { Py_tp_dealloc, (void*)CDeviceEx_dealloc },
|
|
+ { Py_tp_members, CDeviceEx_members },
|
|
+ { Py_tp_methods, CDeviceEx_methods },
|
|
+ { Py_tp_str, (void*)CDeviceEx_str },
|
|
+ { 0 },
|
|
+ };
|
|
+ PyType_Spec DeviceExSpec = { "DomoticzEx.Device", sizeof(CDeviceEx), 0,
|
|
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE, DeviceExSlots };
|
|
+
|
|
+ pModState->pDeviceClass = (PyTypeObject*)PyType_FromSpec(&DeviceExSpec); // Calls PyType_Ready internally from, 3.9 onwards
|
|
+ PyModule_AddObject(pModule, "Device", (PyObject *)pModState->pDeviceClass);
|
|
+ PyType_Ready(pModState->pDeviceClass);
|
|
+
|
|
+ PyType_Slot UnitExSlots[] = {
|
|
+ { Py_tp_doc, (void*)"DomoticzEx Unit" },
|
|
+ { Py_tp_new, (void*)CUnitEx_new },
|
|
+ { Py_tp_init, (void*)CUnitEx_init },
|
|
+ { Py_tp_dealloc, (void*)CUnitEx_dealloc },
|
|
+ { Py_tp_members, CUnitEx_members },
|
|
+ { Py_tp_methods, CUnitEx_methods },
|
|
+ { Py_tp_str, (void*)CUnitEx_str },
|
|
+ { 0 },
|
|
+ };
|
|
+ PyType_Spec UnitExSpec = { "DomoticzEx.Unit", sizeof(CUnitEx), 0,
|
|
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE, UnitExSlots };
|
|
+
|
|
+ pModState->pUnitClass = (PyTypeObject*)PyType_FromSpec(&UnitExSpec);
|
|
+ PyModule_AddObject(pModule, "Unit", (PyObject*)pModState->pUnitClass);
|
|
+ PyType_Ready(pModState->pUnitClass);
|
|
+
|
|
+ if (!CConnectionType)
|
|
+ {
|
|
+ CConnectionType = (PyTypeObject*)PyType_FromSpec(&ConnectionSpec);
|
|
+ PyType_Ready(CConnectionType);
|
|
}
|
|
- Py_INCREF((PyObject *)&CConnectionType);
|
|
- PyModule_AddObject(pModule, "Connection", (PyObject *)&CConnectionType);
|
|
+ PyModule_AddObject(pModule, "Connection", (PyObject*)CConnectionType);
|
|
|
|
- if (PyType_Ready(&CImageType) < 0)
|
|
+ if (!CImageType)
|
|
{
|
|
- _log.Log(LOG_ERROR, "%s, Image Type not ready.", __func__);
|
|
- return pModule;
|
|
+ CImageType = (PyTypeObject*)PyType_FromSpec(&ImageSpec);
|
|
+ PyType_Ready(CImageType);
|
|
}
|
|
- Py_INCREF((PyObject *)&CImageType);
|
|
- PyModule_AddObject(pModule, "Image", (PyObject *)&CImageType);
|
|
+ PyModule_AddObject(pModule, "Image", (PyObject*)CImageType);
|
|
|
|
return pModule;
|
|
}
|
|
@@ -900,8 +697,7 @@ namespace Plugins
|
|
module_state *pModState = ((struct module_state *)PyModule_GetState(brModule));
|
|
if (!pModState)
|
|
{
|
|
- _log.Log(LOG_ERROR, "CPlugin:%s, unable to obtain module state.", __func__);
|
|
- return nullptr;
|
|
+ _log.Log(LOG_ERROR, "%s, unable to obtain module state.", __func__);
|
|
}
|
|
|
|
return pModState;
|
|
@@ -910,205 +706,76 @@ namespace Plugins
|
|
CPlugin *CPlugin::FindPlugin()
|
|
{
|
|
module_state *pModState = FindModule();
|
|
- if (!pModState)
|
|
- return nullptr;
|
|
- return pModState->pPlugin;
|
|
+ return pModState ? pModState->pPlugin : nullptr;
|
|
}
|
|
|
|
- void CPlugin::LogTraceback(PyTracebackObject *pTraceback)
|
|
- {
|
|
- if (pTraceback)
|
|
- {
|
|
- Log(LOG_ERROR, "Exception traceback:");
|
|
- }
|
|
- else
|
|
- {
|
|
- Log(LOG_ERROR, "No traceback available");
|
|
- }
|
|
-
|
|
- // Log a stack trace if there is one
|
|
- PyTracebackObject *pTraceFrame = pTraceback;
|
|
- while (pTraceFrame)
|
|
- {
|
|
- PyFrameObject *frame = pTraceFrame->tb_frame;
|
|
- if (frame)
|
|
- {
|
|
- int lineno = PyFrame_GetLineNumber(frame);
|
|
- PyCodeObject *pCode = frame->f_code;
|
|
- std::string FileName;
|
|
- if (pCode->co_filename)
|
|
- {
|
|
- FileName = (std::string)PyBorrowedRef(pCode->co_filename);
|
|
- }
|
|
- std::string FuncName = "Unknown";
|
|
- if (pCode->co_name)
|
|
- {
|
|
- FuncName = (std::string)PyBorrowedRef(pCode->co_name);
|
|
- }
|
|
- if (!FileName.empty())
|
|
- Log(LOG_ERROR, " ----> Line %d in '%s', function %s", lineno, FileName.c_str(), FuncName.c_str());
|
|
- else
|
|
- Log(LOG_ERROR, " ----> Line %d in '%s'", lineno, FuncName.c_str());
|
|
- }
|
|
- pTraceFrame = pTraceFrame->tb_next;
|
|
- }
|
|
- }
|
|
-
|
|
void CPlugin::LogPythonException()
|
|
{
|
|
- PyTracebackObject *pTraceback;
|
|
+ PyNewRef pTraceback;
|
|
PyNewRef pExcept;
|
|
PyNewRef pValue;
|
|
|
|
- PyErr_Fetch(&pExcept, &pValue, (PyObject **)&pTraceback);
|
|
- PyErr_NormalizeException(&pExcept, &pValue, (PyObject **)&pTraceback);
|
|
- PyErr_Clear();
|
|
+ PyErr_Fetch(&pExcept, &pValue, &pTraceback);
|
|
+ PyErr_NormalizeException(&pExcept, &pValue, &pTraceback);
|
|
|
|
- if (pExcept)
|
|
+ if (!pExcept && !pValue && !pTraceback)
|
|
{
|
|
- Log(LOG_ERROR, "Module Import failed, exception: '%s'", ((PyTypeObject *)pExcept)->tp_name);
|
|
+ Log(LOG_ERROR, "Unable to decode exception.");
|
|
}
|
|
- if (pValue)
|
|
+ else
|
|
{
|
|
- std::string sError;
|
|
- PyNewRef pErrBytes = PyUnicode_AsASCIIString(pValue); // Won't normally return text for Import related errors
|
|
- if (!pErrBytes)
|
|
+ std::string sTypeText("Unknown");
|
|
+ if (pExcept)
|
|
{
|
|
- // ImportError has name and path attributes
|
|
- PyErr_Clear();
|
|
- if (PyObject_HasAttrString(pValue, "path"))
|
|
- {
|
|
- std::string sPath = PyNewRef(PyObject_GetAttrString(pValue, "path"));
|
|
- if (sPath.length() && (sPath != "None"))
|
|
- {
|
|
- sError += "Path: " + sPath;
|
|
- }
|
|
- }
|
|
- PyErr_Clear();
|
|
- if (PyObject_HasAttrString(pValue, "name"))
|
|
- {
|
|
- std::string sName = PyNewRef(PyObject_GetAttrString(pValue, "name"));
|
|
- if (sName.length() && (sName != "None"))
|
|
- {
|
|
- sError += " Name: " + sName;
|
|
- }
|
|
- }
|
|
- if (!sError.empty())
|
|
- {
|
|
- Log(LOG_ERROR, "Module Import failed: '%s'", sError.c_str());
|
|
- sError = "";
|
|
- }
|
|
-
|
|
- // SyntaxError, IndentationError & TabError have filename, lineno, offset and text attributes
|
|
- PyErr_Clear();
|
|
- if (PyObject_HasAttrString(pValue, "filename"))
|
|
- {
|
|
- std::string sName = PyNewRef(PyObject_GetAttrString(pValue, "name"));
|
|
- sError += "File: " + sName;
|
|
- }
|
|
- long long lineno = -1;
|
|
- long long offset = -1;
|
|
- PyErr_Clear();
|
|
- if (PyObject_HasAttrString(pValue, "lineno"))
|
|
- {
|
|
- PyNewRef pString = PyObject_GetAttrString(pValue, "lineno");
|
|
- lineno = PyLong_AsLongLong(pString);
|
|
- }
|
|
- PyErr_Clear();
|
|
- if (PyObject_HasAttrString(pValue, "offset"))
|
|
- {
|
|
- PyNewRef pString = PyObject_GetAttrString(pValue, "offset");
|
|
- offset = PyLong_AsLongLong(pString);
|
|
- }
|
|
+ PyTypeObject* TypeName = (PyTypeObject*)pExcept;
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)TypeName, "__name__");
|
|
+ sTypeText = (std::string)pName;
|
|
+ }
|
|
|
|
- if (!sError.empty())
|
|
- {
|
|
- if ((lineno > 0) && (lineno < 1000))
|
|
+ /* See if we can get a full traceback */
|
|
+ PyNewRef pModule = PyImport_ImportModule("traceback");
|
|
+ if (pModule)
|
|
+ {
|
|
+ PyNewRef pFunc = PyObject_GetAttrString(pModule, "format_exception");
|
|
+ if (pFunc && PyCallable_Check(pFunc)) {
|
|
+ PyNewRef pList = PyObject_CallFunctionObjArgs(pFunc, pExcept, pValue, pTraceback, NULL);
|
|
+ if (pList)
|
|
{
|
|
- Log(LOG_ERROR, "Import detail: %s, Line: %lld, offset: %lld", sError.c_str(), lineno, offset);
|
|
+ for (Py_ssize_t i = 0; i < PyList_Size(pList); i++)
|
|
+ {
|
|
+ PyBorrowedRef pPyStr = PyList_GetItem(pList, i);
|
|
+ std::string pStr(pPyStr);
|
|
+ size_t pos = 0;
|
|
+ std::string token;
|
|
+ while ((pos = pStr.find('\n')) != std::string::npos) {
|
|
+ token = pStr.substr(0, pos);
|
|
+ Log(LOG_ERROR, "%s", token.c_str());
|
|
+ pStr.erase(0, pos + 1);
|
|
+ }
|
|
+ }
|
|
}
|
|
else
|
|
{
|
|
- Log(LOG_ERROR, "Import detail: %s, Line: %lld", sError.c_str(), offset);
|
|
+ Log(LOG_ERROR, "Exception: '%s'. No traceback available.", sTypeText.c_str());
|
|
}
|
|
- sError = "";
|
|
- }
|
|
-
|
|
- PyErr_Clear();
|
|
- if (PyObject_HasAttrString(pValue, "text"))
|
|
- {
|
|
- std::string sUTF = PyNewRef(PyObject_GetAttrString(pValue, "text"));
|
|
- Log(LOG_ERROR, "Error Line '%s'", sUTF.c_str());
|
|
}
|
|
else
|
|
{
|
|
- Log(LOG_ERROR, "Error Line details not available.");
|
|
- }
|
|
-
|
|
- if (!sError.empty())
|
|
- {
|
|
- Log(LOG_ERROR, "Import detail: %s", sError.c_str());
|
|
+ Log(LOG_ERROR, "'format_exception' lookup failed, exception: '%s'. No traceback available.", sTypeText.c_str());
|
|
}
|
|
}
|
|
else
|
|
- Log(LOG_ERROR, "Module Import failed '%s'", std::string(pErrBytes).c_str());
|
|
- }
|
|
-
|
|
- // Log a stack trace if there is one
|
|
- LogTraceback(pTraceback);
|
|
-
|
|
- if (!pExcept && !pValue && !pTraceback)
|
|
- {
|
|
- Log(LOG_ERROR, "Call to import module failed, unable to decode exception.");
|
|
+ {
|
|
+ Log(LOG_ERROR, "'Traceback' module import failed, exception: '%s'. No traceback available.", sTypeText.c_str());
|
|
+ }
|
|
}
|
|
-
|
|
- if (pTraceback)
|
|
- Py_XDECREF(pTraceback);
|
|
+ PyErr_Clear();
|
|
}
|
|
|
|
void CPlugin::LogPythonException(const std::string &sHandler)
|
|
{
|
|
- PyTracebackObject *pTraceback;
|
|
- PyNewRef pExcept;
|
|
- PyNewRef pValue;
|
|
- PyTypeObject *TypeName;
|
|
- PyNewRef pErrBytes;
|
|
- const char *pTypeText = nullptr;
|
|
-
|
|
- PyErr_Fetch(&pExcept, &pValue, (PyObject **)&pTraceback);
|
|
-
|
|
- if (pExcept)
|
|
- {
|
|
- TypeName = (PyTypeObject *)pExcept;
|
|
- pTypeText = TypeName->tp_name;
|
|
- }
|
|
- if (pTypeText && pValue)
|
|
- {
|
|
- Log(LOG_ERROR, "'%s' failed '%s':'%s'.", sHandler.c_str(), pTypeText, std::string(pValue).c_str());
|
|
- }
|
|
- if (pTypeText && !pValue)
|
|
- {
|
|
- Log(LOG_ERROR, "'%s' failed '%s'.", sHandler.c_str(), pTypeText);
|
|
- }
|
|
- if (!pTypeText && pValue)
|
|
- {
|
|
- Log(LOG_ERROR, "'%s' failed '%s'.",sHandler.c_str(), std::string(pValue).c_str());
|
|
- }
|
|
- if (!pTypeText && !pValue)
|
|
- {
|
|
- Log(LOG_ERROR, "'%s' failed, unable to determine error.", sHandler.c_str());
|
|
- }
|
|
-
|
|
- // Log a stack trace if there is one
|
|
- LogTraceback(pTraceback);
|
|
-
|
|
- if (!pExcept && !pValue && !pTraceback)
|
|
- {
|
|
- Log(LOG_ERROR, "Call to message handler '%s' failed, unable to decode exception.", sHandler.c_str());
|
|
- }
|
|
-
|
|
- if (pTraceback)
|
|
- Py_XDECREF(pTraceback);
|
|
+ Log(LOG_ERROR, "Call to function '%s' failed, exception details:", sHandler.c_str());
|
|
+ LogPythonException();
|
|
}
|
|
|
|
int CPlugin::PollInterval(int Interval)
|
|
@@ -1222,7 +889,6 @@ namespace Plugins
|
|
// Tell transport to disconnect if required
|
|
if (pPluginTransport)
|
|
{
|
|
- // std::lock_guard<std::mutex> l(PythonMutex); // Take mutex to guard access to CPluginTransport::m_pConnection
|
|
MessagePlugin(new DisconnectDirective(pPluginTransport->Connection()));
|
|
}
|
|
}
|
|
@@ -1314,25 +980,26 @@ namespace Plugins
|
|
{
|
|
if (m_bDebug & PDM_QUEUE)
|
|
{
|
|
- Log(LOG_NORM, "(" + m_Name + ") Processing '" + std::string(Message->Name()) + "' message");
|
|
+ Log(LOG_NORM, "Processing '" + std::string(Message->Name()) + "' message");
|
|
}
|
|
Message->Process(this);
|
|
}
|
|
catch (...)
|
|
{
|
|
- Log(LOG_ERROR, "PluginSystem: Exception processing message.");
|
|
+ Log(LOG_ERROR, "Exception processing '%s' message.", Message->Name());
|
|
+ }
|
|
+
|
|
+ // Free the memory for the message
|
|
+ if (!m_PyInterpreter)
|
|
+ {
|
|
+ // Can't lock because there is no interpreter to lock
|
|
+ delete Message;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ AccessPython Guard(this, Message->Name());
|
|
+ delete Message;
|
|
}
|
|
- }
|
|
- // Free the memory for the message
|
|
- if (!m_PyInterpreter)
|
|
- {
|
|
- // Can't lock because there is no interpreter to lock
|
|
- delete Message;
|
|
- }
|
|
- else
|
|
- {
|
|
- AccessPython Guard(this, m_Name.c_str());
|
|
- delete Message;
|
|
}
|
|
}
|
|
|
|
@@ -1351,7 +1018,6 @@ namespace Plugins
|
|
{
|
|
for (const auto &pPluginTransport : m_Transports)
|
|
{
|
|
- // std::lock_guard<std::mutex> l(PythonMutex); // Take mutex to guard access to CPluginTransport::m_pConnection
|
|
pPluginTransport->VerifyConnection();
|
|
}
|
|
}
|
|
@@ -1371,6 +1037,7 @@ namespace Plugins
|
|
|
|
try
|
|
{
|
|
+ // Only initialise one plugin at a time to prevent issues with module creation
|
|
PyEval_RestoreThread((PyThreadState *)m_mainworker.m_pluginsystem.PythonThread());
|
|
m_PyInterpreter = Py_NewInterpreter();
|
|
if (!m_PyInterpreter)
|
|
@@ -1379,10 +1046,6 @@ namespace Plugins
|
|
goto Error;
|
|
}
|
|
|
|
- // Get an instance of the single, central Py_None to use in local code
|
|
- PyBorrowedRef globalNone = Py_BuildValue("");
|
|
- Py_None = globalNone;
|
|
-
|
|
// Prepend plugin directory to path so that python will search it early when importing
|
|
#ifdef WIN32
|
|
std::wstring sSeparator = L";";
|
|
@@ -1433,7 +1096,7 @@ namespace Plugins
|
|
for (Py_ssize_t i = 0; i < PyList_Size(pSites); i++)
|
|
{
|
|
PyBorrowedRef pSite = PyList_GetItem(pSites, i);
|
|
- if (pSite && PyUnicode_Check(pSite))
|
|
+ if (pSite.IsString())
|
|
{
|
|
std::wstringstream ssPath;
|
|
ssPath << ((std::string)PyBorrowedRef(pSite)).c_str();
|
|
@@ -1501,6 +1164,25 @@ namespace Plugins
|
|
}
|
|
pModState->pPlugin = this;
|
|
|
|
+ // Get reference to global 'Py_None' instance for comparisons
|
|
+ if (!Py_None)
|
|
+ {
|
|
+ PyBorrowedRef global_dict = PyModule_GetDict(m_PyModule);
|
|
+ PyNewRef local_dict = PyDict_New();
|
|
+ PyNewRef pCode = Py_CompileString("# Eval will return 'None'\n", "<domoticz>", Py_file_input);
|
|
+ if (pCode)
|
|
+ {
|
|
+ PyNewRef pEval = PyEval_EvalCode(pCode, global_dict, local_dict);
|
|
+ Py_None = pEval;
|
|
+ Py_INCREF(Py_None);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ Log(LOG_ERROR, "Failed to compile script to set global Py_None");
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
// Add start command to message queue
|
|
MessagePlugin(new onStartCallback());
|
|
|
|
@@ -1611,7 +1293,7 @@ namespace Plugins
|
|
}
|
|
}
|
|
|
|
- m_DeviceDict = (PyDictObject*)PyDict_New();
|
|
+ m_DeviceDict = PyDict_New();
|
|
if (PyDict_SetItemString(pModuleDict, "Devices", (PyObject *)m_DeviceDict) == -1)
|
|
{
|
|
Log(LOG_ERROR, "(%s) failed to add Device dictionary.", m_PluginKey.c_str());
|
|
@@ -1647,7 +1329,6 @@ namespace Plugins
|
|
// load associated devices to make them available to python
|
|
if (!result.empty())
|
|
{
|
|
- PyType_Ready(pModState->pDeviceClass);
|
|
// Add device objects into the device dictionary with Unit as the key
|
|
for (const auto &sd : result)
|
|
{
|
|
@@ -1689,7 +1370,7 @@ namespace Plugins
|
|
}
|
|
}
|
|
|
|
- m_ImageDict = (PyDictObject *)PyDict_New();
|
|
+ m_ImageDict = PyDict_New();
|
|
if (PyDict_SetItemString(pModuleDict, "Images", (PyObject *)m_ImageDict) == -1)
|
|
{
|
|
Log(LOG_ERROR, "(%s) failed to add Image dictionary.", m_PluginKey.c_str());
|
|
@@ -1700,11 +1381,10 @@ namespace Plugins
|
|
result = m_sql.safe_query("SELECT ID, Base, Name, Description FROM CustomImages WHERE Base LIKE '%q%%' ORDER BY ID ASC", m_PluginKey.c_str());
|
|
if (!result.empty())
|
|
{
|
|
- PyType_Ready(&CImageType);
|
|
// Add image objects into the image dictionary with ID as the key
|
|
for (const auto &sd : result)
|
|
{
|
|
- CImage *pImage = (CImage *)CImage_new(&CImageType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
+ CImage *pImage = (CImage *)CImage_new(CImageType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
|
|
PyNewRef pKey = PyUnicode_FromString(sd[1].c_str());
|
|
if (PyDict_SetItem((PyObject *)m_ImageDict, pKey, (PyObject *)pImage) == -1)
|
|
@@ -2098,7 +1778,7 @@ namespace Plugins
|
|
}
|
|
else
|
|
{
|
|
- CDevice *pDevice = (CDevice *)CDevice_new(&CDeviceType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
+ CDevice *pDevice = (CDevice *)CDevice_new(CDeviceType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
|
|
PyNewRef pKey = PyLong_FromLong(Unit);
|
|
if (PyDict_SetItem((PyObject *)m_DeviceDict, pKey, (PyObject *)pDevice) == -1)
|
|
@@ -2250,13 +1930,24 @@ namespace Plugins
|
|
void CPlugin::RestoreThread()
|
|
{
|
|
if (m_PyInterpreter)
|
|
- PyEval_RestoreThread((PyThreadState *)m_PyInterpreter);
|
|
+ {
|
|
+ PyEval_RestoreThread((PyThreadState*)m_PyInterpreter);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ Log(LOG_ERROR, "Attempt to aquire the GIL with NULL Interpreter details.");
|
|
+ }
|
|
}
|
|
|
|
void CPlugin::ReleaseThread()
|
|
{
|
|
if (m_PyInterpreter)
|
|
- PyEval_SaveThread();
|
|
+ {
|
|
+ if (!PyEval_SaveThread())
|
|
+ {
|
|
+ Log(LOG_ERROR, "Attempt to release GIL returned NULL value");
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
void CPlugin::Callback(PyObject *pTarget, const std::string &sHandler, PyObject *pParams)
|
|
@@ -2294,7 +1985,11 @@ namespace Plugins
|
|
}
|
|
|
|
if (m_bDebug & PDM_QUEUE)
|
|
- Log(LOG_NORM, "Calling message handler '%s' on '%s' type object.", sHandler.c_str(), pTarget->ob_type->tp_name);
|
|
+ {
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)(pTarget->ob_type), "__name__");
|
|
+ if (pName)
|
|
+ Log(LOG_NORM, "Calling message handler '%s' on '%s' type object.", sHandler.c_str(), (std::string(pName).c_str()));
|
|
+ }
|
|
|
|
PyErr_Clear();
|
|
|
|
@@ -2315,7 +2010,7 @@ namespace Plugins
|
|
{
|
|
// See if additional information is available
|
|
PyNewRef pLocals = PyObject_Dir(pTarget);
|
|
- if (PyList_Check(pLocals)) // && PyIter_Check(pLocals)) // Check fails but iteration works??!?
|
|
+ if (pLocals.IsList()) // && PyIter_Check(pLocals)) // Check fails but iteration works??!?
|
|
{
|
|
Log(LOG_NORM, "Local context:");
|
|
PyNewRef pIter = PyObject_GetIter(pLocals);
|
|
@@ -2391,7 +2086,7 @@ namespace Plugins
|
|
module_state *pModState = ((struct module_state *)PyModule_GetState(brModule));
|
|
if (!pModState)
|
|
{
|
|
- Log(LOG_ERROR, "CPlugin:%s, unable to obtain module state.", __func__);
|
|
+ Log(LOG_ERROR, "%s, unable to obtain module state.", __func__);
|
|
return;
|
|
}
|
|
|
|
@@ -2409,7 +2104,8 @@ namespace Plugins
|
|
}
|
|
else if (isDevice == 0)
|
|
{
|
|
- Log(LOG_NORM, "%s: Device dictionary contained non-Device entry '%s'.", __func__, pDevice->ob_type->tp_name);
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)pDevice->ob_type, "__name__");
|
|
+ Log(LOG_NORM, "%s: Device dictionary contained non-Device entry '%s'.", __func__, ((std::string)pName).c_str());
|
|
}
|
|
else
|
|
{
|
|
@@ -2430,7 +2126,8 @@ namespace Plugins
|
|
}
|
|
else if (isValue == 0)
|
|
{
|
|
- _log.Log(LOG_NORM, "%s: Unit dictionary contained non-Unit entry '%s'.", __func__, pUnit->ob_type->tp_name);
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)pUnit->ob_type, "__name__");
|
|
+ _log.Log(LOG_NORM, "%s: Unit dictionary contained non-Unit entry '%s'.", __func__, ((std::string)pName).c_str());
|
|
}
|
|
else
|
|
{
|
|
@@ -2520,7 +2217,7 @@ namespace Plugins
|
|
PyBorrowedRef pModuleDict = PyModule_GetDict(PythonModule()); // returns a borrowed referece to the __dict__ object for the module
|
|
if (m_SettingsDict)
|
|
Py_XDECREF(m_SettingsDict);
|
|
- m_SettingsDict = (PyDictObject *)PyDict_New();
|
|
+ m_SettingsDict = PyDict_New();
|
|
if (PyDict_SetItemString(pModuleDict, "Settings", (PyObject *)m_SettingsDict) == -1)
|
|
{
|
|
Log(LOG_ERROR, "(%s) failed to add Settings dictionary.", m_PluginKey.c_str());
|
|
@@ -2532,7 +2229,6 @@ namespace Plugins
|
|
result = m_sql.safe_query("SELECT Key, nValue, sValue FROM Preferences");
|
|
if (!result.empty())
|
|
{
|
|
- PyType_Ready(&CDeviceType);
|
|
// Add settings strings into the settings dictionary with Unit as the key
|
|
for (const auto &sd : result)
|
|
{
|
|
@@ -2617,12 +2313,15 @@ namespace Plugins
|
|
if (!m_DeviceDict)
|
|
return true;
|
|
|
|
+ return false;
|
|
+
|
|
PyObject *key, *value;
|
|
Py_ssize_t pos = 0;
|
|
while (PyDict_Next((PyObject *)m_DeviceDict, &pos, &key, &value))
|
|
{
|
|
// Handle different Device dictionaries types
|
|
- if (PyUnicode_Check(key))
|
|
+ PyBorrowedRef pKeyType(key);
|
|
+ if (pKeyType.IsString())
|
|
{
|
|
// Version 2+ of the framework, keyed by DeviceID
|
|
std::string sKey = PyUnicode_AsUTF8(key);
|
|
@@ -2632,7 +2331,7 @@ namespace Plugins
|
|
return (pDevice->TimedOut != 0);
|
|
}
|
|
}
|
|
- else
|
|
+ else if (pKeyType.IsLong())
|
|
{
|
|
// Version 1 of the framework, keyed by Unit
|
|
long iKey = PyLong_AsLong(key);
|
|
@@ -2648,6 +2347,10 @@ namespace Plugins
|
|
return (pDevice->TimedOut != 0);
|
|
}
|
|
}
|
|
+ else
|
|
+ {
|
|
+ Log(LOG_ERROR, "'%s' Invalid Node key type.", __func__);
|
|
+ }
|
|
}
|
|
|
|
return false;
|
|
@@ -2655,7 +2358,7 @@ namespace Plugins
|
|
|
|
PyBorrowedRef CPlugin::FindDevice(const std::string &Key)
|
|
{
|
|
- if (m_DeviceDict && PyDict_Check(m_DeviceDict))
|
|
+ if (m_DeviceDict && PyBorrowedRef(m_DeviceDict).IsDict())
|
|
{
|
|
return PyDict_GetItemString((PyObject*)m_DeviceDict, Key.c_str());
|
|
}
|
|
@@ -2934,5 +2637,47 @@ namespace Plugins
|
|
|
|
return true;
|
|
}
|
|
+
|
|
+ bool PyBorrowedRef::TypeCheck(long PyType)
|
|
+ {
|
|
+ if (m_pObject)
|
|
+ {
|
|
+ PyNewRef pType = PyObject_Type(m_pObject);
|
|
+ return pType && (PyType_GetFlags((PyTypeObject*)pType) & PyType);
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ std::string PyBorrowedRef::Attribute(const char* name)
|
|
+ {
|
|
+ std::string sAttr = "";
|
|
+ if (m_pObject)
|
|
+ {
|
|
+ try
|
|
+ {
|
|
+ if (PyObject_HasAttrString(m_pObject, name))
|
|
+ {
|
|
+ PyNewRef pAttr = PyObject_GetAttrString(m_pObject, name);
|
|
+ sAttr = (std::string)pAttr;
|
|
+ }
|
|
+ }
|
|
+ catch (...)
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "[%s] Exception determining Python object attribute '%s'.", __func__, name);
|
|
+ }
|
|
+ }
|
|
+ return sAttr;
|
|
+ }
|
|
+
|
|
+ std::string PyBorrowedRef::Type()
|
|
+ {
|
|
+ std::string sType = "";
|
|
+ if (m_pObject)
|
|
+ {
|
|
+ PyNewRef pType = PyObject_Type(m_pObject);
|
|
+ sType = pType.Attribute("__name__");
|
|
+ }
|
|
+ return sType;
|
|
+ }
|
|
} // namespace Plugins
|
|
#endif
|
|
--- a/hardware/plugins/Plugins.h
|
|
+++ b/hardware/plugins/Plugins.h
|
|
@@ -62,8 +62,6 @@ namespace Plugins {
|
|
|
|
void Do_Work();
|
|
|
|
- void LogPythonException(const std::string &);
|
|
-
|
|
public:
|
|
CPlugin(int HwdID, const std::string &Name, const std::string &PluginKey);
|
|
~CPlugin() override;
|
|
@@ -75,7 +73,7 @@ namespace Plugins {
|
|
bool StopHardware() override;
|
|
|
|
void LogPythonException();
|
|
- void LogTraceback(PyTracebackObject *pTraceback);
|
|
+ void LogPythonException(const std::string&);
|
|
|
|
int PollInterval(int Interval = -1);
|
|
PyObject* PythonModule() { return m_PyModule; };
|
|
@@ -119,9 +117,9 @@ namespace Plugins {
|
|
PyBorrowedRef FindUnitInDevice(const std::string &deviceKey, const int unitKey);
|
|
|
|
std::string m_PluginKey;
|
|
- PyDictObject* m_DeviceDict;
|
|
- PyDictObject* m_ImageDict;
|
|
- PyDictObject* m_SettingsDict;
|
|
+ PyObject* m_DeviceDict;
|
|
+ PyObject* m_ImageDict;
|
|
+ PyObject* m_SettingsDict;
|
|
std::string m_HomeFolder;
|
|
PluginDebugMask m_bDebug;
|
|
bool m_bTracing;
|
|
@@ -147,16 +145,29 @@ namespace Plugins {
|
|
//
|
|
class PyBorrowedRef
|
|
{
|
|
- protected:
|
|
+ protected:
|
|
PyObject *m_pObject;
|
|
+ bool TypeCheck(long);
|
|
|
|
- public:
|
|
+ public:
|
|
PyBorrowedRef()
|
|
: m_pObject(NULL){};
|
|
PyBorrowedRef(PyObject *pObject)
|
|
{
|
|
m_pObject = pObject;
|
|
};
|
|
+ std::string Attribute(const char* name);
|
|
+ std::string Type();
|
|
+ bool IsDict() { return TypeCheck(Py_TPFLAGS_DICT_SUBCLASS); };
|
|
+ bool IsList() { return TypeCheck(Py_TPFLAGS_LIST_SUBCLASS); };
|
|
+ bool IsLong() { return TypeCheck(Py_TPFLAGS_LONG_SUBCLASS); };
|
|
+ bool IsTuple() { return TypeCheck(Py_TPFLAGS_TUPLE_SUBCLASS); };
|
|
+ bool IsString() { return TypeCheck(Py_TPFLAGS_UNICODE_SUBCLASS); };
|
|
+ bool IsBytes() { return TypeCheck(Py_TPFLAGS_BYTES_SUBCLASS); };
|
|
+ bool IsByteArray() { return Type() == "bytearray"; };
|
|
+ bool IsFloat() { return Type() == "float"; };
|
|
+ bool IsBool() { return Type() == "bool"; };
|
|
+ bool IsNone() { return m_pObject && (m_pObject == Py_None); };
|
|
operator PyObject *() const
|
|
{
|
|
return m_pObject;
|
|
@@ -165,10 +176,6 @@ namespace Plugins {
|
|
{
|
|
return (PyTypeObject *)m_pObject;
|
|
}
|
|
- operator PyBytesObject *() const
|
|
- {
|
|
- return (PyBytesObject *)m_pObject;
|
|
- }
|
|
operator bool() const
|
|
{
|
|
return (m_pObject != NULL);
|
|
@@ -283,12 +290,8 @@ namespace Plugins {
|
|
class AccessPython
|
|
{
|
|
private:
|
|
- static std::mutex PythonMutex;
|
|
- static volatile bool m_bHasThreadState;
|
|
- std::unique_lock<std::mutex>* m_Lock;
|
|
- PyThreadState* m_Python;
|
|
CPlugin* m_pPlugin;
|
|
- const char* m_Text;
|
|
+ std::string m_Text;
|
|
|
|
public:
|
|
AccessPython(CPlugin* pPlugin, const char* sWhat);
|
|
--- a/hardware/plugins/PythonObjectEx.cpp
|
|
+++ b/hardware/plugins/PythonObjectEx.cpp
|
|
@@ -8,7 +8,6 @@
|
|
#include "../../main/Logger.h"
|
|
#include "../../main/SQLHelper.h"
|
|
#include "../../hardware/hardwaretypes.h"
|
|
-#include "../../main/localtime_r.h"
|
|
#include "../../main/mainstructs.h"
|
|
#include "../../main/mainworker.h"
|
|
#include "../../main/EventSystem.h"
|
|
@@ -23,19 +22,22 @@
|
|
namespace Plugins {
|
|
|
|
extern struct PyModuleDef DomoticzExModuleDef;
|
|
- extern void LogPythonException(CPlugin *pPlugin, const std::string &sHandler);
|
|
extern void maptypename(const std::string &sTypeName, int &Type, int &SubType, int &SwitchType, std::string &sValue, PyObject *OptionsIn, PyObject *OptionsOut);
|
|
|
|
void CDeviceEx_dealloc(CDeviceEx *self)
|
|
{
|
|
Py_XDECREF(self->DeviceID);
|
|
Py_XDECREF(self->m_UnitDict);
|
|
- Py_TYPE(self)->tp_free((PyObject *)self);
|
|
+
|
|
+ PyNewRef pType = PyObject_Type((PyObject*)self);
|
|
+ freefunc pFree = (freefunc)PyType_GetSlot(pType, Py_tp_free);
|
|
+ pFree((PyObject*)self);
|
|
}
|
|
|
|
PyObject *CDeviceEx_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
- CDeviceEx *self = (CDeviceEx *)type->tp_alloc(type, 0);
|
|
+ allocfunc pAlloc = (allocfunc)PyType_GetSlot(type, Py_tp_alloc);
|
|
+ CDeviceEx* self = (CDeviceEx*)pAlloc(type, 0);
|
|
|
|
try
|
|
{
|
|
@@ -95,11 +97,8 @@ namespace Plugins {
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &DeviceID))
|
|
{
|
|
- CPlugin *pPlugin = nullptr;
|
|
- if (pModState)
|
|
- pPlugin = pModState->pPlugin;
|
|
pModState->pPlugin->Log(LOG_ERROR, R"(Expected: myVar = Domoticz.DeviceEx(DeviceID='xxxx'))");
|
|
- LogPythonException(pPlugin, __func__);
|
|
+ pModState->pPlugin->LogPythonException(__func__);
|
|
}
|
|
else
|
|
{
|
|
@@ -108,7 +107,7 @@ namespace Plugins {
|
|
{
|
|
self->DeviceID = PyUnicode_FromString(DeviceID);
|
|
}
|
|
- self->m_UnitDict = (PyDictObject *)PyDict_New();
|
|
+ self->m_UnitDict = (PyObject *)PyDict_New();
|
|
}
|
|
|
|
return true;
|
|
@@ -147,7 +146,6 @@ namespace Plugins {
|
|
if (!result.empty())
|
|
{
|
|
|
|
- PyType_Ready(&CUnitExType);
|
|
// Create Unit objects and add the Units dictionary with Unit number as the key
|
|
for (auto itt = result.begin(); itt != result.end(); ++itt)
|
|
{
|
|
@@ -236,12 +234,16 @@ namespace Plugins {
|
|
Py_XDECREF(self->Options);
|
|
Py_XDECREF(self->Color);
|
|
Py_XDECREF(self->Parent);
|
|
- Py_TYPE(self)->tp_free((PyObject *)self);
|
|
+
|
|
+ PyNewRef pType = PyObject_Type((PyObject*)self);
|
|
+ freefunc pFree = (freefunc)PyType_GetSlot(pType, Py_tp_free);
|
|
+ pFree((PyObject*)self);
|
|
}
|
|
|
|
PyObject *CUnitEx_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
- CUnitEx *self = (CUnitEx *)type->tp_alloc(type, 0);
|
|
+ allocfunc pAlloc = (allocfunc)PyType_GetSlot(type, Py_tp_alloc);
|
|
+ CUnitEx *self = (CUnitEx*)pAlloc(type, 0);
|
|
|
|
try
|
|
{
|
|
@@ -380,7 +382,6 @@ namespace Plugins {
|
|
else
|
|
{
|
|
// Create a temporary one
|
|
- PyType_Ready(pModState->pDeviceClass);
|
|
PyNewRef nrArgList = Py_BuildValue("(s)", DeviceID);
|
|
if (!nrArgList)
|
|
{
|
|
@@ -411,43 +412,40 @@ namespace Plugins {
|
|
self->Image = Image;
|
|
if (Used == 1)
|
|
self->Used = Used;
|
|
- if (Options && PyDict_Check(Options) && PyDict_Size(Options) > 0)
|
|
+ if (Options && PyBorrowedRef(Options).IsDict() && PyDict_Size(Options) > 0)
|
|
{
|
|
PyObject *pKey, *pValue;
|
|
Py_ssize_t pos = 0;
|
|
PyDict_Clear(self->Options);
|
|
while (PyDict_Next(Options, &pos, &pKey, &pValue))
|
|
{
|
|
- if (PyUnicode_Check(pValue))
|
|
+ PyNewRef pKeyDict = PyObject_Str(pKey);
|
|
+ PyNewRef pValueDict = PyObject_Str(pValue);
|
|
+
|
|
+ if (pKeyDict && pValueDict)
|
|
{
|
|
- PyNewRef pKeyDict = PyUnicode_FromKindAndData(PyUnicode_KIND(pKey), PyUnicode_DATA(pKey), PyUnicode_GET_LENGTH(pKey));
|
|
- PyNewRef pValueDict = PyUnicode_FromKindAndData(PyUnicode_KIND(pValue), PyUnicode_DATA(pValue), PyUnicode_GET_LENGTH(pValue));
|
|
if (PyDict_SetItem(self->Options, pKeyDict, pValueDict) == -1)
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Failed to initialize Options dictionary for Hardware/Unit combination (%d:%d).",
|
|
- pModState->pPlugin->m_Name.c_str(), pModState->pPlugin->m_HwdID, self->Unit);
|
|
+ pModState->pPlugin->Log(LOG_ERROR, "(%s) Failed to initialize Options dictionary for Hardware/Unit combination (%d:%d).",
|
|
+ pModState->pPlugin->m_Name.c_str(), pModState->pPlugin->m_HwdID, self->Unit);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)pValue->ob_type, "__name__");
|
|
+ pModState->pPlugin->Log(
|
|
LOG_ERROR,
|
|
- R"((%s) Failed to initialize Options dictionary for Hardware/Unit combination (%d:%d): Only "string" type dictionary entries supported, but entry has type "%s")",
|
|
- pModState->pPlugin->m_Name.c_str(), pModState->pPlugin->m_HwdID, self->Unit, pValue->ob_type->tp_name);
|
|
+ "(%s) Failed to initialize Options dictionary for Hardware / Unit combination(%d:%d): Unable to convert to string.)",
|
|
+ pModState->pPlugin->m_Name.c_str(), pModState->pPlugin->m_HwdID, self->Unit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- CPlugin *pPlugin = nullptr;
|
|
- if (pModState)
|
|
- {
|
|
- pPlugin = pModState->pPlugin;
|
|
- _log.Log(LOG_ERROR, R"(Expected: myVar = DomoticzEx.Unit(Name="myDevice", DeviceID="", Unit=0, TypeName="", Type=0, Subtype=0, Switchtype=0, Image=0, Options={}, Used=1, Description=""))");
|
|
- LogPythonException(pPlugin, __func__);
|
|
- }
|
|
+ pModState->pPlugin->Log(LOG_ERROR, R"(Expected: myVar = DomoticzEx.Unit(Name="myDevice", DeviceID="", Unit=0, TypeName="", Type=0, Subtype=0, Switchtype=0, Image=0, Options={}, Used=1, Description=""))");
|
|
+ pModState->pPlugin->LogPythonException(__func__);
|
|
}
|
|
}
|
|
catch (std::exception *e)
|
|
@@ -756,7 +754,7 @@ namespace Plugins {
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ps", kwlist, &bWriteLog, &TypeName))
|
|
{
|
|
pModState->pPlugin->Log(LOG_ERROR, "(%s) Failed to parse parameters: 'Log' and/or 'TypeName' expected.", __func__);
|
|
- LogPythonException(pModState->pPlugin, __func__);
|
|
+ pModState->pPlugin->LogPythonException(__func__);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
@@ -789,7 +787,7 @@ namespace Plugins {
|
|
|
|
// Options provided, assume change
|
|
std::string sOptionValue;
|
|
- if (pOptionsDict && PyDict_Check(pOptionsDict))
|
|
+ if (pOptionsDict && pOptionsDict.IsDict())
|
|
{
|
|
if (self->SubType != sTypeCustom)
|
|
{
|
|
--- a/hardware/plugins/PythonObjectEx.h
|
|
+++ b/hardware/plugins/PythonObjectEx.h
|
|
@@ -12,7 +12,7 @@ namespace Plugins {
|
|
PyObject_HEAD
|
|
PyObject* DeviceID;
|
|
int TimedOut;
|
|
- PyDictObject* m_UnitDict;
|
|
+ PyObject* m_UnitDict;
|
|
|
|
static bool isInstance(PyObject *pObject);
|
|
};
|
|
@@ -36,46 +36,6 @@ namespace Plugins {
|
|
{ nullptr } /* Sentinel */
|
|
};
|
|
|
|
- static PyTypeObject CDeviceExType = {
|
|
- PyVarObject_HEAD_INIT(nullptr, 0) "DomoticzEx.Device", /* tp_name */
|
|
- sizeof(CDeviceEx), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)CDeviceEx_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- nullptr, /* tp_getattr */
|
|
- nullptr, /* tp_setattr */
|
|
- nullptr, /* tp_reserved */
|
|
- nullptr, /* tp_repr */
|
|
- nullptr, /* tp_as_number */
|
|
- nullptr, /* tp_as_sequence */
|
|
- nullptr, /* tp_as_mapping */
|
|
- nullptr, /* tp_hash */
|
|
- nullptr, /* tp_call */
|
|
- (reprfunc)CDeviceEx_str, /* tp_str */
|
|
- nullptr, /* tp_getattro */
|
|
- nullptr, /* tp_setattro */
|
|
- nullptr, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
- "DomoticzEx Device", /* tp_doc */
|
|
- nullptr, /* tp_traverse */
|
|
- nullptr, /* tp_clear */
|
|
- nullptr, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- nullptr, /* tp_iter */
|
|
- nullptr, /* tp_iternext */
|
|
- CDeviceEx_methods, /* tp_methods */
|
|
- CDeviceEx_members, /* tp_members */
|
|
- nullptr, /* tp_getset */
|
|
- nullptr, /* tp_base */
|
|
- nullptr, /* tp_dict */
|
|
- nullptr, /* tp_descr_get */
|
|
- nullptr, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)CDeviceEx_init, /* tp_init */
|
|
- nullptr, /* tp_alloc */
|
|
- CDeviceEx_new /* tp_new */
|
|
- };
|
|
-
|
|
class CUnitEx
|
|
{
|
|
public:
|
|
@@ -146,44 +106,5 @@ namespace Plugins {
|
|
{ "Touch", (PyCFunction)CUnitEx_touch, METH_NOARGS, "Notify Domoticz that device has been seen." },
|
|
{ nullptr } /* Sentinel */
|
|
};
|
|
-
|
|
- static PyTypeObject CUnitExType = {
|
|
- PyVarObject_HEAD_INIT(nullptr, 0) "DomoticzEx.Unit", /* tp_name */
|
|
- sizeof(CUnitEx), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)CUnitEx_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- nullptr, /* tp_getattr */
|
|
- nullptr, /* tp_setattr */
|
|
- nullptr, /* tp_reserved */
|
|
- nullptr, /* tp_repr */
|
|
- nullptr, /* tp_as_number */
|
|
- nullptr, /* tp_as_sequence */
|
|
- nullptr, /* tp_as_mapping */
|
|
- nullptr, /* tp_hash */
|
|
- nullptr, /* tp_call */
|
|
- (reprfunc)CUnitEx_str, /* tp_str */
|
|
- nullptr, /* tp_getattro */
|
|
- nullptr, /* tp_setattro */
|
|
- nullptr, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
- "DomoticzEx Unit", /* tp_doc */
|
|
- nullptr, /* tp_traverse */
|
|
- nullptr, /* tp_clear */
|
|
- nullptr, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- nullptr, /* tp_iter */
|
|
- nullptr, /* tp_iternext */
|
|
- CUnitEx_methods, /* tp_methods */
|
|
- CUnitEx_members, /* tp_members */
|
|
- nullptr, /* tp_getset */
|
|
- nullptr, /* tp_base */
|
|
- nullptr, /* tp_dict */
|
|
- nullptr, /* tp_descr_get */
|
|
- nullptr, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)CUnitEx_init, /* tp_init */
|
|
- nullptr, /* tp_alloc */
|
|
- CUnitEx_new /* tp_new */
|
|
- };
|
|
+
|
|
} // namespace Plugins
|
|
--- a/hardware/plugins/PythonObjects.cpp
|
|
+++ b/hardware/plugins/PythonObjects.cpp
|
|
@@ -8,7 +8,6 @@
|
|
#include "../../main/Logger.h"
|
|
#include "../../main/SQLHelper.h"
|
|
#include "../../hardware/hardwaretypes.h"
|
|
-#include "../../main/localtime_r.h"
|
|
#include "../../main/mainstructs.h"
|
|
#include "../../main/mainworker.h"
|
|
#include "../../main/EventSystem.h"
|
|
@@ -22,21 +21,28 @@
|
|
|
|
namespace Plugins {
|
|
|
|
+ PyTypeObject* CDeviceType = nullptr;
|
|
+ PyTypeObject* CConnectionType = nullptr;
|
|
+ PyTypeObject* CImageType = nullptr;
|
|
+
|
|
extern struct PyModuleDef DomoticzModuleDef;
|
|
extern struct PyModuleDef DomoticzExModuleDef;
|
|
- extern void LogPythonException(CPlugin *pPlugin, const std::string &sHandler);
|
|
|
|
void CImage_dealloc(CImage* self)
|
|
{
|
|
Py_XDECREF(self->Base);
|
|
Py_XDECREF(self->Name);
|
|
Py_XDECREF(self->Description);
|
|
- Py_TYPE(self)->tp_free((PyObject*)self);
|
|
+
|
|
+ PyNewRef pType = PyObject_Type((PyObject*)self);
|
|
+ freefunc pFree = (freefunc)PyType_GetSlot(pType, Py_tp_free);
|
|
+ pFree((PyObject*)self);
|
|
}
|
|
|
|
PyObject* CImage_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
- CImage *self = (CImage *)type->tp_alloc(type, 0);
|
|
+ allocfunc pAlloc = (allocfunc)PyType_GetSlot(type, Py_tp_alloc);
|
|
+ CImage *self = (CImage *)pAlloc(type, 0);
|
|
|
|
try
|
|
{
|
|
@@ -130,10 +136,8 @@ namespace Plugins {
|
|
}
|
|
else
|
|
{
|
|
- CPlugin *pPlugin = nullptr;
|
|
- if (pModState) pPlugin = pModState->pPlugin;
|
|
- _log.Log(LOG_ERROR, "Expected: myVar = Domoticz.Image(Filename=\"MyImages.zip\")");
|
|
- LogPythonException(pPlugin, __func__);
|
|
+ pModState->pPlugin->Log(LOG_ERROR, "Expected: myVar = Domoticz.Image(Filename=\"MyImages.zip\")");
|
|
+ pModState->pPlugin->LogPythonException(__func__);
|
|
}
|
|
}
|
|
catch (std::exception *e)
|
|
@@ -177,11 +181,10 @@ namespace Plugins {
|
|
std::vector<std::vector<std::string> > result = m_sql.safe_query("SELECT max(ID), Base, Name, Description FROM CustomImages");
|
|
if (!result.empty())
|
|
{
|
|
- PyType_Ready(&CImageType);
|
|
// Add image objects into the image dictionary with ID as the key
|
|
for (const auto &sd : result)
|
|
{
|
|
- CImage *pImage = (CImage *)CImage_new(&CImageType, (PyObject *)nullptr,
|
|
+ CImage *pImage = (CImage *)CImage_new(CImageType, (PyObject *)nullptr,
|
|
(PyObject *)nullptr);
|
|
|
|
PyObject* pKey = PyUnicode_FromString(sd[1].c_str());
|
|
@@ -226,7 +229,7 @@ namespace Plugins {
|
|
{
|
|
if (self->pPlugin->m_bDebug & PDM_IMAGE)
|
|
{
|
|
- _log.Log(LOG_NORM, "(%s) Deleting Image '%s'.", self->pPlugin->m_Name.c_str(), sName.c_str());
|
|
+ _log.Log(LOG_NORM, "Deleting Image '%s'.", sName.c_str());
|
|
}
|
|
|
|
std::vector<std::vector<std::string> > result;
|
|
@@ -238,19 +241,18 @@ namespace Plugins {
|
|
PyNewRef pKey = PyLong_FromLong(self->ImageID);
|
|
if (PyDict_DelItem((PyObject*)self->pPlugin->m_ImageDict, pKey) == -1)
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) failed to delete image '%d' from images dictionary.", self->pPlugin->m_Name.c_str(), self->ImageID);
|
|
- Py_INCREF(Py_None);
|
|
- return Py_None;
|
|
+ self->pPlugin->Log(LOG_ERROR, "Failed to delete image '%d' from images dictionary.", self->ImageID);
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Image deletion failed, Image %d not found in Domoticz.", self->pPlugin->m_Name.c_str(), self->ImageID);
|
|
+ self->pPlugin->Log(LOG_ERROR, "Image deletion failed, Image %d not found in Domoticz.", self->ImageID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Image deletion failed, '%s' does not represent a Image in Domoticz.", self->pPlugin->m_Name.c_str(), sName.c_str());
|
|
+ self->pPlugin->Log(LOG_ERROR, "Image deletion failed, '%s' does not represent a Image in Domoticz.", sName.c_str());
|
|
}
|
|
}
|
|
else
|
|
@@ -278,12 +280,16 @@ namespace Plugins {
|
|
PyDict_Clear(self->Options);
|
|
Py_XDECREF(self->Options);
|
|
Py_XDECREF(self->Color);
|
|
- Py_TYPE(self)->tp_free((PyObject*)self);
|
|
+
|
|
+ PyNewRef pType = PyObject_Type((PyObject*)self);
|
|
+ freefunc pFree = (freefunc)PyType_GetSlot(pType, Py_tp_free);
|
|
+ pFree((PyObject*)self);
|
|
}
|
|
|
|
PyObject* CDevice_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
- CDevice *self = (CDevice *)type->tp_alloc(type, 0);
|
|
+ allocfunc pAlloc = (allocfunc)PyType_GetSlot(type, Py_tp_alloc);
|
|
+ CDevice *self = (CDevice*)pAlloc(type, 0);
|
|
|
|
try
|
|
{
|
|
@@ -473,7 +479,7 @@ namespace Plugins {
|
|
}
|
|
else if (sTypeName == "Selector Switch")
|
|
{
|
|
- if (!OptionsIn || !PyDict_Check(OptionsIn)) {
|
|
+ if (!OptionsIn || !PyBorrowedRef(OptionsIn).IsDict()) {
|
|
PyDict_Clear(OptionsOut);
|
|
PyDict_SetItemString(OptionsOut, "LevelActions", PyUnicode_FromString("|||"));
|
|
PyDict_SetItemString(OptionsOut, "LevelNames", PyUnicode_FromString("Off|Level1|Level2|Level3"));
|
|
@@ -517,7 +523,7 @@ namespace Plugins {
|
|
else if (sTypeName == "Custom")
|
|
{
|
|
SubType = sTypeCustom;
|
|
- if (!OptionsIn || !PyDict_Check(OptionsIn)) {
|
|
+ if (!OptionsIn || !PyBorrowedRef(OptionsIn).IsDict()) {
|
|
PyDict_Clear(OptionsOut);
|
|
PyDict_SetItemString(OptionsOut, "Custom", PyUnicode_FromString("1"));
|
|
}
|
|
@@ -615,42 +621,39 @@ namespace Plugins {
|
|
if (SwitchType != -1) self->SwitchType = SwitchType;
|
|
if (Image != -1) self->Image = Image;
|
|
if (Used == 1) self->Used = Used;
|
|
- if (Options && PyDict_Check(Options) && PyDict_Size(Options) > 0) {
|
|
+ if (Options && PyBorrowedRef(Options).IsDict() && PyDict_Size(Options) > 0) {
|
|
PyObject *pKey, *pValue;
|
|
Py_ssize_t pos = 0;
|
|
PyDict_Clear(self->Options);
|
|
- while(PyDict_Next(Options, &pos, &pKey, &pValue))
|
|
+ while (PyDict_Next(Options, &pos, &pKey, &pValue))
|
|
{
|
|
- if (PyUnicode_Check(pValue))
|
|
+ PyNewRef pKeyDict = PyObject_Str(pKey);
|
|
+ PyNewRef pValueDict = PyObject_Str(pValue);
|
|
+
|
|
+ if (pKeyDict && pValueDict)
|
|
{
|
|
- PyObject *pKeyDict = PyUnicode_FromKindAndData(PyUnicode_KIND(pKey), PyUnicode_DATA(pKey), PyUnicode_GET_LENGTH(pKey));
|
|
- PyObject *pValueDict = PyUnicode_FromKindAndData(PyUnicode_KIND(pValue), PyUnicode_DATA(pValue), PyUnicode_GET_LENGTH(pValue));
|
|
if (PyDict_SetItem(self->Options, pKeyDict, pValueDict) == -1)
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Failed to initialize Options dictionary for Hardware/Unit combination (%d:%d).", self->pPlugin->m_Name.c_str(), self->HwdID, self->Unit);
|
|
- Py_XDECREF(pKeyDict);
|
|
- Py_XDECREF(pValueDict);
|
|
+ pModState->pPlugin->Log(LOG_ERROR, "(%s) Failed to initialize Options dictionary for Hardware/Unit combination (%d:%d).",
|
|
+ pModState->pPlugin->m_Name.c_str(), pModState->pPlugin->m_HwdID, self->Unit);
|
|
break;
|
|
}
|
|
- Py_XDECREF(pKeyDict);
|
|
- Py_XDECREF(pValueDict);
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)pValue->ob_type, "__name__");
|
|
+ pModState->pPlugin->Log(
|
|
LOG_ERROR,
|
|
- R"((%s) Failed to initialize Options dictionary for Hardware/Unit combination (%d:%d): Only "string" type dictionary entries supported, but entry has type "%s")",
|
|
- self->pPlugin->m_Name.c_str(), self->HwdID, self->Unit, pValue->ob_type->tp_name);
|
|
+ "(%s) Failed to initialize Options dictionary for Hardware / Unit combination(%d:%d): Unable to convert to string.)",
|
|
+ pModState->pPlugin->m_Name.c_str(), pModState->pPlugin->m_HwdID, self->Unit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- CPlugin *pPlugin = nullptr;
|
|
- if (pModState) pPlugin = pModState->pPlugin;
|
|
- _log.Log(LOG_ERROR, R"(Expected: myVar = Domoticz.Device(Name="myDevice", Unit=0, TypeName="", Type=0, Subtype=0, Switchtype=0, Image=0, Options={}, Used=1))");
|
|
- LogPythonException(pPlugin, __func__);
|
|
+ pModState->pPlugin->Log(LOG_ERROR, R"(Expected: myVar = Domoticz.Device(Name="myDevice", Unit=0, TypeName="", Type=0, Subtype=0, Switchtype=0, Image=0, Options={}, Used=1))");
|
|
+ pModState->pPlugin->LogPythonException(__func__);
|
|
}
|
|
}
|
|
catch (std::exception *e)
|
|
@@ -745,12 +748,12 @@ namespace Plugins {
|
|
{
|
|
if (self->pPlugin->m_bDebug & PDM_DEVICE)
|
|
{
|
|
- _log.Log(LOG_NORM, "(%s) Creating device '%s'.", self->pPlugin->m_Name.c_str(), sName.c_str());
|
|
+ self->pPlugin->Log(LOG_NORM, "Creating device '%s'.", sName.c_str());
|
|
}
|
|
|
|
if (!m_sql.m_bAcceptNewHardware)
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Device creation failed, Domoticz settings prevent accepting new devices.", self->pPlugin->m_Name.c_str());
|
|
+ self->pPlugin->Log(LOG_ERROR, "Device creation failed, Domoticz settings prevent accepting new devices.");
|
|
}
|
|
else
|
|
{
|
|
@@ -792,9 +795,8 @@ namespace Plugins {
|
|
PyNewRef pKey = PyLong_FromLong(self->Unit);
|
|
if (PyDict_SetItem((PyObject*)self->pPlugin->m_DeviceDict, pKey, (PyObject*)self) == -1)
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) failed to add unit '%d' to device dictionary.", self->pPlugin->m_Name.c_str(), self->Unit);
|
|
- Py_INCREF(Py_None);
|
|
- return Py_None;
|
|
+ self->pPlugin->Log(LOG_ERROR, "Failed to add unit '%d' to device dictionary.", self->Unit);
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
|
|
// Device successfully created, now set the options when supplied
|
|
@@ -817,18 +819,18 @@ namespace Plugins {
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Device creation failed, Hardware/Unit combination (%d:%d) not found in Domoticz.", self->pPlugin->m_Name.c_str(), self->HwdID, self->Unit);
|
|
+ self->pPlugin->Log(LOG_ERROR, "Device creation failed, Hardware/Unit combination (%d:%d) not found in Domoticz.", self->HwdID, self->Unit);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Device creation failed, Hardware/Unit combination (%d:%d) already exists in Domoticz.", self->pPlugin->m_Name.c_str(), self->HwdID, self->Unit);
|
|
+ self->pPlugin->Log(LOG_ERROR, "Device creation failed, Hardware/Unit combination (%d:%d) already exists in Domoticz.", self->HwdID, self->Unit);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Device creation failed, '%s' already exists in Domoticz with Device ID '%d'.", self->pPlugin->m_Name.c_str(), sName.c_str(), self->ID);
|
|
+ self->pPlugin->Log(LOG_ERROR, "Device creation failed, '%s' already exists in Domoticz with Device ID '%d'.", sName.c_str(), self->ID);
|
|
}
|
|
}
|
|
else
|
|
@@ -874,11 +876,10 @@ namespace Plugins {
|
|
|
|
// Try to extract parameters needed to update device settings
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|iiiOissiiiissp", kwlist, &nValue, &sValue, &iImage, &iSignalLevel, &iBatteryLevel, &pOptionsDict, &iTimedOut, &Name, &TypeName, &iType, &iSubType, &iSwitchType, &iUsed, &Description, &Color, &SuppressTriggers))
|
|
- {
|
|
- _log.Log(LOG_ERROR, "(%s) %s: Failed to parse parameters: 'nValue', 'sValue', 'Image', 'SignalLevel', 'BatteryLevel', 'Options', 'TimedOut', 'Name', 'TypeName', 'Type', 'Subtype', 'Switchtype', 'Used', 'Description', 'Color' or 'SuppressTriggers' expected.", __func__, sName.c_str());
|
|
- LogPythonException(self->pPlugin, __func__);
|
|
- Py_INCREF(Py_None);
|
|
- return Py_None;
|
|
+ {
|
|
+ self->pPlugin->Log(LOG_ERROR, "(%s) %s: Failed to parse parameters: 'nValue', 'sValue', 'Image', 'SignalLevel', 'BatteryLevel', 'Options', 'TimedOut', 'Name', 'TypeName', 'Type', 'Subtype', 'Switchtype', 'Used', 'Description', 'Color' or 'SuppressTriggers' expected.", __func__, sName.c_str());
|
|
+ self->pPlugin->LogPythonException(__func__);
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
|
|
std::string sID = std::to_string(self->ID);
|
|
@@ -979,7 +980,7 @@ namespace Plugins {
|
|
}
|
|
|
|
// Options provided, assume change
|
|
- if (pOptionsDict && PyDict_Check(pOptionsDict))
|
|
+ if (pOptionsDict && PyBorrowedRef(pOptionsDict).IsDict())
|
|
{
|
|
if (self->SubType != sTypeCustom)
|
|
{
|
|
@@ -1094,7 +1095,7 @@ namespace Plugins {
|
|
{
|
|
if (self->pPlugin->m_bDebug & PDM_DEVICE)
|
|
{
|
|
- _log.Log(LOG_NORM, "(%s) Deleting device '%s'.", self->pPlugin->m_Name.c_str(), sName.c_str());
|
|
+ self->pPlugin->Log(LOG_NORM, "Deleting device '%s'.", sName.c_str());
|
|
}
|
|
|
|
std::vector<std::vector<std::string> > result;
|
|
@@ -1106,19 +1107,18 @@ namespace Plugins {
|
|
PyNewRef pKey = PyLong_FromLong(self->Unit);
|
|
if (PyDict_DelItem((PyObject*)self->pPlugin->m_DeviceDict, pKey) == -1)
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) failed to delete unit '%d' from device dictionary.", self->pPlugin->m_Name.c_str(), self->Unit);
|
|
- Py_INCREF(Py_None);
|
|
- return Py_None;
|
|
+ self->pPlugin->Log(LOG_ERROR, "Failed to delete unit '%d' from device dictionary.", self->Unit);
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Device deletion failed, Hardware/Unit combination (%d:%d) not found in Domoticz.", self->pPlugin->m_Name.c_str(), self->HwdID, self->Unit);
|
|
+ self->pPlugin->Log(LOG_ERROR, "Device deletion failed, Hardware/Unit combination (%d:%d) not found in Domoticz.", self->HwdID, self->Unit);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
- _log.Log(LOG_ERROR, "(%s) Device deletion failed, '%s' does not represent a device in Domoticz.", self->pPlugin->m_Name.c_str(), sName.c_str());
|
|
+ self->pPlugin->Log(LOG_ERROR, "Device deletion failed, '%s' does not represent a device in Domoticz.", sName.c_str());
|
|
}
|
|
}
|
|
else
|
|
@@ -1155,10 +1155,14 @@ namespace Plugins {
|
|
|
|
void CConnection_dealloc(CConnection * self)
|
|
{
|
|
- CPlugin *pPlugin = CPlugin::FindPlugin();
|
|
+ CPlugin *pPlugin = self->pPlugin;
|
|
+ if (!pPlugin)
|
|
+ {
|
|
+ pPlugin = CPlugin::FindPlugin();
|
|
+ }
|
|
if (pPlugin && (pPlugin->m_bDebug & PDM_CONNECTION))
|
|
{
|
|
- _log.Log(LOG_NORM, "(%s) Deallocating connection object '%s' (%s:%s).", pPlugin->m_Name.c_str(), PyUnicode_AsUTF8(self->Name), PyUnicode_AsUTF8(self->Address), PyUnicode_AsUTF8(self->Port));
|
|
+ pPlugin->Log(LOG_NORM, "Deallocating connection object '%s' (%s:%s).", PyUnicode_AsUTF8(self->Name), PyUnicode_AsUTF8(self->Address), PyUnicode_AsUTF8(self->Port));
|
|
}
|
|
|
|
Py_XDECREF(self->Target);
|
|
@@ -1180,22 +1184,15 @@ namespace Plugins {
|
|
self->pProtocol = nullptr;
|
|
}
|
|
|
|
- Py_TYPE(self)->tp_free((PyObject*)self);
|
|
+ PyNewRef pType = PyObject_Type((PyObject*)self);
|
|
+ freefunc pFree = (freefunc)PyType_GetSlot(pType, Py_tp_free);
|
|
+ pFree((PyObject*)self);
|
|
}
|
|
|
|
PyObject * CConnection_new(PyTypeObject * type, PyObject * args, PyObject * kwds)
|
|
{
|
|
- CConnection *self = nullptr;
|
|
- if ((CConnection *)type->tp_alloc)
|
|
- {
|
|
- self = (CConnection *)type->tp_alloc(type, 0);
|
|
- }
|
|
- else
|
|
- {
|
|
- //!Giz: self = NULL here!!
|
|
- //_log.Log(LOG_ERROR, "(%s) CConnection Type is not ready.", self->pPlugin->m_Name.c_str());
|
|
- _log.Log(LOG_ERROR, "(Python plugin) CConnection Type is not ready!");
|
|
- }
|
|
+ allocfunc pAlloc = (allocfunc)PyType_GetSlot(type, Py_tp_alloc);
|
|
+ CConnection *self = (CConnection*)pAlloc(type, 0);
|
|
|
|
try
|
|
{
|
|
@@ -1335,19 +1332,19 @@ namespace Plugins {
|
|
if (pPlugin->IsStopRequested(0))
|
|
{
|
|
pPlugin->Log(LOG_NORM, "%s, connect request from '%s' ignored. Plugin is stopping.", __func__, self->pPlugin->m_Name.c_str());
|
|
- return Py_None;
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
|
|
if (self->pTransport && self->pTransport->IsConnecting())
|
|
{
|
|
pPlugin->Log(LOG_ERROR, "%s, connect request from '%s' ignored. Transport is connecting.", __func__, self->pPlugin->m_Name.c_str());
|
|
- return Py_None;
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
|
|
if (self->pTransport && self->pTransport->IsConnected())
|
|
{
|
|
pPlugin->Log(LOG_ERROR, "%s, connect request from '%s' ignored. Transport is connected.", __func__, self->pPlugin->m_Name.c_str());
|
|
- return Py_None;
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
|
|
PyObject *pTarget = NULL;
|
|
@@ -1457,7 +1454,7 @@ namespace Plugins {
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, &pData, &iDelay))
|
|
{
|
|
pPlugin->Log(LOG_ERROR, "(%s) failed to parse parameters, Message or Message, Delay expected.", pPlugin->m_Name.c_str());
|
|
- LogPythonException(pPlugin, std::string(__func__));
|
|
+ pPlugin->LogPythonException(__func__);
|
|
}
|
|
else
|
|
{
|
|
--- a/hardware/plugins/PythonObjects.h
|
|
+++ b/hardware/plugins/PythonObjects.h
|
|
@@ -40,46 +40,6 @@ namespace Plugins {
|
|
{ nullptr } /* Sentinel */
|
|
};
|
|
|
|
- static PyTypeObject CImageType = {
|
|
- PyVarObject_HEAD_INIT(nullptr, 0) "Domoticz.Image", /* tp_name */
|
|
- sizeof(CImage), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)CImage_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- nullptr, /* tp_getattr */
|
|
- nullptr, /* tp_setattr */
|
|
- nullptr, /* tp_reserved */
|
|
- nullptr, /* tp_repr */
|
|
- nullptr, /* tp_as_number */
|
|
- nullptr, /* tp_as_sequence */
|
|
- nullptr, /* tp_as_mapping */
|
|
- nullptr, /* tp_hash */
|
|
- nullptr, /* tp_call */
|
|
- (reprfunc)CImage_str, /* tp_str */
|
|
- nullptr, /* tp_getattro */
|
|
- nullptr, /* tp_setattro */
|
|
- nullptr, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
- "Domoticz Image", /* tp_doc */
|
|
- nullptr, /* tp_traverse */
|
|
- nullptr, /* tp_clear */
|
|
- nullptr, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- nullptr, /* tp_iter */
|
|
- nullptr, /* tp_iternext */
|
|
- CImage_methods, /* tp_methods */
|
|
- CImage_members, /* tp_members */
|
|
- nullptr, /* tp_getset */
|
|
- nullptr, /* tp_base */
|
|
- nullptr, /* tp_dict */
|
|
- nullptr, /* tp_descr_get */
|
|
- nullptr, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)CImage_init, /* tp_init */
|
|
- nullptr, /* tp_alloc */
|
|
- CImage_new /* tp_new */
|
|
- };
|
|
-
|
|
class CDevice
|
|
{
|
|
public:
|
|
@@ -151,46 +111,6 @@ namespace Plugins {
|
|
{ nullptr } /* Sentinel */
|
|
};
|
|
|
|
- static PyTypeObject CDeviceType = {
|
|
- PyVarObject_HEAD_INIT(nullptr, 0) "Domoticz.Device", /* tp_name */
|
|
- sizeof(CDevice), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)CDevice_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- nullptr, /* tp_getattr */
|
|
- nullptr, /* tp_setattr */
|
|
- nullptr, /* tp_reserved */
|
|
- nullptr, /* tp_repr */
|
|
- nullptr, /* tp_as_number */
|
|
- nullptr, /* tp_as_sequence */
|
|
- nullptr, /* tp_as_mapping */
|
|
- nullptr, /* tp_hash */
|
|
- nullptr, /* tp_call */
|
|
- (reprfunc)CDevice_str, /* tp_str */
|
|
- nullptr, /* tp_getattro */
|
|
- nullptr, /* tp_setattro */
|
|
- nullptr, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
- "Domoticz Device", /* tp_doc */
|
|
- nullptr, /* tp_traverse */
|
|
- nullptr, /* tp_clear */
|
|
- nullptr, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- nullptr, /* tp_iter */
|
|
- nullptr, /* tp_iternext */
|
|
- CDevice_methods, /* tp_methods */
|
|
- CDevice_members, /* tp_members */
|
|
- nullptr, /* tp_getset */
|
|
- nullptr, /* tp_base */
|
|
- nullptr, /* tp_dict */
|
|
- nullptr, /* tp_descr_get */
|
|
- nullptr, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)CDevice_init, /* tp_init */
|
|
- nullptr, /* tp_alloc */
|
|
- CDevice_new /* tp_new */
|
|
- };
|
|
-
|
|
class CPluginTransport;
|
|
class CPluginProtocol;
|
|
|
|
@@ -248,43 +168,4 @@ namespace Plugins {
|
|
{ nullptr } /* Sentinel */
|
|
};
|
|
|
|
- static PyTypeObject CConnectionType = {
|
|
- PyVarObject_HEAD_INIT(nullptr, 0) "Domoticz.Connection", /* tp_name */
|
|
- sizeof(CConnection), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)CConnection_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- nullptr, /* tp_getattr */
|
|
- nullptr, /* tp_setattr */
|
|
- nullptr, /* tp_reserved */
|
|
- nullptr, /* tp_repr */
|
|
- nullptr, /* tp_as_number */
|
|
- nullptr, /* tp_as_sequence */
|
|
- nullptr, /* tp_as_mapping */
|
|
- nullptr, /* tp_hash */
|
|
- nullptr, /* tp_call */
|
|
- (reprfunc)CConnection_str, /* tp_str */
|
|
- nullptr, /* tp_getattro */
|
|
- nullptr, /* tp_setattro */
|
|
- nullptr, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
- "Domoticz Connection", /* tp_doc */
|
|
- nullptr, /* tp_traverse */
|
|
- nullptr, /* tp_clear */
|
|
- nullptr, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- nullptr, /* tp_iter */
|
|
- nullptr, /* tp_iternext */
|
|
- CConnection_methods, /* tp_methods */
|
|
- CConnection_members, /* tp_members */
|
|
- nullptr, /* tp_getset */
|
|
- nullptr, /* tp_base */
|
|
- nullptr, /* tp_dict */
|
|
- nullptr, /* tp_descr_get */
|
|
- nullptr, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)CConnection_init, /* tp_init */
|
|
- nullptr, /* tp_alloc */
|
|
- CConnection_new /* tp_new */
|
|
- };
|
|
} // namespace Plugins
|
|
--- a/main/EventSystem.cpp
|
|
+++ b/main/EventSystem.cpp
|
|
@@ -42,7 +42,6 @@ extern http::server::CWebServerHelper m_
|
|
#include "../hardware/plugins/PluginMessages.h"
|
|
#include "EventsPythonModule.h"
|
|
#include "EventsPythonDevice.h"
|
|
-extern PyObject * PDevice_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
|
|
#endif
|
|
|
|
// Helper table for Blockly and SQL name mapping
|
|
@@ -275,7 +274,7 @@ void CEventSystem::LoadEvents()
|
|
{
|
|
s = dzv_Dir + eitem.Name + ".lua";
|
|
_log.Log(LOG_STATUS, "dzVents: Write file: %s", s.c_str());
|
|
- FILE *fOut = fopen(s.c_str(), "wb+");
|
|
+ FILE* fOut = fopen(s.c_str(), "wb+");
|
|
if (fOut)
|
|
{
|
|
fwrite(eitem.Actions.c_str(), 1, eitem.Actions.size(), fOut);
|
|
--- a/main/EventsPythonDevice.cpp
|
|
+++ b/main/EventsPythonDevice.cpp
|
|
@@ -14,15 +14,21 @@
|
|
Py_XDECREF(self->n_value_string);
|
|
Py_XDECREF(self->s_value);
|
|
Py_XDECREF(self->last_update_string);
|
|
- Py_TYPE(self)->tp_free((PyObject*)self);
|
|
- }
|
|
+
|
|
+ PyTypeObject* pType = (PyTypeObject*)PyObject_Type((PyObject*)self);
|
|
+ freefunc pFree = (freefunc)PyType_GetSlot(pType, Py_tp_free);
|
|
+ pFree((PyObject*)self);
|
|
+ Py_XDECREF(pType);
|
|
+ }
|
|
|
|
PyObject *
|
|
PDevice_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
PDevice *self;
|
|
|
|
- self = (PDevice *)type->tp_alloc(type, 0);
|
|
+ allocfunc pAlloc = (allocfunc)PyType_GetSlot(type, Py_tp_alloc);
|
|
+ self = (PDevice*)pAlloc(type, 0);
|
|
+
|
|
if (self != nullptr)
|
|
{
|
|
self->name = PyUnicode_FromString("");
|
|
--- a/main/EventsPythonDevice.h
|
|
+++ b/main/EventsPythonDevice.h
|
|
@@ -47,7 +47,7 @@
|
|
|
|
static PyModuleDef PDevicemodule = { PyModuleDef_HEAD_INIT,
|
|
"DomoticzEvents",
|
|
- "Example module that creates an extension type.",
|
|
+ "DomoticzEvents module type.",
|
|
-1,
|
|
nullptr,
|
|
nullptr,
|
|
@@ -55,44 +55,6 @@
|
|
nullptr,
|
|
nullptr };
|
|
|
|
- static PyTypeObject PDeviceType = {
|
|
- PyVarObject_HEAD_INIT(nullptr, 0) "DomoticzEvents.PDevice", /* tp_name */
|
|
- sizeof(PDevice), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)PDevice_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- 0, /* tp_getattr */
|
|
- 0, /* tp_setattr */
|
|
- 0, /* tp_reserved */
|
|
- 0, /* tp_repr */
|
|
- 0, /* tp_as_number */
|
|
- 0, /* tp_as_sequence */
|
|
- 0, /* tp_as_mapping */
|
|
- 0, /* tp_hash */
|
|
- 0, /* tp_call */
|
|
- 0, /* tp_str */
|
|
- 0, /* tp_getattro */
|
|
- 0, /* tp_setattro */
|
|
- 0, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
- "PDevice objects", /* tp_doc */
|
|
- 0, /* tp_traverse */
|
|
- 0, /* tp_clear */
|
|
- 0, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- 0, /* tp_iter */
|
|
- 0, /* tp_iternext */
|
|
- PDevice_methods, /* tp_methods */
|
|
- PDevice_members, /* tp_members */
|
|
- 0, /* tp_getset */
|
|
- 0, /* tp_base */
|
|
- 0, /* tp_dict */
|
|
- 0, /* tp_descr_get */
|
|
- 0, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)PDevice_init, /* tp_init */
|
|
- 0, /* tp_alloc */
|
|
- PDevice_new, /* tp_new */
|
|
- };
|
|
+ static PyObject* PDeviceType;
|
|
}
|
|
#endif
|
|
--- a/main/EventsPythonModule.cpp
|
|
+++ b/main/EventsPythonModule.cpp
|
|
@@ -6,22 +6,27 @@
|
|
#include "EventSystem.h"
|
|
#include "mainworker.h"
|
|
#include "localtime_r.h"
|
|
+#include "../hardware/plugins/Plugins.h"
|
|
|
|
-#ifdef ENABLE_PYTHON
|
|
-
|
|
- namespace Plugins {
|
|
- #define GETSTATE(m) ((struct eventModule_state*)PyModule_GetState(m))
|
|
+#include <fstream>
|
|
|
|
- void* m_PyInterpreter;
|
|
- bool ModuleInitialized = false;
|
|
+#ifdef ENABLE_PYTHON
|
|
|
|
- struct eventModule_state {
|
|
- PyObject* error;
|
|
- };
|
|
-
|
|
- static PyMethodDef DomoticzEventsMethods[] = { { "Log", PyDomoticz_EventsLog, METH_VARARGS, "Write message to Domoticz log." },
|
|
- { "Command", PyDomoticz_EventsCommand, METH_VARARGS, "Schedule a command." },
|
|
- { nullptr, nullptr, 0, nullptr } };
|
|
+namespace Plugins
|
|
+{
|
|
+#define GETSTATE(m) ((struct eventModule_state*)PyModule_GetState(m))
|
|
+
|
|
+ void* m_PyInterpreter;
|
|
+ bool ModuleInitialized = false;
|
|
+
|
|
+ struct eventModule_state {
|
|
+ PyObject* error;
|
|
+ };
|
|
+
|
|
+ static PyMethodDef DomoticzEventsMethods[] = {
|
|
+ { "Log", PyDomoticz_EventsLog, METH_VARARGS, "Write message to Domoticz log." },
|
|
+ { "Command", PyDomoticz_EventsCommand, METH_VARARGS, "Schedule a command." },
|
|
+ { nullptr, nullptr, 0, nullptr } };
|
|
|
|
static int DomoticzEventsTraverse(PyObject *m, visitproc visit, void *arg)
|
|
{
|
|
@@ -44,7 +49,6 @@
|
|
if (!PyArg_ParseTuple(args, "s", &msg))
|
|
{
|
|
_log.Log(LOG_ERROR, "Pyhton Event System: Failed to parse parameters: string expected.");
|
|
- // LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
@@ -52,8 +56,7 @@
|
|
_log.Log((_eLogLevel)LOG_NORM, message);
|
|
}
|
|
|
|
- Py_INCREF(Py_None);
|
|
- return Py_None;
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *PyDomoticz_EventsCommand(PyObject *self, PyObject *args)
|
|
@@ -68,7 +71,6 @@
|
|
if (!PyArg_ParseTuple(args, "ss", &device, &action))
|
|
{
|
|
_log.Log(LOG_ERROR, "Pyhton EventSystem: Failed to parse parameters: Two strings expected.");
|
|
- // LogPythonException(pModState->pPlugin, std::string(__func__));
|
|
}
|
|
else
|
|
{
|
|
@@ -78,13 +80,20 @@
|
|
m_mainworker.m_eventsystem.PythonScheduleEvent(device, action, "Test");
|
|
}
|
|
|
|
- Py_INCREF(Py_None);
|
|
- return Py_None;
|
|
+ Py_RETURN_NONE;
|
|
}
|
|
|
|
- struct PyModuleDef DomoticzEventsModuleDef
|
|
- = { PyModuleDef_HEAD_INIT, "DomoticzEvents", nullptr, sizeof(struct eventModule_state), DomoticzEventsMethods, nullptr,
|
|
- DomoticzEventsTraverse, DomoticzEventsClear, nullptr };
|
|
+ struct PyModuleDef DomoticzEventsModuleDef = {
|
|
+ PyModuleDef_HEAD_INIT,
|
|
+ "DomoticzEvents",
|
|
+ nullptr,
|
|
+ sizeof(struct eventModule_state),
|
|
+ DomoticzEventsMethods,
|
|
+ nullptr,
|
|
+ DomoticzEventsTraverse,
|
|
+ DomoticzEventsClear,
|
|
+ nullptr
|
|
+ };
|
|
|
|
PyMODINIT_FUNC PyInit_DomoticzEvents(void)
|
|
{
|
|
@@ -94,6 +103,22 @@
|
|
_log.Log(LOG_STATUS, "Python EventSystem: Initializing event module.");
|
|
|
|
PyObject *pModule = PyModule_Create2(&DomoticzEventsModuleDef, PYTHON_API_VERSION);
|
|
+
|
|
+ PyType_Slot PDeviceSlots[] = {
|
|
+ { Py_tp_doc, (void*)"PDevice objects" },
|
|
+ { Py_tp_new, (void*)PDevice_new },
|
|
+ { Py_tp_init, (void*)PDevice_init },
|
|
+ { Py_tp_dealloc, (void*)PDevice_dealloc },
|
|
+ { Py_tp_members, PDevice_members },
|
|
+ { Py_tp_methods, PDevice_methods },
|
|
+ { 0, nullptr },
|
|
+ };
|
|
+ PyType_Spec PDeviceSpec = { "DomoticzEvents.PDevice", sizeof(PDevice), 0,
|
|
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, PDeviceSlots };
|
|
+
|
|
+ PDeviceType = PyType_FromSpec(&PDeviceSpec);
|
|
+ PyModule_AddObject(pModule, "PDevice", (PyObject*)PDeviceType);
|
|
+
|
|
return pModule;
|
|
}
|
|
|
|
@@ -166,22 +191,21 @@
|
|
|
|
PyObject *PythonEventsGetModule()
|
|
{
|
|
- PyObject *pModule = PyState_FindModule(&DomoticzEventsModuleDef);
|
|
+ PyBorrowedRef pModule = PyState_FindModule(&DomoticzEventsModuleDef);
|
|
|
|
if (pModule)
|
|
{
|
|
// _log.Log(LOG_STATUS, "Python Event System: Module found");
|
|
return pModule;
|
|
}
|
|
- Plugins::PyRun_SimpleStringFlags("import DomoticzEvents", nullptr);
|
|
+ PyImport_ImportModule("DomoticzEvents");
|
|
pModule = PyState_FindModule(&DomoticzEventsModuleDef);
|
|
|
|
if (pModule)
|
|
{
|
|
return pModule;
|
|
}
|
|
- // Py_INCREF(Py_None);
|
|
- // return Py_None;
|
|
+
|
|
return nullptr;
|
|
}
|
|
|
|
@@ -189,7 +213,70 @@
|
|
|
|
PyObject *mapToPythonDict(const std::map<std::string, float> &floatMap)
|
|
{
|
|
- return Py_None;
|
|
+ Py_RETURN_NONE;
|
|
+ }
|
|
+
|
|
+ void LogPythonException()
|
|
+ {
|
|
+ PyNewRef pTraceback;
|
|
+ PyNewRef pExcept;
|
|
+ PyNewRef pValue;
|
|
+
|
|
+ PyErr_Fetch(&pExcept, &pValue, &pTraceback);
|
|
+ PyErr_NormalizeException(&pExcept, &pValue, &pTraceback);
|
|
+
|
|
+ if (!pExcept && !pValue && !pTraceback)
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "Unable to decode exception.");
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ std::string sTypeText("Unknown");
|
|
+ if (pExcept)
|
|
+ {
|
|
+ PyTypeObject* TypeName = (PyTypeObject*)pExcept;
|
|
+ PyNewRef pName = PyObject_GetAttrString((PyObject*)TypeName, "__name__");
|
|
+ sTypeText = (std::string)pName;
|
|
+ }
|
|
+
|
|
+ /* See if we can get a full traceback */
|
|
+ PyNewRef pModule = PyImport_ImportModule("traceback");
|
|
+ if (pModule)
|
|
+ {
|
|
+ PyNewRef pFunc = PyObject_GetAttrString(pModule, "format_exception");
|
|
+ if (pFunc && PyCallable_Check(pFunc)) {
|
|
+ PyNewRef pList = PyObject_CallFunctionObjArgs(pFunc, pExcept, pValue, pTraceback, NULL);
|
|
+ if (pList)
|
|
+ {
|
|
+ for (Py_ssize_t i = 0; i < PyList_Size(pList); i++)
|
|
+ {
|
|
+ PyBorrowedRef pPyStr = PyList_GetItem(pList, i);
|
|
+ std::string pStr(pPyStr);
|
|
+ size_t pos = 0;
|
|
+ std::string token;
|
|
+ while ((pos = pStr.find('\n')) != std::string::npos) {
|
|
+ token = pStr.substr(0, pos);
|
|
+ _log.Log(LOG_ERROR, "%s", token.c_str());
|
|
+ pStr.erase(0, pos + 1);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "Exception: '%s'. No traceback available.", sTypeText.c_str());
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "'format_exception' lookup failed, exception: '%s'. No traceback available.", sTypeText.c_str());
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "'Traceback' module import failed, exception: '%s'. No traceback available.", sTypeText.c_str());
|
|
+ }
|
|
+ }
|
|
+ PyErr_Clear();
|
|
}
|
|
|
|
void PythonEventsProcessPython(const std::string &reason, const std::string &filename, const std::string &PyString,
|
|
@@ -202,22 +289,15 @@
|
|
return;
|
|
}
|
|
|
|
- if (Plugins::Py_IsInitialized())
|
|
+ if (Py_IsInitialized())
|
|
{
|
|
-
|
|
if (m_PyInterpreter)
|
|
PyEval_RestoreThread((PyThreadState *)m_PyInterpreter);
|
|
|
|
- /*{
|
|
- _log.Log(LOG_ERROR, "EventSystem - Python: Failed to attach to interpreter");
|
|
- }*/
|
|
-
|
|
- PyObject *pModule = Plugins::PythonEventsGetModule();
|
|
+ PyBorrowedRef pModule = PythonEventsGetModule();
|
|
if (pModule)
|
|
{
|
|
-
|
|
- PyObject *pModuleDict = Plugins::PyModule_GetDict((PyObject *)pModule); // borrowed referece
|
|
-
|
|
+ PyBorrowedRef pModuleDict = Plugins::PyModule_GetDict(pModule);
|
|
if (!pModuleDict)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to open module dictionary.");
|
|
@@ -225,26 +305,22 @@
|
|
return;
|
|
}
|
|
|
|
- if (Plugins::PyDict_SetItemString(
|
|
- pModuleDict, "changed_device_name",
|
|
- Plugins::PyUnicode_FromString(m_devicestates[DeviceID].deviceName.c_str()))
|
|
- == -1)
|
|
+ PyNewRef pStrVal = PyUnicode_FromString(m_devicestates[DeviceID].deviceName.c_str());
|
|
+ if (PyDict_SetItemString(pModuleDict, "changed_device_name", pStrVal) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to set changed_device_name.");
|
|
return;
|
|
}
|
|
|
|
- PyObject *m_DeviceDict = Plugins::PyDict_New();
|
|
-
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "Devices", (PyObject *)m_DeviceDict) == -1)
|
|
+ PyNewRef pDeviceDict = Plugins::PyDict_New();
|
|
+ if (PyDict_SetItemString(pModuleDict, "Devices", pDeviceDict) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add Device dictionary.");
|
|
PyEval_SaveThread();
|
|
return;
|
|
}
|
|
- Py_DECREF(m_DeviceDict);
|
|
|
|
- if (Plugins::PyType_Ready(&Plugins::PDeviceType) < 0)
|
|
+ if (PyType_Ready((PyTypeObject*)Plugins::PDeviceType) < 0)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Unable to ready DeviceType Object.");
|
|
PyEval_SaveThread();
|
|
@@ -261,13 +337,12 @@
|
|
// sitem.subType, sitem.switchtype, sitem.nValue, sitem.nValueWording, sitem.sValue,
|
|
// sitem.lastUpdate); devices[sitem.deviceName] = deviceStatus;
|
|
|
|
- Plugins::PDevice *aDevice = (Plugins::PDevice *)Plugins::PDevice_new(
|
|
- &Plugins::PDeviceType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
- PyObject *pKey = Plugins::PyUnicode_FromString(sitem.deviceName.c_str());
|
|
+ PDevice *aDevice = (PDevice *)PDevice_new((PyTypeObject*)PDeviceType, (PyObject *)nullptr, (PyObject *)nullptr);
|
|
+ PyNewRef pKey = PyUnicode_FromString(sitem.deviceName.c_str());
|
|
|
|
if (sitem.ID == DeviceID)
|
|
{
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "changed_device", (PyObject *)aDevice) == -1)
|
|
+ if (PyDict_SetItemString(pModuleDict, "changed_device", (PyObject *)aDevice) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR,
|
|
"Python EventSystem: Failed to add device '%s' as changed_device.",
|
|
@@ -275,7 +350,7 @@
|
|
}
|
|
}
|
|
|
|
- if (Plugins::PyDict_SetItem((PyObject *)m_DeviceDict, pKey, (PyObject *)aDevice) == -1)
|
|
+ if (PyDict_SetItem(pDeviceDict, pKey, (PyObject *)aDevice) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add device '%s' to device dictionary.",
|
|
sitem.deviceName.c_str());
|
|
@@ -291,19 +366,18 @@
|
|
// If nValueWording contains %, unicode fails?
|
|
|
|
aDevice->id = static_cast<int>(sitem.ID);
|
|
- aDevice->name = Plugins::PyUnicode_FromString(sitem.deviceName.c_str());
|
|
+ aDevice->name = PyUnicode_FromString(sitem.deviceName.c_str());
|
|
aDevice->type = sitem.devType;
|
|
aDevice->sub_type = sitem.subType;
|
|
aDevice->switch_type = sitem.switchtype;
|
|
aDevice->n_value = sitem.nValue;
|
|
- aDevice->n_value_string = Plugins::PyUnicode_FromString(temp_n_value_string.c_str());
|
|
+ aDevice->n_value_string = PyUnicode_FromString(temp_n_value_string.c_str());
|
|
aDevice->s_value = Plugins::PyUnicode_FromString(sitem.sValue.c_str());
|
|
- aDevice->last_update_string = Plugins::PyUnicode_FromString(sitem.lastUpdate.c_str());
|
|
+ aDevice->last_update_string = PyUnicode_FromString(sitem.lastUpdate.c_str());
|
|
// _log.Log(LOG_STATUS, "Python EventSystem: deviceName %s added to device dictionary",
|
|
// sitem.deviceName.c_str());
|
|
}
|
|
Py_DECREF(aDevice);
|
|
- Py_DECREF(pKey);
|
|
}
|
|
// devicestatesMutexLock1.unlock();
|
|
|
|
@@ -315,28 +389,24 @@
|
|
localtime_r(&now, <ime);
|
|
int minutesSinceMidnight = (ltime.tm_hour * 60) + ltime.tm_min;
|
|
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "minutes_since_midnight",
|
|
- Plugins::PyLong_FromLong(minutesSinceMidnight))
|
|
- == -1)
|
|
+ PyNewRef pPyLong = PyLong_FromLong(minutesSinceMidnight);
|
|
+ if (PyDict_SetItemString(pModuleDict, "minutes_since_midnight", pPyLong) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add 'minutesSinceMidnight' to module_dict");
|
|
}
|
|
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "sunrise_in_minutes", Plugins::PyLong_FromLong(intSunRise))
|
|
- == -1)
|
|
+ pPyLong = PyLong_FromLong(intSunRise);
|
|
+ if (PyDict_SetItemString(pModuleDict, "sunrise_in_minutes", pPyLong) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add 'sunrise_in_minutes' to module_dict");
|
|
}
|
|
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "sunset_in_minutes", Plugins::PyLong_FromLong(intSunSet))
|
|
- == -1)
|
|
+ pPyLong = PyLong_FromLong(intSunSet);
|
|
+ if (PyDict_SetItemString(pModuleDict, "sunset_in_minutes", pPyLong) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add 'sunset_in_minutes' to module_dict");
|
|
}
|
|
-
|
|
- // PyObject* dayTimeBool = Py_False;
|
|
- // PyObject* nightTimeBool = Py_False;
|
|
-
|
|
+
|
|
bool isDaytime = false;
|
|
bool isNightime = false;
|
|
|
|
@@ -349,75 +419,121 @@
|
|
isNightime = true;
|
|
}
|
|
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "is_daytime", Plugins::PyBool_FromLong(isDaytime)) == -1)
|
|
+ PyNewRef pPyBool = PyBool_FromLong(isDaytime);
|
|
+ if (PyDict_SetItemString(pModuleDict, "is_daytime", pPyBool) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add 'is_daytime' to module_dict");
|
|
}
|
|
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "is_nighttime", Plugins::PyBool_FromLong(isNightime)) == -1)
|
|
+ pPyBool = PyBool_FromLong(isNightime);
|
|
+ if (PyDict_SetItemString(pModuleDict, "is_nighttime", pPyBool) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add 'is_daytime' to module_dict");
|
|
}
|
|
|
|
// UserVariables
|
|
- PyObject *m_uservariablesDict = Plugins::PyDict_New();
|
|
-
|
|
- if (Plugins::PyDict_SetItemString(pModuleDict, "user_variables", (PyObject *)m_uservariablesDict) == -1)
|
|
+ PyNewRef userVariablesDict = PyDict_New();
|
|
+ if (PyDict_SetItemString(pModuleDict, "user_variables", userVariablesDict) == -1)
|
|
{
|
|
_log.Log(LOG_ERROR, "Python EventSystem: Failed to add uservariables dictionary.");
|
|
PyEval_SaveThread();
|
|
return;
|
|
}
|
|
- Py_DECREF(m_uservariablesDict);
|
|
-
|
|
- // This doesn't work
|
|
- // boost::unique_lock<boost::shared_mutex> uservariablesMutexLock2 (m_uservariablesMutex);
|
|
|
|
for (auto it_var = m_uservariables.begin(); it_var != m_uservariables.end(); ++it_var)
|
|
{
|
|
CEventSystem::_tUserVariable uvitem = it_var->second;
|
|
- Plugins::PyDict_SetItemString(m_uservariablesDict, uvitem.variableName.c_str(),
|
|
- Plugins::PyUnicode_FromString(uvitem.variableValue.c_str()));
|
|
+ PyDict_SetItemString(userVariablesDict, uvitem.variableName.c_str(),
|
|
+ PyUnicode_FromString(uvitem.variableValue.c_str()));
|
|
}
|
|
|
|
- // uservariablesMutexLock2.unlock();
|
|
-
|
|
// Add __main__ module
|
|
- PyObject *pModule = Plugins::PyImport_AddModule("__main__");
|
|
- Py_INCREF(pModule);
|
|
+ PyBorrowedRef pMainModule = PyImport_AddModule("__main__");
|
|
+ PyBorrowedRef global_dict = PyModule_GetDict(pMainModule);
|
|
+ PyNewRef local_dict = PyDict_New();
|
|
|
|
// Override sys.stderr
|
|
- Plugins::PyRun_SimpleStringFlags("import sys\nclass StdErrRedirect:\n def __init__(self):\n "
|
|
- "self.buffer = ''\n def write(self, "
|
|
- "msg):\n self.buffer += msg\nstdErrRedirect = "
|
|
- "StdErrRedirect()\nsys.stderr = stdErrRedirect\n",
|
|
- nullptr);
|
|
+ {
|
|
+ PyNewRef pCode = Py_CompileString("import sys\nclass StdErrRedirect:\n def __init__(self):\n "
|
|
+ "self.buffer = ''\n def write(self, "
|
|
+ "msg):\n self.buffer += msg\nstdErrRedirect = "
|
|
+ "StdErrRedirect()\nsys.stderr = stdErrRedirect\n",
|
|
+ filename.c_str(), Py_file_input);
|
|
+ if (pCode)
|
|
+ {
|
|
+ PyNewRef pEval = PyEval_EvalCode(pCode, global_dict, local_dict);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "EventSystem: Failed to compile stderror redirection for event script '%s'", reason.c_str());
|
|
+ }
|
|
+ }
|
|
|
|
- if (PyString.length() > 0)
|
|
+ if (!PyErr_Occurred() && (PyString.length() > 0))
|
|
{
|
|
// Python-string from WebEditor
|
|
- Plugins::PyRun_SimpleStringFlags(PyString.c_str(), nullptr);
|
|
+ PyNewRef pCode = Py_CompileString(PyString.c_str(), filename.c_str(), Py_file_input);
|
|
+ if (pCode)
|
|
+ {
|
|
+ PyNewRef pEval = PyEval_EvalCode(pCode, global_dict, local_dict);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "EventSystem: Failed to compile python '%s' event script '%s'", reason.c_str(), filename.c_str());
|
|
+ }
|
|
}
|
|
else
|
|
{
|
|
// Script-file
|
|
- FILE *PythonScriptFile = fopen(filename.c_str(), "r");
|
|
- Plugins::PyRun_SimpleFileExFlags(PythonScriptFile, filename.c_str(), 0, nullptr);
|
|
+ std::ifstream PythonScriptFile(filename.c_str());
|
|
+ if (PythonScriptFile.is_open())
|
|
+ {
|
|
+ char PyLine[256];
|
|
+ std::string PyString;
|
|
+ while (PythonScriptFile.getline(PyLine, sizeof(PyLine), '\n'))
|
|
+ {
|
|
+ PyString.append(PyLine);
|
|
+ PyString += '\n';
|
|
+ }
|
|
+ PythonScriptFile.close();
|
|
+
|
|
+ PyNewRef pCode = Py_CompileString(PyString.c_str(), filename.c_str(), Py_file_input);
|
|
+ if (pCode)
|
|
+ {
|
|
+ PyNewRef pEval = PyEval_EvalCode(pCode, global_dict, local_dict);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "EventSystem: Failed to compile python '%s' event script file '%s'", reason.c_str(), filename.c_str());
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ _log.Log(LOG_ERROR, "EventSystem: Failed to open python script file '%s'", filename.c_str());
|
|
+ }
|
|
+ }
|
|
|
|
- if (PythonScriptFile != nullptr)
|
|
- fclose(PythonScriptFile);
|
|
+ // Log any exceptions
|
|
+ if (PyErr_Occurred())
|
|
+ {
|
|
+ LogPythonException();
|
|
}
|
|
|
|
// Get message from stderr redirect
|
|
- PyObject *stdErrRedirect = nullptr, *logBuffer = nullptr, *logBytes = nullptr;
|
|
std::string logString;
|
|
- if ((stdErrRedirect = Plugins::PyObject_GetAttrString(pModule, "stdErrRedirect")) == nullptr)
|
|
- goto free_module;
|
|
- if ((logBuffer = Plugins::PyObject_GetAttrString(stdErrRedirect, "buffer")) == nullptr)
|
|
- goto free_stderrredirect;
|
|
- if ((logBytes = PyUnicode_AsUTF8String(logBuffer)) == nullptr)
|
|
- goto free_logbuffer;
|
|
- logString.append(PyBytes_AsString(logBytes));
|
|
+ if (PyObject_HasAttrString(pModule, "stdErrRedirect"))
|
|
+ {
|
|
+ PyNewRef stdErrRedirect = PyObject_GetAttrString(pModule, "stdErrRedirect");
|
|
+ if (PyObject_HasAttrString(stdErrRedirect, "buffer"))
|
|
+ {
|
|
+ PyNewRef logBuffer = PyObject_GetAttrString(stdErrRedirect, "buffer");
|
|
+ PyNewRef logBytes = PyUnicode_AsUTF8String(logBuffer);
|
|
+ if (logBytes)
|
|
+ {
|
|
+ logString.append(PyBytes_AsString(logBytes));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
|
|
// Check if there were some errors written to stderr
|
|
if (logString.length() > 0)
|
|
@@ -436,15 +552,6 @@
|
|
logString = logString.substr(lineBreakPos + 1);
|
|
}
|
|
}
|
|
-
|
|
- // Cleanup
|
|
- Py_DECREF(logBytes);
|
|
- free_logbuffer:
|
|
- Py_DECREF(logBuffer);
|
|
- free_stderrredirect:
|
|
- Py_DECREF(stdErrRedirect);
|
|
- free_module:
|
|
- Py_DECREF(pModule);
|
|
}
|
|
else
|
|
{
|
|
@@ -458,5 +565,5 @@
|
|
_log.Log(LOG_ERROR, "EventSystem: Python not Initialized");
|
|
}
|
|
}
|
|
- } // namespace Plugins
|
|
+} // namespace Plugins
|
|
#endif
|
|
--- a/main/SQLHelper.cpp
|
|
+++ b/main/SQLHelper.cpp
|
|
@@ -5226,7 +5226,7 @@ uint64_t CSQLHelper::UpdateValueInt(
|
|
)
|
|
{
|
|
if (
|
|
- (pHardware->HwdType != HTYPE_MQTTAutoDiscovery)
|
|
+ (HWtype != HTYPE_MQTTAutoDiscovery)
|
|
&&
|
|
(switchtype == STYPE_BlindsPercentage
|
|
|| switchtype == STYPE_BlindsPercentageWithStop
|