1 /*
2 * python-loader.c: Support for Python plugins.
3 *
4 * Author: Zbigniew Chyla (cyba@gnome.pl)
5 */
6
7 #include <gnumeric-config.h>
8 #include <gnumeric.h>
9 #include "python-loader.h"
10
11 #include "py-gnumeric.h"
12 #include "gnm-python.h"
13
14 #include <gnm-plugin.h>
15 #include <application.h>
16 #include <workbook.h>
17 #include <sheet.h>
18 #include <workbook-view.h>
19 #include <wbc-gtk.h>
20 #include <gui-util.h>
21 #include <value.h>
22 #include <expr.h>
23 #include <expr-impl.h>
24 #include <func.h>
25
26 #include <goffice/goffice.h>
27 #include <goffice/app/module-plugin-defs.h>
28
29 #include <gsf/gsf-impl-utils.h>
30
31 #include <glib/gi18n-lib.h>
32
33 #include <glib/gstdio.h>
34
35 #include <stdlib.h>
36
37 #include <Python.h>
38 #include <pygobject.h>
39
40 #define SERVICE_KEY "python-loader::service"
41
42 typedef struct {
43 GObject base;
44
45 gchar *module_name;
46
47 GnmPython *py_object;
48 GnmPyInterpreter *py_interpreter_info;
49 PyObject *main_module;
50 PyObject *main_module_dict;
51 } GnmPythonPluginLoader;
52 typedef GObjectClass GnmPythonPluginLoaderClass;
53
54 #define PLUGIN_GET_LOADER(plugin) \
55 GNM_PYTHON_PLUGIN_LOADER (g_object_get_data (G_OBJECT (plugin), "python-loader"))
56 #define SERVICE_GET_LOADER(service) \
57 PLUGIN_GET_LOADER (go_plugin_service_get_plugin (service))
58 #define SWITCH_TO_PLUGIN(plugin) \
59 gnm_py_interpreter_switch_to (PLUGIN_GET_LOADER (plugin)->py_interpreter_info)
60
61 static void
gplp_set_attributes(GOPluginLoader * loader,GHashTable * attrs,GOErrorInfo ** ret_error)62 gplp_set_attributes (GOPluginLoader *loader, GHashTable *attrs, GOErrorInfo **ret_error)
63 {
64 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (loader);
65 gchar *module_name = NULL;
66
67 GO_INIT_RET_ERROR_INFO (ret_error);
68 module_name = g_hash_table_lookup (attrs, "module_name");
69 if (module_name != NULL) {
70 loader_python->module_name = g_strdup (module_name);
71 } else {
72 *ret_error = go_error_info_new_str (
73 _("Python module name not given."));
74 }
75 }
76
77 static FILE *
gnumeric_fopen_error_info(const char * file_name,const char * mode,GOErrorInfo ** ret_error)78 gnumeric_fopen_error_info (const char *file_name, const char *mode, GOErrorInfo **ret_error)
79 {
80 FILE *f;
81
82 g_return_val_if_fail (file_name != NULL, NULL);
83 g_return_val_if_fail (mode != NULL, NULL);
84 g_return_val_if_fail (ret_error != NULL, NULL);
85
86 *ret_error = NULL;
87 f = g_fopen (file_name, mode);
88 if (f == NULL) {
89 if (strchr (mode, 'w') != NULL && strchr (mode, 'r') == NULL) {
90 *ret_error = go_error_info_new_printf (
91 _("Error while opening file \"%s\" for writing."),
92 file_name);
93 } else {
94 *ret_error = go_error_info_new_printf (
95 _("Error while opening file \"%s\" for reading."),
96 file_name);
97 }
98 go_error_info_add_details (*ret_error, go_error_info_new_from_errno ());
99 }
100
101 return f;
102 }
103 static void
gplp_load_base(GOPluginLoader * loader,GOErrorInfo ** ret_error)104 gplp_load_base (GOPluginLoader *loader, GOErrorInfo **ret_error)
105 {
106 static gchar const *python_file_extensions[] = {"py", "pyc", "pyo", NULL};
107
108 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (loader);
109 gchar const **file_ext;
110 GnmPython *py_object;
111 GnmPyInterpreter *py_interpreter_info;
112 gchar *full_module_file_name = NULL;
113 FILE *f;
114 GOPlugin *plugin = go_plugin_loader_get_plugin (loader);
115 GOErrorInfo *open_error = NULL;
116 PyObject *modules, *main_module, *main_module_dict;
117
118 GO_INIT_RET_ERROR_INFO (ret_error);
119 g_object_set_data (G_OBJECT (plugin), "python-loader", loader);
120
121 py_object = gnm_python_object_get (ret_error);
122 if (py_object == NULL)
123 return; /* gnm_python_object_get sets ret_error */
124 py_interpreter_info = gnm_python_new_interpreter (py_object, plugin);
125 if (py_interpreter_info == NULL) {
126 *ret_error = go_error_info_new_str (_("Cannot create new Python interpreter."));
127 gnm_python_clear_error_if_needed (py_object);
128 g_object_unref (py_object);
129 return;
130 }
131
132 for (file_ext = python_file_extensions; *file_ext != NULL; file_ext++) {
133 gchar *file_name = g_strconcat (
134 loader_python->module_name, ".", *file_ext, NULL);
135 gchar *path = g_build_filename (
136 go_plugin_get_dir_name (plugin),
137 file_name, NULL);
138 g_free (file_name);
139 if (g_file_test (path, G_FILE_TEST_EXISTS)) {
140 full_module_file_name = path;
141 break;
142 } else
143 g_free (path);
144 }
145 if (full_module_file_name == NULL) {
146 *ret_error = go_error_info_new_printf (
147 _("Module \"%s\" doesn't exist."),
148 loader_python->module_name);
149 gnm_python_destroy_interpreter (py_object, py_interpreter_info);
150 g_object_unref (py_object);
151 return;
152 }
153 f = gnumeric_fopen_error_info (full_module_file_name, "r", &open_error);
154 g_free (full_module_file_name);
155 if (f == NULL) {
156 *ret_error = open_error;
157 gnm_python_destroy_interpreter (py_object, py_interpreter_info);
158 g_object_unref (py_object);
159 return;
160 }
161
162 if (PyRun_SimpleFile (f, loader_python->module_name) != 0) {
163 (void) fclose (f);
164 *ret_error = go_error_info_new_printf (
165 _("Execution of module \"%s\" failed."),
166 loader_python->module_name);
167 gnm_python_destroy_interpreter (py_object, py_interpreter_info);
168 g_object_unref (py_object);
169 return;
170 }
171 (void) fclose (f);
172
173 modules = PyImport_GetModuleDict ();
174 g_return_if_fail (modules != NULL);
175 main_module = PyDict_GetItemString (modules, "__main__");
176 g_return_if_fail (main_module != NULL);
177 main_module_dict = PyModule_GetDict (main_module);
178 g_return_if_fail (main_module_dict != NULL);
179 loader_python->py_object = py_object;
180 loader_python->py_interpreter_info = py_interpreter_info;
181 loader_python->main_module = main_module;
182 loader_python->main_module_dict = main_module_dict;
183 }
184
185 static void
gplp_unload_base(GOPluginLoader * loader,GOErrorInfo ** ret_error)186 gplp_unload_base (GOPluginLoader *loader, GOErrorInfo **ret_error)
187 {
188 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (loader);
189 GOPlugin *plugin = go_plugin_loader_get_plugin (loader);
190
191 GO_INIT_RET_ERROR_INFO (ret_error);
192 g_object_steal_data (G_OBJECT (plugin), "python-loader");
193 gnm_python_destroy_interpreter (
194 loader_python->py_object, loader_python->py_interpreter_info);
195 g_object_unref (loader_python->py_object);
196 }
197
198 /*
199 * Service - file_opener
200 */
201
202 typedef struct {
203 PyObject *python_func_file_probe;
204 PyObject *python_func_file_open;
205 } ServiceLoaderDataFileOpener;
206
207 static void
gplp_loader_data_opener_free(ServiceLoaderDataFileOpener * loader_data)208 gplp_loader_data_opener_free (ServiceLoaderDataFileOpener *loader_data)
209 {
210 Py_DECREF (loader_data->python_func_file_probe);
211 Py_DECREF (loader_data->python_func_file_open);
212 g_free (loader_data);
213 }
214
215 static gboolean
gplp_func_file_probe(G_GNUC_UNUSED GOFileOpener const * fo,GOPluginService * service,GsfInput * input,G_GNUC_UNUSED GOFileProbeLevel pl)216 gplp_func_file_probe (G_GNUC_UNUSED GOFileOpener const *fo, GOPluginService *service,
217 GsfInput *input, G_GNUC_UNUSED GOFileProbeLevel pl)
218 {
219 ServiceLoaderDataFileOpener *loader_data;
220 PyObject *probe_result = NULL;
221 PyObject *input_wrapper;
222 gboolean result;
223
224 g_return_val_if_fail (GO_IS_PLUGIN_SERVICE_FILE_OPENER (service), FALSE);
225 g_return_val_if_fail (input != NULL, FALSE);
226 if (_PyGObject_API == NULL) {
227 pygobject_init (3, 0, 0);
228 g_return_val_if_fail (_PyGObject_API != NULL, FALSE);
229 }
230
231 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
232 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
233 input_wrapper = pygobject_new (G_OBJECT (input));
234 if (input_wrapper == NULL) {
235 g_warning ("%s", py_exc_to_string ());
236 gnm_python_clear_error_if_needed (SERVICE_GET_LOADER (service)->py_object);
237 }
238 if (input_wrapper != NULL &&
239 loader_data->python_func_file_probe != NULL) {
240 /* wrapping adds a reference */
241 g_object_unref (input);
242 probe_result = PyObject_CallFunction
243 (loader_data->python_func_file_probe,
244 "O", input_wrapper);
245 }
246 Py_XDECREF (input_wrapper);
247 if (probe_result != NULL) {
248 result = PyObject_IsTrue (probe_result);
249 Py_DECREF (probe_result);
250 } else {
251 PyErr_Clear ();
252 result = FALSE;
253 }
254
255 return result;
256 }
257
258 static void
gplp_func_file_open(G_GNUC_UNUSED GOFileOpener const * fo,GOPluginService * service,GOIOContext * io_context,gpointer wb_view,GsfInput * input,G_GNUC_UNUSED char const * enc)259 gplp_func_file_open (G_GNUC_UNUSED GOFileOpener const *fo,
260 GOPluginService *service,
261 GOIOContext *io_context,
262 gpointer wb_view,
263 GsfInput *input, G_GNUC_UNUSED char const *enc)
264 {
265 ServiceLoaderDataFileOpener *loader_data;
266 Sheet *sheet, *old_sheet;
267 PyObject *open_result = NULL;
268 PyObject *input_wrapper;
269
270 g_return_if_fail (GO_IS_PLUGIN_SERVICE_FILE_OPENER (service));
271 g_return_if_fail (input != NULL);
272 if (_PyGObject_API == NULL) {
273 pygobject_init (3, 0, 0);
274 g_return_if_fail (_PyGObject_API != NULL);
275 }
276
277 old_sheet = wb_view_cur_sheet (wb_view);
278
279 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
280 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
281 sheet = sheet_new (wb_view_get_workbook (wb_view), _("Some name"),
282 gnm_sheet_get_max_cols (old_sheet),
283 gnm_sheet_get_max_rows (old_sheet));
284 input_wrapper = pygobject_new (G_OBJECT (input));
285 if (input_wrapper != NULL) {
286 /* wrapping adds a reference */
287 g_object_unref (input);
288 open_result = PyObject_CallFunction
289 (loader_data->python_func_file_open,
290 "NO",
291 pygobject_new (G_OBJECT (sheet)), input_wrapper);
292 Py_DECREF (input_wrapper);
293 }
294 if (open_result != NULL) {
295 Py_DECREF (open_result);
296 workbook_sheet_attach (wb_view_get_workbook (wb_view), sheet);
297 } else {
298 go_io_error_string (io_context, py_exc_to_string ());
299 gnm_python_clear_error_if_needed (SERVICE_GET_LOADER (service)->py_object);
300 g_object_unref (sheet);
301 }
302 }
303
304 static void
gplp_load_service_file_opener(GOPluginLoader * loader,GOPluginService * service,GOErrorInfo ** ret_error)305 gplp_load_service_file_opener (GOPluginLoader *loader,
306 GOPluginService *service,
307 GOErrorInfo **ret_error)
308 {
309 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (loader);
310 gchar *func_name_file_probe, *func_name_file_open;
311 PyObject *python_func_file_probe, *python_func_file_open;
312
313 g_return_if_fail (GO_IS_PLUGIN_SERVICE_FILE_OPENER (service));
314
315 GO_INIT_RET_ERROR_INFO (ret_error);
316 gnm_py_interpreter_switch_to (loader_python->py_interpreter_info);
317 func_name_file_probe = g_strconcat (
318 go_plugin_service_get_id (service), "_file_probe", NULL);
319 python_func_file_probe = PyDict_GetItemString (loader_python->main_module_dict,
320 func_name_file_probe);
321 gnm_python_clear_error_if_needed (loader_python->py_object);
322 func_name_file_open = g_strconcat (
323 go_plugin_service_get_id (service), "_file_open", NULL);
324 python_func_file_open = PyDict_GetItemString (loader_python->main_module_dict,
325 func_name_file_open);
326 gnm_python_clear_error_if_needed (loader_python->py_object);
327 if (python_func_file_open != NULL) {
328 GOPluginServiceFileOpenerCallbacks *cbs;
329 ServiceLoaderDataFileOpener *loader_data;
330
331 cbs = go_plugin_service_get_cbs (service);
332 cbs->plugin_func_file_probe = gplp_func_file_probe;
333 cbs->plugin_func_file_open = (gpointer) gplp_func_file_open;
334
335 loader_data = g_new (ServiceLoaderDataFileOpener, 1);
336 loader_data->python_func_file_probe = python_func_file_probe;
337 loader_data->python_func_file_open = python_func_file_open;
338 Py_XINCREF (loader_data->python_func_file_probe);
339 Py_INCREF (loader_data->python_func_file_open);
340 g_object_set_data_full
341 (G_OBJECT (service), "loader_data", loader_data,
342 (GDestroyNotify) gplp_loader_data_opener_free);
343 } else {
344 *ret_error = go_error_info_new_printf (
345 _("Python file \"%s\" has invalid format."),
346 loader_python->module_name);
347 go_error_info_add_details (*ret_error,
348 go_error_info_new_printf (
349 _("File doesn't contain \"%s\" function."),
350 func_name_file_open));
351 }
352 g_free (func_name_file_probe);
353 g_free (func_name_file_open);
354 }
355
356 /*
357 * Service - file_saver
358 */
359
360 typedef struct {
361 PyObject *python_func_file_save;
362 } ServiceLoaderDataFileSaver;
363
364 static void
gplp_loader_data_saver_free(ServiceLoaderDataFileSaver * loader_data)365 gplp_loader_data_saver_free (ServiceLoaderDataFileSaver *loader_data)
366 {
367 Py_DECREF (loader_data->python_func_file_save);
368 g_free (loader_data);
369 }
370
371 static void
gplp_func_file_save(G_GNUC_UNUSED GOFileSaver const * fs,GOPluginService * service,GOIOContext * io_context,gconstpointer wb_view,GsfOutput * output)372 gplp_func_file_save (G_GNUC_UNUSED GOFileSaver const *fs, GOPluginService *service,
373 GOIOContext *io_context, gconstpointer wb_view,
374 GsfOutput *output)
375 {
376 ServiceLoaderDataFileSaver *saver_data;
377 PyObject *py_workbook;
378 PyObject *save_result = NULL;
379 PyObject *output_wrapper;
380
381 g_return_if_fail (GO_IS_PLUGIN_SERVICE_FILE_SAVER (service));
382 g_return_if_fail (output != NULL);
383 if (_PyGObject_API == NULL) {
384 pygobject_init (3, 0, 0);
385 g_return_if_fail (_PyGObject_API != NULL);
386 }
387 g_return_if_fail (_PyGObject_API != NULL);
388
389 saver_data = g_object_get_data (G_OBJECT (service), "loader_data");
390 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
391 py_workbook = pygobject_new (G_OBJECT (wb_view_get_workbook (wb_view)));
392 output_wrapper = pygobject_new (G_OBJECT (output));
393 if (output_wrapper != NULL) {
394 /* wrapping adds a reference */
395 g_object_unref (output);
396 save_result = PyObject_CallFunction
397 (saver_data->python_func_file_save,
398 "NO", py_workbook, output_wrapper);
399 Py_DECREF (output_wrapper);
400 }
401 if (save_result != NULL) {
402 Py_DECREF (save_result);
403 } else {
404 go_io_error_string (io_context, py_exc_to_string ());
405 gnm_python_clear_error_if_needed (SERVICE_GET_LOADER (service)->py_object);
406 }
407 }
408
409 static void
gplp_load_service_file_saver(GOPluginLoader * loader,GOPluginService * service,GOErrorInfo ** ret_error)410 gplp_load_service_file_saver (GOPluginLoader *loader,
411 GOPluginService *service,
412 GOErrorInfo **ret_error)
413 {
414 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (loader);
415 gchar *func_name_file_save;
416 PyObject *python_func_file_save;
417
418 g_return_if_fail (GO_IS_PLUGIN_SERVICE_FILE_SAVER (service));
419
420 GO_INIT_RET_ERROR_INFO (ret_error);
421 gnm_py_interpreter_switch_to (loader_python->py_interpreter_info);
422 func_name_file_save = g_strconcat (
423 go_plugin_service_get_id (service), "_file_save", NULL);
424 python_func_file_save = PyDict_GetItemString (loader_python->main_module_dict,
425 func_name_file_save);
426 gnm_python_clear_error_if_needed (loader_python->py_object);
427 if (python_func_file_save != NULL) {
428 GOPluginServiceFileSaverCallbacks *cbs;
429 ServiceLoaderDataFileSaver *saver_data;
430
431 cbs = go_plugin_service_get_cbs (service);
432 cbs->plugin_func_file_save = (gpointer)gplp_func_file_save;
433
434 saver_data = g_new (ServiceLoaderDataFileSaver, 1);
435 saver_data->python_func_file_save = python_func_file_save;
436 Py_INCREF (saver_data->python_func_file_save);
437 g_object_set_data_full
438 (G_OBJECT (service), "loader_data", saver_data,
439 (GDestroyNotify) gplp_loader_data_saver_free);
440 } else {
441 *ret_error = go_error_info_new_printf (
442 _("Python file \"%s\" has invalid format."),
443 loader_python->module_name);
444 if (python_func_file_save == NULL) {
445 go_error_info_add_details (*ret_error,
446 go_error_info_new_printf (
447 _("File doesn't contain \"%s\" function."),
448 func_name_file_save));
449 }
450 }
451 g_free (func_name_file_save);
452 }
453
454 /*
455 * Service - function_group
456 */
457
458 typedef struct {
459 PyObject *python_fn_info_dict;
460 } ServiceLoaderDataFunctionGroup;
461
462
463 static void
gplp_loader_data_fngroup_free(ServiceLoaderDataFunctionGroup * loader_data)464 gplp_loader_data_fngroup_free (ServiceLoaderDataFunctionGroup *loader_data)
465 {
466 Py_DECREF (loader_data->python_fn_info_dict);
467 g_free (loader_data);
468 }
469
470 static GnmValue *
call_python_function_args(GnmFuncEvalInfo * ei,GnmValue const * const * args)471 call_python_function_args (GnmFuncEvalInfo *ei, GnmValue const * const *args)
472 {
473 GOPluginService *service;
474 ServiceLoaderDataFunctionGroup *loader_data;
475 PyObject *fn_info_tuple;
476 PyObject *python_fn;
477 GnmFunc *fndef;
478
479 gint min_n_args, max_n_args, n_args;
480
481 g_return_val_if_fail (ei != NULL, NULL);
482 g_return_val_if_fail (ei->func_call != NULL, NULL);
483 g_return_val_if_fail (args != NULL, NULL);
484
485 fndef = ei->func_call->func;
486 service = (GOPluginService *)g_object_get_data (G_OBJECT (fndef), SERVICE_KEY);
487 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
488 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
489 fn_info_tuple = PyDict_GetItemString (loader_data->python_fn_info_dict,
490 gnm_func_get_name (fndef, FALSE));
491 g_assert (fn_info_tuple != NULL && PyTuple_Check (fn_info_tuple));
492 python_fn = PyTuple_GetItem (fn_info_tuple, 2);
493 gnm_func_count_args (fndef, &min_n_args, &max_n_args);
494 for (n_args = min_n_args; n_args < max_n_args && args[n_args] != NULL; n_args++) {
495 ;
496 }
497 return call_python_function (python_fn, ei->pos, n_args, args);
498 }
499
500 static GnmValue *
call_python_function_nodes(GnmFuncEvalInfo * ei,int argc,GnmExprConstPtr const * argv)501 call_python_function_nodes (GnmFuncEvalInfo *ei,
502 int argc, GnmExprConstPtr const *argv)
503 {
504 GOPluginService *service;
505 ServiceLoaderDataFunctionGroup *loader_data;
506 PyObject *python_fn;
507 GnmFunc const * fndef;
508 GnmValue **values;
509 gint i;
510 GnmValue *ret_value;
511
512 g_return_val_if_fail (ei != NULL, NULL);
513 g_return_val_if_fail (ei->func_call != NULL, NULL);
514
515 fndef = ei->func_call->func;
516 service = (GOPluginService *)g_object_get_data (G_OBJECT (fndef), SERVICE_KEY);
517 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
518 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
519 python_fn = PyDict_GetItemString (loader_data->python_fn_info_dict,
520 gnm_func_get_name (fndef, FALSE));
521
522 values = g_new (GnmValue *, argc);
523 for (i = 0; i < argc; i++) {
524 values[i] = gnm_expr_eval (argv[i], ei->pos, GNM_EXPR_EVAL_PERMIT_NON_SCALAR);
525 }
526 ret_value = call_python_function (python_fn, ei->pos, argc,
527 (GnmValue const * const *)values);
528 for (i = 0; i < argc; i++) {
529 value_release (values[i]);
530 }
531 g_free (values);
532
533 return ret_value;
534 }
535
FuncHelpDestructor(PyObject * object)536 static void FuncHelpDestructor (PyObject *object)
537 {
538 g_free (PyCapsule_GetPointer (object, "FuncHelp"));
539 }
540
541 static GnmFuncHelp const *
python_function_get_gnumeric_help(PyObject * python_fn_info_dict,PyObject * python_fn,const gchar * fn_name)542 python_function_get_gnumeric_help (PyObject *python_fn_info_dict, PyObject *python_fn,
543 const gchar *fn_name)
544 {
545 gchar *help_attr_name;
546 PyObject *cobject_help_value;
547 PyObject *python_arg_names;
548 PyObject *fn_info_obj;
549 GnmFuncHelp const *res = NULL;
550
551 fn_info_obj = PyDict_GetItemString (python_fn_info_dict, fn_name);
552 python_arg_names = PyTuple_Check (fn_info_obj)
553 ? PyTuple_GetItem (fn_info_obj, 1)
554 : NULL;
555
556 help_attr_name = g_strdup_printf ("_CGnumericHelp_%s", fn_name);
557 cobject_help_value = PyDict_GetItemString (python_fn_info_dict, help_attr_name);
558 if (cobject_help_value == NULL) {
559 PyObject *python_fn_help =
560 PyFunction_Check (python_fn)
561 ? ((PyFunctionObject *) python_fn)->func_doc
562 : NULL;
563 if (python_fn_help != NULL && PyUnicode_Check (python_fn_help)) {
564 guint n = 0;
565 GnmFuncHelp *new_help = NULL;
566 gboolean arg_names_written = FALSE;
567 char const *help_text = PyUnicode_AsUTF8 (python_fn_help);
568
569 if (g_str_has_prefix (help_text, "@GNM_FUNC_HELP_NAME@")) {
570 /* New-style documentation */
571 gchar **items = g_strsplit (help_text, "\n", 0), **fitems = items;
572 while (*items) {
573 if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_NAME@")) {
574 guint it = n;
575 new_help = g_renew (GnmFuncHelp, new_help, ++n);
576 new_help[it].type = GNM_FUNC_HELP_NAME;
577 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_NAME@"));
578 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_ARG@")) {
579 guint it = n;
580 new_help = g_renew (GnmFuncHelp, new_help, ++n);
581 new_help[it].type = GNM_FUNC_HELP_ARG;
582 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_ARG@"));
583 arg_names_written = TRUE;
584 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_DESCRIPTION@")) {
585 guint it = n;
586 new_help = g_renew (GnmFuncHelp, new_help, ++n);
587 new_help[it].type = GNM_FUNC_HELP_DESCRIPTION;
588 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_DESCRIPTION@"));
589 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_EXAMPLES@")) {
590 guint it = n;
591 new_help = g_renew (GnmFuncHelp, new_help, ++n);
592 new_help[it].type = GNM_FUNC_HELP_EXAMPLES;
593 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_EXAMPLES@"));
594 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_SEEALSO@")) {
595 guint it = n;
596 new_help = g_renew (GnmFuncHelp, new_help, ++n);
597 new_help[it].type = GNM_FUNC_HELP_SEEALSO;
598 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_SEEALSO@"));
599 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_EXTREF@")) {
600 guint it = n;
601 new_help = g_renew (GnmFuncHelp, new_help, ++n);
602 new_help[it].type = GNM_FUNC_HELP_EXTREF;
603 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_EXTREF@"));
604 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_NOTE@")) {
605 guint it = n;
606 new_help = g_renew (GnmFuncHelp, new_help, ++n);
607 new_help[it].type = GNM_FUNC_HELP_NOTE;
608 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_NOTE@"));
609 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_END@")) {
610 /* ignore */
611 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_EXCEL@")) {
612 guint it = n;
613 new_help = g_renew (GnmFuncHelp, new_help, ++n);
614 new_help[it].type = GNM_FUNC_HELP_EXCEL;
615 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_EXCEL@"));
616 } else if (g_str_has_prefix (*items, "@GNM_FUNC_HELP_ODF@")) {
617 guint it = n;
618 new_help = g_renew (GnmFuncHelp, new_help, ++n);
619 new_help[it].type = GNM_FUNC_HELP_ODF;
620 new_help[it].text = g_strdup ((*items) + strlen ("@GNM_FUNC_HELP_ODF@"));
621 } else if (n > 0) {
622 gchar *old_text = (gchar *) new_help[n].text;
623 new_help[n].text = g_strconcat (old_text, "\n", *items, NULL);
624 g_free (old_text);
625 }
626 items++;
627 }
628 g_strfreev (fitems);
629 }
630
631 if (python_arg_names != NULL && !arg_names_written) {
632 /* We only try this if we did not get argument */
633 /* descriptions via the new style documentation */
634 char const *arg_names = PyUnicode_AsUTF8 (python_arg_names);
635 if (arg_names != NULL && arg_names[0] != '\0') {
636 gchar **args = g_strsplit (arg_names, ",", 0);
637 guint nitems = g_strv_length (args), nstart = n, i;
638 n += nitems;
639 new_help = g_renew (GnmFuncHelp, new_help, n);
640 for (i = 0; i < nitems; i++, nstart++) {
641 char const *arg_name = args[i];
642 while (*arg_name == ' ') arg_name++;
643 new_help[nstart].type = GNM_FUNC_HELP_ARG;
644 new_help[nstart].text = g_strdup_printf ("%s:", arg_name);
645 }
646 g_strfreev (args);
647 }
648 }
649
650 n++;
651 new_help = g_renew (GnmFuncHelp, new_help, n);
652 new_help[n-1].type = GNM_FUNC_HELP_END;
653 new_help[n-1].text = NULL;
654
655 cobject_help_value = PyCapsule_New (new_help, "FuncHelp", FuncHelpDestructor);
656 PyDict_SetItemString (python_fn_info_dict, help_attr_name, cobject_help_value);
657 }
658 }
659 g_free (help_attr_name);
660
661 if (cobject_help_value) {
662 res = (GnmFuncHelp const *) PyCapsule_GetPointer (cobject_help_value, "FuncHelp");
663 Py_DECREF (cobject_help_value);
664 }
665
666 return res;
667 }
668
669 static void
gplp_func_load_stub(GOPluginService * service,GnmFunc * func)670 gplp_func_load_stub (GOPluginService *service,
671 GnmFunc *func)
672 {
673 ServiceLoaderDataFunctionGroup *loader_data;
674 PyObject *fn_info_obj;
675 char const *name;
676
677 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
678 g_return_if_fail (GNM_IS_FUNC (func));
679
680 name = gnm_func_get_name (func, FALSE);
681 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
682 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
683 fn_info_obj = PyDict_GetItemString (loader_data->python_fn_info_dict,
684 name);
685 if (fn_info_obj == NULL) {
686 gnm_python_clear_error_if_needed (SERVICE_GET_LOADER (service)->py_object);
687 return;
688 }
689
690 if (PyTuple_Check (fn_info_obj)) {
691 PyObject *python_args;
692 PyObject *python_fn;
693
694 if (PyTuple_Size (fn_info_obj) == 3 &&
695 (python_args = PyTuple_GetItem (fn_info_obj, 0)) != NULL &&
696 PyUnicode_Check (python_args) &&
697 (python_fn = PyTuple_GetItem (fn_info_obj, 2)) != NULL &&
698 PyCallable_Check (python_fn)) {
699 GnmFuncHelp const *help = python_function_get_gnumeric_help
700 (loader_data->python_fn_info_dict, python_fn, name);
701 gnm_func_set_fixargs (func, call_python_function_args, PyUnicode_AsUTF8 (python_args));
702 gnm_func_set_help (func, help, -1);
703 gnm_func_set_impl_status (func, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC);
704 g_object_set_data (G_OBJECT (func), SERVICE_KEY, service);
705 return;
706 }
707
708 gnm_python_clear_error_if_needed (SERVICE_GET_LOADER (service)->py_object);
709 return;
710 }
711
712 if (PyCallable_Check (fn_info_obj)) {
713 GnmFuncHelp const *help = python_function_get_gnumeric_help
714 (loader_data->python_fn_info_dict, fn_info_obj, name);
715 gnm_func_set_varargs (func, call_python_function_nodes, NULL);
716 gnm_func_set_help (func, help, -1);
717 gnm_func_set_impl_status (func, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC);
718 g_object_set_data (G_OBJECT (func), SERVICE_KEY, service);
719 return;
720 }
721
722 gnm_python_clear_error_if_needed (SERVICE_GET_LOADER (service)->py_object);
723 return;
724 }
725
726 static void
gplp_load_service_function_group(GOPluginLoader * loader,GOPluginService * service,GOErrorInfo ** ret_error)727 gplp_load_service_function_group (GOPluginLoader *loader,
728 GOPluginService *service,
729 GOErrorInfo **ret_error)
730 {
731 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (loader);
732 gchar *fn_info_dict_name;
733 PyObject *python_fn_info_dict;
734
735 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
736
737 GO_INIT_RET_ERROR_INFO (ret_error);
738 gnm_py_interpreter_switch_to (loader_python->py_interpreter_info);
739 fn_info_dict_name = g_strconcat (
740 go_plugin_service_get_id (service), "_functions", NULL);
741 python_fn_info_dict = PyDict_GetItemString (loader_python->main_module_dict,
742 fn_info_dict_name);
743 gnm_python_clear_error_if_needed (loader_python->py_object);
744 if (python_fn_info_dict != NULL && PyDict_Check (python_fn_info_dict)) {
745 GnmPluginServiceFunctionGroupCallbacks *cbs;
746 ServiceLoaderDataFunctionGroup *loader_data;
747
748 cbs = go_plugin_service_get_cbs (service);
749 cbs->load_stub = &gplp_func_load_stub;
750
751 loader_data = g_new (ServiceLoaderDataFunctionGroup, 1);
752 loader_data->python_fn_info_dict = (PyObject *) python_fn_info_dict;
753 Py_INCREF (loader_data->python_fn_info_dict);
754 g_object_set_data_full
755 (G_OBJECT (service), "loader_data", loader_data,
756 (GDestroyNotify) gplp_loader_data_fngroup_free);
757 } else {
758 *ret_error = go_error_info_new_printf (
759 _("Python file \"%s\" has invalid format."),
760 loader_python->module_name);
761 if (python_fn_info_dict == NULL) {
762 go_error_info_add_details (*ret_error,
763 go_error_info_new_printf (
764 _("File doesn't contain \"%s\" dictionary."),
765 fn_info_dict_name));
766 } else if (!PyDict_Check (python_fn_info_dict)) {
767 go_error_info_add_details (*ret_error,
768 go_error_info_new_printf (
769 _("Object \"%s\" is not a dictionary."),
770 fn_info_dict_name));
771 }
772 }
773 g_free (fn_info_dict_name);
774 }
775
776 static void
gplp_unload_service_function_group(GOPluginLoader * loader,GOPluginService * service,GOErrorInfo ** ret_error)777 gplp_unload_service_function_group (GOPluginLoader *loader,
778 GOPluginService *service,
779 GOErrorInfo **ret_error)
780 {
781 ServiceLoaderDataFunctionGroup *loader_data;
782
783 g_return_if_fail (GNM_IS_PYTHON_PLUGIN_LOADER (loader));
784 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
785
786 GO_INIT_RET_ERROR_INFO (ret_error);
787 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
788 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
789 Py_CLEAR (loader_data->python_fn_info_dict);
790 }
791
792 typedef struct {
793 PyObject *ui_actions;
794 } ServiceLoaderDataUI;
795
796 static void
gplp_loader_data_ui_free(ServiceLoaderDataUI * loader_data)797 gplp_loader_data_ui_free (ServiceLoaderDataUI *loader_data)
798 {
799 Py_DECREF (loader_data->ui_actions);
800 g_free (loader_data);
801 }
802
803 static void
gplp_func_exec_action(GOPluginService * service,GnmAction const * action,WorkbookControl * wbc,GOErrorInfo ** ret_error)804 gplp_func_exec_action (GOPluginService *service,
805 GnmAction const *action,
806 WorkbookControl *wbc,
807 GOErrorInfo **ret_error)
808 {
809 ServiceLoaderDataUI *loader_data;
810 PyObject *fn, *ret;
811
812 if (_PyGObject_API == NULL) {
813 pygobject_init (3, 0, 0);
814 g_return_if_fail (_PyGObject_API != NULL);
815 }
816
817 GO_INIT_RET_ERROR_INFO (ret_error);
818 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
819 SWITCH_TO_PLUGIN (go_plugin_service_get_plugin (service));
820 fn = PyDict_GetItemString (loader_data->ui_actions, action->id);
821 if (fn == NULL) {
822 *ret_error = go_error_info_new_printf (_("Unknown action: %s"),
823 action->id);
824 return;
825 } else if (!PyCallable_Check (fn)) {
826 *ret_error = go_error_info_new_printf (
827 _("Not a valid function for action: %s"), action->id);
828 return;
829 }
830 ret = PyObject_CallFunction (fn, "N",
831 pygobject_new (G_OBJECT (WBC_GTK (wbc))));
832 if (ret == NULL) {
833 *ret_error = go_error_info_new_str (py_exc_to_string ());
834 PyErr_Clear ();
835 } else {
836 Py_DECREF (ret);
837 }
838 }
839
840 static void
gplp_load_service_ui(GOPluginLoader * loader,GOPluginService * service,GOErrorInfo ** ret_error)841 gplp_load_service_ui (GOPluginLoader *loader,
842 GOPluginService *service,
843 GOErrorInfo **ret_error)
844 {
845
846 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (loader);
847 gchar *ui_action_names;
848 PyObject *ui_actions;
849
850 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
851
852 GO_INIT_RET_ERROR_INFO (ret_error);
853 gnm_py_interpreter_switch_to (loader_python->py_interpreter_info);
854 ui_action_names = g_strconcat (go_plugin_service_get_id (service),
855 "_ui_actions", NULL);
856 ui_actions = PyDict_GetItemString (loader_python->main_module_dict,
857 ui_action_names);
858 gnm_python_clear_error_if_needed (loader_python->py_object);
859 if (ui_actions != NULL && PyDict_Check (ui_actions)) {
860 GnmPluginServiceUICallbacks *cbs;
861 ServiceLoaderDataUI *loader_data;
862
863 cbs = go_plugin_service_get_cbs (service);
864 cbs->plugin_func_exec_action = gplp_func_exec_action;
865
866 loader_data = g_new (ServiceLoaderDataUI, 1);
867 loader_data->ui_actions = ui_actions;
868 Py_INCREF (loader_data->ui_actions);
869 g_object_set_data_full
870 (G_OBJECT (service), "loader_data", loader_data,
871 (GDestroyNotify) gplp_loader_data_ui_free);
872 } else {
873 *ret_error = go_error_info_new_printf (
874 _("Python file \"%s\" has invalid format."),
875 loader_python->module_name);
876 if (ui_actions == NULL) {
877 go_error_info_add_details (*ret_error,
878 go_error_info_new_printf (
879 _("File doesn't contain \"%s\" dictionary."),
880 ui_action_names));
881 } else if (!PyDict_Check (ui_actions)) {
882 go_error_info_add_details (*ret_error,
883 go_error_info_new_printf (
884 _("Object \"%s\" is not a dictionary."),
885 ui_action_names));
886 }
887 }
888 g_free (ui_action_names);
889 }
890
891 static gboolean
gplp_service_load(GOPluginLoader * l,GOPluginService * s,GOErrorInfo ** err)892 gplp_service_load (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
893 {
894 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s))
895 gplp_load_service_function_group (l, s, err);
896 else if (GNM_IS_PLUGIN_SERVICE_UI (s))
897 gplp_load_service_ui (l, s, err);
898 else
899 return FALSE;
900 return TRUE;
901 }
902
903 static gboolean
gplp_service_unload(GOPluginLoader * l,GOPluginService * s,GOErrorInfo ** err)904 gplp_service_unload (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
905 {
906 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s))
907 gplp_unload_service_function_group (l, s, err);
908 else if (GNM_IS_PLUGIN_SERVICE_UI (s))
909 ;
910 else
911 return FALSE;
912 return TRUE;
913 }
914
915 static void
gplp_finalize(GObject * obj)916 gplp_finalize (GObject *obj)
917 {
918 GnmPythonPluginLoader *loader_python = GNM_PYTHON_PLUGIN_LOADER (obj);
919
920 g_free (loader_python->module_name);
921 loader_python->module_name = NULL;
922
923 G_OBJECT_CLASS (g_type_class_peek (G_TYPE_OBJECT))->finalize (obj);
924 }
925
926 static void
gplp_init(GnmPythonPluginLoader * loader_python)927 gplp_init (GnmPythonPluginLoader *loader_python)
928 {
929 g_return_if_fail (GNM_IS_PYTHON_PLUGIN_LOADER (loader_python));
930
931 loader_python->module_name = NULL;
932 loader_python->py_object = NULL;
933 loader_python->py_interpreter_info = NULL;
934 }
935
936 static void
gplp_class_init(GObjectClass * gobject_class)937 gplp_class_init (GObjectClass *gobject_class)
938 {
939 gobject_class->finalize = gplp_finalize;
940 }
941 static void
go_plugin_loader_init(GOPluginLoaderClass * iface)942 go_plugin_loader_init (GOPluginLoaderClass *iface)
943 {
944 iface->set_attributes = gplp_set_attributes;
945 iface->load_base = gplp_load_base;
946 iface->unload_base = gplp_unload_base;
947 iface->load_service_file_opener = gplp_load_service_file_opener;
948 iface->load_service_file_saver = gplp_load_service_file_saver;
949
950 iface->service_load = gplp_service_load;
951 iface->service_unload = gplp_service_unload;
952 }
953
954 GSF_DYNAMIC_CLASS_FULL (GnmPythonPluginLoader, gnm_python_plugin_loader,
955 NULL, NULL, gplp_class_init, NULL,
956 gplp_init, G_TYPE_OBJECT, 0,
957 GSF_INTERFACE_FULL (gnm_python_plugin_loader_type, go_plugin_loader_init, GO_TYPE_PLUGIN_LOADER))
958