Merge PR #401: Add support for the plugin and api key storage to python plugins
This commit is contained in:
commit
84fe40b3e3
155
libnymea-core/integrations/python/pyapikeystorage.h
Normal file
155
libnymea-core/integrations/python/pyapikeystorage.h
Normal file
@ -0,0 +1,155 @@
|
||||
#ifndef PYAPIKEYSTORAGE_H
|
||||
#define PYAPIKEYSTORAGE_H
|
||||
|
||||
#include <Python.h>
|
||||
#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<initproc>(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<PyObject*>(&PyApiKeyStorageType));
|
||||
|
||||
PyApiKeyType.tp_new = PyType_GenericNew;
|
||||
PyApiKeyType.tp_methods = PyApiKey_methods;
|
||||
PyApiKeyType.tp_init = reinterpret_cast<initproc>(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<PyObject*>(&PyApiKeyType));
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif // PYAPIKEYSTORAGE_H
|
||||
@ -11,6 +11,8 @@
|
||||
#include "pyparam.h"
|
||||
#include "pythingactioninfo.h"
|
||||
#include "pythingpairinginfo.h"
|
||||
#include "pypluginstorage.h"
|
||||
#include "pyapikeystorage.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||
@ -27,6 +29,8 @@ static int nymea_exec(PyObject *m) {
|
||||
registerThingPairingInfoType(m);
|
||||
registerThingSetupInfoType(m);
|
||||
registerThingActionInfoType(m);
|
||||
registerPluginStorageType(m);
|
||||
registerApiKeyStorageType(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
126
libnymea-core/integrations/python/pypluginstorage.h
Normal file
126
libnymea-core/integrations/python/pypluginstorage.h
Normal file
@ -0,0 +1,126 @@
|
||||
#ifndef PYPLUGINSTORAGE_H
|
||||
#define PYPLUGINSTORAGE_H
|
||||
|
||||
#include <Python.h>
|
||||
#include "structmember.h"
|
||||
#include "pyutils.h"
|
||||
|
||||
#include "loggingcategories.h"
|
||||
#include "nymeasettings.h"
|
||||
#include "typeutils.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
#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 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 *pluginStorage;
|
||||
} PyPluginStorage;
|
||||
|
||||
static int PyPluginStorage_init(PyPluginStorage */*self*/, PyObject */*args*/, PyObject */*kwds*/)
|
||||
{
|
||||
qCDebug(dcPythonIntegrations()) << "+++ PyPluginStorage";
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!PyArg_ParseTuple(args, "s", &keyStr)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Invalid arguments in value call. Expected: value(key)");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QVariant value = self->pluginStorage->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->pluginStorage->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->pluginStorage->beginGroup(groupStr);
|
||||
Py_RETURN_NONE;
|
||||
};
|
||||
|
||||
static PyObject * PyPluginStorage_endGroup(PyPluginStorage* self, PyObject* args) {
|
||||
Q_UNUSED(args)
|
||||
self->pluginStorage->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 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_methods = PyPluginStorage_methods;
|
||||
PyPluginStorageType.tp_init = reinterpret_cast<initproc>(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<PyObject*>(&PyPluginStorageType));
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif // PYPLUGINSTORAGE_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));
|
||||
}
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
#include "pythonintegrationplugin.h"
|
||||
#include "python/pynymeamodule.h"
|
||||
#include "python/pystdouthandler.h"
|
||||
#include "python/pypluginstorage.h"
|
||||
#include "python/pyapikeystorage.h"
|
||||
|
||||
#include "loggingcategories.h"
|
||||
|
||||
@ -176,6 +178,24 @@ PyObject *PythonIntegrationPlugin::pyAutoThingDisappeared(PyObject *self, PyObje
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *PythonIntegrationPlugin::pyPluginStorage(PyObject *self, PyObject */*args*/)
|
||||
{
|
||||
// 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."},
|
||||
@ -184,6 +204,8 @@ 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."},
|
||||
{"apiKeyStorage", PythonIntegrationPlugin::pyApiKeyStorage, METH_VARARGS, "Obtain the API key storage for this plugin."},
|
||||
{nullptr, nullptr, 0, nullptr} // sentinel
|
||||
};
|
||||
|
||||
|
||||
@ -45,6 +45,8 @@ 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);
|
||||
static PyObject* pyApiKeyStorage(PyObject* self, PyObject* args);
|
||||
|
||||
private:
|
||||
void exportIds();
|
||||
@ -88,7 +90,6 @@ private:
|
||||
|
||||
// Need to keep a copy of plugin params and sync that in a thread-safe manner
|
||||
ParamList m_pluginConfigCopy;
|
||||
|
||||
};
|
||||
|
||||
#endif // PYTHONINTEGRATIONPLUGIN_H
|
||||
|
||||
@ -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 << ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +46,8 @@ 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 \
|
||||
experiences/experiencemanager.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:
|
||||
|
||||
Reference in New Issue
Block a user