78026d6a1fd87e6ddb6883c0ff61fae4eecc964b
[babeltrace.git] / src / python-plugin-provider / python-plugin-provider.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
5 *
6 * Babeltrace Python plugin provider
7 */
8
9 #define BT_LOG_TAG "LIB/PLUGIN-PY"
10 #include "lib/logging.h"
11
12 #include "python-plugin-provider.h"
13
14 #include "common/macros.h"
15 #include "compat/compiler.h"
16 #include <babeltrace2/plugin/plugin-loading.h>
17 #include "lib/plugin/plugin.h"
18 #include <babeltrace2/graph/component-class.h>
19 #include <babeltrace2/error-reporting.h>
20 #include "lib/graph/component-class.h"
21 #include "py-common/py-common.h"
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <signal.h>
25 #include <Python.h>
26 #include <glib.h>
27 #include <gmodule.h>
28
29 #define PYTHON_PLUGIN_FILE_PREFIX "bt_plugin_"
30 #define PYTHON_PLUGIN_FILE_PREFIX_LEN (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1)
31 #define PYTHON_PLUGIN_FILE_EXT ".py"
32 #define PYTHON_PLUGIN_FILE_EXT_LEN (sizeof(PYTHON_PLUGIN_FILE_EXT) - 1)
33
34 static enum python_state {
35 /* init_python() not called yet */
36 PYTHON_STATE_NOT_INITED,
37
38 /* init_python() called once with success */
39 PYTHON_STATE_FULLY_INITIALIZED,
40
41 /* init_python() called once without success */
42 PYTHON_STATE_CANNOT_INITIALIZE,
43
44 /*
45 * init_python() called, but environment variable asks the
46 * Python interpreter not to be loaded.
47 */
48 PYTHON_STATE_WONT_INITIALIZE,
49 } python_state = PYTHON_STATE_NOT_INITED;
50
51 static PyObject *py_try_load_plugin_module_func = NULL;
52 static bool python_was_initialized_by_us;
53
54 static
55 void append_python_traceback_error_cause(void)
56 {
57 GString *exc = NULL;
58
59 if (Py_IsInitialized() && PyErr_Occurred()) {
60 exc = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL);
61 if (!exc) {
62 BT_LOGE_STR("Failed to format Python exception.");
63 goto end;
64 }
65
66 (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
67 BT_LIB_LOG_LIBBABELTRACE2_NAME, "%s", exc->str);
68 }
69
70 end:
71 if (exc) {
72 g_string_free(exc, TRUE);
73 }
74 }
75
76 static
77 void log_python_traceback(int log_level)
78 {
79 GString *exc = NULL;
80
81 if (Py_IsInitialized() && PyErr_Occurred()) {
82 exc = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL);
83 if (!exc) {
84 BT_LOGE_STR("Failed to format Python exception.");
85 goto end;
86 }
87
88 BT_LOG_WRITE(log_level, BT_LOG_TAG,
89 "Exception occurred: Python traceback:\n%s", exc->str);
90 }
91
92 end:
93 if (exc) {
94 g_string_free(exc, TRUE);
95 }
96 }
97
98 static
99 void pyerr_clear(void)
100 {
101 if (Py_IsInitialized()) {
102 PyErr_Clear();
103 }
104 }
105
106 static
107 int init_python(void)
108 {
109 int ret = BT_FUNC_STATUS_OK;
110 PyObject *py_bt2_py_plugin_mod = NULL;
111 const char *dis_python_env;
112 #ifndef __MINGW32__
113 sig_t old_sigint = signal(SIGINT, SIG_DFL);
114 #endif
115
116 switch (python_state) {
117 case PYTHON_STATE_NOT_INITED:
118 break;
119 case PYTHON_STATE_FULLY_INITIALIZED:
120 goto end;
121 case PYTHON_STATE_WONT_INITIALIZE:
122 ret = BT_FUNC_STATUS_NOT_FOUND;
123 goto end;
124 case PYTHON_STATE_CANNOT_INITIALIZE:
125 ret = BT_FUNC_STATUS_ERROR;
126 goto end;
127 default:
128 bt_common_abort();
129 }
130
131 /*
132 * User can disable Python plugin support with the
133 * `LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` environment variable
134 * set to 1.
135 */
136 dis_python_env = getenv("LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS");
137 if (dis_python_env && strcmp(dis_python_env, "1") == 0) {
138 BT_LOGI_STR("Python plugin support is disabled because the "
139 "`LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` environment "
140 "variable is set to `1`.");
141 python_state = PYTHON_STATE_WONT_INITIALIZE;
142 ret = BT_FUNC_STATUS_NOT_FOUND;
143 goto end;
144 }
145
146 if (!Py_IsInitialized()) {
147 BT_LOGI_STR("Python interpreter is not initialized: initializing Python interpreter.");
148 Py_InitializeEx(0);
149 python_was_initialized_by_us = true;
150 BT_LOGI("Initialized Python interpreter: version=\"%s\"",
151 Py_GetVersion());
152 } else {
153 BT_LOGI("Python interpreter is already initialized: version=\"%s\"",
154 Py_GetVersion());
155 }
156
157 py_bt2_py_plugin_mod = PyImport_ImportModule("bt2.py_plugin");
158 if (!py_bt2_py_plugin_mod) {
159 append_python_traceback_error_cause();
160 BT_LIB_LOGW_APPEND_CAUSE(
161 "Cannot import `bt2.py_plugin` Python module: "
162 "Python plugin support is disabled.");
163 python_state = PYTHON_STATE_CANNOT_INITIALIZE;
164 ret = BT_FUNC_STATUS_ERROR;
165 goto end;
166 }
167
168 py_try_load_plugin_module_func =
169 PyObject_GetAttrString(py_bt2_py_plugin_mod, "_try_load_plugin_module");
170 if (!py_try_load_plugin_module_func) {
171 append_python_traceback_error_cause();
172 BT_LIB_LOGW_APPEND_CAUSE(
173 "Cannot get `_try_load_plugin_module` attribute from `bt2.py_plugin` Python module: "
174 "Python plugin support is disabled.");
175 python_state = PYTHON_STATE_CANNOT_INITIALIZE;
176 ret = BT_FUNC_STATUS_ERROR;
177 goto end;
178 }
179
180 python_state = PYTHON_STATE_FULLY_INITIALIZED;
181
182 end:
183 #ifndef __MINGW32__
184 if (old_sigint != SIG_ERR) {
185 (void) signal(SIGINT, old_sigint);
186 }
187 #endif
188
189 log_python_traceback(ret == BT_FUNC_STATUS_ERROR ?
190 BT_LOG_WARNING : BT_LOG_INFO);
191 pyerr_clear();
192 Py_XDECREF(py_bt2_py_plugin_mod);
193 return ret;
194 }
195
196 __attribute__((destructor)) static
197 void fini_python(void) {
198 if (Py_IsInitialized() && python_was_initialized_by_us) {
199 if (py_try_load_plugin_module_func) {
200 Py_DECREF(py_try_load_plugin_module_func);
201 py_try_load_plugin_module_func = NULL;
202 }
203
204 Py_Finalize();
205 BT_LOGI_STR("Finalized Python interpreter.");
206 }
207
208 python_state = PYTHON_STATE_NOT_INITED;
209 }
210
211 static
212 int bt_plugin_from_python_plugin_info(PyObject *plugin_info,
213 bool fail_on_load_error, bt_plugin **plugin_out)
214 {
215 int status = BT_FUNC_STATUS_OK;
216 PyObject *py_name = NULL;
217 PyObject *py_author = NULL;
218 PyObject *py_description = NULL;
219 PyObject *py_license = NULL;
220 PyObject *py_version = NULL;
221 PyObject *py_comp_class_addrs = NULL;
222 const char *name = NULL;
223 const char *author = NULL;
224 const char *description = NULL;
225 const char *license = NULL;
226 unsigned int major = 0, minor = 0, patch = 0;
227 const char *version_extra = NULL;
228
229 BT_ASSERT(plugin_out);
230 *plugin_out = NULL;
231 BT_ASSERT(plugin_info);
232 BT_ASSERT(python_state == PYTHON_STATE_FULLY_INITIALIZED);
233 py_name = PyObject_GetAttrString(plugin_info, "name");
234 if (!py_name) {
235 if (fail_on_load_error) {
236 append_python_traceback_error_cause();
237 BT_LIB_LOGW_APPEND_CAUSE(
238 "Cannot find `name` attribute in Python plugin info object: "
239 "py-plugin-info-addr=%p", plugin_info);
240 status = BT_FUNC_STATUS_ERROR;
241 } else {
242 BT_LIB_LOGW(
243 "Cannot find `name` attribute in Python plugin info object: "
244 "py-plugin-info-addr=%p", plugin_info);
245 status = BT_FUNC_STATUS_NOT_FOUND;
246 }
247
248 goto error;
249 }
250
251 py_author = PyObject_GetAttrString(plugin_info, "author");
252 if (!py_author) {
253 if (fail_on_load_error) {
254 append_python_traceback_error_cause();
255 BT_LIB_LOGW_APPEND_CAUSE(
256 "Cannot find `author` attribute in Python plugin info object: "
257 "py-plugin-info-addr=%p", plugin_info);
258 status = BT_FUNC_STATUS_ERROR;
259 } else {
260 BT_LIB_LOGW(
261 "Cannot find `author` attribute in Python plugin info object: "
262 "py-plugin-info-addr=%p", plugin_info);
263 status = BT_FUNC_STATUS_NOT_FOUND;
264 }
265
266 goto error;
267 }
268
269 py_description = PyObject_GetAttrString(plugin_info, "description");
270 if (!py_description) {
271 if (fail_on_load_error) {
272 append_python_traceback_error_cause();
273 BT_LIB_LOGW_APPEND_CAUSE(
274 "Cannot find `description` attribute in Python plugin info object: "
275 "py-plugin-info-addr=%p", plugin_info);
276 status = BT_FUNC_STATUS_ERROR;
277 } else {
278 BT_LIB_LOGW(
279 "Cannot find `description` attribute in Python plugin info object: "
280 "py-plugin-info-addr=%p", plugin_info);
281 status = BT_FUNC_STATUS_NOT_FOUND;
282 }
283
284 goto error;
285 }
286
287 py_license = PyObject_GetAttrString(plugin_info, "license");
288 if (!py_license) {
289 if (fail_on_load_error) {
290 append_python_traceback_error_cause();
291 BT_LIB_LOGW_APPEND_CAUSE(
292 "Cannot find `license` attribute in Python plugin info object: "
293 "py-plugin-info-addr=%p", plugin_info);
294 status = BT_FUNC_STATUS_ERROR;
295 } else {
296 BT_LIB_LOGW(
297 "Cannot find `license` attribute in Python plugin info object: "
298 "py-plugin-info-addr=%p", plugin_info);
299 status = BT_FUNC_STATUS_NOT_FOUND;
300 }
301
302 goto error;
303 }
304
305 py_version = PyObject_GetAttrString(plugin_info, "version");
306 if (!py_version) {
307 if (fail_on_load_error) {
308 append_python_traceback_error_cause();
309 BT_LIB_LOGW_APPEND_CAUSE(
310 "Cannot find `version` attribute in Python plugin info object: "
311 "py-plugin-info-addr=%p", plugin_info);
312 status = BT_FUNC_STATUS_ERROR;
313 } else {
314 BT_LIB_LOGW(
315 "Cannot find `version` attribute in Python plugin info object: "
316 "py-plugin-info-addr=%p", plugin_info);
317 status = BT_FUNC_STATUS_NOT_FOUND;
318 }
319
320 goto error;
321 }
322
323 py_comp_class_addrs = PyObject_GetAttrString(plugin_info,
324 "comp_class_addrs");
325 if (!py_comp_class_addrs) {
326 if (fail_on_load_error) {
327 append_python_traceback_error_cause();
328 BT_LIB_LOGW_APPEND_CAUSE(
329 "Cannot find `comp_class_addrs` attribute in Python plugin info object: "
330 "py-plugin-info-addr=%p", plugin_info);
331 status = BT_FUNC_STATUS_ERROR;
332 } else {
333 BT_LIB_LOGW(
334 "Cannot find `comp_class_addrs` attribute in Python plugin info object: "
335 "py-plugin-info-addr=%p", plugin_info);
336 status = BT_FUNC_STATUS_NOT_FOUND;
337 }
338
339 goto error;
340 }
341
342 if (PyUnicode_Check(py_name)) {
343 name = PyUnicode_AsUTF8(py_name);
344 if (!name) {
345 if (fail_on_load_error) {
346 append_python_traceback_error_cause();
347 BT_LIB_LOGW_APPEND_CAUSE(
348 "Cannot decode Python plugin name string: "
349 "py-plugin-info-addr=%p", plugin_info);
350 status = BT_FUNC_STATUS_ERROR;
351 } else {
352 BT_LIB_LOGW(
353 "Cannot decode Python plugin name string: "
354 "py-plugin-info-addr=%p", plugin_info);
355 status = BT_FUNC_STATUS_NOT_FOUND;
356 }
357
358 goto error;
359 }
360 } else {
361 /* Plugin name is mandatory */
362 if (fail_on_load_error) {
363 append_python_traceback_error_cause();
364 BT_LIB_LOGW_APPEND_CAUSE(
365 "Plugin name is not a string: "
366 "py-plugin-info-addr=%p", plugin_info);
367 status = BT_FUNC_STATUS_ERROR;
368 } else {
369 BT_LIB_LOGW(
370 "Plugin name is not a string: "
371 "py-plugin-info-addr=%p", plugin_info);
372 status = BT_FUNC_STATUS_NOT_FOUND;
373 }
374
375 goto error;
376 }
377
378 if (PyUnicode_Check(py_author)) {
379 author = PyUnicode_AsUTF8(py_author);
380 if (!author) {
381 if (fail_on_load_error) {
382 append_python_traceback_error_cause();
383 BT_LIB_LOGW_APPEND_CAUSE(
384 "Cannot decode Python plugin author string: "
385 "py-plugin-info-addr=%p", plugin_info);
386 status = BT_FUNC_STATUS_ERROR;
387 } else {
388 BT_LIB_LOGW(
389 "Cannot decode Python plugin author string: "
390 "py-plugin-info-addr=%p", plugin_info);
391 status = BT_FUNC_STATUS_NOT_FOUND;
392 }
393
394 goto error;
395 }
396 }
397
398 if (PyUnicode_Check(py_description)) {
399 description = PyUnicode_AsUTF8(py_description);
400 if (!description) {
401 if (fail_on_load_error) {
402 append_python_traceback_error_cause();
403 BT_LIB_LOGW_APPEND_CAUSE(
404 "Cannot decode Python plugin description string: "
405 "py-plugin-info-addr=%p", plugin_info);
406 status = BT_FUNC_STATUS_ERROR;
407 } else {
408 BT_LIB_LOGW(
409 "Cannot decode Python plugin description string: "
410 "py-plugin-info-addr=%p", plugin_info);
411 status = BT_FUNC_STATUS_NOT_FOUND;
412 }
413
414 goto error;
415 }
416 }
417
418 if (PyUnicode_Check(py_license)) {
419 license = PyUnicode_AsUTF8(py_license);
420 if (!license) {
421 if (fail_on_load_error) {
422 append_python_traceback_error_cause();
423 BT_LIB_LOGW_APPEND_CAUSE(
424 "Cannot decode Python plugin license string: "
425 "py-plugin-info-addr=%p", plugin_info);
426 status = BT_FUNC_STATUS_ERROR;
427 } else {
428 BT_LIB_LOGW(
429 "Cannot decode Python plugin license string: "
430 "py-plugin-info-addr=%p", plugin_info);
431 status = BT_FUNC_STATUS_NOT_FOUND;
432 }
433
434 goto error;
435 }
436 }
437
438 if (PyTuple_Check(py_version)) {
439 if (PyTuple_Size(py_version) >= 3) {
440 PyObject *py_major = PyTuple_GetItem(py_version, 0);
441 PyObject *py_minor = PyTuple_GetItem(py_version, 1);
442 PyObject *py_patch = PyTuple_GetItem(py_version, 2);
443
444 BT_ASSERT(py_major);
445 BT_ASSERT(py_minor);
446 BT_ASSERT(py_patch);
447
448 if (PyLong_Check(py_major)) {
449 major = PyLong_AsUnsignedLong(py_major);
450 }
451
452 if (PyLong_Check(py_minor)) {
453 minor = PyLong_AsUnsignedLong(py_minor);
454 }
455
456 if (PyLong_Check(py_patch)) {
457 patch = PyLong_AsUnsignedLong(py_patch);
458 }
459
460 if (PyErr_Occurred()) {
461 /* Overflow error, most probably */
462 if (fail_on_load_error) {
463 append_python_traceback_error_cause();
464 BT_LIB_LOGW_APPEND_CAUSE(
465 "Invalid Python plugin version format: "
466 "py-plugin-info-addr=%p", plugin_info);
467 status = BT_FUNC_STATUS_ERROR;
468 } else {
469 BT_LIB_LOGW(
470 "Invalid Python plugin version format: "
471 "py-plugin-info-addr=%p", plugin_info);
472 status = BT_FUNC_STATUS_NOT_FOUND;
473 }
474
475 goto error;
476 }
477 }
478
479 if (PyTuple_Size(py_version) >= 4) {
480 PyObject *py_extra = PyTuple_GetItem(py_version, 3);
481
482 BT_ASSERT(py_extra);
483
484 if (PyUnicode_Check(py_extra)) {
485 version_extra = PyUnicode_AsUTF8(py_extra);
486 if (!version_extra) {
487 if (fail_on_load_error) {
488 append_python_traceback_error_cause();
489 BT_LIB_LOGW_APPEND_CAUSE(
490 "Cannot decode Python plugin version's extra string: "
491 "py-plugin-info-addr=%p", plugin_info);
492 status = BT_FUNC_STATUS_ERROR;
493 } else {
494 BT_LIB_LOGW(
495 "Cannot decode Python plugin version's extra string: "
496 "py-plugin-info-addr=%p", plugin_info);
497 status = BT_FUNC_STATUS_NOT_FOUND;
498 }
499
500 goto error;
501 }
502 }
503 }
504 }
505
506 *plugin_out = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON);
507 if (!*plugin_out) {
508 BT_LIB_LOGE_APPEND_CAUSE("Cannot create empty plugin object.");
509 status = BT_FUNC_STATUS_MEMORY_ERROR;
510 goto error;
511 }
512
513 bt_plugin_set_name(*plugin_out, name);
514
515 if (description) {
516 bt_plugin_set_description(*plugin_out, description);
517 }
518
519 if (author) {
520 bt_plugin_set_author(*plugin_out, author);
521 }
522
523 if (license) {
524 bt_plugin_set_license(*plugin_out, license);
525 }
526
527 bt_plugin_set_version(*plugin_out, major, minor, patch, version_extra);
528
529 if (PyList_Check(py_comp_class_addrs)) {
530 size_t i;
531
532 for (i = 0; i < PyList_Size(py_comp_class_addrs); i++) {
533 bt_component_class *comp_class;
534 PyObject *py_comp_class_addr;
535
536 py_comp_class_addr =
537 PyList_GetItem(py_comp_class_addrs, i);
538 BT_ASSERT(py_comp_class_addr);
539 if (PyLong_Check(py_comp_class_addr)) {
540 comp_class = PyLong_AsVoidPtr(py_comp_class_addr);
541 } else {
542 if (fail_on_load_error) {
543 append_python_traceback_error_cause();
544 BT_LIB_LOGW_APPEND_CAUSE(
545 "Component class address is not an integer in Python plugin info object: "
546 "py-plugin-info-addr=%p, index=%zu",
547 plugin_info, i);
548 status = BT_FUNC_STATUS_ERROR;
549 } else {
550 BT_LIB_LOGW(
551 "Component class address is not an integer in Python plugin info object: "
552 "py-plugin-info-addr=%p, index=%zu",
553 plugin_info, i);
554 status = BT_FUNC_STATUS_NOT_FOUND;
555 }
556
557 continue;
558 }
559
560 status = bt_plugin_add_component_class(*plugin_out,
561 comp_class);
562 if (status < 0) {
563 BT_LIB_LOGE_APPEND_CAUSE(
564 "Cannot add component class to plugin: "
565 "py-plugin-info-addr=%p, "
566 "plugin-addr=%p, plugin-name=\"%s\", "
567 "comp-class-addr=%p, "
568 "comp-class-name=\"%s\", "
569 "comp-class-type=%s",
570 plugin_info, *plugin_out,
571 bt_plugin_get_name(*plugin_out),
572 comp_class,
573 bt_component_class_get_name(comp_class),
574 bt_component_class_type_string(
575 bt_component_class_get_type(comp_class)));
576 goto error;
577 }
578 }
579 }
580
581 goto end;
582
583 error:
584 BT_ASSERT(status != BT_FUNC_STATUS_OK);
585 log_python_traceback(fail_on_load_error ? BT_LOG_WARNING : BT_LOG_INFO);
586 pyerr_clear();
587 BT_OBJECT_PUT_REF_AND_RESET(*plugin_out);
588
589 end:
590 Py_XDECREF(py_name);
591 Py_XDECREF(py_author);
592 Py_XDECREF(py_description);
593 Py_XDECREF(py_license);
594 Py_XDECREF(py_version);
595 Py_XDECREF(py_comp_class_addrs);
596 return status;
597 }
598
599 G_MODULE_EXPORT
600 int bt_plugin_python_create_all_from_file(const char *path,
601 bool fail_on_load_error, struct bt_plugin_set **plugin_set_out)
602 {
603 bt_plugin *plugin = NULL;
604 PyObject *py_plugin_info = NULL;
605 gchar *basename = NULL;
606 size_t path_len;
607 int status = BT_FUNC_STATUS_OK;
608
609 BT_ASSERT(path);
610
611 if (python_state == PYTHON_STATE_CANNOT_INITIALIZE) {
612 /*
613 * We do not even care about the rest of the function
614 * here because we already know Python cannot be fully
615 * initialized.
616 */
617 BT_LIB_LOGE_APPEND_CAUSE(
618 "Python interpreter could not be initialized previously.");
619 status = BT_FUNC_STATUS_ERROR;
620 goto error;
621 } else if (python_state == PYTHON_STATE_WONT_INITIALIZE) {
622 /*
623 * This is not an error: the environment requires that
624 * Python plugins are disabled, so it's simply not
625 * found.
626 */
627 BT_LOGI_STR("Python plugin support was disabled previously "
628 "because the `LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` "
629 "environment variable is set to `1`.");
630 status = BT_FUNC_STATUS_NOT_FOUND;
631 goto error;
632 }
633
634 BT_LOGI("Trying to create all Python plugins from file: path=\"%s\"",
635 path);
636 path_len = strlen(path);
637
638 /* File name ends with `.py` */
639 if (strncmp(path + path_len - PYTHON_PLUGIN_FILE_EXT_LEN,
640 PYTHON_PLUGIN_FILE_EXT,
641 PYTHON_PLUGIN_FILE_EXT_LEN) != 0) {
642 BT_LOGI("Skipping non-Python file: path=\"%s\"", path);
643 status = BT_FUNC_STATUS_NOT_FOUND;
644 goto error;
645 }
646
647 /* File name starts with `bt_plugin_` */
648 basename = g_path_get_basename(path);
649 if (!basename) {
650 BT_LIB_LOGE_APPEND_CAUSE(
651 "Cannot get path's basename: path=\"%s\"", path);
652 status = BT_FUNC_STATUS_ERROR;
653 goto error;
654 }
655
656 if (strncmp(basename, PYTHON_PLUGIN_FILE_PREFIX,
657 PYTHON_PLUGIN_FILE_PREFIX_LEN) != 0) {
658 BT_LOGI("Skipping Python file not starting with `%s`: "
659 "path=\"%s\"", PYTHON_PLUGIN_FILE_PREFIX, path);
660 status = BT_FUNC_STATUS_NOT_FOUND;
661 goto error;
662 }
663
664 /*
665 * Initialize Python now.
666 *
667 * This is not done in the library contructor because the
668 * interpreter is somewhat slow to initialize. If you don't
669 * have any potential Python plugins, you don't need to endure
670 * this waiting time everytime you load the library.
671 */
672 status = init_python();
673 if (status != BT_FUNC_STATUS_OK) {
674 /* init_python() logs and append errors */
675 goto error;
676 }
677
678 /*
679 * Call bt2.py_plugin._try_load_plugin_module() with this path
680 * to get plugin info if the plugin is loadable and complete.
681 * This function returns None when there's an error, but just in
682 * case we also manually clear the last Python error state.
683 */
684 BT_LOGD_STR("Getting Python plugin info object from Python module.");
685 py_plugin_info = PyObject_CallFunction(py_try_load_plugin_module_func,
686 "(s)", path);
687 if (!py_plugin_info || py_plugin_info == Py_None) {
688 if (fail_on_load_error) {
689 append_python_traceback_error_cause();
690 BT_LIB_LOGW_APPEND_CAUSE(
691 "Cannot load Python plugin: path=\"%s\"", path);
692 status = BT_FUNC_STATUS_ERROR;
693 } else {
694 BT_LIB_LOGW(
695 "Cannot load Python plugin: path=\"%s\"", path);
696 status = BT_FUNC_STATUS_NOT_FOUND;
697 }
698
699 goto error;
700 }
701
702 /*
703 * Get bt_plugin from plugin info object.
704 */
705 plugin = NULL;
706 status = bt_plugin_from_python_plugin_info(py_plugin_info,
707 fail_on_load_error, &plugin);
708 if (status < 0) {
709 /*
710 * bt_plugin_from_python_plugin_info() handles
711 * `fail_on_load_error`, so this is a "real" error.
712 */
713 BT_LIB_LOGW_APPEND_CAUSE(
714 "Cannot create plugin object from Python plugin info object: "
715 "path=\"%s\", py-plugin-info-addr=%p",
716 path, py_plugin_info);
717 BT_ASSERT(!plugin);
718 goto error;
719 } else if (status == BT_FUNC_STATUS_NOT_FOUND) {
720 BT_ASSERT(!plugin);
721 goto error;
722 }
723
724 BT_ASSERT(status == BT_FUNC_STATUS_OK);
725 BT_ASSERT(plugin);
726 bt_plugin_set_path(plugin, path);
727 *plugin_set_out = bt_plugin_set_create();
728 if (!*plugin_set_out) {
729 BT_LIB_LOGE_APPEND_CAUSE("Cannot create empty plugin set.");
730 status = BT_FUNC_STATUS_MEMORY_ERROR;
731 goto error;
732 }
733
734 bt_plugin_set_add_plugin(*plugin_set_out, plugin);
735 BT_LOGD("Created all Python plugins from file: path=\"%s\", "
736 "plugin-addr=%p, plugin-name=\"%s\"",
737 path, plugin, bt_plugin_get_name(plugin));
738 goto end;
739
740 error:
741 BT_ASSERT(status != BT_FUNC_STATUS_OK);
742 log_python_traceback(BT_LOG_WARNING);
743 pyerr_clear();
744 BT_OBJECT_PUT_REF_AND_RESET(*plugin_set_out);
745
746 end:
747 bt_plugin_put_ref(plugin);
748 Py_XDECREF(py_plugin_info);
749
750 g_free(basename);
751
752 return status;
753 }
This page took 0.045169 seconds and 4 git commands to generate.