2 * python-plugin-provider.c
4 * Babeltrace Python plugin provider
6 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 #define BT_LOG_TAG "LIB/PLUGIN-PY"
28 #include "lib/logging.h"
30 #include "python-plugin-provider.h"
32 #include "common/macros.h"
33 #include "compat/compiler.h"
34 #include <babeltrace2/plugin/plugin-const.h>
35 #include "lib/plugin/plugin.h"
36 #include <babeltrace2/graph/component-class.h>
37 #include <babeltrace2/current-thread.h>
38 #include "lib/graph/component-class.h"
39 #include "py-common/py-common.h"
46 #define PYTHON_PLUGIN_FILE_PREFIX "bt_plugin_"
47 #define PYTHON_PLUGIN_FILE_PREFIX_LEN (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1)
48 #define PYTHON_PLUGIN_FILE_EXT ".py"
49 #define PYTHON_PLUGIN_FILE_EXT_LEN (sizeof(PYTHON_PLUGIN_FILE_EXT) - 1)
52 /* init_python() not called yet */
53 PYTHON_STATE_NOT_INITED
,
55 /* init_python() called once with success */
56 PYTHON_STATE_FULLY_INITIALIZED
,
58 /* init_python() called once without success */
59 PYTHON_STATE_CANNOT_INITIALIZE
,
62 * init_python() called, but environment variable asks the
63 * Python interpreter not to be loaded.
65 PYTHON_STATE_WONT_INITIALIZE
,
66 } python_state
= PYTHON_STATE_NOT_INITED
;
68 static PyObject
*py_try_load_plugin_module_func
= NULL
;
69 static bool python_was_initialized_by_us
;
72 void append_python_traceback_error_cause(void)
76 if (Py_IsInitialized() && PyErr_Occurred()) {
77 exc
= bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL
);
79 BT_LOGE_STR("Failed to format Python exception.");
83 (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
84 "Babeltrace library", "%s", exc
->str
);
89 g_string_free(exc
, TRUE
);
94 void log_python_traceback(int log_level
)
98 if (Py_IsInitialized() && PyErr_Occurred()) {
99 exc
= bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL
);
101 BT_LOGE_STR("Failed to format Python exception.");
105 BT_LOG_WRITE(log_level
, BT_LOG_TAG
,
106 "Exception occured: Python traceback:\n%s", exc
->str
);
111 g_string_free(exc
, TRUE
);
116 void pyerr_clear(void)
118 if (Py_IsInitialized()) {
124 int init_python(void)
126 int ret
= BT_FUNC_STATUS_OK
;
127 PyObject
*py_bt2_py_plugin_mod
= NULL
;
128 const char *dis_python_env
;
130 sig_t old_sigint
= signal(SIGINT
, SIG_DFL
);
133 switch (python_state
) {
134 case PYTHON_STATE_NOT_INITED
:
136 case PYTHON_STATE_FULLY_INITIALIZED
:
138 case PYTHON_STATE_WONT_INITIALIZE
:
139 ret
= BT_FUNC_STATUS_NOT_FOUND
;
141 case PYTHON_STATE_CANNOT_INITIALIZE
:
142 ret
= BT_FUNC_STATUS_ERROR
;
149 * User can disable Python plugin support with the
150 * `LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` environment variable
153 dis_python_env
= getenv("LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS");
154 if (dis_python_env
&& strcmp(dis_python_env
, "1") == 0) {
155 BT_LOGI_STR("Python plugin support is disabled because the "
156 "`LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` environment "
157 "variable is set to `1`.");
158 python_state
= PYTHON_STATE_WONT_INITIALIZE
;
159 ret
= BT_FUNC_STATUS_NOT_FOUND
;
163 if (!Py_IsInitialized()) {
164 BT_LOGI_STR("Python interpreter is not initialized: initializing Python interpreter.");
166 python_was_initialized_by_us
= true;
167 BT_LOGI("Initialized Python interpreter: version=\"%s\"",
170 BT_LOGI("Python interpreter is already initialized: version=\"%s\"",
174 py_bt2_py_plugin_mod
= PyImport_ImportModule("bt2.py_plugin");
175 if (!py_bt2_py_plugin_mod
) {
176 append_python_traceback_error_cause();
177 BT_LIB_LOGW_APPEND_CAUSE(
178 "Cannot import `bt2.py_plugin` Python module: "
179 "Python plugin support is disabled.");
180 python_state
= PYTHON_STATE_CANNOT_INITIALIZE
;
181 ret
= BT_FUNC_STATUS_ERROR
;
185 py_try_load_plugin_module_func
=
186 PyObject_GetAttrString(py_bt2_py_plugin_mod
, "_try_load_plugin_module");
187 if (!py_try_load_plugin_module_func
) {
188 append_python_traceback_error_cause();
189 BT_LIB_LOGW_APPEND_CAUSE(
190 "Cannot get `_try_load_plugin_module` attribute from `bt2.py_plugin` Python module: "
191 "Python plugin support is disabled.");
192 python_state
= PYTHON_STATE_CANNOT_INITIALIZE
;
193 ret
= BT_FUNC_STATUS_ERROR
;
197 python_state
= PYTHON_STATE_FULLY_INITIALIZED
;
201 if (old_sigint
!= SIG_ERR
) {
202 (void) signal(SIGINT
, old_sigint
);
206 log_python_traceback(ret
== BT_FUNC_STATUS_ERROR
?
207 BT_LOG_WARNING
: BT_LOG_INFO
);
209 Py_XDECREF(py_bt2_py_plugin_mod
);
213 __attribute__((destructor
)) static
214 void fini_python(void) {
215 if (Py_IsInitialized() && python_was_initialized_by_us
) {
216 if (py_try_load_plugin_module_func
) {
217 Py_DECREF(py_try_load_plugin_module_func
);
218 py_try_load_plugin_module_func
= NULL
;
222 BT_LOGI_STR("Finalized Python interpreter.");
225 python_state
= PYTHON_STATE_NOT_INITED
;
229 int bt_plugin_from_python_plugin_info(PyObject
*plugin_info
,
230 bool fail_on_load_error
, bt_plugin
**plugin_out
)
232 int status
= BT_FUNC_STATUS_OK
;
233 PyObject
*py_name
= NULL
;
234 PyObject
*py_author
= NULL
;
235 PyObject
*py_description
= NULL
;
236 PyObject
*py_license
= NULL
;
237 PyObject
*py_version
= NULL
;
238 PyObject
*py_comp_class_addrs
= NULL
;
239 const char *name
= NULL
;
240 const char *author
= NULL
;
241 const char *description
= NULL
;
242 const char *license
= NULL
;
243 unsigned int major
= 0, minor
= 0, patch
= 0;
244 const char *version_extra
= NULL
;
246 BT_ASSERT(plugin_out
);
248 BT_ASSERT(plugin_info
);
249 BT_ASSERT(python_state
== PYTHON_STATE_FULLY_INITIALIZED
);
250 py_name
= PyObject_GetAttrString(plugin_info
, "name");
252 if (fail_on_load_error
) {
253 append_python_traceback_error_cause();
254 BT_LIB_LOGW_APPEND_CAUSE(
255 "Cannot find `name` attribute in Python plugin info object: "
256 "py-plugin-info-addr=%p", plugin_info
);
257 status
= BT_FUNC_STATUS_ERROR
;
260 "Cannot find `name` attribute in Python plugin info object: "
261 "py-plugin-info-addr=%p", plugin_info
);
262 status
= BT_FUNC_STATUS_NOT_FOUND
;
268 py_author
= PyObject_GetAttrString(plugin_info
, "author");
270 if (fail_on_load_error
) {
271 append_python_traceback_error_cause();
272 BT_LIB_LOGW_APPEND_CAUSE(
273 "Cannot find `author` attribute in Python plugin info object: "
274 "py-plugin-info-addr=%p", plugin_info
);
275 status
= BT_FUNC_STATUS_ERROR
;
278 "Cannot find `author` attribute in Python plugin info object: "
279 "py-plugin-info-addr=%p", plugin_info
);
280 status
= BT_FUNC_STATUS_NOT_FOUND
;
286 py_description
= PyObject_GetAttrString(plugin_info
, "description");
287 if (!py_description
) {
288 if (fail_on_load_error
) {
289 append_python_traceback_error_cause();
290 BT_LIB_LOGW_APPEND_CAUSE(
291 "Cannot find `description` attribute in Python plugin info object: "
292 "py-plugin-info-addr=%p", plugin_info
);
293 status
= BT_FUNC_STATUS_ERROR
;
296 "Cannot find `description` attribute in Python plugin info object: "
297 "py-plugin-info-addr=%p", plugin_info
);
298 status
= BT_FUNC_STATUS_NOT_FOUND
;
304 py_license
= PyObject_GetAttrString(plugin_info
, "license");
306 if (fail_on_load_error
) {
307 append_python_traceback_error_cause();
308 BT_LIB_LOGW_APPEND_CAUSE(
309 "Cannot find `license` attribute in Python plugin info object: "
310 "py-plugin-info-addr=%p", plugin_info
);
311 status
= BT_FUNC_STATUS_ERROR
;
314 "Cannot find `license` attribute in Python plugin info object: "
315 "py-plugin-info-addr=%p", plugin_info
);
316 status
= BT_FUNC_STATUS_NOT_FOUND
;
322 py_version
= PyObject_GetAttrString(plugin_info
, "version");
324 if (fail_on_load_error
) {
325 append_python_traceback_error_cause();
326 BT_LIB_LOGW_APPEND_CAUSE(
327 "Cannot find `version` attribute in Python plugin info object: "
328 "py-plugin-info-addr=%p", plugin_info
);
329 status
= BT_FUNC_STATUS_ERROR
;
332 "Cannot find `version` attribute in Python plugin info object: "
333 "py-plugin-info-addr=%p", plugin_info
);
334 status
= BT_FUNC_STATUS_NOT_FOUND
;
340 py_comp_class_addrs
= PyObject_GetAttrString(plugin_info
,
342 if (!py_comp_class_addrs
) {
343 if (fail_on_load_error
) {
344 append_python_traceback_error_cause();
345 BT_LIB_LOGW_APPEND_CAUSE(
346 "Cannot find `comp_class_addrs` attribute in Python plugin info object: "
347 "py-plugin-info-addr=%p", plugin_info
);
348 status
= BT_FUNC_STATUS_ERROR
;
351 "Cannot find `comp_class_addrs` attribute in Python plugin info object: "
352 "py-plugin-info-addr=%p", plugin_info
);
353 status
= BT_FUNC_STATUS_NOT_FOUND
;
359 if (PyUnicode_Check(py_name
)) {
360 name
= PyUnicode_AsUTF8(py_name
);
362 if (fail_on_load_error
) {
363 append_python_traceback_error_cause();
364 BT_LIB_LOGW_APPEND_CAUSE(
365 "Cannot decode Python plugin name string: "
366 "py-plugin-info-addr=%p", plugin_info
);
367 status
= BT_FUNC_STATUS_ERROR
;
370 "Cannot decode Python plugin name string: "
371 "py-plugin-info-addr=%p", plugin_info
);
372 status
= BT_FUNC_STATUS_NOT_FOUND
;
378 /* Plugin name is mandatory */
379 if (fail_on_load_error
) {
380 append_python_traceback_error_cause();
381 BT_LIB_LOGW_APPEND_CAUSE(
382 "Plugin name is not a string: "
383 "py-plugin-info-addr=%p", plugin_info
);
384 status
= BT_FUNC_STATUS_ERROR
;
387 "Plugin name is not a string: "
388 "py-plugin-info-addr=%p", plugin_info
);
389 status
= BT_FUNC_STATUS_NOT_FOUND
;
395 if (PyUnicode_Check(py_author
)) {
396 author
= PyUnicode_AsUTF8(py_author
);
398 if (fail_on_load_error
) {
399 append_python_traceback_error_cause();
400 BT_LIB_LOGW_APPEND_CAUSE(
401 "Cannot decode Python plugin author string: "
402 "py-plugin-info-addr=%p", plugin_info
);
403 status
= BT_FUNC_STATUS_ERROR
;
406 "Cannot decode Python plugin author string: "
407 "py-plugin-info-addr=%p", plugin_info
);
408 status
= BT_FUNC_STATUS_NOT_FOUND
;
415 if (PyUnicode_Check(py_description
)) {
416 description
= PyUnicode_AsUTF8(py_description
);
418 if (fail_on_load_error
) {
419 append_python_traceback_error_cause();
420 BT_LIB_LOGW_APPEND_CAUSE(
421 "Cannot decode Python plugin description string: "
422 "py-plugin-info-addr=%p", plugin_info
);
423 status
= BT_FUNC_STATUS_ERROR
;
426 "Cannot decode Python plugin description string: "
427 "py-plugin-info-addr=%p", plugin_info
);
428 status
= BT_FUNC_STATUS_NOT_FOUND
;
435 if (PyUnicode_Check(py_license
)) {
436 license
= PyUnicode_AsUTF8(py_license
);
438 if (fail_on_load_error
) {
439 append_python_traceback_error_cause();
440 BT_LIB_LOGW_APPEND_CAUSE(
441 "Cannot decode Python plugin license string: "
442 "py-plugin-info-addr=%p", plugin_info
);
443 status
= BT_FUNC_STATUS_ERROR
;
446 "Cannot decode Python plugin license string: "
447 "py-plugin-info-addr=%p", plugin_info
);
448 status
= BT_FUNC_STATUS_NOT_FOUND
;
455 if (PyTuple_Check(py_version
)) {
456 if (PyTuple_Size(py_version
) >= 3) {
457 PyObject
*py_major
= PyTuple_GetItem(py_version
, 0);
458 PyObject
*py_minor
= PyTuple_GetItem(py_version
, 1);
459 PyObject
*py_patch
= PyTuple_GetItem(py_version
, 2);
465 if (PyLong_Check(py_major
)) {
466 major
= PyLong_AsUnsignedLong(py_major
);
469 if (PyLong_Check(py_minor
)) {
470 minor
= PyLong_AsUnsignedLong(py_minor
);
473 if (PyLong_Check(py_patch
)) {
474 patch
= PyLong_AsUnsignedLong(py_patch
);
477 if (PyErr_Occurred()) {
478 /* Overflow error, most probably */
479 if (fail_on_load_error
) {
480 append_python_traceback_error_cause();
481 BT_LIB_LOGW_APPEND_CAUSE(
482 "Invalid Python plugin version format: "
483 "py-plugin-info-addr=%p", plugin_info
);
484 status
= BT_FUNC_STATUS_ERROR
;
487 "Invalid Python plugin version format: "
488 "py-plugin-info-addr=%p", plugin_info
);
489 status
= BT_FUNC_STATUS_NOT_FOUND
;
496 if (PyTuple_Size(py_version
) >= 4) {
497 PyObject
*py_extra
= PyTuple_GetItem(py_version
, 3);
501 if (PyUnicode_Check(py_extra
)) {
502 version_extra
= PyUnicode_AsUTF8(py_extra
);
503 if (!version_extra
) {
504 if (fail_on_load_error
) {
505 append_python_traceback_error_cause();
506 BT_LIB_LOGW_APPEND_CAUSE(
507 "Cannot decode Python plugin version's extra string: "
508 "py-plugin-info-addr=%p", plugin_info
);
509 status
= BT_FUNC_STATUS_ERROR
;
512 "Cannot decode Python plugin version's extra string: "
513 "py-plugin-info-addr=%p", plugin_info
);
514 status
= BT_FUNC_STATUS_NOT_FOUND
;
523 *plugin_out
= bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON
);
525 BT_LIB_LOGE_APPEND_CAUSE("Cannot create empty plugin object.");
526 status
= BT_FUNC_STATUS_MEMORY_ERROR
;
530 bt_plugin_set_name(*plugin_out
, name
);
533 bt_plugin_set_description(*plugin_out
, description
);
537 bt_plugin_set_author(*plugin_out
, author
);
541 bt_plugin_set_license(*plugin_out
, license
);
544 bt_plugin_set_version(*plugin_out
, major
, minor
, patch
, version_extra
);
546 if (PyList_Check(py_comp_class_addrs
)) {
549 for (i
= 0; i
< PyList_Size(py_comp_class_addrs
); i
++) {
550 bt_component_class
*comp_class
;
551 PyObject
*py_comp_class_addr
;
554 PyList_GetItem(py_comp_class_addrs
, i
);
555 BT_ASSERT(py_comp_class_addr
);
556 if (PyLong_Check(py_comp_class_addr
)) {
557 comp_class
= PyLong_AsVoidPtr(py_comp_class_addr
);
559 if (fail_on_load_error
) {
560 append_python_traceback_error_cause();
561 BT_LIB_LOGW_APPEND_CAUSE(
562 "Component class address is not an integer in Python plugin info object: "
563 "py-plugin-info-addr=%p, index=%zu",
565 status
= BT_FUNC_STATUS_ERROR
;
568 "Component class address is not an integer in Python plugin info object: "
569 "py-plugin-info-addr=%p, index=%zu",
571 status
= BT_FUNC_STATUS_NOT_FOUND
;
577 status
= bt_plugin_add_component_class(*plugin_out
,
580 BT_LIB_LOGE_APPEND_CAUSE(
581 "Cannot add component class to plugin: "
582 "py-plugin-info-addr=%p, "
583 "plugin-addr=%p, plugin-name=\"%s\", "
584 "comp-class-addr=%p, "
585 "comp-class-name=\"%s\", "
586 "comp-class-type=%s",
587 plugin_info
, *plugin_out
,
588 bt_plugin_get_name(*plugin_out
),
590 bt_component_class_get_name(comp_class
),
591 bt_component_class_type_string(
592 bt_component_class_get_type(comp_class
)));
601 BT_ASSERT(status
!= BT_FUNC_STATUS_OK
);
602 log_python_traceback(fail_on_load_error
? BT_LOG_WARNING
: BT_LOG_INFO
);
604 BT_OBJECT_PUT_REF_AND_RESET(*plugin_out
);
608 Py_XDECREF(py_author
);
609 Py_XDECREF(py_description
);
610 Py_XDECREF(py_license
);
611 Py_XDECREF(py_version
);
612 Py_XDECREF(py_comp_class_addrs
);
617 int bt_plugin_python_create_all_from_file(const char *path
,
618 bool fail_on_load_error
, struct bt_plugin_set
**plugin_set_out
)
620 bt_plugin
*plugin
= NULL
;
621 PyObject
*py_plugin_info
= NULL
;
622 gchar
*basename
= NULL
;
624 int status
= BT_FUNC_STATUS_OK
;
628 if (python_state
== PYTHON_STATE_CANNOT_INITIALIZE
) {
630 * We do not even care about the rest of the function
631 * here because we already know Python cannot be fully
634 BT_LIB_LOGE_APPEND_CAUSE(
635 "Python interpreter could not be initialized previously.");
636 status
= BT_FUNC_STATUS_ERROR
;
638 } else if (python_state
== PYTHON_STATE_WONT_INITIALIZE
) {
640 * This is not an error: the environment requires that
641 * Python plugins are disabled, so it's simply not
644 BT_LOGI_STR("Python plugin support was disabled previously "
645 "because the `LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` "
646 "environment variable is set to `1`.");
647 status
= BT_FUNC_STATUS_NOT_FOUND
;
651 BT_LOGI("Trying to create all Python plugins from file: path=\"%s\"",
653 path_len
= strlen(path
);
655 /* File name ends with `.py` */
656 if (strncmp(path
+ path_len
- PYTHON_PLUGIN_FILE_EXT_LEN
,
657 PYTHON_PLUGIN_FILE_EXT
,
658 PYTHON_PLUGIN_FILE_EXT_LEN
) != 0) {
659 BT_LOGI("Skipping non-Python file: path=\"%s\"", path
);
660 status
= BT_FUNC_STATUS_NOT_FOUND
;
664 /* File name starts with `bt_plugin_` */
665 basename
= g_path_get_basename(path
);
667 BT_LIB_LOGE_APPEND_CAUSE(
668 "Cannot get path's basename: path=\"%s\"", path
);
669 status
= BT_FUNC_STATUS_ERROR
;
673 if (strncmp(basename
, PYTHON_PLUGIN_FILE_PREFIX
,
674 PYTHON_PLUGIN_FILE_PREFIX_LEN
) != 0) {
675 BT_LOGI("Skipping Python file not starting with `%s`: "
676 "path=\"%s\"", PYTHON_PLUGIN_FILE_PREFIX
, path
);
677 status
= BT_FUNC_STATUS_NOT_FOUND
;
682 * Initialize Python now.
684 * This is not done in the library contructor because the
685 * interpreter is somewhat slow to initialize. If you don't
686 * have any potential Python plugins, you don't need to endure
687 * this waiting time everytime you load the library.
689 status
= init_python();
690 if (status
!= BT_FUNC_STATUS_OK
) {
691 /* init_python() logs and append errors */
696 * Call bt2.py_plugin._try_load_plugin_module() with this path
697 * to get plugin info if the plugin is loadable and complete.
698 * This function returns None when there's an error, but just in
699 * case we also manually clear the last Python error state.
701 BT_LOGD_STR("Getting Python plugin info object from Python module.");
702 py_plugin_info
= PyObject_CallFunction(py_try_load_plugin_module_func
,
704 if (!py_plugin_info
|| py_plugin_info
== Py_None
) {
705 if (fail_on_load_error
) {
706 append_python_traceback_error_cause();
707 BT_LIB_LOGW_APPEND_CAUSE(
708 "Cannot load Python plugin: path=\"%s\"", path
);
709 status
= BT_FUNC_STATUS_ERROR
;
712 "Cannot load Python plugin: path=\"%s\"", path
);
713 status
= BT_FUNC_STATUS_NOT_FOUND
;
720 * Get bt_plugin from plugin info object.
723 status
= bt_plugin_from_python_plugin_info(py_plugin_info
,
724 fail_on_load_error
, &plugin
);
727 * bt_plugin_from_python_plugin_info() handles
728 * `fail_on_load_error`, so this is a "real" error.
730 BT_LIB_LOGW_APPEND_CAUSE(
731 "Cannot create plugin object from Python plugin info object: "
732 "path=\"%s\", py-plugin-info-addr=%p",
733 path
, py_plugin_info
);
736 } else if (status
== BT_FUNC_STATUS_NOT_FOUND
) {
741 BT_ASSERT(status
== BT_FUNC_STATUS_OK
);
743 bt_plugin_set_path(plugin
, path
);
744 *plugin_set_out
= bt_plugin_set_create();
745 if (!*plugin_set_out
) {
746 BT_LIB_LOGE_APPEND_CAUSE("Cannot create empty plugin set.");
747 status
= BT_FUNC_STATUS_MEMORY_ERROR
;
751 bt_plugin_set_add_plugin(*plugin_set_out
, plugin
);
752 BT_LOGD("Created all Python plugins from file: path=\"%s\", "
753 "plugin-addr=%p, plugin-name=\"%s\"",
754 path
, plugin
, bt_plugin_get_name(plugin
));
758 BT_ASSERT(status
!= BT_FUNC_STATUS_OK
);
759 log_python_traceback(BT_LOG_WARNING
);
761 BT_OBJECT_PUT_REF_AND_RESET(*plugin_set_out
);
764 bt_plugin_put_ref(plugin
);
765 Py_XDECREF(py_plugin_info
);