From 6c8f532692242f0a0f338217d27cc3e812b43257 Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Mon, 24 Jul 2006 18:52:25 +0200 Subject: [PATCH] Fixes to the Python scripting by M. Levinson --- AUTHORS | 3 + contrib/python/README.Python | 13 ++--- src/scripting/python/core.c | 102 +++++++++++++++++++++++++++----- src/scripting/python/core.h | 2 + src/scripting/python/hooks.c | 110 +++++++++++++++++------------------ src/scripting/scripting.c | 7 ++- 6 files changed, 158 insertions(+), 79 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0b0444e7..a8fe034b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -331,6 +331,9 @@ Len Lattanzi M. K. Srikant Small fix in forms +M. Levinson + Python scripting fixes + Marco Bodrato Twinterm support diff --git a/contrib/python/README.Python b/contrib/python/README.Python index 9fde5abf..862b60aa 100644 --- a/contrib/python/README.Python +++ b/contrib/python/README.Python @@ -1,13 +1,8 @@ -If you want to use Python scripting with ELinks add ---with-python to the configure invocation copy hooks.py to ~/.elinks -When your Python installation is your own build, you could give prefix -to the configure, eg. ---with-python=/usr/local when Python binary is placed in /usr/local/bin, etc. +If you want to use Python scripting with ELinks, add --with-python to the +configure invocation and copy hooks.py to your ~/.elinks directory. -When 'configure' cannot find -lpython make symbolic link to the appropriate -library, eg. -# cd /usr/local/lib -# ln -s libpython2.4.so.1.0 libpython.so +If configure cannot find Python you can supply a path, e.g. +--with-python=/usr/local/bin if your Python binary is in /usr/local/bin, etc. For the present hooks.py is not very usable. You are welcome to make it better. Good Luck! diff --git a/src/scripting/python/core.c b/src/scripting/python/core.c index fda82188..83eb2c39 100644 --- a/src/scripting/python/core.c +++ b/src/scripting/python/core.c @@ -4,7 +4,6 @@ #include "config.h" #endif -#include "scripting/python/core.h" #include #include @@ -14,24 +13,91 @@ #include "config/home.h" #include "main/module.h" +#include "scripting/scripting.h" +#include "scripting/python/core.h" +#include "scripting/python/python.h" #include "util/env.h" #include "util/file.h" #include "util/string.h" -PyObject *pDict, *pModule; +PyObject *pDict = NULL, *pModule = NULL; + +/* Error reporting. */ + +void +alert_python_error(struct session *ses) +{ + unsigned char *msg = "(no traceback available)"; + PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL; + PyObject *tb_module = NULL; + PyObject *tb_dict; + PyObject *format_function; + PyObject *msg_list = NULL; + PyObject *empty_string = NULL; + PyObject *join_method = NULL; + PyObject *msg_string = NULL; + unsigned char *temp; + + /* + * Retrieve the current error indicator and use the format_exception() + * function in Python's traceback module to produce an informative + * error message. It returns a list of Python string objects. + */ + PyErr_Fetch(&err_type, &err_value, &err_traceback); + PyErr_NormalizeException(&err_type, &err_value, &err_traceback); + if (!err_traceback) goto end; + + tb_module = PyImport_ImportModule("traceback"); + if (!tb_module) goto end; + + tb_dict = PyModule_GetDict(tb_module); + format_function = PyDict_GetItemString(tb_dict, "format_exception"); + if (!format_function || !PyCallable_Check(format_function)) goto end; + + msg_list = PyObject_CallFunction(format_function, "OOO", + err_type, err_value, err_traceback); + if (!msg_list) goto end; + + /* + * Use the join() method of an empty Python string to join the list + * of strings into one Python string containing the entire error + * message. Then get the contents of the Python string. + */ + empty_string = PyString_FromString(""); + if (!empty_string) goto end; + + join_method = PyObject_GetAttrString(empty_string, "join"); + if (!join_method || !PyCallable_Check(join_method)) goto end; + + msg_string = PyObject_CallFunction(join_method, "O", msg_list); + if (!msg_string) goto end; + + temp = (unsigned char *)PyString_AsString(msg_string); + if (temp) msg = temp; + +end: + report_scripting_error(&python_scripting_module, ses, msg); + + Py_XDECREF(err_type); + Py_XDECREF(err_value); + Py_XDECREF(err_traceback); + Py_XDECREF(tb_module); + Py_XDECREF(msg_list); + Py_XDECREF(empty_string); + Py_XDECREF(join_method); + Py_XDECREF(msg_string); + + /* In case another error occurred while reporting the original error: */ + PyErr_Clear(); +} void cleanup_python(struct module *module) { if (Py_IsInitialized()) { - if (pModule) { - Py_DECREF(pModule); - } - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } + Py_XDECREF(pDict); + Py_XDECREF(pModule); Py_Finalize(); } } @@ -44,15 +110,25 @@ init_python(struct module *module) if (!python_path) return; env_set("PYTHONPATH", python_path, -1); mem_free(python_path); + + /* Treat warnings as errors so they can be caught and handled; + * otherwise they would be printed to stderr. + * + * NOTE: PySys_ResetWarnOptions() and PySys_AddWarnOption() have been + * available and stable for many years but they're not officially + * documented as part of Python's public API, so in theory these two + * functions might no longer be available in some hypothetical future + * version of Python. */ + PySys_ResetWarnOptions(); + PySys_AddWarnOption("error"); + Py_Initialize(); pModule = PyImport_ImportModule("hooks"); if (pModule) { pDict = PyModule_GetDict(pModule); + Py_INCREF(pDict); } else { - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } + alert_python_error(NULL); } } diff --git a/src/scripting/python/core.h b/src/scripting/python/core.h index 00b08dee..44503138 100644 --- a/src/scripting/python/core.h +++ b/src/scripting/python/core.h @@ -3,7 +3,9 @@ #define EL__SCRIPTING_PYTHON_CORE_H struct module; +struct session; +void alert_python_error(struct session *ses); void init_python(struct module *module); void cleanup_python(struct module *module); diff --git a/src/scripting/python/hooks.c b/src/scripting/python/hooks.c index 9f3d0952..af9b6186 100644 --- a/src/scripting/python/hooks.c +++ b/src/scripting/python/hooks.c @@ -32,29 +32,29 @@ do_script_hook_goto_url(struct session *ses, unsigned char **url) if (pFunc && PyCallable_Check(pFunc)) { PyObject *pValue; - unsigned char *str; + unsigned char *current_url; if (!ses || !have_location(ses)) { - str = NULL; + current_url = NULL; } else { - str = struri(cur_loc(ses)->vs.uri); + current_url = struri(cur_loc(ses)->vs.uri); } - pValue = PyObject_CallFunction(pFunc, "ss", *url, str); - if (pValue && (pValue != Py_None)) { - const unsigned char *res = PyString_AsString(pValue); + pValue = PyObject_CallFunction(pFunc, "ss", *url, current_url); + if (pValue) { + if (pValue != Py_None) { + const unsigned char *str; + unsigned char *new_url; - if (res) { - unsigned char *new_url = stracpy((unsigned char *)res); - - if (new_url) mem_free_set(url, new_url); + str = PyString_AsString(pValue); + if (str) { + new_url = stracpy((unsigned char *)str); + if (new_url) mem_free_set(url, new_url); + } } Py_DECREF(pValue); } else { - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } + alert_python_error(ses); } } } @@ -72,26 +72,26 @@ script_hook_goto_url(va_list ap, void *data) } static void -do_script_hook_follow_url(unsigned char **url) +do_script_hook_follow_url(struct session *ses, unsigned char **url) { PyObject *pFunc = PyDict_GetItemString(pDict, "follow_url_hook"); if (pFunc && PyCallable_Check(pFunc)) { PyObject *pValue = PyObject_CallFunction(pFunc, "s", *url); - if (pValue && (pValue != Py_None)) { - const unsigned char *str = PyString_AsString(pValue); - unsigned char *new_url; + if (pValue) { + if (pValue != Py_None) { + const unsigned char *str; + unsigned char *new_url; - if (str) { - new_url = stracpy((unsigned char *)str); - if (new_url) mem_free_set(url, new_url); + str = PyString_AsString(pValue); + if (str) { + new_url = stracpy((unsigned char *)str); + if (new_url) mem_free_set(url, new_url); + } } Py_DECREF(pValue); } else { - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } + alert_python_error(ses); } } } @@ -100,15 +100,17 @@ static enum evhook_status script_hook_follow_url(va_list ap, void *data) { unsigned char **url = va_arg(ap, unsigned char **); + struct session *ses = va_arg(ap, struct session *); if (pDict && *url) - do_script_hook_follow_url(url); + do_script_hook_follow_url(ses, url); return EVENT_HOOK_STATUS_NEXT; } static void -do_script_hook_pre_format_html(unsigned char *url, struct cache_entry *cached, +do_script_hook_pre_format_html(struct session *ses, unsigned char *url, + struct cache_entry *cached, struct fragment *fragment) { PyObject *pFunc = PyDict_GetItemString(pDict, "pre_format_html_hook"); @@ -118,21 +120,21 @@ do_script_hook_pre_format_html(unsigned char *url, struct cache_entry *cached, fragment->data, fragment->length); - if (pValue && (pValue != Py_None)) { - const unsigned char *str = PyString_AsString(pValue); + if (pValue) { + if (pValue != Py_None) { + const unsigned char *str; + int len; - if (str) { - int len = PyString_Size(pValue); /* strlen(str); */ - - add_fragment(cached, 0, str, len); - normalize_cache_entry(cached, len); + str = PyString_AsString(pValue); + if (str) { + len = PyString_Size(pValue); + add_fragment(cached, 0, str, len); + normalize_cache_entry(cached, len); + } } Py_DECREF(pValue); } else { - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } + alert_python_error(ses); } } } @@ -146,7 +148,7 @@ script_hook_pre_format_html(va_list ap, void *data) unsigned char *url = struri(cached->uri); if (pDict && ses && url && cached->length && *fragment->data) - do_script_hook_pre_format_html(url, cached, fragment); + do_script_hook_pre_format_html(ses, url, cached, fragment); return EVENT_HOOK_STATUS_NEXT; } @@ -159,20 +161,21 @@ do_script_hook_get_proxy(unsigned char **new_proxy_url, unsigned char *url) if (pFunc && PyCallable_Check(pFunc)) { PyObject *pValue = PyObject_CallFunction(pFunc, "s", url); - if (pValue && (pValue != Py_None)) { - const unsigned char *str = PyString_AsString(pValue); + if (pValue) { + if (pValue != Py_None) { + const unsigned char *str; + unsigned char *new_url; - if (str) { - unsigned char *new_url = stracpy((unsigned char *)str); - - if (new_url) mem_free_set(new_proxy_url, new_url); + str = PyString_AsString(pValue); + if (str) { + new_url = stracpy((unsigned char *)str); + if (new_url) mem_free_set(new_proxy_url, + new_url); + } } Py_DECREF(pValue); } else { - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } + alert_python_error(NULL); } } } @@ -198,14 +201,9 @@ do_script_hook_quit(void) PyObject *pValue = PyObject_CallFunction(pFunc, NULL); if (pValue) { - if (pValue != Py_None) { - Py_DECREF(pValue); - } + Py_DECREF(pValue); } else { - if (PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } + alert_python_error(NULL); } } } diff --git a/src/scripting/scripting.c b/src/scripting/scripting.c index 4a3360c5..78ff5b9e 100644 --- a/src/scripting/scripting.c +++ b/src/scripting/scripting.c @@ -24,10 +24,14 @@ #include "scripting/ruby/ruby.h" #include "scripting/smjs/smjs.h" +#ifdef HAVE_UNISTD_H +#include +#endif + /* Error reporting. */ -#if defined(CONFIG_SCRIPTING_RUBY) || defined(CONFIG_SCRIPTING_SPIDERMONKEY) +#if defined(CONFIG_SCRIPTING_RUBY) || defined(CONFIG_SCRIPTING_SPIDERMONKEY) || defined(CONFIG_SCRIPTING_PYTHON) void report_scripting_error(struct module *module, struct session *ses, unsigned char *msg) @@ -38,6 +42,7 @@ report_scripting_error(struct module *module, struct session *ses, if (!ses) { if (list_empty(terminals)) { usrerror("[%s error] %s", module->name, msg); + sleep(3); return; }