summaryrefslogtreecommitdiff
path: root/src/eom-python-module.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/eom-python-module.c')
-rw-r--r--src/eom-python-module.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/src/eom-python-module.c b/src/eom-python-module.c
new file mode 100644
index 0000000..d0e05a5
--- /dev/null
+++ b/src/eom-python-module.c
@@ -0,0 +1,527 @@
+/*
+ * eom-python-module.c
+ * This file is part of eom
+ *
+ * Copyright (C) 2005 Raphael Slinckx
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This needs to be included before any standard header
+ * see http://docs.python.org/c-api/intro.html#include-files */
+#include <Python.h>
+
+#include <pygobject.h>
+#include <pygtk/pygtk.h>
+
+#include <signal.h>
+
+#include <gmodule.h>
+
+#include "eom-python-module.h"
+#include "eom-python-plugin.h"
+#include "eom-debug.h"
+
+#if PY_VERSION_HEX < 0x02050000
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+#define EOM_PYTHON_MODULE_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PYTHON_MODULE, EomPythonModulePrivate))
+
+struct _EomPythonModulePrivate {
+ gchar *module;
+ gchar *path;
+ GType type;
+};
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ PROP_MODULE
+};
+
+void pyeom_register_classes (PyObject *d);
+void pyeom_add_constants (PyObject *module, const gchar *strip_prefix);
+extern PyMethodDef pyeom_functions[];
+
+static PyTypeObject *PyEomPlugin_Type;
+
+G_DEFINE_TYPE (EomPythonModule, eom_python_module, G_TYPE_TYPE_MODULE)
+
+static gboolean
+eom_python_module_load (GTypeModule *gmodule)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (gmodule);
+ PyObject *main_module, *main_locals, *locals, *key, *value;
+ PyObject *module, *fromlist;
+ Py_ssize_t pos = 0;
+
+ g_return_val_if_fail (Py_IsInitialized (), FALSE);
+
+ main_module = PyImport_AddModule ("__main__");
+
+ if (main_module == NULL) {
+ g_warning ("Could not get __main__.");
+ return FALSE;
+ }
+
+ /* If we have a special path, we register it */
+ if (priv->path != NULL) {
+ PyObject *sys_path = PySys_GetObject ("path");
+ PyObject *path = PyString_FromString (priv->path);
+
+ if (PySequence_Contains(sys_path, path) == 0)
+ PyList_Insert (sys_path, 0, path);
+
+ Py_DECREF(path);
+ }
+
+ main_locals = PyModule_GetDict (main_module);
+
+ /* We need a fromlist to be able to import modules with
+ * a '.' in the name. */
+ fromlist = PyTuple_New(0);
+
+ module = PyImport_ImportModuleEx (priv->module, main_locals, main_locals, fromlist);
+
+ Py_DECREF(fromlist);
+
+ if (!module) {
+ PyErr_Print ();
+ return FALSE;
+ }
+
+ locals = PyModule_GetDict (module);
+
+ while (PyDict_Next (locals, &pos, &key, &value)) {
+ if (!PyType_Check(value))
+ continue;
+
+ if (PyObject_IsSubclass (value, (PyObject*) PyEomPlugin_Type)) {
+ priv->type = eom_python_plugin_get_type (gmodule, value);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+eom_python_module_unload (GTypeModule *module)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (module);
+
+ eom_debug_message (DEBUG_PLUGINS, "Unloading Python module");
+
+ priv->type = 0;
+}
+
+GObject *
+eom_python_module_new_object (EomPythonModule *module)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (module);
+
+ eom_debug_message (DEBUG_PLUGINS, "Creating object of type %s", g_type_name (priv->type));
+
+ if (priv->type == 0)
+ return NULL;
+
+ return g_object_new (priv->type, NULL);
+}
+
+static void
+eom_python_module_init (EomPythonModule *module)
+{
+ eom_debug_message (DEBUG_PLUGINS, "Init of Python module");
+}
+
+static void
+eom_python_module_finalize (GObject *object)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (object);
+
+ eom_debug_message (DEBUG_PLUGINS, "Finalizing Python module %s", g_type_name (priv->type));
+
+ g_free (priv->module);
+ g_free (priv->path);
+
+ G_OBJECT_CLASS (eom_python_module_parent_class)->finalize (object);
+}
+
+static void
+eom_python_module_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ g_return_if_reached ();
+}
+
+static void
+eom_python_module_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomPythonModule *mod = EOM_PYTHON_MODULE (object);
+
+ switch (prop_id) {
+ case PROP_MODULE:
+ EOM_PYTHON_MODULE_GET_PRIVATE (mod)->module = g_value_dup_string (value);
+ break;
+
+ case PROP_PATH:
+ EOM_PYTHON_MODULE_GET_PRIVATE (mod)->path = g_value_dup_string (value);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+}
+
+static void
+eom_python_module_class_init (EomPythonModuleClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
+
+ object_class->finalize = eom_python_module_finalize;
+ object_class->get_property = eom_python_module_get_property;
+ object_class->set_property = eom_python_module_set_property;
+
+ g_object_class_install_property
+ (object_class,
+ PROP_MODULE,
+ g_param_spec_string ("module",
+ "Module Name",
+ "The Python module to load for this plugin",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path",
+ "Path",
+ "The Python path to use when loading this module",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (object_class, sizeof (EomPythonModulePrivate));
+
+ module_class->load = eom_python_module_load;
+ module_class->unload = eom_python_module_unload;
+}
+
+EomPythonModule *
+eom_python_module_new (const gchar *path,
+ const gchar *module)
+{
+ EomPythonModule *result;
+
+ if (module == NULL || module[0] == '\0')
+ return NULL;
+
+ result = g_object_new (EOM_TYPE_PYTHON_MODULE,
+ "module", module,
+ "path", path,
+ NULL);
+
+ g_type_module_set_name (G_TYPE_MODULE (result), module);
+
+ return result;
+}
+
+
+static gint idle_garbage_collect_id = 0;
+
+/* C equivalent of
+ * import pygtk
+ * pygtk.require ("2.0")
+ */
+static gboolean
+check_pygtk2 (void)
+{
+ PyObject *pygtk, *mdict, *require;
+
+ /* pygtk.require("2.0") */
+ pygtk = PyImport_ImportModule ("pygtk");
+
+ if (pygtk == NULL) {
+ g_warning ("Error initializing Python interpreter: could not import pygtk.");
+ return FALSE;
+ }
+
+ mdict = PyModule_GetDict (pygtk);
+
+ require = PyDict_GetItemString (mdict, "require");
+
+ PyObject_CallObject (require,
+ Py_BuildValue ("(S)", PyString_FromString ("2.0")));
+
+ if (PyErr_Occurred()) {
+ g_warning ("Error initializing Python interpreter: pygtk 2 is required.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Note: the following two functions are needed because
+ * init_pyobject and init_pygtk which are *macros* which in case
+ * case of error set the PyErr and then make the calling
+ * function return behind our back.
+ * It's up to the caller to check the result with PyErr_Occurred()
+ */
+static void
+eom_init_pygobject (void)
+{
+ init_pygobject_check (2, 11, 5); /* FIXME: get from config */
+}
+
+static void
+eom_init_pygtk (void)
+{
+ PyObject *gtk, *mdict, *version, *required_version;
+
+ init_pygtk ();
+
+ /* There isn't init_pygtk_check(), do the version
+ * check ourselves */
+ gtk = PyImport_ImportModule("gtk");
+
+ mdict = PyModule_GetDict(gtk);
+
+ version = PyDict_GetItemString (mdict, "pygtk_version");
+
+ if (!version) {
+ PyErr_SetString (PyExc_ImportError,
+ "PyGObject version too old");
+ return;
+ }
+
+ required_version = Py_BuildValue ("(iii)", 2, 4, 0); /* FIXME */
+
+ if (PyObject_Compare (version, required_version) == -1) {
+ PyErr_SetString (PyExc_ImportError,
+ "PyGObject version too old");
+
+ Py_DECREF (required_version);
+ return;
+ }
+
+ Py_DECREF (required_version);
+}
+
+gboolean
+eom_python_init (void)
+{
+ PyObject *mdict, *path, *tuple;
+ PyObject *sys_path, *eom;
+ PyObject *gettext, *install, *gettext_args;
+ struct sigaction old_sigint;
+ gint res;
+ /* Workaround for python bug. See #569228. */
+ char *argv[] = { "/dev/null/python/is/buggy/eom", NULL };
+
+ static gboolean init_failed = FALSE;
+
+ if (init_failed) {
+ /* We already failed to initialized Python, don't need to
+ * retry again */
+ return FALSE;
+ }
+
+ if (Py_IsInitialized ()) {
+ /* Python has already been successfully initialized */
+ return TRUE;
+ }
+
+ /* We are trying to initialize Python for the first time,
+ set init_failed to FALSE only if the entire initialization process
+ ends with success */
+ init_failed = TRUE;
+
+ /* Hack to make python not overwrite SIGINT: this is needed to avoid
+ * the crash reported on bug #326191 */
+
+ /* CHECK: can't we use Py_InitializeEx instead of Py_Initialize in order
+ to avoid to manage signal handlers ? - Paolo (Dec. 31, 2006) */
+
+ /* Save old handler */
+ res = sigaction (SIGINT, NULL, &old_sigint);
+
+ if (res != 0) {
+ g_warning ("Error initializing Python interpreter: cannot get "
+ "handler to SIGINT signal (%s)", strerror (errno));
+
+ return FALSE;
+ }
+
+ /* Python initialization */
+ Py_Initialize ();
+
+ /* Restore old handler */
+ res = sigaction (SIGINT, &old_sigint, NULL);
+
+ if (res != 0) {
+ g_warning ("Error initializing Python interpreter: cannot restore "
+ "handler to SIGINT signal (%s).", strerror (errno));
+
+ goto python_init_error;
+ }
+
+ PySys_SetArgv (1, argv);
+
+ /* Sanitize sys.path */
+ PyRun_SimpleString("import sys; sys.path = filter(None, sys.path)");
+
+ if (!check_pygtk2 ()) {
+ /* Warning message already printed in check_pygtk2 */
+ goto python_init_error;
+ }
+
+ /* import gobject */
+ eom_init_pygobject ();
+
+ if (PyErr_Occurred ()) {
+ g_warning ("Error initializing Python interpreter: could not import pygobject.");
+
+ goto python_init_error;
+ }
+
+ /* import gtk */
+ eom_init_pygtk ();
+
+ if (PyErr_Occurred ()) {
+ g_warning ("Error initializing Python interpreter: could not import pygtk.");
+
+ goto python_init_error;
+ }
+
+ /* sys.path.insert(0, ...) for system-wide plugins */
+ sys_path = PySys_GetObject ("path");
+ path = PyString_FromString (EOM_PLUGIN_DIR "/");
+ PyList_Insert (sys_path, 0, path);
+ Py_DECREF(path);
+
+ /* import eom */
+ eom = Py_InitModule ("eom", pyeom_functions);
+ mdict = PyModule_GetDict (eom);
+
+ pyeom_register_classes (mdict);
+ pyeom_add_constants (eom, "EOM_");
+
+ /* eom version */
+ tuple = Py_BuildValue("(iii)",
+ EOM_MAJOR_VERSION,
+ EOM_MINOR_VERSION,
+ EOM_MICRO_VERSION);
+ PyDict_SetItemString(mdict, "version", tuple);
+ Py_DECREF(tuple);
+
+ /* Retrieve the Python type for eom.Plugin */
+ PyEomPlugin_Type = (PyTypeObject *) PyDict_GetItemString (mdict, "Plugin");
+
+ if (PyEomPlugin_Type == NULL) {
+ PyErr_Print ();
+
+ goto python_init_error;
+ }
+
+ /* i18n support */
+ gettext = PyImport_ImportModule ("gettext");
+
+ if (gettext == NULL) {
+ g_warning ("Error initializing Python interpreter: could not import gettext.");
+
+ goto python_init_error;
+ }
+
+ mdict = PyModule_GetDict (gettext);
+ install = PyDict_GetItemString (mdict, "install");
+ gettext_args = Py_BuildValue ("ss", GETTEXT_PACKAGE, EOM_LOCALE_DIR);
+ PyObject_CallObject (install, gettext_args);
+ Py_DECREF (gettext_args);
+
+ /* Python has been successfully initialized */
+ init_failed = FALSE;
+
+ return TRUE;
+
+python_init_error:
+
+ g_warning ("Please check the installation of all the Python related packages required "
+ "by eom and try again.");
+
+ PyErr_Clear ();
+
+ eom_python_shutdown ();
+
+ return FALSE;
+}
+
+void
+eom_python_shutdown (void)
+{
+ if (Py_IsInitialized ()) {
+ if (idle_garbage_collect_id != 0) {
+ g_source_remove (idle_garbage_collect_id);
+ idle_garbage_collect_id = 0;
+ }
+
+ while (PyGC_Collect ())
+ ;
+
+ Py_Finalize ();
+ }
+}
+
+static gboolean
+run_gc (gpointer data)
+{
+ while (PyGC_Collect ())
+ ;
+
+ idle_garbage_collect_id = 0;
+
+ return FALSE;
+}
+
+void
+eom_python_garbage_collect (void)
+{
+ if (Py_IsInitialized()) {
+ /*
+ * We both run the GC right now and we schedule
+ * a further collection in the main loop.
+ */
+
+ while (PyGC_Collect ())
+ ;
+
+ if (idle_garbage_collect_id == 0)
+ idle_garbage_collect_id = g_idle_add (run_gc, NULL);
+ }
+}
+