PageRenderTime 64ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/src/scripting/python/core.c

https://github.com/nabetaro/elinks
C | 337 lines | 238 code | 68 blank | 31 comment | 55 complexity | 2d7a4d3acc2b5e3e781c447afc775e79 MD5 | raw file
  1. /* Python scripting engine */
  2. #ifdef HAVE_CONFIG_H
  3. #include "config.h"
  4. #endif
  5. #include <Python.h>
  6. #include <osdefs.h>
  7. #include <stdlib.h>
  8. #include "elinks.h"
  9. #include "config/home.h"
  10. #include "main/module.h"
  11. #include "scripting/python/core.h"
  12. #include "scripting/python/dialogs.h"
  13. #include "scripting/python/document.h"
  14. #include "scripting/python/keybinding.h"
  15. #include "scripting/python/load.h"
  16. #include "scripting/python/menu.h"
  17. #include "scripting/python/open.h"
  18. #include "scripting/python/python.h"
  19. #include "scripting/scripting.h"
  20. #include "session/session.h"
  21. #include "util/env.h"
  22. #include "util/string.h"
  23. struct session *python_ses = NULL;
  24. PyObject *python_elinks_err = NULL;
  25. PyObject *python_hooks = NULL;
  26. /* Error reporting. */
  27. void
  28. alert_python_error(void)
  29. {
  30. unsigned char *msg = "(no traceback available)";
  31. PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
  32. PyObject *tb_module = NULL;
  33. PyObject *msg_list = NULL;
  34. PyObject *empty_string = NULL;
  35. PyObject *msg_string = NULL;
  36. unsigned char *temp;
  37. /*
  38. * Retrieve the current error indicator and use the format_exception()
  39. * function in Python's traceback module to produce an informative
  40. * error message. It returns a list of Python string objects.
  41. */
  42. PyErr_Fetch(&err_type, &err_value, &err_traceback);
  43. PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
  44. if (!err_type) goto end;
  45. tb_module = PyImport_ImportModule("traceback");
  46. if (!tb_module) goto end;
  47. msg_list = PyObject_CallMethod(tb_module, "format_exception", "OOO",
  48. err_type,
  49. err_value ? err_value : Py_None,
  50. err_traceback ? err_traceback : Py_None);
  51. if (!msg_list) goto end;
  52. /*
  53. * Use the join() method of an empty Python string to join the list
  54. * of strings into one Python string containing the entire error
  55. * message. Then get the contents of the Python string.
  56. */
  57. empty_string = PyString_FromString("");
  58. if (!empty_string) goto end;
  59. msg_string = PyObject_CallMethod(empty_string, "join", "O", msg_list);
  60. if (!msg_string) goto end;
  61. temp = (unsigned char *) PyString_AsString(msg_string);
  62. if (temp) msg = temp;
  63. end:
  64. report_scripting_error(&python_scripting_module, python_ses, msg);
  65. Py_XDECREF(err_type);
  66. Py_XDECREF(err_value);
  67. Py_XDECREF(err_traceback);
  68. Py_XDECREF(tb_module);
  69. Py_XDECREF(msg_list);
  70. Py_XDECREF(empty_string);
  71. Py_XDECREF(msg_string);
  72. /* In case another error occurred while reporting the original error: */
  73. PyErr_Clear();
  74. }
  75. /* Prepend ELinks directories to Python's search path. */
  76. static int
  77. set_python_search_path(void)
  78. {
  79. struct string new_python_path;
  80. unsigned char *old_python_path;
  81. int result = -1;
  82. if (!init_string(&new_python_path)) return result;
  83. if (elinks_home && !add_format_to_string(&new_python_path, "%s%c",
  84. elinks_home, DELIM))
  85. goto end;
  86. if (!add_to_string(&new_python_path, CONFDIR))
  87. goto end;
  88. old_python_path = (unsigned char *) getenv("PYTHONPATH");
  89. if (old_python_path && !add_format_to_string(&new_python_path, "%c%s",
  90. DELIM, old_python_path))
  91. goto end;
  92. result = env_set("PYTHONPATH", new_python_path.source, -1);
  93. end:
  94. done_string(&new_python_path);
  95. return result;
  96. }
  97. /* Determine whether or not a user-supplied hooks module exists. */
  98. static int
  99. hooks_module_exists(void)
  100. {
  101. PyObject *imp_module = NULL, *result = NULL;
  102. int found_hooks = 0;
  103. /*
  104. * Use the find_module() function in Python's imp module to look for
  105. * the hooks module in Python's search path. An ImportError exception
  106. * indicates that no such module was found; any other exception will
  107. * be reported as an error.
  108. */
  109. imp_module = PyImport_ImportModule("imp");
  110. if (!imp_module) goto python_error;
  111. result = PyObject_CallMethod(imp_module, "find_module", "s", "hooks");
  112. if (result) {
  113. found_hooks = 1;
  114. goto end;
  115. } else if (PyErr_ExceptionMatches(PyExc_ImportError)) {
  116. PyErr_Clear();
  117. goto end;
  118. }
  119. python_error:
  120. alert_python_error();
  121. end:
  122. Py_XDECREF(imp_module);
  123. Py_XDECREF(result);
  124. return found_hooks;
  125. }
  126. /*
  127. * Turn any warnings that aren't filtered into exceptions so they can be caught
  128. * and handled; otherwise they would be printed to stderr.
  129. */
  130. static char python_showwarnings_doc[] =
  131. PYTHON_DOCSTRING("showwarnings(message, category, filename, lineno, \
  132. file=None, line=None)\n\
  133. \n\
  134. Report a Python warning as an ELinks scripting error.\n\
  135. \n\
  136. Arguments:\n\
  137. \n\
  138. message -- An instance of the class Warning (or a subclass).\n\
  139. \n\
  140. All other arguments are ignored.\n");
  141. static PyCFunction *
  142. python_showwarning(PyObject *self, PyObject *args, PyObject *kwargs)
  143. {
  144. PyObject *warning;
  145. if (!PyTuple_Check(args)) {
  146. PyErr_Format(PyExc_TypeError, "expected a tuple, got '%s'",
  147. args->ob_type->tp_name);
  148. return NULL;
  149. }
  150. warning = PyTuple_GetItem(args, 0);
  151. if (warning) PyErr_SetObject((PyObject *) warning->ob_type, warning);
  152. return NULL;
  153. }
  154. static PyMethodDef warning_methods[] = {
  155. {"showwarning", (PyCFunction) python_showwarning,
  156. METH_VARARGS | METH_KEYWORDS,
  157. python_showwarnings_doc},
  158. {NULL, NULL, 0, NULL}
  159. };
  160. /* Replace the standard warnings.showwarning() function. */
  161. static int
  162. replace_showwarning(void)
  163. {
  164. PyObject *warnings_module = NULL, *module_name = NULL, *module_dict;
  165. int result = -1;
  166. warnings_module = PyImport_ImportModule("warnings");
  167. if (!warnings_module) goto end;
  168. module_name = PyString_FromString("warnings");
  169. if (!module_name) goto end;
  170. module_dict = PyModule_GetDict(warnings_module);
  171. if (!module_dict) goto end;
  172. if (add_python_methods(module_dict, module_name, warning_methods) != 0)
  173. goto end;
  174. result = 0;
  175. end:
  176. Py_XDECREF(warnings_module);
  177. Py_XDECREF(module_name);
  178. return result;
  179. }
  180. /* Module-level documentation for the Python interpreter's elinks module. */
  181. static char module_doc[] =
  182. PYTHON_DOCSTRING("Interface to the ELinks web browser.\n\
  183. \n\
  184. Functions:\n\
  185. \n\
  186. bind_key() -- Bind a keystroke to a callable object.\n\
  187. current_document() -- Return the body of the document being viewed.\n\
  188. current_header() -- Return the header of the document being viewed.\n\
  189. current_link_url() -- Return the URL of the currently selected link.\n\
  190. current_title() -- Return the title of the document being viewed.\n\
  191. current_url() -- Return the URL of the document being viewed.\n\
  192. info_box() -- Display information to the user.\n\
  193. input_box() -- Prompt for user input.\n\
  194. load() -- Load a document into the ELinks cache.\n\
  195. menu() -- Display a menu.\n\
  196. open() -- View a document.\n\
  197. \n\
  198. Exception classes:\n\
  199. \n\
  200. error -- Errors internal to ELinks.\n\
  201. \n\
  202. Other public objects:\n\
  203. \n\
  204. home -- A string containing the pathname of the ~/.elinks directory, or\n\
  205. None if ELinks has no configuration directory.\n");
  206. void
  207. init_python(struct module *module)
  208. {
  209. PyObject *elinks_module, *module_dict, *module_name;
  210. if (set_python_search_path() != 0) return;
  211. Py_Initialize();
  212. if (!hooks_module_exists()) return;
  213. if (replace_showwarning() != 0) goto python_error;
  214. elinks_module = Py_InitModule3("elinks", NULL, module_doc);
  215. if (!elinks_module) goto python_error;
  216. /* If @elinks_home is NULL, Py_BuildValue() returns a None reference. */
  217. if (PyModule_AddObject(elinks_module, "home",
  218. Py_BuildValue("s", elinks_home)) != 0)
  219. goto python_error;
  220. python_elinks_err = PyErr_NewException("elinks.error", NULL, NULL);
  221. if (!python_elinks_err) goto python_error;
  222. if (PyModule_AddObject(elinks_module, "error", python_elinks_err) != 0)
  223. goto python_error;
  224. module_dict = PyModule_GetDict(elinks_module);
  225. if (!module_dict) goto python_error;
  226. module_name = PyString_FromString("elinks");
  227. 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)
  234. goto python_error;
  235. python_hooks = PyImport_ImportModule("hooks");
  236. if (!python_hooks) goto python_error;
  237. return;
  238. python_error:
  239. alert_python_error();
  240. }
  241. void
  242. cleanup_python(struct module *module)
  243. {
  244. if (Py_IsInitialized()) {
  245. PyObject *temp;
  246. python_done_keybinding_interface();
  247. /* This is equivalent to Py_CLEAR(), but it works with older
  248. * versions of Python predating that macro: */
  249. temp = python_hooks;
  250. python_hooks = NULL;
  251. Py_XDECREF(temp);
  252. Py_Finalize();
  253. }
  254. }
  255. /* Add methods to a Python extension module. */
  256. int
  257. add_python_methods(PyObject *dict, PyObject *name, PyMethodDef *methods)
  258. {
  259. PyMethodDef *method;
  260. for (method = methods; method && method->ml_name; method++) {
  261. PyObject *function = PyCFunction_NewEx(method, NULL, name);
  262. int result;
  263. if (!function) return -1;
  264. result = PyDict_SetItemString(dict, method->ml_name, function);
  265. Py_DECREF(function);
  266. if (result != 0) return -1;
  267. }
  268. return 0;
  269. }