From a21315efc57750f0591e29f6d613a8dab5034916 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 31 May 2020 00:16:58 +0200 Subject: [PATCH] some more hacking --- .../python/PyThingDiscoveryInfo.h | 79 ++++++++++++++++++ .../integrations/pythonintegrationplugin.cpp | 82 +++++++++++++------ libnymea-core/libnymea-core.pro | 1 + 3 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 libnymea-core/integrations/python/PyThingDiscoveryInfo.h diff --git a/libnymea-core/integrations/python/PyThingDiscoveryInfo.h b/libnymea-core/integrations/python/PyThingDiscoveryInfo.h new file mode 100644 index 00000000..a98fcd0e --- /dev/null +++ b/libnymea-core/integrations/python/PyThingDiscoveryInfo.h @@ -0,0 +1,79 @@ +#ifndef PYTHINGDISCOVERYINFO_H +#define PYTHINGDISCOVERYINFO_H + + +#include + +#include "integrations/thingdiscoveryinfo.h" + +#include + +typedef struct { + PyObject_HEAD + ThingDiscoveryInfo* ptrObj; +} PyThingDiscoveryInfo; + + +static int PyThingDiscoveryInfo_init(PyThingDiscoveryInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) +// initialize PyVoice Object +{ + return 0; +} + + +static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self) +// destruct the object +{ + // FIXME: Why is this not called? Seems we're leaking... + Q_ASSERT(false); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * PyThingDiscoveryInfo_finish(PyThingDiscoveryInfo* self, PyObject* args) +{ + int status; + char *message; + + if (PyArg_ParseTuple(args, "is", &status, &message)) { + (self->ptrObj)->finish(static_cast(status), QString(message)); + return Py_BuildValue(""); + } + + if (PyArg_ParseTuple(args, "i", &status)) { + (self->ptrObj)->finish(static_cast(status)); + return Py_BuildValue(""); + } + + return Py_False; +} + +static PyMethodDef PyThingDiscoveryInfo_methods[] = { + { "finish", (PyCFunction)PyThingDiscoveryInfo_finish, METH_VARARGS, "finish a discovery" }, + {nullptr, nullptr, 0, nullptr} // sentinel +}; + +static PyTypeObject PyThingDiscoveryInfoType = { + PyVarObject_HEAD_INIT(NULL, 0) + "nymea.ThingDiscoveryInfo", /* tp_name */ + sizeof(PyThingDiscoveryInfo), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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, /* tp_flags */ + "Noddy objects", /* tp_doc */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; +#endif // PYTHINGDISCOVERYINFO_H diff --git a/libnymea-core/integrations/pythonintegrationplugin.cpp b/libnymea-core/integrations/pythonintegrationplugin.cpp index ea3885fa..9fe77af0 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.cpp +++ b/libnymea-core/integrations/pythonintegrationplugin.cpp @@ -1,4 +1,5 @@ #include +#include "python/PyThingDiscoveryInfo.h" #include "pythonintegrationplugin.h" @@ -6,7 +7,8 @@ #include -PyObject* aview_write(PyObject* /*self*/, PyObject* args) +// Write to stdout/stderr +PyObject* nymea_write(PyObject* /*self*/, PyObject* args) { const char *what; if (!PyArg_ParseTuple(args, "s", &what)) @@ -15,35 +17,53 @@ PyObject* aview_write(PyObject* /*self*/, PyObject* args) return Py_BuildValue(""); } - -PyObject* aview_flush(PyObject* /*self*/, PyObject* /*args*/) +// Flush stdout/stderr +PyObject* nymea_flush(PyObject* /*self*/, PyObject* /*args*/) { + // Not really needed... qDebug() fluses already on its own return Py_BuildValue(""); } -static PyMethodDef aview_methods[] = +static PyMethodDef nymea_methods[] = { - {"write", aview_write, METH_VARARGS, "doc for write"}, - {"flush", aview_flush, METH_VARARGS, "doc for flush"}, + {"write", nymea_write, METH_VARARGS, "write to stdout through qDebug()"}, + {"flush", nymea_flush, METH_VARARGS, "flush stdout (no-op)"}, {nullptr, nullptr, 0, nullptr} // sentinel }; -static PyModuleDef aview_module = +static PyModuleDef nymea_module = { PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base; - "aview", // const char* m_name; - "doc for aview", // const char* m_doc; + "nymea", // const char* m_name; + "nymea module for python based integration plugins", // const char* m_doc; -1, // Py_ssize_t m_size; - aview_methods, // PyMethodDef *m_methods + nymea_methods, // PyMethodDef *m_methods // inquiry m_reload; traverseproc m_traverse; inquiry m_clear; freefunc m_free; nullptr, nullptr, nullptr, nullptr }; -PyMODINIT_FUNC PyInit_aview(void) +PyMODINIT_FUNC PyInit_nymea(void) { - PyObject* m = PyModule_Create(&aview_module); + PyObject* m = PyModule_Create(&nymea_module); + // Overrride stdout/stderr to use qDebug instead PySys_SetObject("stdout", m); PySys_SetObject("stderr", m); + + + PyThingDiscoveryInfoType.tp_new = PyType_GenericNew; + PyThingDiscoveryInfoType.tp_basicsize=sizeof(PyThingDiscoveryInfo); + PyThingDiscoveryInfoType.tp_dealloc=(destructor) PyThingDiscoveryInfo_dealloc; + PyThingDiscoveryInfoType.tp_flags=Py_TPFLAGS_DEFAULT; + PyThingDiscoveryInfoType.tp_doc="ThingDiscoveryInfo class"; + PyThingDiscoveryInfoType.tp_methods=PyThingDiscoveryInfo_methods; + //~ PyVoiceType.tp_members=Noddy_members; + PyThingDiscoveryInfoType.tp_init=(initproc)PyThingDiscoveryInfo_init; + + if (PyType_Ready(&PyThingDiscoveryInfoType) < 0) + return NULL; + + PyModule_AddObject(m, "ThingDiscoveryInfo", (PyObject *)&PyThingDiscoveryInfoType); // Add Voice object to the module + return m; } @@ -55,9 +75,9 @@ PythonIntegrationPlugin::PythonIntegrationPlugin(QObject *parent) : IntegrationP bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) { // TODO: Call this just once and call Py_Finalize() - PyImport_AppendInittab("aview", PyInit_aview); + PyImport_AppendInittab("nymea", PyInit_nymea); Py_Initialize(); - PyImport_ImportModule("aview"); + PyImport_ImportModule("nymea"); QFileInfo fi(scriptFile); qCDebug(dcThingManager()) << "Importing" << fi.absolutePath() << fi.fileName() << fi.baseName(); @@ -161,20 +181,36 @@ void PythonIntegrationPlugin::init() void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info) { qCDebug(dcThingManager()) << "Python wrapper: discoverThings()" << info; - PyObject *pFunc = PyObject_GetAttrString(m_module, "init"); + PyObject *pFunc = PyObject_GetAttrString(m_module, "discoverThings"); if(!pFunc || !PyCallable_Check(pFunc)) { qCWarning(dcThingManager()) << "Python plugin does not implement \"discoverThings()\" method."; return; } -// PyObject* args = Py_BuildValue("(s)", ln.c_str()); -// if (!args) -// { -// PyErr_Print(); -// Py_DECREF(filterFunc); -// return "Error building args tuple"; -// } -// PyObject_CallObject(pFunc, nullptr); + PyThingDiscoveryInfo *pyInfo = (PyThingDiscoveryInfo*)_PyObject_New(&PyThingDiscoveryInfoType); + pyInfo->ptrObj = info; + + connect(info, &ThingDiscoveryInfo::finished, this, [=](){ + PyObject_Free(pyInfo); + }); + + PyObject_CallFunction(pFunc, "O", pyInfo); + + if (PyErr_Occurred()) { + PyObject *ptype, *pvalue, *ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + if(pvalue) { + PyObject *pstr = PyObject_Str(pvalue); + if(pstr) { + const char* err_msg = PyUnicode_AsUTF8(pstr); + if(pstr) { + qWarning() << QString(err_msg); + } + + } + PyErr_Restore(ptype, pvalue, ptraceback); + } + } } diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index 8e149afe..03a9448d 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -20,6 +20,7 @@ RESOURCES += $$top_srcdir/icons.qrc \ HEADERS += nymeacore.h \ integrations/plugininfocache.h \ + integrations/python/PyThingDiscoveryInfo.h \ integrations/thingmanagerimplementation.h \ integrations/translator.h \ integrations/pythonintegrationplugin.h \