bt2: don't print previous causes in causes created from bt2._Error
[babeltrace.git] / src / py-common / py-common.c
CommitLineData
2d7aa9b6
PP
1/*
2 * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
3 * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
4 * Copyright (c) 2019 Simon Marchi <simon.marchi@efficios.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#define BT_LOG_OUTPUT_LEVEL log_level
26#define BT_LOG_TAG "PY-COMMON"
27#include "logging/log.h"
28
969c1d8a
FD
29#include <stdbool.h>
30
2d7aa9b6
PP
31#include <Python.h>
32
33#include "common/assert.h"
34#include "py-common.h"
35
b53cd768
SM
36/*
37 * Concatenate strings in list `py_str_list`, returning the result as a
38 * GString. Remove the trailing \n, if the last string ends with a \n.
39 */
40static
41GString *py_str_list_to_gstring(PyObject *py_str_list, int log_level)
42{
43 Py_ssize_t i;
44 GString *gstr;
45
46 gstr = g_string_new(NULL);
47 if (!gstr) {
48 BT_LOGE("Failed to allocate a GString.");
49 goto end;
50 }
51
52 for (i = 0; i < PyList_Size(py_str_list); i++) {
53 PyObject *py_str;
54 const char *str;
55
56 py_str = PyList_GetItem(py_str_list, i);
57 BT_ASSERT(py_str);
58 BT_ASSERT(PyUnicode_CheckExact(py_str));
59
60 /* `str` is owned by `py_str`, not by us */
61 str = PyUnicode_AsUTF8(py_str);
62 if (!str) {
63 if (BT_LOG_ON_ERROR) {
64 BT_LOGE_STR("PyUnicode_AsUTF8() failed:");
65 PyErr_Print();
66 }
67
68 goto error;
69 }
70
71 /* `str` has a trailing newline */
72 g_string_append(gstr, str);
73 }
74
75 if (gstr->len > 0) {
76 /* Remove trailing newline if any */
77 if (gstr->str[gstr->len - 1] == '\n') {
78 g_string_truncate(gstr, gstr->len - 1);
79 }
80 }
81
82 goto end;
83
84error:
85 g_string_free(gstr, TRUE);
86 gstr = NULL;
87
88end:
89 return gstr;
90}
91
92BT_HIDDEN
93GString *bt_py_common_format_tb(PyObject *py_exc_tb, int log_level)
94{
95 PyObject *traceback_module = NULL;
96 PyObject *format_tb_func = NULL;
97 PyObject *exc_str_list = NULL;
98 GString *msg_buf = NULL;
99
100 BT_ASSERT(py_exc_tb);
101
102 /*
103 * Import the standard `traceback` module which contains the
104 * functions to format a traceback.
105 */
106 traceback_module = PyImport_ImportModule("traceback");
107 if (!traceback_module) {
108 BT_LOGE_STR("Failed to import `traceback` module.");
109 goto error;
110 }
111
112 format_tb_func = PyObject_GetAttrString(traceback_module,
113 "format_tb");
114 if (!format_tb_func) {
115 BT_LOGE("Cannot find `format_tb` attribute in `traceback` module.");
116 goto error;
117 }
118
119 if (!PyCallable_Check(format_tb_func)) {
120 BT_LOGE("`traceback.format_tb` attribute is not callable.");
121 goto error;
122 }
123
124 /*
125 * If we are calling format_exception_only, `py_exc_tb` is NULL, so the
126 * effective argument list is of length 2.
127 */
128 exc_str_list = PyObject_CallFunctionObjArgs(
129 format_tb_func, py_exc_tb, NULL);
130 if (!exc_str_list) {
131 if (BT_LOG_ON_ERROR) {
132 BT_LOGE("Failed to call `traceback.format_tb` function:");
133 PyErr_Print();
134 }
135
136 goto error;
137 }
138
139 msg_buf = py_str_list_to_gstring(exc_str_list, log_level);
140 if (!msg_buf) {
141 goto error;
142 }
143
144 goto end;
145
146error:
147 if (msg_buf) {
148 g_string_free(msg_buf, TRUE);
149 msg_buf = NULL;
150 }
151
152end:
153 Py_XDECREF(traceback_module);
154 Py_XDECREF(format_tb_func);
155 Py_XDECREF(exc_str_list);
156
157 return msg_buf;
158}
159
2d7aa9b6 160BT_HIDDEN
f33c35fb
SM
161GString *bt_py_common_format_exception(PyObject *py_exc_type,
162 PyObject *py_exc_value, PyObject *py_exc_tb,
163 int log_level, bool chain)
2d7aa9b6 164{
2d7aa9b6
PP
165 PyObject *traceback_module = NULL;
166 PyObject *format_exception_func = NULL;
167 PyObject *exc_str_list = NULL;
168 GString *msg_buf = NULL;
169 const char *format_exc_func_name;
2d7aa9b6 170
2d7aa9b6
PP
171 /*
172 * Import the standard `traceback` module which contains the
173 * functions to format an exception.
174 */
175 traceback_module = PyImport_ImportModule("traceback");
176 if (!traceback_module) {
177 BT_LOGE_STR("Failed to import `traceback` module.");
178 goto error;
179 }
180
181 /*
f33c35fb 182 * `py_exc_tb` can be `NULL`, when we fail to call a Python
2d7aa9b6
PP
183 * function from native code (there is no Python stack at that
184 * point). For example, a function which takes 5 positional
185 * arguments, but 8 were given.
186 */
f33c35fb 187 format_exc_func_name = py_exc_tb ? "format_exception" :
2d7aa9b6
PP
188 "format_exception_only";
189 format_exception_func = PyObject_GetAttrString(traceback_module,
190 format_exc_func_name);
191 if (!format_exception_func) {
192 BT_LOGE("Cannot find `%s` attribute in `traceback` module.",
193 format_exc_func_name);
194 goto error;
195 }
196
197 if (!PyCallable_Check(format_exception_func)) {
198 BT_LOGE("`traceback.%s` attribute is not callable.",
199 format_exc_func_name);
200 goto error;
201 }
202
f33c35fb
SM
203 /*
204 * If we are calling format_exception_only, `py_exc_tb` is NULL, so the
205 * effective argument list is of length 2.
206 */
2d7aa9b6 207 exc_str_list = PyObject_CallFunctionObjArgs(format_exception_func,
f33c35fb
SM
208 py_exc_type, py_exc_value, py_exc_tb, Py_None /* limit */,
209 chain ? Py_True : Py_False /* chain */, NULL);
2d7aa9b6
PP
210 if (!exc_str_list) {
211 if (BT_LOG_ON_ERROR) {
212 BT_LOGE("Failed to call `traceback.%s` function:",
213 format_exc_func_name);
214 PyErr_Print();
215 }
216
217 goto error;
218 }
219
b53cd768
SM
220 msg_buf = py_str_list_to_gstring(exc_str_list, log_level);
221 if (!msg_buf) {
222 goto error;
2d7aa9b6
PP
223 }
224
225 goto end;
226
227error:
228 if (msg_buf) {
229 g_string_free(msg_buf, TRUE);
230 msg_buf = NULL;
231 }
232
233end:
234 Py_XDECREF(exc_str_list);
235 Py_XDECREF(format_exception_func);
236 Py_XDECREF(traceback_module);
237
f33c35fb
SM
238 return msg_buf;
239}
240
241BT_HIDDEN
242GString *bt_py_common_format_current_exception(int log_level)
243{
244 GString *result;
245 PyObject *py_exc_type = NULL;
246 PyObject *py_exc_value = NULL;
247 PyObject *py_exc_tb = NULL;
248
249 BT_ASSERT(PyErr_Occurred());
250 PyErr_Fetch(&py_exc_type, &py_exc_value, &py_exc_tb);
251 BT_ASSERT(py_exc_type);
252
253 /*
254 * Make sure `py_exc_value` is what we expected: an instance of
255 * `py_exc_type`.
256 */
257 PyErr_NormalizeException(&py_exc_type, &py_exc_value, &py_exc_tb);
258
259 result = bt_py_common_format_exception(py_exc_type, py_exc_value,
260 py_exc_tb, log_level, true);
261
2d7aa9b6
PP
262 /*
263 * We can safely call PyErr_Restore() because we always call
264 * PyErr_Fetch(), and having an error indicator is a function's
265 * precondition.
266 *
267 * PyErr_Restore() steals our references.
268 */
f33c35fb 269 PyErr_Restore(py_exc_type, py_exc_value, py_exc_tb);
2d7aa9b6 270
f33c35fb 271 return result;
2d7aa9b6 272}
This page took 0.045796 seconds and 4 git commands to generate.