talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

commit b5b69753f875f9515fc61216899dbc8a51660b7a
parent 67df492152493bec7f3c645403d7efd3415df6ae
Author: Holger Berndt <hb@claws-mail.org>
Date:   Tue, 11 Jun 2013 23:27:28 +0200

Python plugin: Improve error reporting during plugin init

Diffstat:
Msrc/plugins/python/clawsmailmodule.c | 50+++++++++++++++++++++++++++-----------------------
Msrc/plugins/python/clawsmailmodule.h | 5++++-
Msrc/plugins/python/composewindowtype.c | 12++++++------
Msrc/plugins/python/composewindowtype.h | 6++----
Msrc/plugins/python/foldertype.c | 25++++++++++++-------------
Msrc/plugins/python/foldertype.h | 7+++----
Msrc/plugins/python/messageinfotype.c | 13++++++-------
Msrc/plugins/python/messageinfotype.h | 7++-----
Msrc/plugins/python/nodetype.c | 9+++++++--
Msrc/plugins/python/nodetype.h | 7+++----
Msrc/plugins/python/python-hooks.c | 36+++++++++++++++++++-----------------
Msrc/plugins/python/python-hooks.h | 2+-
Msrc/plugins/python/python_plugin.c | 126++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
13 files changed, 207 insertions(+), 98 deletions(-)

