From 32f545cdf031ebe3718791f18e8fb6b6141fd081 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 28 Aug 2009 20:21:54 -0400 Subject: Simplify EPlugin loading at startup. - Require all EPlugin and EPluginHook subtypes be registered before loading plugins. This drastically simplifies the EPlugin/EPluginHook negotiation. - Turn most EPluginHook subtypes into GTypeModules and register their types from an e_module_load() function (does not include shell hooks). - Convert EPluginLib and the Mono and Python bindings to GTypeModules and register their types from an e_module_load() function, and kill EPluginTypeHook. --- modules/plugin-python/Makefile.am | 31 +++ modules/plugin-python/e-plugin-python.c | 230 +++++++++++++++++++++ modules/plugin-python/e-plugin-python.h | 70 +++++++ .../plugin-python/evolution-module-plugin-python.c | 41 ++++ modules/plugin-python/example/Makefile.am | 29 +++ modules/plugin-python/example/hello_python.py | 5 + .../example/org-gnome-hello-python-ui.xml | 16 ++ .../example/org-gnome-hello-python.eplug.xml | 20 ++ 8 files changed, 442 insertions(+) create mode 100644 modules/plugin-python/Makefile.am create mode 100644 modules/plugin-python/e-plugin-python.c create mode 100644 modules/plugin-python/e-plugin-python.h create mode 100644 modules/plugin-python/evolution-module-plugin-python.c create mode 100644 modules/plugin-python/example/Makefile.am create mode 100644 modules/plugin-python/example/hello_python.py create mode 100644 modules/plugin-python/example/org-gnome-hello-python-ui.xml create mode 100644 modules/plugin-python/example/org-gnome-hello-python.eplug.xml (limited to 'modules/plugin-python') diff --git a/modules/plugin-python/Makefile.am b/modules/plugin-python/Makefile.am new file mode 100644 index 0000000000..e6a32c3c83 --- /dev/null +++ b/modules/plugin-python/Makefile.am @@ -0,0 +1,31 @@ +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"evolution-plugin-python\" \ + -I$(top_srcdir) \ + $(E_UTIL_CFLAGS) \ + $(PY_INCLUDES) + +module_LTLIBRARIES = libevolution-module-plugin-python.la + +libevolution_module_plugin_python_la_SOURCES = \ + evolution-module-plugin-python.c \ + e-plugin-python.c \ + e-plugin-python.h + +libevolution_module_plugin_python_la_LIBADD = \ + -lpthread -ldl -lutil -lm \ + $(top_builddir)/e-util/libeutil.la \ + $(E_UTIL_LIBS) \ + $(PY_LIBS) + +libevolution_module_plugin_python_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) + +example_sources = \ + example/hello_python.py \ + example/org-gnome-hello-python-ui.xml \ + example/org-gnome-hello-python.eplug.xml \ + example/Makefile.am + +EXTRA_DIST = $(example_sources) + +-include $(top_srcdir)/git.mk diff --git a/modules/plugin-python/e-plugin-python.c b/modules/plugin-python/e-plugin-python.c new file mode 100644 index 0000000000..747ba57bac --- /dev/null +++ b/modules/plugin-python/e-plugin-python.c @@ -0,0 +1,230 @@ +/* + * e-plugin-python.c + * + * This program 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) version 3. + * + * This program 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 the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +/* Include first to avoid: + * warning: "_POSIX_C_SOURCE" redefined */ +#include + +#include "e-plugin-python.h" + +#include +#include + +#define E_PLUGIN_PYTHON_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_PLUGIN_PYTHON, EPluginPythonPrivate)) + +struct _EPluginPythonPrivate { + PyObject *pModule; + PyObject *pClass; + PyObject *pFunc; + PyObject *pDict; + GHashTable *methods; +}; + +static gpointer parent_class; +static GType plugin_python_type; + +static gchar * +get_xml_prop (xmlNodePtr node, const gchar *id) +{ + xmlChar *prop; + gchar *out = NULL; + + prop = xmlGetProp (node, (xmlChar *) id); + + if (prop != NULL) { + out = g_strdup ((gchar *) prop); + xmlFree (prop); + } + + return out; +} + +static void +plugin_python_finalize (GObject *object) +{ + EPluginPython *plugin_python; + + plugin_python = E_PLUGIN_PYTHON (object); + + g_free (plugin_python->location); + g_free (plugin_python->module_name); + g_free (plugin_python->pClass); + + g_hash_table_destroy (plugin_python->priv->methods); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint +plugin_python_construct (EPlugin *plugin, xmlNodePtr root) +{ + EPluginPython *plugin_python; + + /* Chain up to parent's construct() method. */ + if (E_PLUGIN_CLASS (parent_class)->construct (plugin, root) == -1) + return -1; + + plugin_python = E_PLUGIN_PYTHON (plugin); + plugin_python->location = get_xml_prop (root, "location"); + plugin_python->module_name = get_xml_prop (root, "module_name"); + plugin_python->pClass = get_xml_prop (root, "pClass"); + + return (plugin_python->location != NULL) ? 0 : -1; +} + +static gpointer +plugin_python_invoke (EPlugin *plugin, + const gchar *name, + gpointer data) +{ + EPluginPython *plugin_python; + EPluginPythonPrivate *priv; + PyObject *pModuleName, *pFunc; + PyObject *pInstance, *pValue = NULL; + + plugin_python = E_PLUGIN_PYTHON (plugin); + priv = plugin_python->priv; + + /* We need to do this every time since we may be called + * from any thread for some uses. */ + Py_Initialize (); + + if (priv->pModule == NULL) { + gchar *string; + + pModuleName = PyString_FromString (plugin_python->module_name); + + string = g_strdup_printf ( + "import sys; " + "sys.path.insert(0, '%s')", + plugin_python->location); + PyRun_SimpleString (string); + g_free (string); + + priv->pModule = PyImport_Import (pModuleName); + + Py_DECREF (pModuleName); //Free + + if (priv->pModule == NULL) { + PyErr_Print (); + g_warning ( + "Can't load python module '%s'", + plugin_python->location); + return NULL; + } + + priv->pDict = PyModule_GetDict (priv->pModule); + + if (plugin_python->pClass != NULL) { + priv->pClass = PyDict_GetItemString ( + priv->pDict, plugin_python->pClass); + } + } + + if (priv->pClass) { + + if (PyCallable_Check (priv->pClass)) + pInstance = PyObject_CallObject (priv->pClass, NULL); + + pValue = PyObject_CallMethod (pInstance, (gchar *) name, NULL); + + } else { + + pFunc = PyDict_GetItemString (priv->pDict, name); + + if (pFunc && PyCallable_Check (pFunc)) + pValue = PyObject_CallObject (pFunc, NULL); + else + PyErr_Print (); + } + + if (pValue) { + Py_DECREF(pValue); + /* Fixme */ + return NULL; + } else + return NULL; +} + +static void +plugin_python_class_init (EPluginPythonClass *class) +{ + GObjectClass *object_class; + EPluginClass *plugin_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EPluginPythonPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = plugin_python_finalize; + + plugin_class = E_PLUGIN_CLASS (class); + plugin_class->construct = plugin_python_construct; + plugin_class->invoke = plugin_python_invoke; + plugin_class->type = "python"; +} + +static void +plugin_python_init (EPluginPython *plugin_python) +{ + GHashTable *methods; + + methods = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + + plugin_python->priv = E_PLUGIN_PYTHON_GET_PRIVATE (plugin_python); + plugin_python->priv->methods = methods; +} + +GType +e_plugin_python_get_type (void) +{ + return plugin_python_type; +} + +void +e_plugin_python_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (EPluginPythonClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) plugin_python_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EPluginPython), + 0, /* n_preallocs */ + (GInstanceInitFunc) plugin_python_init, + NULL /* value_table */ + }; + + plugin_python_type = g_type_module_register_type ( + type_module, E_TYPE_PLUGIN, + "EPluginPython", &type_info, 0); + + /* TODO Does this mean I can cache the instance of pyobjects? */ + Py_Initialize (); +} diff --git a/modules/plugin-python/e-plugin-python.h b/modules/plugin-python/e-plugin-python.h new file mode 100644 index 0000000000..9ee780c76c --- /dev/null +++ b/modules/plugin-python/e-plugin-python.h @@ -0,0 +1,70 @@ +/* + * e-plugin-python.h + * + * This program 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) version 3. + * + * This program 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 the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_PLUGIN_PYTHON_H +#define E_PLUGIN_PYTHON_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_PLUGIN_PYTHON \ + (e_plugin_python_get_type ()) +#define E_PLUGIN_PYTHON(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_PLUGIN_PYTHON, EPluginPython)) +#define E_PLUGIN_PYTHON_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_PLUGIN_PYTHON, EPluginPythonClass)) +#define E_IS_PLUGIN_PYTHON(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_PLUGIN_PYTHON)) +#define E_IS_PLUGIN_PYTHON_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_PLUGIN_PYTHON)) +#define E_PLUGIN_PYTHON_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_PLUGIN_PYTHON, EPluginPythonClass)) + +G_BEGIN_DECLS + +typedef struct _EPluginPython EPluginPython; +typedef struct _EPluginPythonClass EPluginPythonClass; +typedef struct _EPluginPythonPrivate EPluginPythonPrivate; + +struct _EPluginPython { + EPlugin parent; + EPluginPythonPrivate *priv; + + gchar *location; + gchar *pClass; + gchar *module_name; +}; + +struct _EPluginPythonClass { + EPluginClass parent_class; +}; + +GType e_plugin_python_get_type (void); +void e_plugin_python_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_PLUGIN_PYTHON_H */ diff --git a/modules/plugin-python/evolution-module-plugin-python.c b/modules/plugin-python/evolution-module-plugin-python.c new file mode 100644 index 0000000000..84ab3b3e8c --- /dev/null +++ b/modules/plugin-python/evolution-module-plugin-python.c @@ -0,0 +1,41 @@ +/* + * evolution-module-plugin-python.c + * + * This program 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) version 3. + * + * This program 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 the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include + +#include "e-plugin-python.h" + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + /* Register dynamically loaded types. */ + + e_plugin_python_register_type (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} diff --git a/modules/plugin-python/example/Makefile.am b/modules/plugin-python/example/Makefile.am new file mode 100644 index 0000000000..cc14dc94d9 --- /dev/null +++ b/modules/plugin-python/example/Makefile.am @@ -0,0 +1,29 @@ +AM_CPPFLAGS = \ + -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" + +@EVO_PLUGIN_RULE@ + +plugin_DATA = \ + hello_python.py \ + org-gnome-hello-python-ui.xml \ + org-gnome-hello-python.eplug + +liborg_gnome_py_plug_test_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) + +errordir = $(privdatadir)/errors + +BUILDME = org-gnome-hello-python.eplug \ +$(error_i18n) + +BUILT_SOURCES = \ + $(BUILDME) + +EXTRA_DIST = \ + hello_python.py \ + org-gnome-hello-python-ui.xml \ + org-gnome-hello-python.eplug.xml + +CLEANFILES = $(BUILT_SOURCES) + +-include $(top_srcdir)/git.mk diff --git a/modules/plugin-python/example/hello_python.py b/modules/plugin-python/example/hello_python.py new file mode 100644 index 0000000000..16dc2a12f8 --- /dev/null +++ b/modules/plugin-python/example/hello_python.py @@ -0,0 +1,5 @@ +'''hello_python.py - Python source designed to ''' +'''demonstrate the use of python Eplugins''' + +def say_hello(): + print 'Hello ! From python' diff --git a/modules/plugin-python/example/org-gnome-hello-python-ui.xml b/modules/plugin-python/example/org-gnome-hello-python-ui.xml new file mode 100644 index 0000000000..074960e84d --- /dev/null +++ b/modules/plugin-python/example/org-gnome-hello-python-ui.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/modules/plugin-python/example/org-gnome-hello-python.eplug.xml b/modules/plugin-python/example/org-gnome-hello-python.eplug.xml new file mode 100644 index 0000000000..8f77d5ba01 --- /dev/null +++ b/modules/plugin-python/example/org-gnome-hello-python.eplug.xml @@ -0,0 +1,20 @@ + + + + + + + <_description> + Test Plugin for Python EPlugin loader. + + + + + + + + + + + + -- cgit v1.2.3