Move to kernel style SPDX license identifiers
[babeltrace.git] / src / py-common / py-common.c
CommitLineData
7085eeaa 1/*
0235b0db
MJ
2 * SPDX-License-Identifier: MIT
3 *
7085eeaa
PP
4 * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
5 * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
6 * Copyright (c) 2019 Simon Marchi <simon.marchi@efficios.com>
7085eeaa
PP
7 */
8
9#define BT_LOG_OUTPUT_LEVEL log_level
10#define BT_LOG_TAG "PY-COMMON"
11#include "logging/log.h"
12
c4f23e30
FD
13#include <stdbool.h>
14
7085eeaa
PP
15#include <Python.h>
16
17#include "common/assert.h"
18#include "py-common.h"
19
332c7e11
SM
20/*
21 * Concatenate strings in list `py_str_list`, returning the result as a
22 * GString. Remove the trailing \n, if the last string ends with a \n.
23 */
24static
25GString *py_str_list_to_gstring(PyObject *py_str_list, int log_level)
26{
27 Py_ssize_t i;
28 GString *gstr;
29
30 gstr = g_string_new(NULL);
31 if (!gstr) {
32 BT_LOGE("Failed to allocate a GString.");
33 goto end;
34 }
35
36 for (i = 0; i < PyList_Size(py_str_list); i++) {
37 PyObject *py_str;
38 const char *str;
39
40 py_str = PyList_GetItem(py_str_list, i);
41 BT_ASSERT(py_str);
42 BT_ASSERT(PyUnicode_CheckExact(py_str));
43
44 /* `str` is owned by `py_str`, not by us */
45 str = PyUnicode_AsUTF8(py_str);
46 if (!str) {
47 if (BT_LOG_ON_ERROR) {
48 BT_LOGE_STR("PyUnicode_AsUTF8() failed:");
49 PyErr_Print();
50 }
51
52 goto error;
53 }
54
55 /* `str` has a trailing newline */
56 g_string_append(gstr, str);
57 }
58
59 if (gstr->len > 0) {
60 /* Remove trailing newline if any */
61 if (gstr->str[gstr->len - 1] == '\n') {
62 g_string_truncate(gstr, gstr->len - 1);
63 }
64 }
65
66 goto end;
67
68error:
69 g_string_free(gstr, TRUE);
70 gstr = NULL;
71
72end:
73 return gstr;
74}
75
76BT_HIDDEN
77GString *bt_py_common_format_tb(PyObject *py_exc_tb, int log_level)
78{
79 PyObject *traceback_module = NULL;
80 PyObject *format_tb_func = NULL;
81 PyObject *exc_str_list = NULL;
82 GString *msg_buf = NULL;
83
84 BT_ASSERT(py_exc_tb);
85
86 /*
87 * Import the standard `traceback` module which contains the
88 * functions to format a traceback.
89 */
90 traceback_module = PyImport_ImportModule("traceback");
91 if (!traceback_module) {
92 BT_LOGE_STR("Failed to import `traceback` module.");
93 goto error;
94 }
95
96 format_tb_func = PyObject_GetAttrString(traceback_module,
97 "format_tb");
98 if (!format_tb_func) {
99 BT_LOGE("Cannot find `format_tb` attribute in `traceback` module.");
100 goto error;
101 }
102
103 if (!PyCallable_Check(format_tb_func)) {
104 BT_LOGE("`traceback.format_tb` attribute is not callable.");
105 goto error;
106 }
107
108 /*
109 * If we are calling format_exception_only, `py_exc_tb` is NULL, so the
110 * effective argument list is of length 2.
111 */
112 exc_str_list = PyObject_CallFunctionObjArgs(
113 format_tb_func, py_exc_tb, NULL);
114 if (!exc_str_list) {
115 if (BT_LOG_ON_ERROR) {
116 BT_LOGE("Failed to call `traceback.format_tb` function:");
117 PyErr_Print();
118 }
119
120 goto error;
121 }
122
123 msg_buf = py_str_list_to_gstring(exc_str_list, log_level);
124 if (!msg_buf) {
125 goto error;
126 }
127
332c7e11 128error:
332c7e11
SM
129 Py_XDECREF(traceback_module);
130 Py_XDECREF(format_tb_func);
131 Py_XDECREF(exc_str_list);
132
133 return msg_buf;
134}
135
7085eeaa 136BT_HIDDEN
cacd0713
SM
137GString *bt_py_common_format_exception(PyObject *py_exc_type,
138 PyObject *py_exc_value, PyObject *py_exc_tb,
139 int log_level, bool chain)
7085eeaa 140{
7085eeaa
PP
141 PyObject *traceback_module = NULL;
142 PyObject *format_exception_func = NULL;
143 PyObject *exc_str_list = NULL;
144 GString *msg_buf = NULL;
145 const char *format_exc_func_name;
7085eeaa 146
7085eeaa
PP
147 /*
148 * Import the standard `traceback` module which contains the
149 * functions to format an exception.
150 */
151 traceback_module = PyImport_ImportModule("traceback");
152 if (!traceback_module) {
153 BT_LOGE_STR("Failed to import `traceback` module.");
154 goto error;
155 }
156
157 /*
cacd0713 158 * `py_exc_tb` can be `NULL`, when we fail to call a Python
7085eeaa
PP
159 * function from native code (there is no Python stack at that
160 * point). For example, a function which takes 5 positional
161 * arguments, but 8 were given.
162 */
cacd0713 163 format_exc_func_name = py_exc_tb ? "format_exception" :
7085eeaa
PP
164 "format_exception_only";
165 format_exception_func = PyObject_GetAttrString(traceback_module,
166 format_exc_func_name);
167 if (!format_exception_func) {
168 BT_LOGE("Cannot find `%s` attribute in `traceback` module.",
169 format_exc_func_name);
170 goto error;
171 }
172
173 if (!PyCallable_Check(format_exception_func)) {
174 BT_LOGE("`traceback.%s` attribute is not callable.",
175 format_exc_func_name);
176 goto error;
177 }
178
cacd0713
SM
179 /*
180 * If we are calling format_exception_only, `py_exc_tb` is NULL, so the
181 * effective argument list is of length 2.
182 */
7085eeaa 183 exc_str_list = PyObject_CallFunctionObjArgs(format_exception_func,
cacd0713
SM
184 py_exc_type, py_exc_value, py_exc_tb, Py_None /* limit */,
185 chain ? Py_True : Py_False /* chain */, NULL);
7085eeaa
PP
186 if (!exc_str_list) {
187 if (BT_LOG_ON_ERROR) {
188 BT_LOGE("Failed to call `traceback.%s` function:",
189 format_exc_func_name);
190 PyErr_Print();
191 }
192
193 goto error;
194 }
195
332c7e11
SM
196 msg_buf = py_str_list_to_gstring(exc_str_list, log_level);
197 if (!msg_buf) {
198 goto error;
7085eeaa
PP
199 }
200
7085eeaa 201error:
7085eeaa
PP
202 Py_XDECREF(exc_str_list);
203 Py_XDECREF(format_exception_func);
204 Py_XDECREF(traceback_module);
205
cacd0713
SM
206 return msg_buf;
207}
208
209BT_HIDDEN
210GString *bt_py_common_format_current_exception(int log_level)
211{
212 GString *result;
213 PyObject *py_exc_type = NULL;
214 PyObject *py_exc_value = NULL;
215 PyObject *py_exc_tb = NULL;
216
217 BT_ASSERT(PyErr_Occurred());
218 PyErr_Fetch(&py_exc_type, &py_exc_value, &py_exc_tb);
219 BT_ASSERT(py_exc_type);
220
221 /*
222 * Make sure `py_exc_value` is what we expected: an instance of
223 * `py_exc_type`.
224 */
225 PyErr_NormalizeException(&py_exc_type, &py_exc_value, &py_exc_tb);
226
227 result = bt_py_common_format_exception(py_exc_type, py_exc_value,
228 py_exc_tb, log_level, true);
229
7085eeaa
PP
230 /*
231 * We can safely call PyErr_Restore() because we always call
232 * PyErr_Fetch(), and having an error indicator is a function's
233 * precondition.
234 *
235 * PyErr_Restore() steals our references.
236 */
cacd0713 237 PyErr_Restore(py_exc_type, py_exc_value, py_exc_tb);
7085eeaa 238
cacd0713 239 return result;
7085eeaa 240}
This page took 0.050464 seconds and 4 git commands to generate.