diff --git a/src/plugins/python/clawsmailmodule.c b/src/plugins/python/clawsmailmodule.c @@ -519,7 +519,7 @@ static gboolean get_message_list_for_move_or_copy(PyObject *messagelist, PyObjec { Py_ssize_t size, iEl; FolderItem *folderitem; - + *list = NULL; folderitem = clawsmail_folder_get_item(folder); @@ -537,17 +537,17 @@ static gboolean get_message_list_for_move_or_copy(PyObject *messagelist, PyObjec PyErr_SetString(PyExc_TypeError, "Argument must be a list of MessageInfo objects."); return FALSE; } - + msginfo = clawsmail_messageinfo_get_msginfo(element); if(!msginfo) { PyErr_SetString(PyExc_LookupError, "Broken MessageInfo object."); return FALSE; } - + procmsg_msginfo_set_to_folder(msginfo, folderitem); *list = g_slist_prepend(*list, msginfo); } - + return TRUE; } @@ -557,23 +557,23 @@ static PyObject* move_or_copy_messages(PyObject *self, PyObject *args, gboolean PyObject *folder; int retval; GSList *list = NULL; - + retval = PyArg_ParseTuple(args, "O!O!", &PyList_Type, &messagelist, clawsmail_folder_get_type_object(), &folder); if(!retval ) - return NULL; + return NULL; folder_item_update_freeze(); - + if(!get_message_list_for_move_or_copy(messagelist, folder, &list)) goto err; - - if(move) + + if(move) procmsg_move_messages(list); else procmsg_copy_messages(list); - + folder_item_update_thaw(); g_slist_free(list); Py_RETURN_NONE; @@ -602,7 +602,7 @@ static PyMethodDef ClawsMailMethods[] = { "\n" "Returns the gtk.ActionGroup for the main window."}, - {"get_mainwindow_ui_manager", get_mainwindow_ui_manager, METH_NOARGS, + {"get_mainwindow_ui_manager", get_mainwindow_ui_manager, METH_NOARGS, "get_mainwindow_ui_manager() - get ui manager of main window\n" "\n" "Returns the gtk.UIManager for the main window."}, @@ -705,8 +705,9 @@ static PyMethodDef ClawsMailMethods[] = { {NULL, NULL, 0, NULL} }; -static void initmiscstuff(PyObject *module) +static gboolean add_miscstuff(PyObject *module) { + gboolean retval; PyObject *dict; PyObject *res; const char *cmd = @@ -719,14 +720,15 @@ static void initmiscstuff(PyObject *module) "\n"; dict = PyModule_GetDict(module); res = PyRun_String(cmd, Py_file_input, dict, dict); + retval = (res != NULL); Py_XDECREF(res); + return retval; } -void claws_mail_python_init(void) +PyMODINIT_FUNC initclawsmail(void) { - if (!Py_IsInitialized()) - Py_Initialize(); + gboolean ok = TRUE; /* create module */ cm_module = Py_InitModule3("clawsmail", ClawsMailMethods, @@ -740,17 +742,19 @@ void claws_mail_python_init(void) "The interface to Claws Mail in this module is extended on a 'as-needed' basis.\n" "If you're missing something specific, try contacting the author."); + /* add module member "compose_window" set to None */ + Py_INCREF(Py_None); + PyModule_AddObject(cm_module, "compose window", Py_None); + /* initialize classes */ - initnode(cm_module); - initcomposewindow(cm_module); - initfolder(cm_module); - initmessageinfo(cm_module); + ok = ok && cmpy_add_node(cm_module); + ok = ok && cmpy_add_composewindow(cm_module); + ok = ok && cmpy_add_folder(cm_module); + ok = ok && cmpy_add_messageinfo(cm_module); /* initialize misc things */ - initmiscstuff(cm_module); - - PyRun_SimpleString("import clawsmail\n"); - PyRun_SimpleString("clawsmail.compose_window = None\n"); + if(ok) + add_miscstuff(cm_module); } diff --git a/src/plugins/python/clawsmailmodule.h b/src/plugins/python/clawsmailmodule.h @@ -24,8 +24,11 @@ #include "compose.h" +#ifndef PyMODINIT_FUNC +# define PyMODINIT_FUNC void +#endif -void claws_mail_python_init(void); +PyMODINIT_FUNC initclawsmail(void); PyObject* get_gobj_from_address(gpointer addr); void put_composewindow_into_module(Compose *compose); diff --git a/src/plugins/python/composewindowtype.c b/src/plugins/python/composewindowtype.c @@ -585,14 +585,14 @@ static PyTypeObject clawsmail_ComposeWindowType = { 0, /* tp_new */ }; -PyMODINIT_FUNC initcomposewindow(PyObject *module) +gboolean cmpy_add_composewindow(PyObject *module) { - clawsmail_ComposeWindowType.tp_new = PyType_GenericNew; - if(PyType_Ready(&clawsmail_ComposeWindowType) < 0) - return; + clawsmail_ComposeWindowType.tp_new = PyType_GenericNew; + if(PyType_Ready(&clawsmail_ComposeWindowType) < 0) + return FALSE; - Py_INCREF(&clawsmail_ComposeWindowType); - PyModule_AddObject(module, "ComposeWindow", (PyObject*)&clawsmail_ComposeWindowType); + Py_INCREF(&clawsmail_ComposeWindowType); + return (PyModule_AddObject(module, "ComposeWindow", (PyObject*)&clawsmail_ComposeWindowType) == 0); } PyObject* clawsmail_compose_new(PyObject *module, Compose *compose) diff --git a/src/plugins/python/composewindowtype.h b/src/plugins/python/composewindowtype.h @@ -18,15 +18,13 @@ #ifndef COMPOSEWINDOWTYPE_H #define COMPOSEWINDOWTYPE_H +#include <glib.h> #include <Python.h> #include "compose.h" -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif -PyMODINIT_FUNC initcomposewindow(PyObject *module); +gboolean cmpy_add_composewindow(PyObject *module); PyObject* clawsmail_compose_new(PyObject *module, Compose *compose); diff --git a/src/plugins/python/foldertype.c b/src/plugins/python/foldertype.c @@ -20,7 +20,6 @@ #include "claws-features.h" #endif -#include <glib.h> #include <glib/gi18n.h> #include "foldertype.h" @@ -70,10 +69,10 @@ static int Folder_init(clawsmail_FolderObject *self, PyObject *args, PyObject *k /* optional constructor argument: folderitem id string */ if(!PyArg_ParseTuple(args, "|sb", &ss, &create)) return -1; - + Py_INCREF(Py_None); self->name = Py_None; - + Py_INCREF(Py_None); self->path = Py_None; @@ -146,14 +145,14 @@ static PyObject* Folder_get_messages(clawsmail_FolderObject *self, PyObject *arg Py_INCREF(Py_None); return Py_None; } - + for(pos = 0, walk = msglist; walk; walk = walk->next, ++pos) { PyObject *msg; msg = clawsmail_messageinfo_new(walk->data); PyTuple_SET_ITEM(retval, pos, msg); } procmsg_msg_list_free(msglist); - + return retval; } @@ -172,10 +171,10 @@ static PyMethodDef Folder_methods[] = { static PyMemberDef Folder_members[] = { {"name", T_OBJECT_EX, offsetof(clawsmail_FolderObject, name), 0, "name - name of folder"}, - + {"path", T_OBJECT_EX, offsetof(clawsmail_FolderObject, path), 0, "path - path of folder"}, - + {"mailbox_name", T_OBJECT_EX, offsetof(clawsmail_FolderObject, mailbox_name), 0, "mailbox_name - name of the corresponding mailbox"}, @@ -228,14 +227,14 @@ static PyTypeObject clawsmail_FolderType = { 0, /* tp_new */ }; -PyMODINIT_FUNC initfolder(PyObject *module) +gboolean cmpy_add_folder(PyObject *module) { - clawsmail_FolderType.tp_new = PyType_GenericNew; - if(PyType_Ready(&clawsmail_FolderType) < 0) - return; + clawsmail_FolderType.tp_new = PyType_GenericNew; + if(PyType_Ready(&clawsmail_FolderType) < 0) + return FALSE; - Py_INCREF(&clawsmail_FolderType); - PyModule_AddObject(module, "Folder", (PyObject*)&clawsmail_FolderType); + Py_INCREF(&clawsmail_FolderType); + return (PyModule_AddObject(module, "Folder", (PyObject*)&clawsmail_FolderType) == 0); } PyObject* clawsmail_folder_new(FolderItem *folderitem) diff --git a/src/plugins/python/foldertype.h b/src/plugins/python/foldertype.h @@ -18,15 +18,14 @@ #ifndef FOLDERTYPE_H #define FOLDERTYPE_H +#include <glib.h> #include <Python.h> #include "folder.h" -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif -PyMODINIT_FUNC initfolder(PyObject *module); + +gboolean cmpy_add_folder(PyObject *module); PyObject* clawsmail_folder_new(FolderItem *folderitem); FolderItem* clawsmail_folder_get_item(PyObject *self); diff --git a/src/plugins/python/messageinfotype.c b/src/plugins/python/messageinfotype.c @@ -20,7 +20,6 @@ #include "claws-features.h" #endif -#include <glib.h> #include <glib/gi18n.h> #include "messageinfotype.h" @@ -374,14 +373,14 @@ static PyTypeObject clawsmail_MessageInfoType = { 0, /* tp_new */ }; -PyMODINIT_FUNC initmessageinfo(PyObject *module) +gboolean cmpy_add_messageinfo(PyObject *module) { - clawsmail_MessageInfoType.tp_new = PyType_GenericNew; - if(PyType_Ready(&clawsmail_MessageInfoType) < 0) - return; + clawsmail_MessageInfoType.tp_new = PyType_GenericNew; + if(PyType_Ready(&clawsmail_MessageInfoType) < 0) + return FALSE; - Py_INCREF(&clawsmail_MessageInfoType); - PyModule_AddObject(module, "MessageInfo", (PyObject*)&clawsmail_MessageInfoType); + Py_INCREF(&clawsmail_MessageInfoType); + return (PyModule_AddObject(module, "MessageInfo", (PyObject*)&clawsmail_MessageInfoType) == 0); } #define MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(fis, pms) \ diff --git a/src/plugins/python/messageinfotype.h b/src/plugins/python/messageinfotype.h @@ -18,15 +18,12 @@ #ifndef MESSAGEINFOTYPE_H #define MESSAGEINFOTYPE_H +#include <glib.h> #include <Python.h> #include "procmsg.h" -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif - -PyMODINIT_FUNC initmessageinfo(PyObject *module); +gboolean cmpy_add_messageinfo(PyObject *module); PyObject* clawsmail_messageinfo_new(MsgInfo *msginfo); MsgInfo* clawsmail_messageinfo_get_msginfo(PyObject *self); diff --git a/src/plugins/python/nodetype.c b/src/plugins/python/nodetype.c @@ -20,15 +20,16 @@ #include "claws-features.h" #endif -#include <glib.h> #include <glib/gi18n.h> #include "nodetype.h" #include <structmember.h> -PyMODINIT_FUNC initnode(PyObject *module) +/* returns true on success, false if an exception was thrown */ +gboolean cmpy_add_node(PyObject *module) { + gboolean retval; PyObject *dict; PyObject *res; const char *cmd = @@ -85,9 +86,13 @@ PyMODINIT_FUNC initnode(PyObject *module) PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()); res = PyRun_String(cmd, Py_file_input, dict, dict); + + retval = (res != NULL); Py_XDECREF(res); + return retval; } + PyObject* clawsmail_node_new(PyObject *module) { PyObject *class, *dict; diff --git a/src/plugins/python/nodetype.h b/src/plugins/python/nodetype.h @@ -18,13 +18,12 @@ #ifndef NODETYPE_H #define NODETYPE_H +#include <glib.h> + #include <Python.h> -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif -PyMODINIT_FUNC initnode(PyObject *module); +gboolean cmpy_add_node(PyObject *module); PyObject* clawsmail_node_new(PyObject *module); diff --git a/src/plugins/python/python-hooks.c b/src/plugins/python/python-hooks.c @@ -122,37 +122,38 @@ is_blacklisted(void) } #endif // ENABLE_PYTHON -void -parasite_python_init(void) +int +parasite_python_init(char **error) { #ifdef ENABLE_PYTHON - int res; struct sigaction old_sigint; PyObject *pygtk; - if (is_blacklisted()) - return; + if (is_blacklisted()) { + *error = g_strdup("Application is blacklisted"); + return 0; + } /* This prevents errors such as "undefined symbol: PyExc_ImportError" */ if (!dlopen(PYTHON_SHARED_LIB, RTLD_NOW | RTLD_GLOBAL)) { - g_error("%s\n", dlerror()); - return; + *error = g_strdup_printf("Parasite: Error on dlopen(): %s\n", dlerror()); + return 0; } captured_stdout = g_string_new(""); captured_stderr = g_string_new(""); /* Back up and later restore SIGINT so Python doesn't steal it from us. */ - res = sigaction(SIGINT, NULL, &old_sigint); + sigaction(SIGINT, NULL, &old_sigint); if (!Py_IsInitialized()) Py_Initialize(); - res = sigaction(SIGINT, &old_sigint, NULL); + sigaction(SIGINT, &old_sigint, NULL); Py_InitModule("parasite", parasite_python_methods); - PyRun_SimpleString( + if(PyRun_SimpleString( "import parasite\n" "import sys\n" "\n" @@ -176,10 +177,11 @@ parasite_python_init(void) " def flush(self):\n" " pass\n" "\n" - ); + ) == -1) + return 0; if (!pygobject_init(-1, -1, -1)) - return; + return 0; pygtk = PyImport_ImportModule("gtk"); @@ -198,18 +200,18 @@ parasite_python_init(void) _PyGtk_API = (struct _PyGtk_FunctionStruct*) PyCObject_AsVoidPtr(cobject); else { - PyErr_SetString(PyExc_RuntimeError, - "could not find _PyGtk_API object"); - return; + *error = g_strdup("Parasite: Could not find _PyGtk_API object"); + return 0; } } } else { - PyErr_SetString(PyExc_ImportError, "could not import gtk"); - return; + *error = g_strdup("Parasite: Could not import gtk"); + return 0; } python_enabled = TRUE; #endif // ENABLE_PYTHON + return !0; } void diff --git a/src/plugins/python/python-hooks.h b/src/plugins/python/python-hooks.h @@ -28,7 +28,7 @@ typedef void (*ParasitePythonLogger)(const char *text, gpointer user_data); -void parasite_python_init(void); +int parasite_python_init(char **error); void parasite_python_run(const char *command, ParasitePythonLogger stdout_logger, ParasitePythonLogger stderr_logger, diff --git a/src/plugins/python/python_plugin.c b/src/plugins/python/python_plugin.c @@ -160,11 +160,12 @@ static void run_script_file(const gchar *filename, Compose *compose) FILE *fp; fp = fopen(filename, "r"); if(!fp) { - g_print("Error: Could not open file '%s'\n", filename); + debug_print("Error: Could not open file '%s'\n", filename); return; } put_composewindow_into_module(compose); - PyRun_SimpleFile(fp, filename); + if(PyRun_SimpleFile(fp, filename) == 0) + debug_print("Problem running script file '%s'\n", filename); fclose(fp); } @@ -297,9 +298,9 @@ static void migrate_scripts_out_of_base_dir(void) gchar *dest_file; dest_file = g_strconcat(dest_dir, G_DIR_SEPARATOR_S, filename, NULL); if(move_file(filepath, dest_file, FALSE) == 0) - g_print("Python plugin: Moved file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR); + debug_print("Python plugin: Moved file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR); else - g_print("Python plugin: Warning: Could not move file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR); + debug_print("Python plugin: Warning: Could not move file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR); g_free(dest_file); } g_free(filepath); @@ -429,7 +430,7 @@ static void refresh_scripts_in_dir(const gchar *subdir, ToolbarType toolbar_type g_free(scripts_dir); if(!dir) { - g_print("Could not open directory '%s': %s\n", subdir, error->message); + debug_print("Could not open directory '%s': %s\n", subdir, error->message); g_error_free(error); return; } @@ -506,12 +507,16 @@ static GtkActionEntry mainwindow_tools_python_actions[] = { {"Tools/PythonScripts/---", NULL, "---" }, }; -static void python_menu_init(void) +static int python_menu_init(char **error) { MainWindow *mainwin; guint id; mainwin = mainwindow_get_mainwindow(); + if(!mainwin) { + *error = g_strdup("Could not get main window"); + return 0; + } gtk_action_group_add_toggle_actions(mainwin->action_group, mainwindow_tools_python_toggle, 1, mainwin); gtk_action_group_add_actions(mainwin->action_group, mainwindow_tools_python_actions, 3, mainwin); @@ -537,6 +542,8 @@ static void python_menu_init(void) menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id)); refresh_python_scripts_menus(NULL, NULL); + + return !0; } static void python_menu_done(void) @@ -560,8 +567,81 @@ static void python_menu_done(void) } } + +static PyObject *get_StringIO_instance(void) +{ + PyObject *module_StringIO = NULL; + PyObject *class_StringIO = NULL; + PyObject *inst_StringIO = NULL; + + module_StringIO = PyImport_ImportModule("cStringIO"); + if(!module_StringIO) { + debug_print("Error getting traceback: Could not import module cStringIO\n"); + goto done; + } + + class_StringIO = PyObject_GetAttrString(module_StringIO, "StringIO"); + if(!class_StringIO) { + debug_print("Error getting traceback: Could not get StringIO class\n"); + goto done; + } + + inst_StringIO = PyObject_CallObject(class_StringIO, NULL); + if(!inst_StringIO) { + debug_print("Error getting traceback: Could not create an instance of the StringIO class\n"); + goto done; + } + +done: + Py_XDECREF(module_StringIO); + Py_XDECREF(class_StringIO); + + return inst_StringIO; +} + +static char* get_exception_information(PyObject *inst_StringIO) +{ + char *retval = NULL; + PyObject *meth_getvalue = NULL; + PyObject *result_getvalue = NULL; + + if(!inst_StringIO) + goto done; + + if(PySys_SetObject("stderr", inst_StringIO) != 0) { + debug_print("Error getting traceback: Could not set sys.stderr to a StringIO instance\n"); + goto done; + } + + meth_getvalue = PyObject_GetAttrString(inst_StringIO, "getvalue"); + if(!meth_getvalue) { + debug_print("Error getting traceback: Could not get the getvalue method of the StringIO instance\n"); + goto done; + } + + PyErr_Print(); + + result_getvalue = PyObject_CallObject(meth_getvalue, NULL); + if(!result_getvalue) { + debug_print("Error getting traceback: Could not call the getvalue method of the StringIO instance\n"); + goto done; + } + + retval = g_strdup(PyString_AsString(result_getvalue)); + +done: + + Py_XDECREF(meth_getvalue); + Py_XDECREF(result_getvalue); + + return retval ? retval : g_strdup("Unspecified error occured"); +} + + gint plugin_init(gchar **error) { + PyObject *inst_StringIO = NULL; + /* Version check */ if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,6,9), VERSION_NUMERIC, _("Python"), error)) return -1; @@ -575,25 +655,49 @@ gint plugin_init(gchar **error) /* script directories */ if(!make_sure_directories_exist(error)) - return -1; + goto err; /* initialize python interpreter */ Py_Initialize(); - /* initialize python interactive shell */ - parasite_python_init(); + /* The Python C API only offers to print an exception to sys.stderr. In order to catch it + * in a string, a StringIO object is created, to which sys.stderr can be redirected in case + * an error occured. */ + inst_StringIO = get_StringIO_instance(); /* initialize Claws Mail Python module */ - claws_mail_python_init(); + initclawsmail(); + if(PyErr_Occurred()) { + *error = get_exception_information(inst_StringIO); + goto err; + } + + if(PyRun_SimpleString("import clawsmail") == -1) { + *error = g_strdup("Error importing the clawsmail module"); + goto err; + } + + /* initialize python interactive shell */ + if(!parasite_python_init(error)) { + goto err; + } /* load menu options */ - python_menu_init(); + if(!python_menu_init(error)) { + goto err; + } + /* problems here are not fatal */ run_auto_script_file_if_it_exists(PYTHON_SCRIPTS_AUTO_STARTUP, NULL); debug_print("Python plugin loaded\n"); return 0; + +err: + hooks_unregister_hook(COMPOSE_CREATED_HOOKLIST, hook_compose_create); + Py_XDECREF(inst_StringIO); + return -1; } gboolean plugin_done(void)