/* * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif /* This needs to be included before any standard header * see http://docs.python.org/c-api/intro.html#include-files */ #include #include #include #include #include #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); } }