1 /* Python scripting engine */
14 #include "config/home.h"
15 #include "main/module.h"
16 #include "scripting/python/core.h"
17 #include "scripting/python/dialogs.h"
18 #include "scripting/python/document.h"
19 #include "scripting/python/keybinding.h"
20 #include "scripting/python/load.h"
21 #include "scripting/python/menu.h"
22 #include "scripting/python/open.h"
23 #include "scripting/python/python.h"
24 #include "scripting/scripting.h"
25 #include "session/session.h"
27 #include "util/string.h"
29 struct session
*python_ses
= NULL
;
31 PyObject
*python_elinks_err
= NULL
;
32 PyObject
*python_hooks
= NULL
;
34 /* Error reporting. */
37 alert_python_error(void)
39 unsigned char *msg
= "(no traceback available)";
40 PyObject
*err_type
= NULL
, *err_value
= NULL
, *err_traceback
= NULL
;
41 PyObject
*tb_module
= NULL
;
42 PyObject
*msg_list
= NULL
;
43 PyObject
*empty_string
= NULL
;
44 PyObject
*msg_string
= NULL
;
48 * Retrieve the current error indicator and use the format_exception()
49 * function in Python's traceback module to produce an informative
50 * error message. It returns a list of Python string objects.
52 PyErr_Fetch(&err_type
, &err_value
, &err_traceback
);
53 PyErr_NormalizeException(&err_type
, &err_value
, &err_traceback
);
54 if (!err_type
) goto end
;
56 tb_module
= PyImport_ImportModule("traceback");
57 if (!tb_module
) goto end
;
59 msg_list
= PyObject_CallMethod(tb_module
, "format_exception", "OOO",
61 err_value
? err_value
: Py_None
,
62 err_traceback
? err_traceback
: Py_None
);
63 if (!msg_list
) goto end
;
66 * Use the join() method of an empty Python string to join the list
67 * of strings into one Python string containing the entire error
68 * message. Then get the contents of the Python string.
70 empty_string
= PyString_FromString("");
71 if (!empty_string
) goto end
;
73 msg_string
= PyObject_CallMethod(empty_string
, "join", "O", msg_list
);
74 if (!msg_string
) goto end
;
76 temp
= (unsigned char *) PyString_AsString(msg_string
);
80 report_scripting_error(&python_scripting_module
, python_ses
, msg
);
83 Py_XDECREF(err_value
);
84 Py_XDECREF(err_traceback
);
85 Py_XDECREF(tb_module
);
87 Py_XDECREF(empty_string
);
88 Py_XDECREF(msg_string
);
90 /* In case another error occurred while reporting the original error: */
94 /* Prepend ELinks directories to Python's search path. */
97 set_python_search_path(void)
99 struct string new_python_path
;
100 unsigned char *old_python_path
;
103 if (!init_string(&new_python_path
)) return result
;
105 if (elinks_home
&& !add_format_to_string(&new_python_path
, "%s%c",
109 if (!add_to_string(&new_python_path
, CONFDIR
))
112 old_python_path
= (unsigned char *) getenv("PYTHONPATH");
113 if (old_python_path
&& !add_format_to_string(&new_python_path
, "%c%s",
114 DELIM
, old_python_path
))
117 result
= env_set("PYTHONPATH", new_python_path
.source
, -1);
120 done_string(&new_python_path
);
124 /* Determine whether or not a user-supplied hooks module exists. */
127 hooks_module_exists(void)
129 PyObject
*imp_module
= NULL
, *result
= NULL
;
133 * Use the find_module() function in Python's imp module to look for
134 * the hooks module in Python's search path. An ImportError exception
135 * indicates that no such module was found; any other exception will
136 * be reported as an error.
138 imp_module
= PyImport_ImportModule("imp");
139 if (!imp_module
) goto python_error
;
141 result
= PyObject_CallMethod(imp_module
, "find_module", "s", "hooks");
145 } else if (PyErr_ExceptionMatches(PyExc_ImportError
)) {
151 alert_python_error();
154 Py_XDECREF(imp_module
);
159 /* Module-level documentation for the Python interpreter's elinks module. */
161 static char module_doc
[] =
162 PYTHON_DOCSTRING("Interface to the ELinks web browser.\n\
166 bind_key() -- Bind a keystroke to a callable object.\n\
167 current_document() -- Return the body of the document being viewed.\n\
168 current_header() -- Return the header of the document being viewed.\n\
169 current_link_url() -- Return the URL of the currently selected link.\n\
170 current_title() -- Return the title of the document being viewed.\n\
171 current_url() -- Return the URL of the document being viewed.\n\
172 info_box() -- Display information to the user.\n\
173 input_box() -- Prompt for user input.\n\
174 load() -- Load a document into the ELinks cache.\n\
175 menu() -- Display a menu.\n\
176 open() -- View a document.\n\
178 Exception classes:\n\
180 error -- Errors internal to ELinks.\n\
182 Other public objects:\n\
184 home -- A string containing the pathname of the ~/.elinks directory, or\n\
185 None if ELinks has no configuration directory.\n");
188 init_python(struct module
*module
)
190 PyObject
*elinks_module
, *module_dict
, *module_name
;
192 if (set_python_search_path() != 0) return;
194 /* Treat warnings as errors so they can be caught and handled;
195 * otherwise they would be printed to stderr.
197 * NOTE: PySys_ResetWarnOptions() and PySys_AddWarnOption() have been
198 * available and stable for many years but they're not officially
199 * documented as part of Python's public API, so in theory these two
200 * functions might no longer be available in some hypothetical future
201 * version of Python. */
202 PySys_ResetWarnOptions();
203 PySys_AddWarnOption("error");
207 if (!hooks_module_exists()) return;
209 elinks_module
= Py_InitModule3("elinks", NULL
, module_doc
);
210 if (!elinks_module
) goto python_error
;
212 /* If @elinks_home is NULL, Py_BuildValue() returns a None reference. */
213 if (PyModule_AddObject(elinks_module
, "home",
214 Py_BuildValue("s", elinks_home
)) != 0)
217 python_elinks_err
= PyErr_NewException("elinks.error", NULL
, NULL
);
218 if (!python_elinks_err
) goto python_error
;
220 if (PyModule_AddObject(elinks_module
, "error", python_elinks_err
) != 0)
223 module_dict
= PyModule_GetDict(elinks_module
);
224 if (!module_dict
) goto python_error
;
225 module_name
= PyString_FromString("elinks");
226 if (!module_name
) goto python_error
;
228 if (python_init_dialogs_interface(module_dict
, module_name
) != 0
229 || python_init_document_interface(module_dict
, module_name
) != 0
230 || python_init_keybinding_interface(module_dict
, module_name
) != 0
231 || python_init_load_interface(module_dict
, module_name
) != 0
232 || python_init_menu_interface(module_dict
, module_name
) != 0
233 || python_init_open_interface(module_dict
, module_name
) != 0)
236 python_hooks
= PyImport_ImportModule("hooks");
237 if (!python_hooks
) goto python_error
;
242 alert_python_error();
246 cleanup_python(struct module
*module
)
248 if (Py_IsInitialized()) {
251 python_done_keybinding_interface();
253 /* This is equivalent to Py_CLEAR(), but it works with older
254 * versions of Python predating that macro: */
263 /* Add methods to a Python extension module. */
266 add_python_methods(PyObject
*dict
, PyObject
*name
, PyMethodDef
*methods
)
270 for (method
= methods
; method
&& method
->ml_name
; method
++) {
271 PyObject
*function
= PyCFunction_NewEx(method
, NULL
, name
);
274 if (!function
) return -1;
275 result
= PyDict_SetItemString(dict
, method
->ml_name
, function
);
277 if (result
!= 0) return -1;