From 9d30be25680f95e43ddab8378bbc50ce9d89fe6f Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 8 Mar 2021 00:47:12 +0100 Subject: [PATCH 1/2] Add support for the plugin storage to python plugins --- .../integrations/python/pynymeamodule.h | 2 + .../integrations/python/pypluginstorage.h | 135 ++++++++++++++++++ .../integrations/python/pythingpairinginfo.h | 2 +- .../integrations/pythonintegrationplugin.cpp | 16 +++ .../integrations/pythonintegrationplugin.h | 4 + libnymea-core/integrations/translator.cpp | 2 +- libnymea-core/libnymea-core.pro | 1 + libnymea/integrations/thingpairinginfo.h | 2 +- 8 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 libnymea-core/integrations/python/pypluginstorage.h diff --git a/libnymea-core/integrations/python/pynymeamodule.h b/libnymea-core/integrations/python/pynymeamodule.h index b9f01844..1044fc29 100644 --- a/libnymea-core/integrations/python/pynymeamodule.h +++ b/libnymea-core/integrations/python/pynymeamodule.h @@ -11,6 +11,7 @@ #include "pyparam.h" #include "pythingactioninfo.h" #include "pythingpairinginfo.h" +#include "pypluginstorage.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winvalid-offsetof" @@ -27,6 +28,7 @@ static int nymea_exec(PyObject *m) { registerThingPairingInfoType(m); registerThingSetupInfoType(m); registerThingActionInfoType(m); + registerPluginStorageType(m); return 0; } diff --git a/libnymea-core/integrations/python/pypluginstorage.h b/libnymea-core/integrations/python/pypluginstorage.h new file mode 100644 index 00000000..7da97481 --- /dev/null +++ b/libnymea-core/integrations/python/pypluginstorage.h @@ -0,0 +1,135 @@ +#ifndef PYPLUGINSTORAGE_H +#define PYPLUGINSTORAGE_H + +#include +#include "structmember.h" +#include "pyutils.h" + +//#include "integrations/pythonintegrationplugin.h" +#include "loggingcategories.h" +#include "nymeasettings.h" +#include "typeutils.h" + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#pragma GCC diagnostic ignored "-Wwrite-strings" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" + + +typedef struct { + PyObject_HEAD + QSettings *settings; +} PyPluginStorage; + + +static PyMemberDef PyPluginStorage_members[] = { + {nullptr, 0, 0, 0, nullptr} /* Sentinel */ +}; + +static PyObject * PyPluginStorage_value(PyPluginStorage* self, PyObject* args) { + char *keyStr = nullptr; + if (!PyArg_ParseTuple(args, "s", &keyStr)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in value call. Expected: value(key)"); + return nullptr; + } + + QVariant value = self->settings->value(keyStr); + return QVariantToPyObject(value); +}; + +static PyObject * PyPluginStorage_setValue(PyPluginStorage* self, PyObject* args) { + char *keyStr = nullptr; + PyObject *value = nullptr; + if (!PyArg_ParseTuple(args, "sO", &keyStr, &value)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in value call. Expected: value(key, value)"); + return nullptr; + } + + self->settings->setValue(keyStr, PyObjectToQVariant(value)); + Py_RETURN_NONE; +}; + +static PyObject * PyPluginStorage_beginGroup(PyPluginStorage* self, PyObject* args) { + char *groupStr = nullptr; + if (!PyArg_ParseTuple(args, "s", &groupStr)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in value call. Expected: beginGroup(group)"); + return nullptr; + } + + self->settings->beginGroup(groupStr); + Py_RETURN_NONE; +}; + +static PyObject * PyPluginStorage_endGroup(PyPluginStorage* self, PyObject* args) { + Q_UNUSED(args) + self->settings->endGroup(); + Py_RETURN_NONE; +}; + +static PyMethodDef PyPluginStorage_methods[] = { + { "value", (PyCFunction)PyPluginStorage_value, METH_VARARGS, "Get a value from the plugin storage" }, + { "setValue", (PyCFunction)PyPluginStorage_setValue, METH_VARARGS, "Set a value to the plugin storage"}, + { "beginGroup", (PyCFunction)PyPluginStorage_beginGroup, METH_VARARGS, "Begin a group in the plugin storage."}, + { "endGroup", (PyCFunction)PyPluginStorage_endGroup, METH_VARARGS, "End a group in the plugin storage."}, + {nullptr, nullptr, 0, nullptr} // sentinel +}; + +static int PyPluginStorage_init(PyPluginStorage *self, PyObject *args, PyObject *kwds) +{ + Q_UNUSED(kwds) + char *pluginIdStr = nullptr; + if (!PyArg_ParseTuple(args, "s", &pluginIdStr)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in constructor. Expected: PluginStorage(pluginId)"); + return -1; + } + + if (!pluginIdStr) { + return -1; + } + PluginId pluginId(pluginIdStr); + if (pluginId.isNull()) { + return -1; + } + + self->settings = new QSettings(NymeaSettings::settingsPath() + "/pluginconfig-" + pluginId.toString().remove(QRegExp("[{}]")) + ".conf", QSettings::IniFormat); + + qCDebug(dcPythonIntegrations()) << "+++ PyPluginStorage"; + return 0; +} + +static void PyPluginStorage_dealloc(PyPluginStorage * self) +{ + qCDebug(dcPythonIntegrations()) << "--- PyPluginStorage"; + Py_TYPE(self)->tp_free(self); +} + +static PyTypeObject PyPluginStorageType = { + PyVarObject_HEAD_INIT(NULL, 0) + "nymea.PluginStorage", /* tp_name */ + sizeof(PyPluginStorage), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PyPluginStorage_dealloc, /* tp_dealloc */ +}; + + + +static void registerPluginStorageType(PyObject *module) +{ + PyPluginStorageType.tp_new = PyType_GenericNew; + PyPluginStorageType.tp_members = PyPluginStorage_members; + PyPluginStorageType.tp_methods = PyPluginStorage_methods; + PyPluginStorageType.tp_init = reinterpret_cast(PyPluginStorage_init); + PyPluginStorageType.tp_doc = "PluginStorage can be used by plugins to store key-value pairs to a persistant place."; + PyPluginStorageType.tp_flags = Py_TPFLAGS_DEFAULT; + + if (PyType_Ready(&PyPluginStorageType) < 0) { + return; + } + PyModule_AddObject(module, "PluginStorage", reinterpret_cast(&PyPluginStorageType)); +} + +#pragma GCC diagnostic pop + +#endif // PYPLUGINSTORAGE_H diff --git a/libnymea-core/integrations/python/pythingpairinginfo.h b/libnymea-core/integrations/python/pythingpairinginfo.h index 7a9a8071..824ba64e 100644 --- a/libnymea-core/integrations/python/pythingpairinginfo.h +++ b/libnymea-core/integrations/python/pythingpairinginfo.h @@ -105,7 +105,7 @@ static PyObject * PyThingPairingInfo_finish(PyThingPairingInfo* self, PyObject* if (self->info) { if (self->pyOAuthUrl) { QString oAuthUrl = QString::fromUtf8(PyUnicode_AsUTF8AndSize(self->pyOAuthUrl, nullptr)); - QMetaObject::invokeMethod(self->info, "setOAuthUrl", Qt::QueuedConnection, Q_ARG(QString, oAuthUrl)); + QMetaObject::invokeMethod(self->info, "setOAuthUrl", Qt::QueuedConnection, Q_ARG(QUrl, oAuthUrl)); } QMetaObject::invokeMethod(self->info, "finish", Qt::QueuedConnection, Q_ARG(Thing::ThingError, thingError), Q_ARG(QString, displayMessage)); } diff --git a/libnymea-core/integrations/pythonintegrationplugin.cpp b/libnymea-core/integrations/pythonintegrationplugin.cpp index 5bb711f2..cd365aef 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.cpp +++ b/libnymea-core/integrations/pythonintegrationplugin.cpp @@ -3,6 +3,7 @@ #include "pythonintegrationplugin.h" #include "python/pynymeamodule.h" #include "python/pystdouthandler.h" +#include "python/pypluginstorage.h" #include "loggingcategories.h" @@ -176,6 +177,14 @@ PyObject *PythonIntegrationPlugin::pyAutoThingDisappeared(PyObject *self, PyObje Py_RETURN_NONE; } +PyObject *PythonIntegrationPlugin::pyPluginStorage(PyObject *self, PyObject *args) +{ + Q_UNUSED(args) + PyObject *pluginStorage = s_plugins.key(self)->m_pyPluginStorage; + Py_INCREF(pluginStorage); + return pluginStorage; +} + static PyMethodDef plugin_methods[] = { {"configuration", PythonIntegrationPlugin::pyConfiguration, METH_VARARGS, "Get the plugin configuration."}, @@ -184,6 +193,7 @@ static PyMethodDef plugin_methods[] = {"myThings", PythonIntegrationPlugin::pyMyThings, METH_VARARGS, "Obtain a list of things owned by this plugin."}, {"autoThingsAppeared", PythonIntegrationPlugin::pyAutoThingsAppeared, METH_VARARGS, "Inform the system about auto setup things having appeared."}, {"autoThingDisappeared", PythonIntegrationPlugin::pyAutoThingDisappeared, METH_VARARGS, "Inform the system about auto setup things having disappeared."}, + {"pluginStorage", PythonIntegrationPlugin::pyPluginStorage, METH_VARARGS, "Obtain the plugin storage for this plugin."}, {nullptr, nullptr, 0, nullptr} // sentinel }; @@ -215,6 +225,7 @@ PythonIntegrationPlugin::~PythonIntegrationPlugin() s_plugins.take(this); Py_XDECREF(m_pluginModule); + Py_DECREF(m_pyPluginStorage); Py_DECREF(m_nymeaModule); Py_EndInterpreter(m_threadState); @@ -354,6 +365,11 @@ bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) Py_DECREF(logger); } + args = Py_BuildValue("(s)", pluginId().toString().toUtf8().data()); + m_pyPluginStorage = PyObject_CallObject((PyObject*)&PyPluginStorageType, args); + Py_DECREF(args); + Py_INCREF(m_pyPluginStorage); + // Export metadata ids into module exportIds(); diff --git a/libnymea-core/integrations/pythonintegrationplugin.h b/libnymea-core/integrations/pythonintegrationplugin.h index 37bcaf28..06a2fcdb 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.h +++ b/libnymea-core/integrations/pythonintegrationplugin.h @@ -45,6 +45,7 @@ public: static PyObject* pyMyThings(PyObject *self, PyObject* args); static PyObject* pyAutoThingsAppeared(PyObject *self, PyObject* args); static PyObject* pyAutoThingDisappeared(PyObject *self, PyObject* args); + static PyObject* pyPluginStorage(PyObject* self, PyObject* args); private: void exportIds(); @@ -89,6 +90,9 @@ private: // Need to keep a copy of plugin params and sync that in a thread-safe manner ParamList m_pluginConfigCopy; + // Keeping our own plugin storage for python + PyObject* m_pyPluginStorage = nullptr; + }; #endif // PYTHONINTEGRATIONPLUGIN_H diff --git a/libnymea-core/integrations/translator.cpp b/libnymea-core/integrations/translator.cpp index 22b3077e..552f60c0 100644 --- a/libnymea-core/integrations/translator.cpp +++ b/libnymea-core/integrations/translator.cpp @@ -127,7 +127,7 @@ void Translator::loadTranslator(IntegrationPlugin *plugin, const QLocale &locale } if (!loaded && locale.name() != "en_US") { - qCWarning(dcTranslations()) << "* Could not load translation" << locale.name() << "for plugin" << plugin->pluginName() << "(" << pluginId << ")"; + qCDebug(dcTranslations()) << "* Could not load translation" << locale.name() << "for plugin" << plugin->pluginName() << "(" << pluginId << ")"; } } diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index ece76d66..4feff683 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -46,6 +46,7 @@ RESOURCES += $$top_srcdir/icons.qrc \ HEADERS += nymeacore.h \ integrations/apikeysprovidersloader.h \ integrations/plugininfocache.h \ + integrations/python/pypluginstorage.h \ integrations/thingmanagerimplementation.h \ integrations/translator.h \ experiences/experiencemanager.h \ diff --git a/libnymea/integrations/thingpairinginfo.h b/libnymea/integrations/thingpairinginfo.h index ee229401..475448e9 100644 --- a/libnymea/integrations/thingpairinginfo.h +++ b/libnymea/integrations/thingpairinginfo.h @@ -53,13 +53,13 @@ public: ThingId parentId() const; QUrl oAuthUrl() const; - void setOAuthUrl(const QUrl &oAuthUrl); Thing::ThingError status() const; QString displayMessage() const; QString translatedDisplayMessage(const QLocale &locale) const; public slots: + void setOAuthUrl(const QUrl &oAuthUrl); void finish(Thing::ThingError status, const QString &displayMessage = QString()); signals: From 193c647dc6b9382ab78b5103efd704937a3b4721 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 9 Mar 2021 00:14:06 +0100 Subject: [PATCH 2/2] Also add api key storage api to python --- .../integrations/python/pyapikeystorage.h | 155 ++++++++++++++++++ .../integrations/python/pynymeamodule.h | 2 + .../integrations/python/pypluginstorage.h | 67 ++++---- .../integrations/pythonintegrationplugin.cpp | 26 +-- .../integrations/pythonintegrationplugin.h | 5 +- libnymea-core/libnymea-core.pro | 1 + 6 files changed, 204 insertions(+), 52 deletions(-) create mode 100644 libnymea-core/integrations/python/pyapikeystorage.h diff --git a/libnymea-core/integrations/python/pyapikeystorage.h b/libnymea-core/integrations/python/pyapikeystorage.h new file mode 100644 index 00000000..0f023b42 --- /dev/null +++ b/libnymea-core/integrations/python/pyapikeystorage.h @@ -0,0 +1,155 @@ +#ifndef PYAPIKEYSTORAGE_H +#define PYAPIKEYSTORAGE_H + +#include +#include "structmember.h" +#include "pyutils.h" + +#include "loggingcategories.h" +#include "nymeasettings.h" +#include "typeutils.h" +#include "network/apikeys/apikeystorage.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#pragma GCC diagnostic ignored "-Wwrite-strings" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" + +/* Note: + * When using this, make sure to call PyApiKeyStorage_setApiKeyStorage() while holding the GIL to initialize + * stuff after constructing it. + * + * The apiKeyStorage() pointer is owned by the C++ plugin class, however, without an actual C++ + * plugin it will never be accessed by anyone. So we pass it into the python context and use it in + * there. + * + */ + +typedef struct { + PyObject_HEAD + ApiKey *apiKey; +} PyApiKey; + +static int PyApiKey_init(PyApiKey */*self*/, PyObject */*args*/, PyObject */*kwds*/) +{ + qCDebug(dcPythonIntegrations()) << "+++ PyApiKey"; + return 0; +} + +void PyApiKey_setApiKey(PyApiKey *self, const ApiKey &apiKey) +{ + self->apiKey = new ApiKey(apiKey); +} + +static void PyApiKey_dealloc(PyApiKey* self) +{ + qCDebug(dcPythonIntegrations()) << "--- PyApiKey"; + delete self->apiKey; + Py_TYPE(self)->tp_free(self); +} + +static PyObject * PyApiKey_data(PyApiKey* self, PyObject* args) { + char *keyStr = nullptr; + if (!PyArg_ParseTuple(args, "s", &keyStr)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in value call. Expected: requestKey(key)"); + return nullptr; + } + + QByteArray data = self->apiKey->data(QString(keyStr)); + return QVariantToPyObject(data); +}; + +static PyMethodDef PyApiKey_methods[] = { + { "data", (PyCFunction)PyApiKey_data, METH_VARARGS, "Get data from the API key." }, + {nullptr, nullptr, 0, nullptr} // sentinel +}; + +static PyTypeObject PyApiKeyType = { + PyVarObject_HEAD_INIT(NULL, 0) + "nymea.ApiKey", /* tp_name */ + sizeof(PyApiKey), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PyApiKey_dealloc, /* tp_dealloc */ +}; + + + + + +typedef struct { + PyObject_HEAD + ApiKeyStorage *apiKeyStorage; +} PyApiKeyStorage; + +static int PyApiKeyStorage_init(PyApiKeyStorage */*self*/, PyObject */*args*/, PyObject */*kwds*/) +{ + qCDebug(dcPythonIntegrations()) << "+++ PyApiKeyStorage"; + return 0; +} + +void PyApiKeyStorage_setApiKeyStorage(PyApiKeyStorage *self, ApiKeyStorage *apiKeyStorage) +{ + self->apiKeyStorage = apiKeyStorage; +} + +static void PyApiKeyStorage_dealloc(PyApiKeyStorage * self) +{ + qCDebug(dcPythonIntegrations()) << "--- PyApiKeyStorage"; + Py_TYPE(self)->tp_free(self); +} + +static PyObject * PyApiKeyStorage_requestKey(PyApiKeyStorage* self, PyObject* args) { + char *nameStr = nullptr; + if (!PyArg_ParseTuple(args, "s", &nameStr)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in value call. Expected: requestKey(name)"); + return nullptr; + } + + PyApiKey *pyApiKey = (PyApiKey*)PyObject_CallObject((PyObject*)&PyApiKeyType, args); + PyApiKey_setApiKey(pyApiKey, self->apiKeyStorage->requestKey(nameStr)); + return (PyObject*)pyApiKey; +}; + +static PyMethodDef PyApiKeyStorage_methods[] = { + { "requestKey", (PyCFunction)PyApiKeyStorage_requestKey, METH_VARARGS, "Get an API key from the API key storage." }, + {nullptr, nullptr, 0, nullptr} // sentinel +}; + +static PyTypeObject PyApiKeyStorageType = { + PyVarObject_HEAD_INIT(NULL, 0) + "nymea.ApiKeyStorage", /* tp_name */ + sizeof(PyApiKeyStorage), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PyApiKeyStorage_dealloc, /* tp_dealloc */ +}; + + + +static void registerApiKeyStorageType(PyObject *module) +{ + PyApiKeyStorageType.tp_new = PyType_GenericNew; + PyApiKeyStorageType.tp_methods = PyApiKeyStorage_methods; + PyApiKeyStorageType.tp_init = reinterpret_cast(PyApiKeyStorage_init); + PyApiKeyStorageType.tp_doc = "ApiKeyStorage holds API keys. API keys need to be requested in the plugin JSON file."; + PyApiKeyStorageType.tp_flags = Py_TPFLAGS_DEFAULT; + + if (PyType_Ready(&PyApiKeyStorageType) < 0) { + return; + } + PyModule_AddObject(module, "ApiKeyStorage", reinterpret_cast(&PyApiKeyStorageType)); + + PyApiKeyType.tp_new = PyType_GenericNew; + PyApiKeyType.tp_methods = PyApiKey_methods; + PyApiKeyType.tp_init = reinterpret_cast(PyApiKey_init); + PyApiKeyType.tp_doc = "ApiKey holds an API keys data as key-value pairs."; + PyApiKeyType.tp_flags = Py_TPFLAGS_DEFAULT; + + if (PyType_Ready(&PyApiKeyType) < 0) { + return; + } + PyModule_AddObject(module, "ApiKey", reinterpret_cast(&PyApiKeyType)); +} + +#pragma GCC diagnostic pop + +#endif // PYAPIKEYSTORAGE_H diff --git a/libnymea-core/integrations/python/pynymeamodule.h b/libnymea-core/integrations/python/pynymeamodule.h index 1044fc29..8936ee65 100644 --- a/libnymea-core/integrations/python/pynymeamodule.h +++ b/libnymea-core/integrations/python/pynymeamodule.h @@ -12,6 +12,7 @@ #include "pythingactioninfo.h" #include "pythingpairinginfo.h" #include "pypluginstorage.h" +#include "pyapikeystorage.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winvalid-offsetof" @@ -29,6 +30,7 @@ static int nymea_exec(PyObject *m) { registerThingSetupInfoType(m); registerThingActionInfoType(m); registerPluginStorageType(m); + registerApiKeyStorageType(m); return 0; } diff --git a/libnymea-core/integrations/python/pypluginstorage.h b/libnymea-core/integrations/python/pypluginstorage.h index 7da97481..1355470b 100644 --- a/libnymea-core/integrations/python/pypluginstorage.h +++ b/libnymea-core/integrations/python/pypluginstorage.h @@ -5,7 +5,6 @@ #include "structmember.h" #include "pyutils.h" -//#include "integrations/pythonintegrationplugin.h" #include "loggingcategories.h" #include "nymeasettings.h" #include "typeutils.h" @@ -17,16 +16,37 @@ #pragma GCC diagnostic ignored "-Wwrite-strings" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +/* Note: + * When using this, make sure to call PyPluginStorage_setPluginStorage() while holding the GIL to initialize + * stuff after constructing it. + * + * The pluginStorage() pointer is owned by the C++ plugin class, however, without an actual C++ + * plugin it will never be accessed by anyone. So we pass it into the python context and use it in + * there. + * + */ typedef struct { PyObject_HEAD - QSettings *settings; + QSettings *pluginStorage; } PyPluginStorage; +static int PyPluginStorage_init(PyPluginStorage */*self*/, PyObject */*args*/, PyObject */*kwds*/) +{ + qCDebug(dcPythonIntegrations()) << "+++ PyPluginStorage"; + return 0; +} -static PyMemberDef PyPluginStorage_members[] = { - {nullptr, 0, 0, 0, nullptr} /* Sentinel */ -}; +void PyPluginStorage_setPluginStorage(PyPluginStorage *self, QSettings* pluginStorage) +{ + self->pluginStorage = pluginStorage; +} + +static void PyPluginStorage_dealloc(PyPluginStorage * self) +{ + qCDebug(dcPythonIntegrations()) << "--- PyPluginStorage"; + Py_TYPE(self)->tp_free(self); +} static PyObject * PyPluginStorage_value(PyPluginStorage* self, PyObject* args) { char *keyStr = nullptr; @@ -35,7 +55,7 @@ static PyObject * PyPluginStorage_value(PyPluginStorage* self, PyObject* args) { return nullptr; } - QVariant value = self->settings->value(keyStr); + QVariant value = self->pluginStorage->value(keyStr); return QVariantToPyObject(value); }; @@ -47,7 +67,7 @@ static PyObject * PyPluginStorage_setValue(PyPluginStorage* self, PyObject* args return nullptr; } - self->settings->setValue(keyStr, PyObjectToQVariant(value)); + self->pluginStorage->setValue(keyStr, PyObjectToQVariant(value)); Py_RETURN_NONE; }; @@ -58,13 +78,13 @@ static PyObject * PyPluginStorage_beginGroup(PyPluginStorage* self, PyObject* ar return nullptr; } - self->settings->beginGroup(groupStr); + self->pluginStorage->beginGroup(groupStr); Py_RETURN_NONE; }; static PyObject * PyPluginStorage_endGroup(PyPluginStorage* self, PyObject* args) { Q_UNUSED(args) - self->settings->endGroup(); + self->pluginStorage->endGroup(); Py_RETURN_NONE; }; @@ -76,34 +96,6 @@ static PyMethodDef PyPluginStorage_methods[] = { {nullptr, nullptr, 0, nullptr} // sentinel }; -static int PyPluginStorage_init(PyPluginStorage *self, PyObject *args, PyObject *kwds) -{ - Q_UNUSED(kwds) - char *pluginIdStr = nullptr; - if (!PyArg_ParseTuple(args, "s", &pluginIdStr)) { - PyErr_SetString(PyExc_TypeError, "Invalid arguments in constructor. Expected: PluginStorage(pluginId)"); - return -1; - } - - if (!pluginIdStr) { - return -1; - } - PluginId pluginId(pluginIdStr); - if (pluginId.isNull()) { - return -1; - } - - self->settings = new QSettings(NymeaSettings::settingsPath() + "/pluginconfig-" + pluginId.toString().remove(QRegExp("[{}]")) + ".conf", QSettings::IniFormat); - - qCDebug(dcPythonIntegrations()) << "+++ PyPluginStorage"; - return 0; -} - -static void PyPluginStorage_dealloc(PyPluginStorage * self) -{ - qCDebug(dcPythonIntegrations()) << "--- PyPluginStorage"; - Py_TYPE(self)->tp_free(self); -} static PyTypeObject PyPluginStorageType = { PyVarObject_HEAD_INIT(NULL, 0) @@ -118,7 +110,6 @@ static PyTypeObject PyPluginStorageType = { static void registerPluginStorageType(PyObject *module) { PyPluginStorageType.tp_new = PyType_GenericNew; - PyPluginStorageType.tp_members = PyPluginStorage_members; PyPluginStorageType.tp_methods = PyPluginStorage_methods; PyPluginStorageType.tp_init = reinterpret_cast(PyPluginStorage_init); PyPluginStorageType.tp_doc = "PluginStorage can be used by plugins to store key-value pairs to a persistant place."; diff --git a/libnymea-core/integrations/pythonintegrationplugin.cpp b/libnymea-core/integrations/pythonintegrationplugin.cpp index cd365aef..ee3b334f 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.cpp +++ b/libnymea-core/integrations/pythonintegrationplugin.cpp @@ -4,6 +4,7 @@ #include "python/pynymeamodule.h" #include "python/pystdouthandler.h" #include "python/pypluginstorage.h" +#include "python/pyapikeystorage.h" #include "loggingcategories.h" @@ -177,14 +178,24 @@ PyObject *PythonIntegrationPlugin::pyAutoThingDisappeared(PyObject *self, PyObje Py_RETURN_NONE; } -PyObject *PythonIntegrationPlugin::pyPluginStorage(PyObject *self, PyObject *args) +PyObject *PythonIntegrationPlugin::pyPluginStorage(PyObject *self, PyObject */*args*/) { - Q_UNUSED(args) - PyObject *pluginStorage = s_plugins.key(self)->m_pyPluginStorage; - Py_INCREF(pluginStorage); + // Note: Passing the pluginsStorage() pointer directly into python. Implies that it must not be + // accessed in the main thread without obtaining the GIL + PyObject *pluginStorage = PyObject_CallObject((PyObject*)&PyPluginStorageType, nullptr); + PyPluginStorage_setPluginStorage((PyPluginStorage*)pluginStorage, s_plugins.key(self)->pluginStorage()); return pluginStorage; } +PyObject *PythonIntegrationPlugin::pyApiKeyStorage(PyObject *self, PyObject *args) +{ + // Note: Passing the apiKeyStorage() pointer directly into python. Implies that it must not be + // accessed in the main thread without obtaining the GIL + PyObject *pyApiKeyStorage = PyObject_CallObject((PyObject*)&PyApiKeyStorageType, args); + PyApiKeyStorage_setApiKeyStorage((PyApiKeyStorage*)pyApiKeyStorage, s_plugins.key(self)->apiKeyStorage()); + return pyApiKeyStorage; +} + static PyMethodDef plugin_methods[] = { {"configuration", PythonIntegrationPlugin::pyConfiguration, METH_VARARGS, "Get the plugin configuration."}, @@ -194,6 +205,7 @@ static PyMethodDef plugin_methods[] = {"autoThingsAppeared", PythonIntegrationPlugin::pyAutoThingsAppeared, METH_VARARGS, "Inform the system about auto setup things having appeared."}, {"autoThingDisappeared", PythonIntegrationPlugin::pyAutoThingDisappeared, METH_VARARGS, "Inform the system about auto setup things having disappeared."}, {"pluginStorage", PythonIntegrationPlugin::pyPluginStorage, METH_VARARGS, "Obtain the plugin storage for this plugin."}, + {"apiKeyStorage", PythonIntegrationPlugin::pyApiKeyStorage, METH_VARARGS, "Obtain the API key storage for this plugin."}, {nullptr, nullptr, 0, nullptr} // sentinel }; @@ -225,7 +237,6 @@ PythonIntegrationPlugin::~PythonIntegrationPlugin() s_plugins.take(this); Py_XDECREF(m_pluginModule); - Py_DECREF(m_pyPluginStorage); Py_DECREF(m_nymeaModule); Py_EndInterpreter(m_threadState); @@ -365,11 +376,6 @@ bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) Py_DECREF(logger); } - args = Py_BuildValue("(s)", pluginId().toString().toUtf8().data()); - m_pyPluginStorage = PyObject_CallObject((PyObject*)&PyPluginStorageType, args); - Py_DECREF(args); - Py_INCREF(m_pyPluginStorage); - // Export metadata ids into module exportIds(); diff --git a/libnymea-core/integrations/pythonintegrationplugin.h b/libnymea-core/integrations/pythonintegrationplugin.h index 06a2fcdb..d57c1305 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.h +++ b/libnymea-core/integrations/pythonintegrationplugin.h @@ -46,6 +46,7 @@ public: static PyObject* pyAutoThingsAppeared(PyObject *self, PyObject* args); static PyObject* pyAutoThingDisappeared(PyObject *self, PyObject* args); static PyObject* pyPluginStorage(PyObject* self, PyObject* args); + static PyObject* pyApiKeyStorage(PyObject* self, PyObject* args); private: void exportIds(); @@ -89,10 +90,6 @@ private: // Need to keep a copy of plugin params and sync that in a thread-safe manner ParamList m_pluginConfigCopy; - - // Keeping our own plugin storage for python - PyObject* m_pyPluginStorage = nullptr; - }; #endif // PYTHONINTEGRATIONPLUGIN_H diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index 4feff683..a98f1598 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -46,6 +46,7 @@ RESOURCES += $$top_srcdir/icons.qrc \ HEADERS += nymeacore.h \ integrations/apikeysprovidersloader.h \ integrations/plugininfocache.h \ + integrations/python/pyapikeystorage.h \ integrations/python/pypluginstorage.h \ integrations/thingmanagerimplementation.h \ integrations/translator.h \