Also add api key storage api to python

This commit is contained in:
Michael Zanetti 2021-03-09 00:14:06 +01:00
parent 9d30be2568
commit 193c647dc6
6 changed files with 204 additions and 52 deletions

View 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

View File

@ -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;
}

View File

@ -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<initproc>(PyPluginStorage_init);
PyPluginStorageType.tp_doc = "PluginStorage can be used by plugins to store key-value pairs to a persistant place.";

View File

@ -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();

View File

@ -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

View File

@ -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 \