gplugin/gplugin

Parents faa362979ee4
Children 4a343ffeb111
When we renamed all the python files to say python3 we missed the actual directory

Testing Done:
Compiled locally, built debian and fedora packages without issues.

Reviewed at https://reviews.imfreedom.org/r/106/
--- a/meson.build Sat Sep 05 20:27:32 2020 -0500
+++ b/meson.build Sat Sep 05 23:31:26 2020 -0500
@@ -91,7 +91,7 @@
subdir('lua')
subdir('perl5')
-subdir('python')
+subdir('python3')
subdir('tcc')
subdir('vala')
--- a/python/gplugin-python3-core.c Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib/gi18n-lib.h>
-
-#include <gplugin.h>
-#include <gplugin-native.h>
-
-#include "gplugin-python3-loader.h"
-#include "gplugin-python3-plugin.h"
-
-G_MODULE_EXPORT GPluginPluginInfo *
-gplugin_query(G_GNUC_UNUSED GError **error)
-{
- /* clang-format-11 formats this correctly, so this can be removed then. */
- /* clang-format off */
- const gchar *const authors[] = {
- "Gary Kramlich <grim@reaperworld.com>",
- NULL};
- /* clang-format on */
-
- /* clang-format off */
- return gplugin_plugin_info_new(
- "gplugin/python3-loader",
- GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
- "internal", TRUE,
- "load-on-query", TRUE,
- "name", "Python Plugin Loader",
- "version", GPLUGIN_VERSION,
- "license-id", "LGPL-2.0-or-later",
- "summary", "A plugin that can load python plugins",
- "description", "This plugin allows the loading of plugins written in "
- "the python programming language.",
- "authors", authors,
- "website", GPLUGIN_WEBSITE,
- "category", "loaders",
- NULL);
- /* clang-format on */
-}
-
-G_MODULE_EXPORT gboolean
-gplugin_load(GPluginNativePlugin *plugin, G_GNUC_UNUSED GError **error)
-{
- gplugin_python3_plugin_register(plugin);
- gplugin_python3_loader_register(plugin);
-
- return gplugin_manager_register_loader(GPLUGIN_PYTHON3_TYPE_LOADER, error);
-}
-
-G_MODULE_EXPORT gboolean
-gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
-{
- g_set_error_literal(
- error,
- GPLUGIN_DOMAIN,
- 0,
- _("The Python loader can not be unloaded"));
-
- return FALSE;
-}
--- a/python/gplugin-python3-loader.c Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,401 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "gplugin-python3-loader.h"
-
-#include <stdlib.h>
-
-#include <glib/gi18n-lib.h>
-
-#include <Python.h>
-#include <pygobject.h>
-
-#include "gplugin-python3-plugin.h"
-#include "gplugin-python3-utils.h"
-
-struct _GPluginPython3Loader {
- GPluginLoader parent;
-
- PyThreadState *py_thread_state;
- guint gc_id;
-};
-
-G_DEFINE_DYNAMIC_TYPE(
- GPluginPython3Loader,
- gplugin_python3_loader,
- GPLUGIN_TYPE_LOADER);
-
-/******************************************************************************
- * GPluginLoader Implementation
- *****************************************************************************/
-static GSList *
-gplugin_python3_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
-{
- return g_slist_append(NULL, "py");
-}
-
-static GPluginPlugin *
-gplugin_python3_loader_query(
- GPluginLoader *loader,
- const gchar *filename,
- G_GNUC_UNUSED GError **error)
-{
- GPluginPlugin *plugin = NULL;
- GObject *info = NULL;
- PyObject *pyinfo = NULL, *args = NULL;
- PyObject *module = NULL, *package_list = NULL, *module_dict = NULL;
- PyObject *query = NULL, *load = NULL, *unload = NULL;
- PyGILState_STATE state;
- gchar *module_name = NULL, *dir_name = NULL;
-
- /* lock the gil */
- state = pyg_gil_state_ensure();
-
- /* create package_list as a tuple to handle 'import foo.bar' */
- package_list = PyTuple_New(0);
-
- /* now figure out the module name from the filename */
- module_name = gplugin_python3_filename_to_module(filename);
-
- /* grab the dirname since we need it on sys.path to import the module */
- dir_name = g_path_get_dirname(filename);
- gplugin_python3_add_module_path(dir_name);
- g_free(dir_name);
-
- /* import the module */
- module = PyImport_ImportModuleEx(module_name, NULL, NULL, package_list);
- if(PyErr_Occurred()) {
- g_warning(_("Failed to query %s"), filename);
- PyErr_Print();
-
- /* clean some stuff up */
- g_free(module_name);
- Py_DECREF(package_list);
-
- pyg_gil_state_release(state);
-
- return NULL;
- }
-
- /* clean some stuff up */
- g_free(module_name);
- Py_DECREF(package_list);
-
- /* at this point we have the module, lets find the query, load, and unload
- * functions.
- */
- module_dict = PyModule_GetDict(module);
-
- query = PyDict_GetItemString(module_dict, "gplugin_query");
- if(query == NULL) {
- g_warning(
- _("Failed to find the gplugin_query function in %s"),
- filename);
-
- Py_DECREF(module);
- pyg_gil_state_release(state);
-
- return NULL;
- }
- if(!PyCallable_Check(query)) {
- g_warning(
- _("Found gplugin_query in %s but it is not a "
- "function"),
- filename);
-
- Py_DECREF(module);
- pyg_gil_state_release(state);
-
- return NULL;
- }
-
- load = PyDict_GetItemString(module_dict, "gplugin_load");
- if(load == NULL) {
- g_warning(
- _("Failed to find the gplugin_load function in %s"),
- filename);
-
- Py_DECREF(module);
- pyg_gil_state_release(state);
-
- return NULL;
- }
- if(!PyCallable_Check(load)) {
- g_warning(
- _("Found gplugin_load in %s but it is not a "
- "function"),
- filename);
-
- Py_DECREF(module);
- pyg_gil_state_release(state);
-
- return NULL;
- }
-
- unload = PyDict_GetItemString(module_dict, "gplugin_unload");
- if(unload == NULL) {
- g_warning(
- _("Failed to find the gplugin_unload function in %s"),
- filename);
-
- Py_DECREF(module);
- pyg_gil_state_release(state);
-
- return NULL;
- }
- if(!PyCallable_Check(unload)) {
- g_warning(
- _("Found gplugin_unload in %s but it is not a "
- "function"),
- filename);
-
- Py_DECREF(module);
- pyg_gil_state_release(state);
-
- return NULL;
- }
-
- /* now that we have everything, call the query method and get the plugin's
- * info.
- */
- args = PyTuple_New(0);
- pyinfo = PyObject_Call(query, args, NULL);
- Py_DECREF(args);
-
- info = pygobject_get(pyinfo);
-
- /* now that we have everything, create the plugin */
- /* clang-format off */
- plugin = g_object_new(
- GPLUGIN_PYTHON3_TYPE_PLUGIN,
- "filename", filename,
- "loader", loader,
- "module", module,
- "info", info,
- "load-func", load,
- "unload-func", unload,
- NULL);
- /* clang-format on */
-
- Py_DECREF(pyinfo);
- Py_DECREF(module);
-
- /* unlock the gil */
- pyg_gil_state_release(state);
-
- return plugin;
-}
-
-static gboolean
-gplugin_python3_loader_load(
- G_GNUC_UNUSED GPluginLoader *loader,
- GPluginPlugin *plugin,
- GError **error)
-{
- PyObject *load = NULL, *pyplugin = NULL, *result = NULL;
- gboolean ret = FALSE;
-
- g_object_get(G_OBJECT(plugin), "load-func", &load, NULL);
-
- pyplugin = pygobject_new(G_OBJECT(plugin));
-
- result = PyObject_CallFunctionObjArgs(load, pyplugin, NULL);
- Py_DECREF(pyplugin);
-
- if(PyErr_Occurred()) {
- Py_XDECREF(result);
-
- if(error) {
- *error = gplugin_python3_exception_to_gerror();
- }
-
- return FALSE;
- }
-
- ret = PyObject_IsTrue(result);
- Py_DECREF(result);
-
- if(!ret) {
- g_set_error_literal(
- error,
- GPLUGIN_DOMAIN,
- 0,
- _("Failed to load plugin"));
- }
-
- return ret;
-}
-
-static gboolean
-gplugin_python3_loader_unload(
- G_GNUC_UNUSED GPluginLoader *loader,
- GPluginPlugin *plugin,
- GError **error)
-{
- PyObject *unload = NULL, *pyplugin = NULL, *result = NULL;
- gboolean ret = FALSE;
-
- g_object_get(G_OBJECT(plugin), "unload-func", &unload, NULL);
-
- pyplugin = pygobject_new(G_OBJECT(plugin));
-
- result = PyObject_CallFunctionObjArgs(unload, pyplugin, NULL);
- Py_DECREF(pyplugin);
-
- if(PyErr_Occurred()) {
- PyErr_Print();
-
- Py_XDECREF(result);
-
- return FALSE;
- }
-
- ret = PyObject_IsTrue(result);
- Py_DECREF(result);
-
- if(!ret) {
- g_set_error_literal(
- error,
- GPLUGIN_DOMAIN,
- 0,
- _("Failed to unload plugin"));
- }
-
- return ret;
-}
-
-/******************************************************************************
- * Python3 Stuff
- *****************************************************************************/
-static gboolean
-gplugin_python3_loader_init_pygobject(void)
-{
- pygobject_init(3, 0, 0);
- if(PyErr_Occurred()) {
- PyObject *type = NULL, *value = NULL, *tb = NULL, *obj = NULL;
-
- PyErr_Fetch(&type, &value, &tb);
- Py_DECREF(type);
- Py_XDECREF(tb);
-
- obj = PyUnicode_AsUTF8String(value);
- Py_DECREF(value);
-
- g_warning("Failed to initialize PyGObject : %s", PyBytes_AsString(obj));
- Py_DECREF(obj);
-
- return FALSE;
- }
-
- /* enable threads */
- pyg_enable_threads();
-
- /* disable g_log redirections */
- pyg_disable_warning_redirections();
-
- return TRUE;
-}
-
-static gboolean
-gplugin_python3_loader_init_gettext(void)
-{
- PyObject *module_dict = NULL, *install = NULL;
- PyObject *gettext = NULL, *result = NULL;
-
- gettext = PyImport_ImportModule("gettext");
- if(gettext == NULL) {
- g_warning("Failed to import gettext");
-
- return FALSE;
- }
-
- module_dict = PyModule_GetDict(gettext);
- install = PyDict_GetItemString(module_dict, "install");
- result = PyObject_CallFunction(install, "ss", GETTEXT_PACKAGE, LOCALEDIR);
- Py_XDECREF(result);
- Py_DECREF(gettext);
-
- return TRUE;
-}
-
-static gboolean
-gplugin_python3_loader_init_python(void)
-{
- wchar_t *argv[] = {NULL, NULL};
-
- /* Initializes Python */
- if(!Py_IsInitialized())
- Py_InitializeEx(FALSE);
-
- argv[0] = Py_DecodeLocale(g_get_prgname(), NULL);
- if(argv[0] == NULL) {
- g_warning("Could not convert program name to wchar_t string.");
- return FALSE;
- }
-
- /* setup sys.path according to
- * https://docs.python.org/3/c-api/init.html#PySys_SetArgvEx
- */
- PySys_SetArgvEx(1, argv, 0);
- PyMem_RawFree(argv[0]);
-
- /* initialize pygobject */
- if(gplugin_python3_loader_init_pygobject()) {
- if(gplugin_python3_loader_init_gettext()) {
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/******************************************************************************
- * GObject Implementation
- *****************************************************************************/
-static void
-gplugin_python3_loader_init(G_GNUC_UNUSED GPluginPython3Loader *loader)
-{
-}
-
-static void
-gplugin_python3_loader_class_finalize(
- G_GNUC_UNUSED GPluginPython3LoaderClass *klass)
-{
-}
-
-static void
-gplugin_python3_loader_class_init(GPluginPython3LoaderClass *klass)
-{
- GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
-
- loader_class->supported_extensions =
- gplugin_python3_loader_supported_extensions;
- loader_class->query = gplugin_python3_loader_query;
- loader_class->load = gplugin_python3_loader_load;
- loader_class->unload = gplugin_python3_loader_unload;
-}
-
-/******************************************************************************
- * API
- *****************************************************************************/
-void
-gplugin_python3_loader_register(GPluginNativePlugin *native)
-{
- gplugin_python3_loader_register_type(G_TYPE_MODULE(native));
-
- gplugin_python3_loader_init_python();
-}
--- a/python/gplugin-python3-loader.h Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef GPLUGIN_PYTHON3_LOADER_H
-#define GPLUGIN_PYTHON3_LOADER_H
-
-#include <gplugin.h>
-#include <gplugin-native.h>
-
-G_BEGIN_DECLS
-
-#define GPLUGIN_PYTHON3_TYPE_LOADER (gplugin_python3_loader_get_type())
-G_DECLARE_FINAL_TYPE(
- GPluginPython3Loader,
- gplugin_python3_loader,
- GPLUGIN_PYTHON3,
- LOADER,
- GPluginLoader)
-
-void gplugin_python3_loader_register(GPluginNativePlugin *native);
-
-G_END_DECLS
-
-#endif /* GPLUGIN_PYTHON3_LOADER_H */
--- a/python/gplugin-python3-plugin.c Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "gplugin-python3-plugin.h"
-
-#include <Python.h>
-
-/******************************************************************************
- * Typedefs
- *****************************************************************************/
-struct _GPluginPython3Plugin {
- GObject parent;
-
- PyObject *module;
- PyObject *query;
- PyObject *load;
- PyObject *unload;
-
- /* overrides */
- gchar *filename;
- GPluginLoader *loader;
- GPluginPluginInfo *info;
- GPluginPluginState state;
- GError *error;
-};
-
-/******************************************************************************
- * Enums
- *****************************************************************************/
-enum {
- PROP_ZERO,
- PROP_MODULE,
- PROP_LOAD_FUNC,
- PROP_UNLOAD_FUNC,
- N_PROPERTIES,
- /* overrides */
- PROP_FILENAME = N_PROPERTIES,
- PROP_LOADER,
- PROP_INFO,
- PROP_STATE,
- PROP_ERROR,
-};
-static GParamSpec *properties[N_PROPERTIES] = {
- NULL,
-};
-
-/* I hate forward declarations... */
-static void gplugin_python3_plugin_iface_init(GPluginPluginInterface *iface);
-
-G_DEFINE_DYNAMIC_TYPE_EXTENDED(
- GPluginPython3Plugin,
- gplugin_python3_plugin,
- G_TYPE_OBJECT,
- 0,
- G_IMPLEMENT_INTERFACE(
- GPLUGIN_TYPE_PLUGIN,
- gplugin_python3_plugin_iface_init));
-
-/******************************************************************************
- * GPluginPlugin Implementation
- *****************************************************************************/
-static void
-gplugin_python3_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface)
-{
-}
-
-/******************************************************************************
- * Private Stuff
- *****************************************************************************/
-static void
-gplugin_python3_plugin_set_module(
- GPluginPython3Plugin *plugin,
- PyObject *module)
-{
- g_return_if_fail(GPLUGIN_IS_PLUGIN(plugin));
- g_return_if_fail(module);
-
- Py_XINCREF(module);
- Py_CLEAR(plugin->module);
- plugin->module = module;
-}
-
-static gpointer
-gplugin_python3_plugin_get_load_func(GPluginPython3Plugin *plugin)
-{
- g_return_val_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin), NULL);
-
- return plugin->load;
-}
-
-static void
-gplugin_python3_plugin_set_load_func(
- GPluginPython3Plugin *plugin,
- PyObject *func)
-{
- g_return_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin));
- g_return_if_fail(func != NULL);
-
- Py_XINCREF(func);
- Py_CLEAR(plugin->load);
- plugin->load = func;
-}
-
-static gpointer
-gplugin_python3_plugin_get_unload_func(GPluginPython3Plugin *plugin)
-{
- g_return_val_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin), NULL);
-
- return plugin->unload;
-}
-
-static void
-gplugin_python3_plugin_set_unload_func(
- GPluginPython3Plugin *plugin,
- PyObject *func)
-{
- g_return_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin));
- g_return_if_fail(func != NULL);
-
- Py_XINCREF(func);
- Py_CLEAR(plugin->unload);
- plugin->unload = func;
-}
-
-/******************************************************************************
- * GObject Implementation
- *****************************************************************************/
-static void
-gplugin_python3_plugin_get_property(
- GObject *obj,
- guint param_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
-
- switch(param_id) {
- case PROP_MODULE:
- g_value_set_pointer(value, plugin->module);
- break;
- case PROP_LOAD_FUNC:
- g_value_set_pointer(
- value,
- gplugin_python3_plugin_get_load_func(plugin));
- break;
- case PROP_UNLOAD_FUNC:
- g_value_set_pointer(
- value,
- gplugin_python3_plugin_get_unload_func(plugin));
- break;
-
- /* overrides */
- case PROP_FILENAME:
- g_value_set_string(value, plugin->filename);
- break;
- case PROP_LOADER:
- g_value_set_object(value, plugin->loader);
- break;
- case PROP_INFO:
- g_value_set_object(value, plugin->info);
- break;
- case PROP_STATE:
- g_value_set_enum(value, plugin->state);
- break;
- case PROP_ERROR:
- g_value_set_boxed(value, plugin->error);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
- break;
- }
-}
-
-static void
-gplugin_python3_plugin_set_property(
- GObject *obj,
- guint param_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
-
- switch(param_id) {
- case PROP_MODULE:
- gplugin_python3_plugin_set_module(
- plugin,
- g_value_get_pointer(value));
- break;
- case PROP_LOAD_FUNC:
- gplugin_python3_plugin_set_load_func(
- plugin,
- g_value_get_pointer(value));
- break;
- case PROP_UNLOAD_FUNC:
- gplugin_python3_plugin_set_unload_func(
- plugin,
- g_value_get_pointer(value));
- break;
-
- /* overrides */
- case PROP_FILENAME:
- plugin->filename = g_value_dup_string(value);
- break;
- case PROP_LOADER:
- plugin->loader = g_value_dup_object(value);
- break;
- case PROP_INFO:
- plugin->info = g_value_dup_object(value);
- break;
- case PROP_STATE:
- plugin->state = g_value_get_enum(value);
- break;
- case PROP_ERROR:
- plugin->error = g_value_dup_boxed(value);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
- break;
- }
-}
-
-static void
-gplugin_python3_plugin_finalize(GObject *obj)
-{
- GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
-
- Py_CLEAR(plugin->module);
- Py_CLEAR(plugin->load);
- Py_CLEAR(plugin->unload);
-
- g_clear_pointer(&plugin->filename, g_free);
- g_clear_object(&plugin->loader);
- g_clear_object(&plugin->info);
- g_clear_error(&plugin->error);
-
- G_OBJECT_CLASS(gplugin_python3_plugin_parent_class)->finalize(obj);
-}
-
-static void
-gplugin_python3_plugin_init(G_GNUC_UNUSED GPluginPython3Plugin *plugin)
-{
-}
-
-static void
-gplugin_python3_plugin_class_finalize(
- G_GNUC_UNUSED GPluginPython3PluginClass *klass)
-{
-}
-
-static void
-gplugin_python3_plugin_class_init(GPluginPython3PluginClass *klass)
-{
- GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-
- obj_class->get_property = gplugin_python3_plugin_get_property;
- obj_class->set_property = gplugin_python3_plugin_set_property;
- obj_class->finalize = gplugin_python3_plugin_finalize;
-
- properties[PROP_MODULE] = g_param_spec_pointer(
- "module",
- "module",
- "The python module object",
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-
- properties[PROP_LOAD_FUNC] = g_param_spec_pointer(
- "load-func",
- "load-func",
- "The python load function",
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-
- properties[PROP_UNLOAD_FUNC] = g_param_spec_pointer(
- "unload-func",
- "unload-func",
- "The python unload function",
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-
- g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
-
- /* add our overrides */
- g_object_class_override_property(obj_class, PROP_FILENAME, "filename");
- g_object_class_override_property(obj_class, PROP_LOADER, "loader");
- g_object_class_override_property(obj_class, PROP_INFO, "info");
- g_object_class_override_property(obj_class, PROP_STATE, "state");
- g_object_class_override_property(obj_class, PROP_ERROR, "error");
-}
-
-/******************************************************************************
- * API
- *****************************************************************************/
-void
-gplugin_python3_plugin_register(GPluginNativePlugin *native)
-{
- gplugin_python3_plugin_register_type(G_TYPE_MODULE(native));
-}
--- a/python/gplugin-python3-plugin.h Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef GPLUGIN_PYTHON3_PLUGIN_H
-#define GPLUGIN_PYTHON3_PLUGIN_H
-
-#include <gplugin.h>
-#include <gplugin-native.h>
-
-G_BEGIN_DECLS
-
-#define GPLUGIN_PYTHON3_TYPE_PLUGIN (gplugin_python3_plugin_get_type())
-G_DECLARE_FINAL_TYPE(
- GPluginPython3Plugin,
- gplugin_python3_plugin,
- GPLUGIN_PYTHON3,
- PLUGIN,
- GObject)
-
-void gplugin_python3_plugin_register(GPluginNativePlugin *native);
-
-G_END_DECLS
-
-#endif /* GPLUGIN_PYTHON3_PLUGIN_H */
--- a/python/gplugin-python3-test-pygobject.c Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-
-#include <glib.h>
-
-#include <Python.h>
-#include <pygobject.h>
-
-gint
-main(gint argc, gchar *argv[])
-{
- wchar_t *wargv[] = {NULL, NULL};
- size_t len;
-
- /* initialize python */
- if(!Py_IsInitialized())
- Py_InitializeEx(FALSE);
-
- /* setup wargv */
- len = mbstowcs(NULL, argv[0], 0);
- if(len == (size_t)-1) {
- printf("Failed to call mbstowcs to find length of argv[0]: %s\n", argv[0]);
- return -1;
- }
-
- wargv[0] = g_new0(wchar_t, len + 1);
- len = mbstowcs(wargv[0], argv[0], len + 1);
- if(len == (size_t)-1) {
- g_free(wargv[0]);
- printf("Failed to call mbstowcs to convert argv[0]: %s\n", argv[0]);
- return -1;
- }
-
- /* setup sys.path */
- PySys_SetArgvEx(1, wargv, 0);
-
- g_free(wargv[0]);
-
- /* initialize pygobject */
- pygobject_init(3, 0, 0);
- if(PyErr_Occurred()) {
- printf("Calling pygobject_init failed.\n");
- PyErr_Print();
- return -1;
- }
-
- return 0;
-}
--- a/python/gplugin-python3-utils.c Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "gplugin-python3-utils.h"
-
-#include <string.h>
-
-#include <gplugin.h>
-
-#include <Python.h>
-
-gchar *
-gplugin_python3_filename_to_module(const gchar *filename)
-{
- gchar *base = NULL;
- gchar *e = NULL, *r = NULL;
-
- g_return_val_if_fail(filename != NULL, NULL);
-
- /* first make sure we just have a filename */
- base = g_path_get_basename(filename);
-
- /* now find the last . for the extension */
- e = g_utf8_strrchr(base, g_utf8_strlen(base, -1), g_utf8_get_char("."));
- if(e == NULL) {
- return base;
- }
-
- /* now copy the module name into r */
- r = g_malloc(e - base + 1);
- memcpy(r, base, e - base);
- r[e - base] = 0;
-
- /* free the basename */
- g_free(base);
-
- return r;
-}
-
-gboolean
-gplugin_python3_add_module_path(const gchar *module_path)
-{
- PyObject *sys_path = NULL, *path = NULL;
- gboolean ret = FALSE;
-
- sys_path = PySys_GetObject("path");
-
- path = PyUnicode_FromString(module_path);
-
- if(PySequence_Contains(sys_path, path) == 0) {
- PyList_Insert(sys_path, 0, path);
- ret = TRUE;
- }
-
- Py_DECREF(path);
-
- return ret;
-}
-
-GError *
-gplugin_python3_exception_to_gerror(void)
-{
- GError *error = NULL;
- PyObject *type = NULL, *value = NULL, *trace = NULL;
- PyObject *type_name = NULL, *value_str = NULL, *obj = NULL;
-
- if(!PyErr_Occurred())
- return NULL;
-
- PyErr_Fetch(&type, &value, &trace);
- if(type == NULL)
- return NULL;
-
- PyErr_NormalizeException(&type, &value, &trace);
- Py_XDECREF(trace);
-
- type_name = PyObject_GetAttrString(type, "__name__");
- Py_DECREF(type);
-
- value_str = PyObject_Str(value);
- Py_DECREF(value);
-
- /* now decode the utf8 into a string we can use */
- obj = PyUnicode_AsUTF8String(type_name);
- Py_DECREF(type_name);
- type_name = obj;
-
- obj = PyUnicode_AsUTF8String(value_str);
- Py_DECREF(value_str);
- value_str = obj;
-
- error = g_error_new(
- GPLUGIN_DOMAIN,
- 0,
- "%s: %s",
- PyBytes_AsString(type_name),
- PyBytes_AsString(value_str));
-
- Py_DECREF(type_name);
- Py_DECREF(value_str);
-
- return error;
-}
--- a/python/gplugin-python3-utils.h Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef GPLUGIN_PYTHON3_UTILS_H
-#define GPLUGIN_PYTHON3_UTILS_H
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-gchar *gplugin_python3_filename_to_module(const gchar *filename);
-
-gboolean gplugin_python3_add_module_path(const gchar *module_path);
-
-GError *gplugin_python3_exception_to_gerror(void);
-
-G_END_DECLS
-
-#endif /* GPLUGIN_PYTHON3_UTILS_H */
--- a/python/meson.build Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-if get_option('python3')
- if not get_option('introspection')
- error('Python plugin requires GObject Introspection.')
- endif
-
- GPLUGIN_PYTHON3_SOURCES = [
- 'gplugin-python3-core.c',
- 'gplugin-python3-loader.c',
- 'gplugin-python3-plugin.c',
- 'gplugin-python3-utils.c',
- ]
-
- GPLUGIN_PYTHON3_HEADERS = [
- 'gplugin-python3-loader.h',
- 'gplugin-python3-plugin.h',
- 'gplugin-python3-utils.h',
- ]
-
- PYTHON3 = dependency('python3-embed', version: '>=3.5.0', required: false)
- if not PYTHON3.found()
- PYTHON3 = dependency('python3', version: '>=3.5.0')
- endif
-
- PYGOBJECT = dependency('pygobject-3.0', version: '>=3.0.0')
-
- # Compile and run our python-gi test program
- python3_gi_test = compiler.run(files('gplugin-python3-test-pygobject.c'),
- dependencies : [GLIB, PYTHON3, PYGOBJECT],
- name : 'Python3 GI')
- if not python3_gi_test.compiled() or python3_gi_test.returncode() != 0
- error('pygobject does not work with Python3')
- endif
-
- # Now add our libraries
- gplugin_python3_inc = include_directories('.')
- gplugin_python3 = shared_library('gplugin-python3',
- GPLUGIN_PYTHON3_SOURCES,
- GPLUGIN_PYTHON3_HEADERS,
- name_prefix : '',
- dependencies : [PYTHON3, PYGOBJECT, gplugin_dep],
- install : true,
- install_dir : get_option('libdir') / 'gplugin'
- )
- gplugin_python3_dep = declare_dependency(
- include_directories : gplugin_python3_inc,
- link_with : gplugin_python3,
- )
-
- gplugin_python3_static = static_library('gplugin-python3-static',
- GPLUGIN_PYTHON3_SOURCES,
- GPLUGIN_PYTHON3_HEADERS,
- dependencies : [PYTHON3, PYGOBJECT, gplugin_dep],
- )
- gplugin_python3_static_dep = declare_dependency(
- include_directories : gplugin_python3_inc,
- link_with : gplugin_python3_static,
- )
-endif # python3
-
-subdir('tests')
--- a/python/tests/meson.build Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-if get_option('python3')
-
-e = executable('test-python3-loader', 'test-python3-loader.c',
- include_directories : include_directories('.'),
- c_args : [
- '-DPYTHON3_LOADER_DIR="@0@/.."'.format(meson.current_build_dir()),
- '-DPYTHON3_PLUGIN_DIR="@0@/plugins"'.format(meson.current_source_dir()),
- ],
- link_with : gplugin_loader_tests,
- dependencies : [GLIB, GOBJECT, PYTHON3, PYGOBJECT, gplugin_dep])
-test('Python3 loader', e)
-
-# we can't see the symbols in gplugin-python externally, so use the static
-# version for testing
-e = executable('test-python3-utils', 'test-python3-utils.c',
- include_directories : include_directories('.'),
- link_with : [gplugin_loader_tests],
- dependencies : [GLIB, GOBJECT, PYTHON3, PYGOBJECT,
- gplugin_python3_static_dep])
-test('Python3 utils', e)
-
-endif # python3
--- a/python/tests/plugins/basic.py Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-# vi:et:ts=4 sw=4 sts=4
-# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, see <http://www.gnu.org/licenses/>.
-
-import gi
-
-gi.require_version('GPlugin', '1.0')
-from gi.repository import GPlugin # noqa
-
-
-def gplugin_query():
- return GPlugin.PluginInfo(
- id='gplugin/python3-basic-plugin',
- abi_version=0x01020304,
- name='basic plugin',
- authors=['author1'],
- category='test',
- version='version',
- license_id='license',
- summary='summary',
- website='website',
- description='description',
- )
-
-
-def gplugin_load(plugin):
- return True
-
-
-def gplugin_unload(plugin):
- return True
--- a/python/tests/plugins/dependent.py Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-# vi:et:ts=4 sw=4 sts=4
-# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, see <http://www.gnu.org/licenses/>.
-
-import gi
-
-gi.require_version('GPlugin', '1.0')
-from gi.repository import GPlugin # noqa
-
-
-def gplugin_query():
- return GPlugin.PluginInfo(
- id='gplugin/python3-dependent-plugin',
- dependencies=['dependency1', 'dependency2'],
- )
-
-
-def gplugin_load(plugin):
- return False
-
-
-def gplugin_unload(plugin):
- return False
--- a/python/tests/plugins/load-exception.py Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-# vi:et:ts=4 sw=4 sts=4
-# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, see <http://www.gnu.org/licenses/>.
-
-import gi
-
-gi.require_version('GPlugin', '1.0')
-from gi.repository import GPlugin # noqa
-
-
-def gplugin_query():
- return GPlugin.PluginInfo(
- id="gplugin/python3-load-exception",
- )
-
-
-def gplugin_load(plugin):
- raise ValueError('explosion!')
-
-
-def gplugin_unload(plugin):
- return True
--- a/python/tests/plugins/load-failed.py Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-# vi:et:ts=4 sw=4 sts=4
-# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, see <http://www.gnu.org/licenses/>.
-
-import gi
-
-gi.require_version('GPlugin', '1.0')
-from gi.repository import GPlugin # noqa
-
-
-def gplugin_query():
- return GPlugin.PluginInfo(
- id="gplugin/python3-load-failed",
- )
-
-
-def gplugin_load(plugin):
- return False
-
-
-def gplugin_unload(plugin):
- return True
--- a/python/tests/plugins/unload-failed.py Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-# vi:et:ts=4 sw=4 sts=4
-# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, see <http://www.gnu.org/licenses/>.
-
-import gi
-
-gi.require_version('GPlugin', '1.0')
-from gi.repository import GPlugin # noqa
-
-
-def gplugin_query():
- return GPlugin.PluginInfo(
- id="gplugin/python3-unload-failed",
- )
-
-
-def gplugin_load(plugin):
- return True
-
-
-def gplugin_unload(plugin):
- return False
--- a/python/tests/test-python3-loader.c Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-
-#include <gplugin.h>
-#include <gplugin/gplugin-loader-tests.h>
-
-/******************************************************************************
- * Main
- *****************************************************************************/
-gint
-main(gint argc, gchar **argv)
-{
- g_test_init(&argc, &argv, NULL);
-
- gplugin_loader_tests_main(
- PYTHON3_LOADER_DIR,
- PYTHON3_PLUGIN_DIR,
- "python3");
-
- return g_test_run();
-}
--- a/python/tests/test-python3-utils.c Sat Sep 05 20:27:32 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-
-#include <Python.h>
-
-#include "gplugin-python3-utils.h"
-
-/******************************************************************************
- * filename to module tests
- *****************************************************************************/
-static void
-test_filename_to_module_NULL_subprocess(void)
-{
- gplugin_python3_filename_to_module(NULL);
-}
-
-static void
-test_filename_to_module_NULL(void)
-{
- g_test_trap_subprocess(
- "/loaders/python/utils/filename_to_module/NULL/subprocess",
- 0,
- 0);
-
- g_test_trap_assert_failed();
-}
-
-static void
-test_filename_to_module_empty(void)
-{
- gchar *module = gplugin_python3_filename_to_module("");
-
- g_assert_cmpstr(module, ==, "");
-
- g_free(module);
-}
-
-static void
-test_filename_to_module_no_extension(void)
-{
- gchar *module = gplugin_python3_filename_to_module("foo");
-
- g_assert_cmpstr(module, ==, "foo");
-
- g_free(module);
-}
-
-/******************************************************************************
- * Main
- *****************************************************************************/
-gint
-main(gint argc, gchar **argv)
-{
- g_test_init(&argc, &argv, NULL);
-
- /* tests */
- g_test_add_func(
- "/loaders/python/utils/filename_to_module/NULL",
- test_filename_to_module_NULL);
- g_test_add_func(
- "/loaders/python/utils/filename_to_module/NULL/subprocess",
- test_filename_to_module_NULL_subprocess);
- g_test_add_func(
- "/loaders/python/utils/filename_to_module/empty",
- test_filename_to_module_empty);
- g_test_add_func(
- "/loaders/python/utils/filename_to_module/no-extension",
- test_filename_to_module_no_extension);
-
- return g_test_run();
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-core.c Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include "gplugin-python3-loader.h"
+#include "gplugin-python3-plugin.h"
+
+G_MODULE_EXPORT GPluginPluginInfo *
+gplugin_query(G_GNUC_UNUSED GError **error)
+{
+ /* clang-format-11 formats this correctly, so this can be removed then. */
+ /* clang-format off */
+ const gchar *const authors[] = {
+ "Gary Kramlich <grim@reaperworld.com>",
+ NULL};
+ /* clang-format on */
+
+ /* clang-format off */
+ return gplugin_plugin_info_new(
+ "gplugin/python3-loader",
+ GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
+ "internal", TRUE,
+ "load-on-query", TRUE,
+ "name", "Python Plugin Loader",
+ "version", GPLUGIN_VERSION,
+ "license-id", "LGPL-2.0-or-later",
+ "summary", "A plugin that can load python plugins",
+ "description", "This plugin allows the loading of plugins written in "
+ "the python programming language.",
+ "authors", authors,
+ "website", GPLUGIN_WEBSITE,
+ "category", "loaders",
+ NULL);
+ /* clang-format on */
+}
+
+G_MODULE_EXPORT gboolean
+gplugin_load(GPluginNativePlugin *plugin, G_GNUC_UNUSED GError **error)
+{
+ gplugin_python3_plugin_register(plugin);
+ gplugin_python3_loader_register(plugin);
+
+ return gplugin_manager_register_loader(GPLUGIN_PYTHON3_TYPE_LOADER, error);
+}
+
+G_MODULE_EXPORT gboolean
+gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
+{
+ g_set_error_literal(
+ error,
+ GPLUGIN_DOMAIN,
+ 0,
+ _("The Python loader can not be unloaded"));
+
+ return FALSE;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-loader.c Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gplugin-python3-loader.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <Python.h>
+#include <pygobject.h>
+
+#include "gplugin-python3-plugin.h"
+#include "gplugin-python3-utils.h"
+
+struct _GPluginPython3Loader {
+ GPluginLoader parent;
+
+ PyThreadState *py_thread_state;
+ guint gc_id;
+};
+
+G_DEFINE_DYNAMIC_TYPE(
+ GPluginPython3Loader,
+ gplugin_python3_loader,
+ GPLUGIN_TYPE_LOADER);
+
+/******************************************************************************
+ * GPluginLoader Implementation
+ *****************************************************************************/
+static GSList *
+gplugin_python3_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
+{
+ return g_slist_append(NULL, "py");
+}
+
+static GPluginPlugin *
+gplugin_python3_loader_query(
+ GPluginLoader *loader,
+ const gchar *filename,
+ G_GNUC_UNUSED GError **error)
+{
+ GPluginPlugin *plugin = NULL;
+ GObject *info = NULL;
+ PyObject *pyinfo = NULL, *args = NULL;
+ PyObject *module = NULL, *package_list = NULL, *module_dict = NULL;
+ PyObject *query = NULL, *load = NULL, *unload = NULL;
+ PyGILState_STATE state;
+ gchar *module_name = NULL, *dir_name = NULL;
+
+ /* lock the gil */
+ state = pyg_gil_state_ensure();
+
+ /* create package_list as a tuple to handle 'import foo.bar' */
+ package_list = PyTuple_New(0);
+
+ /* now figure out the module name from the filename */
+ module_name = gplugin_python3_filename_to_module(filename);
+
+ /* grab the dirname since we need it on sys.path to import the module */
+ dir_name = g_path_get_dirname(filename);
+ gplugin_python3_add_module_path(dir_name);
+ g_free(dir_name);
+
+ /* import the module */
+ module = PyImport_ImportModuleEx(module_name, NULL, NULL, package_list);
+ if(PyErr_Occurred()) {
+ g_warning(_("Failed to query %s"), filename);
+ PyErr_Print();
+
+ /* clean some stuff up */
+ g_free(module_name);
+ Py_DECREF(package_list);
+
+ pyg_gil_state_release(state);
+
+ return NULL;
+ }
+
+ /* clean some stuff up */
+ g_free(module_name);
+ Py_DECREF(package_list);
+
+ /* at this point we have the module, lets find the query, load, and unload
+ * functions.
+ */
+ module_dict = PyModule_GetDict(module);
+
+ query = PyDict_GetItemString(module_dict, "gplugin_query");
+ if(query == NULL) {
+ g_warning(
+ _("Failed to find the gplugin_query function in %s"),
+ filename);
+
+ Py_DECREF(module);
+ pyg_gil_state_release(state);
+
+ return NULL;
+ }
+ if(!PyCallable_Check(query)) {
+ g_warning(
+ _("Found gplugin_query in %s but it is not a "
+ "function"),
+ filename);
+
+ Py_DECREF(module);
+ pyg_gil_state_release(state);
+
+ return NULL;
+ }
+
+ load = PyDict_GetItemString(module_dict, "gplugin_load");
+ if(load == NULL) {
+ g_warning(
+ _("Failed to find the gplugin_load function in %s"),
+ filename);
+
+ Py_DECREF(module);
+ pyg_gil_state_release(state);
+
+ return NULL;
+ }
+ if(!PyCallable_Check(load)) {
+ g_warning(
+ _("Found gplugin_load in %s but it is not a "
+ "function"),
+ filename);
+
+ Py_DECREF(module);
+ pyg_gil_state_release(state);
+
+ return NULL;
+ }
+
+ unload = PyDict_GetItemString(module_dict, "gplugin_unload");
+ if(unload == NULL) {
+ g_warning(
+ _("Failed to find the gplugin_unload function in %s"),
+ filename);
+
+ Py_DECREF(module);
+ pyg_gil_state_release(state);
+
+ return NULL;
+ }
+ if(!PyCallable_Check(unload)) {
+ g_warning(
+ _("Found gplugin_unload in %s but it is not a "
+ "function"),
+ filename);
+
+ Py_DECREF(module);
+ pyg_gil_state_release(state);
+
+ return NULL;
+ }
+
+ /* now that we have everything, call the query method and get the plugin's
+ * info.
+ */
+ args = PyTuple_New(0);
+ pyinfo = PyObject_Call(query, args, NULL);
+ Py_DECREF(args);
+
+ info = pygobject_get(pyinfo);
+
+ /* now that we have everything, create the plugin */
+ /* clang-format off */
+ plugin = g_object_new(
+ GPLUGIN_PYTHON3_TYPE_PLUGIN,
+ "filename", filename,
+ "loader", loader,
+ "module", module,
+ "info", info,
+ "load-func", load,
+ "unload-func", unload,
+ NULL);
+ /* clang-format on */
+
+ Py_DECREF(pyinfo);
+ Py_DECREF(module);
+
+ /* unlock the gil */
+ pyg_gil_state_release(state);
+
+ return plugin;
+}
+
+static gboolean
+gplugin_python3_loader_load(
+ G_GNUC_UNUSED GPluginLoader *loader,
+ GPluginPlugin *plugin,
+ GError **error)
+{
+ PyObject *load = NULL, *pyplugin = NULL, *result = NULL;
+ gboolean ret = FALSE;
+
+ g_object_get(G_OBJECT(plugin), "load-func", &load, NULL);
+
+ pyplugin = pygobject_new(G_OBJECT(plugin));
+
+ result = PyObject_CallFunctionObjArgs(load, pyplugin, NULL);
+ Py_DECREF(pyplugin);
+
+ if(PyErr_Occurred()) {
+ Py_XDECREF(result);
+
+ if(error) {
+ *error = gplugin_python3_exception_to_gerror();
+ }
+
+ return FALSE;
+ }
+
+ ret = PyObject_IsTrue(result);
+ Py_DECREF(result);
+
+ if(!ret) {
+ g_set_error_literal(
+ error,
+ GPLUGIN_DOMAIN,
+ 0,
+ _("Failed to load plugin"));
+ }
+
+ return ret;
+}
+
+static gboolean
+gplugin_python3_loader_unload(
+ G_GNUC_UNUSED GPluginLoader *loader,
+ GPluginPlugin *plugin,
+ GError **error)
+{
+ PyObject *unload = NULL, *pyplugin = NULL, *result = NULL;
+ gboolean ret = FALSE;
+
+ g_object_get(G_OBJECT(plugin), "unload-func", &unload, NULL);
+
+ pyplugin = pygobject_new(G_OBJECT(plugin));
+
+ result = PyObject_CallFunctionObjArgs(unload, pyplugin, NULL);
+ Py_DECREF(pyplugin);
+
+ if(PyErr_Occurred()) {
+ PyErr_Print();
+
+ Py_XDECREF(result);
+
+ return FALSE;
+ }
+
+ ret = PyObject_IsTrue(result);
+ Py_DECREF(result);
+
+ if(!ret) {
+ g_set_error_literal(
+ error,
+ GPLUGIN_DOMAIN,
+ 0,
+ _("Failed to unload plugin"));
+ }
+
+ return ret;
+}
+
+/******************************************************************************
+ * Python3 Stuff
+ *****************************************************************************/
+static gboolean
+gplugin_python3_loader_init_pygobject(void)
+{
+ pygobject_init(3, 0, 0);
+ if(PyErr_Occurred()) {
+ PyObject *type = NULL, *value = NULL, *tb = NULL, *obj = NULL;
+
+ PyErr_Fetch(&type, &value, &tb);
+ Py_DECREF(type);
+ Py_XDECREF(tb);
+
+ obj = PyUnicode_AsUTF8String(value);
+ Py_DECREF(value);
+
+ g_warning("Failed to initialize PyGObject : %s", PyBytes_AsString(obj));
+ Py_DECREF(obj);
+
+ return FALSE;
+ }
+
+ /* enable threads */
+ pyg_enable_threads();
+
+ /* disable g_log redirections */
+ pyg_disable_warning_redirections();
+
+ return TRUE;
+}
+
+static gboolean
+gplugin_python3_loader_init_gettext(void)
+{
+ PyObject *module_dict = NULL, *install = NULL;
+ PyObject *gettext = NULL, *result = NULL;
+
+ gettext = PyImport_ImportModule("gettext");
+ if(gettext == NULL) {
+ g_warning("Failed to import gettext");
+
+ return FALSE;
+ }
+
+ module_dict = PyModule_GetDict(gettext);
+ install = PyDict_GetItemString(module_dict, "install");
+ result = PyObject_CallFunction(install, "ss", GETTEXT_PACKAGE, LOCALEDIR);
+ Py_XDECREF(result);
+ Py_DECREF(gettext);
+
+ return TRUE;
+}
+
+static gboolean
+gplugin_python3_loader_init_python(void)
+{
+ wchar_t *argv[] = {NULL, NULL};
+
+ /* Initializes Python */
+ if(!Py_IsInitialized())
+ Py_InitializeEx(FALSE);
+
+ argv[0] = Py_DecodeLocale(g_get_prgname(), NULL);
+ if(argv[0] == NULL) {
+ g_warning("Could not convert program name to wchar_t string.");
+ return FALSE;
+ }
+
+ /* setup sys.path according to
+ * https://docs.python.org/3/c-api/init.html#PySys_SetArgvEx
+ */
+ PySys_SetArgvEx(1, argv, 0);
+ PyMem_RawFree(argv[0]);
+
+ /* initialize pygobject */
+ if(gplugin_python3_loader_init_pygobject()) {
+ if(gplugin_python3_loader_init_gettext()) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+gplugin_python3_loader_init(G_GNUC_UNUSED GPluginPython3Loader *loader)
+{
+}
+
+static void
+gplugin_python3_loader_class_finalize(
+ G_GNUC_UNUSED GPluginPython3LoaderClass *klass)
+{
+}
+
+static void
+gplugin_python3_loader_class_init(GPluginPython3LoaderClass *klass)
+{
+ GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
+
+ loader_class->supported_extensions =
+ gplugin_python3_loader_supported_extensions;
+ loader_class->query = gplugin_python3_loader_query;
+ loader_class->load = gplugin_python3_loader_load;
+ loader_class->unload = gplugin_python3_loader_unload;
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+void
+gplugin_python3_loader_register(GPluginNativePlugin *native)
+{
+ gplugin_python3_loader_register_type(G_TYPE_MODULE(native));
+
+ gplugin_python3_loader_init_python();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-loader.h Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GPLUGIN_PYTHON3_LOADER_H
+#define GPLUGIN_PYTHON3_LOADER_H
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+G_BEGIN_DECLS
+
+#define GPLUGIN_PYTHON3_TYPE_LOADER (gplugin_python3_loader_get_type())
+G_DECLARE_FINAL_TYPE(
+ GPluginPython3Loader,
+ gplugin_python3_loader,
+ GPLUGIN_PYTHON3,
+ LOADER,
+ GPluginLoader)
+
+void gplugin_python3_loader_register(GPluginNativePlugin *native);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_PYTHON3_LOADER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-plugin.c Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gplugin-python3-plugin.h"
+
+#include <Python.h>
+
+/******************************************************************************
+ * Typedefs
+ *****************************************************************************/
+struct _GPluginPython3Plugin {
+ GObject parent;
+
+ PyObject *module;
+ PyObject *query;
+ PyObject *load;
+ PyObject *unload;
+
+ /* overrides */
+ gchar *filename;
+ GPluginLoader *loader;
+ GPluginPluginInfo *info;
+ GPluginPluginState state;
+ GError *error;
+};
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_ZERO,
+ PROP_MODULE,
+ PROP_LOAD_FUNC,
+ PROP_UNLOAD_FUNC,
+ N_PROPERTIES,
+ /* overrides */
+ PROP_FILENAME = N_PROPERTIES,
+ PROP_LOADER,
+ PROP_INFO,
+ PROP_STATE,
+ PROP_ERROR,
+};
+static GParamSpec *properties[N_PROPERTIES] = {
+ NULL,
+};
+
+/* I hate forward declarations... */
+static void gplugin_python3_plugin_iface_init(GPluginPluginInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED(
+ GPluginPython3Plugin,
+ gplugin_python3_plugin,
+ G_TYPE_OBJECT,
+ 0,
+ G_IMPLEMENT_INTERFACE(
+ GPLUGIN_TYPE_PLUGIN,
+ gplugin_python3_plugin_iface_init));
+
+/******************************************************************************
+ * GPluginPlugin Implementation
+ *****************************************************************************/
+static void
+gplugin_python3_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface)
+{
+}
+
+/******************************************************************************
+ * Private Stuff
+ *****************************************************************************/
+static void
+gplugin_python3_plugin_set_module(
+ GPluginPython3Plugin *plugin,
+ PyObject *module)
+{
+ g_return_if_fail(GPLUGIN_IS_PLUGIN(plugin));
+ g_return_if_fail(module);
+
+ Py_XINCREF(module);
+ Py_CLEAR(plugin->module);
+ plugin->module = module;
+}
+
+static gpointer
+gplugin_python3_plugin_get_load_func(GPluginPython3Plugin *plugin)
+{
+ g_return_val_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin), NULL);
+
+ return plugin->load;
+}
+
+static void
+gplugin_python3_plugin_set_load_func(
+ GPluginPython3Plugin *plugin,
+ PyObject *func)
+{
+ g_return_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin));
+ g_return_if_fail(func != NULL);
+
+ Py_XINCREF(func);
+ Py_CLEAR(plugin->load);
+ plugin->load = func;
+}
+
+static gpointer
+gplugin_python3_plugin_get_unload_func(GPluginPython3Plugin *plugin)
+{
+ g_return_val_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin), NULL);
+
+ return plugin->unload;
+}
+
+static void
+gplugin_python3_plugin_set_unload_func(
+ GPluginPython3Plugin *plugin,
+ PyObject *func)
+{
+ g_return_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin));
+ g_return_if_fail(func != NULL);
+
+ Py_XINCREF(func);
+ Py_CLEAR(plugin->unload);
+ plugin->unload = func;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+gplugin_python3_plugin_get_property(
+ GObject *obj,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
+
+ switch(param_id) {
+ case PROP_MODULE:
+ g_value_set_pointer(value, plugin->module);
+ break;
+ case PROP_LOAD_FUNC:
+ g_value_set_pointer(
+ value,
+ gplugin_python3_plugin_get_load_func(plugin));
+ break;
+ case PROP_UNLOAD_FUNC:
+ g_value_set_pointer(
+ value,
+ gplugin_python3_plugin_get_unload_func(plugin));
+ break;
+
+ /* overrides */
+ case PROP_FILENAME:
+ g_value_set_string(value, plugin->filename);
+ break;
+ case PROP_LOADER:
+ g_value_set_object(value, plugin->loader);
+ break;
+ case PROP_INFO:
+ g_value_set_object(value, plugin->info);
+ break;
+ case PROP_STATE:
+ g_value_set_enum(value, plugin->state);
+ break;
+ case PROP_ERROR:
+ g_value_set_boxed(value, plugin->error);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gplugin_python3_plugin_set_property(
+ GObject *obj,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
+
+ switch(param_id) {
+ case PROP_MODULE:
+ gplugin_python3_plugin_set_module(
+ plugin,
+ g_value_get_pointer(value));
+ break;
+ case PROP_LOAD_FUNC:
+ gplugin_python3_plugin_set_load_func(
+ plugin,
+ g_value_get_pointer(value));
+ break;
+ case PROP_UNLOAD_FUNC:
+ gplugin_python3_plugin_set_unload_func(
+ plugin,
+ g_value_get_pointer(value));
+ break;
+
+ /* overrides */
+ case PROP_FILENAME:
+ plugin->filename = g_value_dup_string(value);
+ break;
+ case PROP_LOADER:
+ plugin->loader = g_value_dup_object(value);
+ break;
+ case PROP_INFO:
+ plugin->info = g_value_dup_object(value);
+ break;
+ case PROP_STATE:
+ plugin->state = g_value_get_enum(value);
+ break;
+ case PROP_ERROR:
+ plugin->error = g_value_dup_boxed(value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gplugin_python3_plugin_finalize(GObject *obj)
+{
+ GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
+
+ Py_CLEAR(plugin->module);
+ Py_CLEAR(plugin->load);
+ Py_CLEAR(plugin->unload);
+
+ g_clear_pointer(&plugin->filename, g_free);
+ g_clear_object(&plugin->loader);
+ g_clear_object(&plugin->info);
+ g_clear_error(&plugin->error);
+
+ G_OBJECT_CLASS(gplugin_python3_plugin_parent_class)->finalize(obj);
+}
+
+static void
+gplugin_python3_plugin_init(G_GNUC_UNUSED GPluginPython3Plugin *plugin)
+{
+}
+
+static void
+gplugin_python3_plugin_class_finalize(
+ G_GNUC_UNUSED GPluginPython3PluginClass *klass)
+{
+}
+
+static void
+gplugin_python3_plugin_class_init(GPluginPython3PluginClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ obj_class->get_property = gplugin_python3_plugin_get_property;
+ obj_class->set_property = gplugin_python3_plugin_set_property;
+ obj_class->finalize = gplugin_python3_plugin_finalize;
+
+ properties[PROP_MODULE] = g_param_spec_pointer(
+ "module",
+ "module",
+ "The python module object",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_LOAD_FUNC] = g_param_spec_pointer(
+ "load-func",
+ "load-func",
+ "The python load function",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_UNLOAD_FUNC] = g_param_spec_pointer(
+ "unload-func",
+ "unload-func",
+ "The python unload function",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+ /* add our overrides */
+ g_object_class_override_property(obj_class, PROP_FILENAME, "filename");
+ g_object_class_override_property(obj_class, PROP_LOADER, "loader");
+ g_object_class_override_property(obj_class, PROP_INFO, "info");
+ g_object_class_override_property(obj_class, PROP_STATE, "state");
+ g_object_class_override_property(obj_class, PROP_ERROR, "error");
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+void
+gplugin_python3_plugin_register(GPluginNativePlugin *native)
+{
+ gplugin_python3_plugin_register_type(G_TYPE_MODULE(native));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-plugin.h Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GPLUGIN_PYTHON3_PLUGIN_H
+#define GPLUGIN_PYTHON3_PLUGIN_H
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+G_BEGIN_DECLS
+
+#define GPLUGIN_PYTHON3_TYPE_PLUGIN (gplugin_python3_plugin_get_type())
+G_DECLARE_FINAL_TYPE(
+ GPluginPython3Plugin,
+ gplugin_python3_plugin,
+ GPLUGIN_PYTHON3,
+ PLUGIN,
+ GObject)
+
+void gplugin_python3_plugin_register(GPluginNativePlugin *native);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_PYTHON3_PLUGIN_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-test-pygobject.c Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <Python.h>
+#include <pygobject.h>
+
+gint
+main(gint argc, gchar *argv[])
+{
+ wchar_t *wargv[] = {NULL, NULL};
+ size_t len;
+
+ /* initialize python */
+ if(!Py_IsInitialized())
+ Py_InitializeEx(FALSE);
+
+ /* setup wargv */
+ len = mbstowcs(NULL, argv[0], 0);
+ if(len == (size_t)-1) {
+ printf("Failed to call mbstowcs to find length of argv[0]: %s\n", argv[0]);
+ return -1;
+ }
+
+ wargv[0] = g_new0(wchar_t, len + 1);
+ len = mbstowcs(wargv[0], argv[0], len + 1);
+ if(len == (size_t)-1) {
+ g_free(wargv[0]);
+ printf("Failed to call mbstowcs to convert argv[0]: %s\n", argv[0]);
+ return -1;
+ }
+
+ /* setup sys.path */
+ PySys_SetArgvEx(1, wargv, 0);
+
+ g_free(wargv[0]);
+
+ /* initialize pygobject */
+ pygobject_init(3, 0, 0);
+ if(PyErr_Occurred()) {
+ printf("Calling pygobject_init failed.\n");
+ PyErr_Print();
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-utils.c Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gplugin-python3-utils.h"
+
+#include <string.h>
+
+#include <gplugin.h>
+
+#include <Python.h>
+
+gchar *
+gplugin_python3_filename_to_module(const gchar *filename)
+{
+ gchar *base = NULL;
+ gchar *e = NULL, *r = NULL;
+
+ g_return_val_if_fail(filename != NULL, NULL);
+
+ /* first make sure we just have a filename */
+ base = g_path_get_basename(filename);
+
+ /* now find the last . for the extension */
+ e = g_utf8_strrchr(base, g_utf8_strlen(base, -1), g_utf8_get_char("."));
+ if(e == NULL) {
+ return base;
+ }
+
+ /* now copy the module name into r */
+ r = g_malloc(e - base + 1);
+ memcpy(r, base, e - base);
+ r[e - base] = 0;
+
+ /* free the basename */
+ g_free(base);
+
+ return r;
+}
+
+gboolean
+gplugin_python3_add_module_path(const gchar *module_path)
+{
+ PyObject *sys_path = NULL, *path = NULL;
+ gboolean ret = FALSE;
+
+ sys_path = PySys_GetObject("path");
+
+ path = PyUnicode_FromString(module_path);
+
+ if(PySequence_Contains(sys_path, path) == 0) {
+ PyList_Insert(sys_path, 0, path);
+ ret = TRUE;
+ }
+
+ Py_DECREF(path);
+
+ return ret;
+}
+
+GError *
+gplugin_python3_exception_to_gerror(void)
+{
+ GError *error = NULL;
+ PyObject *type = NULL, *value = NULL, *trace = NULL;
+ PyObject *type_name = NULL, *value_str = NULL, *obj = NULL;
+
+ if(!PyErr_Occurred())
+ return NULL;
+
+ PyErr_Fetch(&type, &value, &trace);
+ if(type == NULL)
+ return NULL;
+
+ PyErr_NormalizeException(&type, &value, &trace);
+ Py_XDECREF(trace);
+
+ type_name = PyObject_GetAttrString(type, "__name__");
+ Py_DECREF(type);
+
+ value_str = PyObject_Str(value);
+ Py_DECREF(value);
+
+ /* now decode the utf8 into a string we can use */
+ obj = PyUnicode_AsUTF8String(type_name);
+ Py_DECREF(type_name);
+ type_name = obj;
+
+ obj = PyUnicode_AsUTF8String(value_str);
+ Py_DECREF(value_str);
+ value_str = obj;
+
+ error = g_error_new(
+ GPLUGIN_DOMAIN,
+ 0,
+ "%s: %s",
+ PyBytes_AsString(type_name),
+ PyBytes_AsString(value_str));
+
+ Py_DECREF(type_name);
+ Py_DECREF(value_str);
+
+ return error;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/gplugin-python3-utils.h Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GPLUGIN_PYTHON3_UTILS_H
+#define GPLUGIN_PYTHON3_UTILS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar *gplugin_python3_filename_to_module(const gchar *filename);
+
+gboolean gplugin_python3_add_module_path(const gchar *module_path);
+
+GError *gplugin_python3_exception_to_gerror(void);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_PYTHON3_UTILS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/meson.build Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,60 @@
+if get_option('python3')
+ if not get_option('introspection')
+ error('Python plugin requires GObject Introspection.')
+ endif
+
+ GPLUGIN_PYTHON3_SOURCES = [
+ 'gplugin-python3-core.c',
+ 'gplugin-python3-loader.c',
+ 'gplugin-python3-plugin.c',
+ 'gplugin-python3-utils.c',
+ ]
+
+ GPLUGIN_PYTHON3_HEADERS = [
+ 'gplugin-python3-loader.h',
+ 'gplugin-python3-plugin.h',
+ 'gplugin-python3-utils.h',
+ ]
+
+ PYTHON3 = dependency('python3-embed', version: '>=3.5.0', required: false)
+ if not PYTHON3.found()
+ PYTHON3 = dependency('python3', version: '>=3.5.0')
+ endif
+
+ PYGOBJECT = dependency('pygobject-3.0', version: '>=3.0.0')
+
+ # Compile and run our python-gi test program
+ python3_gi_test = compiler.run(files('gplugin-python3-test-pygobject.c'),
+ dependencies : [GLIB, PYTHON3, PYGOBJECT],
+ name : 'Python3 GI')
+ if not python3_gi_test.compiled() or python3_gi_test.returncode() != 0
+ error('pygobject does not work with Python3')
+ endif
+
+ # Now add our libraries
+ gplugin_python3_inc = include_directories('.')
+ gplugin_python3 = shared_library('gplugin-python3',
+ GPLUGIN_PYTHON3_SOURCES,
+ GPLUGIN_PYTHON3_HEADERS,
+ name_prefix : '',
+ dependencies : [PYTHON3, PYGOBJECT, gplugin_dep],
+ install : true,
+ install_dir : get_option('libdir') / 'gplugin'
+ )
+ gplugin_python3_dep = declare_dependency(
+ include_directories : gplugin_python3_inc,
+ link_with : gplugin_python3,
+ )
+
+ gplugin_python3_static = static_library('gplugin-python3-static',
+ GPLUGIN_PYTHON3_SOURCES,
+ GPLUGIN_PYTHON3_HEADERS,
+ dependencies : [PYTHON3, PYGOBJECT, gplugin_dep],
+ )
+ gplugin_python3_static_dep = declare_dependency(
+ include_directories : gplugin_python3_inc,
+ link_with : gplugin_python3_static,
+ )
+endif # python3
+
+subdir('tests')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/meson.build Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,22 @@
+if get_option('python3')
+
+e = executable('test-python3-loader', 'test-python3-loader.c',
+ include_directories : include_directories('.'),
+ c_args : [
+ '-DPYTHON3_LOADER_DIR="@0@/.."'.format(meson.current_build_dir()),
+ '-DPYTHON3_PLUGIN_DIR="@0@/plugins"'.format(meson.current_source_dir()),
+ ],
+ link_with : gplugin_loader_tests,
+ dependencies : [GLIB, GOBJECT, PYTHON3, PYGOBJECT, gplugin_dep])
+test('Python3 loader', e)
+
+# we can't see the symbols in gplugin-python externally, so use the static
+# version for testing
+e = executable('test-python3-utils', 'test-python3-utils.c',
+ include_directories : include_directories('.'),
+ link_with : [gplugin_loader_tests],
+ dependencies : [GLIB, GOBJECT, PYTHON3, PYGOBJECT,
+ gplugin_python3_static_dep])
+test('Python3 utils', e)
+
+endif # python3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/plugins/basic.py Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,43 @@
+# vi:et:ts=4 sw=4 sts=4
+# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+import gi
+
+gi.require_version('GPlugin', '1.0')
+from gi.repository import GPlugin # noqa
+
+
+def gplugin_query():
+ return GPlugin.PluginInfo(
+ id='gplugin/python3-basic-plugin',
+ abi_version=0x01020304,
+ name='basic plugin',
+ authors=['author1'],
+ category='test',
+ version='version',
+ license_id='license',
+ summary='summary',
+ website='website',
+ description='description',
+ )
+
+
+def gplugin_load(plugin):
+ return True
+
+
+def gplugin_unload(plugin):
+ return True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/plugins/dependent.py Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,35 @@
+# vi:et:ts=4 sw=4 sts=4
+# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+import gi
+
+gi.require_version('GPlugin', '1.0')
+from gi.repository import GPlugin # noqa
+
+
+def gplugin_query():
+ return GPlugin.PluginInfo(
+ id='gplugin/python3-dependent-plugin',
+ dependencies=['dependency1', 'dependency2'],
+ )
+
+
+def gplugin_load(plugin):
+ return False
+
+
+def gplugin_unload(plugin):
+ return False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/plugins/load-exception.py Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,34 @@
+# vi:et:ts=4 sw=4 sts=4
+# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+import gi
+
+gi.require_version('GPlugin', '1.0')
+from gi.repository import GPlugin # noqa
+
+
+def gplugin_query():
+ return GPlugin.PluginInfo(
+ id="gplugin/python3-load-exception",
+ )
+
+
+def gplugin_load(plugin):
+ raise ValueError('explosion!')
+
+
+def gplugin_unload(plugin):
+ return True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/plugins/load-failed.py Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,34 @@
+# vi:et:ts=4 sw=4 sts=4
+# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+import gi
+
+gi.require_version('GPlugin', '1.0')
+from gi.repository import GPlugin # noqa
+
+
+def gplugin_query():
+ return GPlugin.PluginInfo(
+ id="gplugin/python3-load-failed",
+ )
+
+
+def gplugin_load(plugin):
+ return False
+
+
+def gplugin_unload(plugin):
+ return True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/plugins/unload-failed.py Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,34 @@
+# vi:et:ts=4 sw=4 sts=4
+# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+import gi
+
+gi.require_version('GPlugin', '1.0')
+from gi.repository import GPlugin # noqa
+
+
+def gplugin_query():
+ return GPlugin.PluginInfo(
+ id="gplugin/python3-unload-failed",
+ )
+
+
+def gplugin_load(plugin):
+ return True
+
+
+def gplugin_unload(plugin):
+ return False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/test-python3-loader.c Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <gplugin.h>
+#include <gplugin/gplugin-loader-tests.h>
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ gplugin_loader_tests_main(
+ PYTHON3_LOADER_DIR,
+ PYTHON3_PLUGIN_DIR,
+ "python3");
+
+ return g_test_run();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python3/tests/test-python3-utils.c Sat Sep 05 23:31:26 2020 -0500
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <Python.h>
+
+#include "gplugin-python3-utils.h"
+
+/******************************************************************************
+ * filename to module tests
+ *****************************************************************************/
+static void
+test_filename_to_module_NULL_subprocess(void)
+{
+ gplugin_python3_filename_to_module(NULL);
+}
+
+static void
+test_filename_to_module_NULL(void)
+{
+ g_test_trap_subprocess(
+ "/loaders/python/utils/filename_to_module/NULL/subprocess",
+ 0,
+ 0);
+
+ g_test_trap_assert_failed();
+}
+
+static void
+test_filename_to_module_empty(void)
+{
+ gchar *module = gplugin_python3_filename_to_module("");
+
+ g_assert_cmpstr(module, ==, "");
+
+ g_free(module);
+}
+
+static void
+test_filename_to_module_no_extension(void)
+{
+ gchar *module = gplugin_python3_filename_to_module("foo");
+
+ g_assert_cmpstr(module, ==, "foo");
+
+ g_free(module);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ /* tests */
+ g_test_add_func(
+ "/loaders/python/utils/filename_to_module/NULL",
+ test_filename_to_module_NULL);
+ g_test_add_func(
+ "/loaders/python/utils/filename_to_module/NULL/subprocess",
+ test_filename_to_module_NULL_subprocess);
+ g_test_add_func(
+ "/loaders/python/utils/filename_to_module/empty",
+ test_filename_to_module_empty);
+ g_test_add_func(
+ "/loaders/python/utils/filename_to_module/no-extension",
+ test_filename_to_module_no_extension);
+
+ return g_test_run();
+}