Blender  V3.3
bpy_traceback.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
10 #include <Python.h>
11 #include <frameobject.h>
12 
13 #include "BLI_path_util.h"
14 #include "BLI_utildefines.h"
15 #ifdef WIN32
16 # include "BLI_string.h" /* BLI_strcasecmp */
17 #endif
18 
19 #include "bpy_traceback.h"
20 
21 static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
22 {
23  PyCodeObject *code = PyFrame_GetCode(tb->tb_frame);
24  *coerce = PyUnicode_EncodeFSDefault(code->co_filename);
25  return PyBytes_AS_STRING(*coerce);
26 }
27 
28 /* copied from pythonrun.c, 3.10.0 */
29 _Py_static_string(PyId_string, "<string>");
30 
31 static int parse_syntax_error(PyObject *err,
32  PyObject **message,
33  PyObject **filename,
34  int *lineno,
35  int *offset,
36  int *end_lineno,
37  int *end_offset,
38  PyObject **text)
39 {
40  Py_ssize_t hold;
41  PyObject *v;
42  _Py_IDENTIFIER(msg);
43  _Py_IDENTIFIER(filename);
44  _Py_IDENTIFIER(lineno);
45  _Py_IDENTIFIER(offset);
46  _Py_IDENTIFIER(end_lineno);
47  _Py_IDENTIFIER(end_offset);
48  _Py_IDENTIFIER(text);
49 
50  *message = NULL;
51  *filename = NULL;
52 
53  /* new style errors. `err' is an instance */
54  *message = _PyObject_GetAttrId(err, &PyId_msg);
55  if (!*message) {
56  goto finally;
57  }
58 
59  v = _PyObject_GetAttrId(err, &PyId_filename);
60  if (!v) {
61  goto finally;
62  }
63  if (v == Py_None) {
64  Py_DECREF(v);
65  *filename = _PyUnicode_FromId(&PyId_string);
66  if (*filename == NULL) {
67  goto finally;
68  }
69  Py_INCREF(*filename);
70  }
71  else {
72  *filename = v;
73  }
74 
75  v = _PyObject_GetAttrId(err, &PyId_lineno);
76  if (!v) {
77  goto finally;
78  }
79  hold = PyLong_AsSsize_t(v);
80  Py_DECREF(v);
81  if (hold < 0 && PyErr_Occurred()) {
82  goto finally;
83  }
84  *lineno = (int)hold;
85 
86  v = _PyObject_GetAttrId(err, &PyId_offset);
87  if (!v) {
88  goto finally;
89  }
90  if (v == Py_None) {
91  *offset = -1;
92  Py_DECREF(v);
93  }
94  else {
95  hold = PyLong_AsSsize_t(v);
96  Py_DECREF(v);
97  if (hold < 0 && PyErr_Occurred()) {
98  goto finally;
99  }
100  *offset = (int)hold;
101  }
102 
103  if (Py_TYPE(err) == (PyTypeObject *)PyExc_SyntaxError) {
104  v = _PyObject_GetAttrId(err, &PyId_end_lineno);
105  if (!v) {
106  PyErr_Clear();
107  *end_lineno = *lineno;
108  }
109  else if (v == Py_None) {
110  *end_lineno = *lineno;
111  Py_DECREF(v);
112  }
113  else {
114  hold = PyLong_AsSsize_t(v);
115  Py_DECREF(v);
116  if (hold < 0 && PyErr_Occurred()) {
117  goto finally;
118  }
119  *end_lineno = hold;
120  }
121 
122  v = _PyObject_GetAttrId(err, &PyId_end_offset);
123  if (!v) {
124  PyErr_Clear();
125  *end_offset = -1;
126  }
127  else if (v == Py_None) {
128  *end_offset = -1;
129  Py_DECREF(v);
130  }
131  else {
132  hold = PyLong_AsSsize_t(v);
133  Py_DECREF(v);
134  if (hold < 0 && PyErr_Occurred()) {
135  goto finally;
136  }
137  *end_offset = hold;
138  }
139  }
140  else {
141  /* `SyntaxError` subclasses. */
142  *end_lineno = *lineno;
143  *end_offset = -1;
144  }
145 
146  v = _PyObject_GetAttrId(err, &PyId_text);
147  if (!v) {
148  goto finally;
149  }
150  if (v == Py_None) {
151  Py_DECREF(v);
152  *text = NULL;
153  }
154  else {
155  *text = v;
156  }
157  return 1;
158 
159 finally:
160  Py_XDECREF(*message);
161  Py_XDECREF(*filename);
162  return 0;
163 }
164 /* end copied function! */
165 
167  const char *filepath, int *r_lineno, int *r_offset, int *r_lineno_end, int *r_offset_end)
168 {
169  /* WARNING(@campbellbarton): The normalized exception is restored (losing line number info).
170  * Ideally this would leave the exception state as it found it, but that needs to be done
171  * carefully with regards to reference counting, see: T97731. */
172 
173  bool success = false;
174  PyObject *exception, *value;
175  PyTracebackObject *tb;
176 
177  *r_lineno = -1;
178  *r_offset = 0;
179 
180  *r_lineno_end = -1;
181  *r_offset_end = 0;
182 
183  PyErr_Fetch(&exception, &value, (PyObject **)&tb);
184  if (exception == NULL) {
185  return false;
186  }
187 
188  if (PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError)) {
189  /* No trace-back available when `SyntaxError`.
190  * Python has no API's to this. reference #parse_syntax_error() from `pythonrun.c`. */
191  PyErr_NormalizeException(&exception, &value, (PyObject **)&tb);
192 
193  if (value) { /* Should always be true. */
194  PyObject *message;
195  PyObject *filepath_exc_py, *text_py;
196 
197  if (parse_syntax_error(value,
198  &message,
199  &filepath_exc_py,
200  r_lineno,
201  r_offset,
202  r_lineno_end,
203  r_offset_end,
204  &text_py)) {
205  const char *filepath_exc = PyUnicode_AsUTF8(filepath_exc_py);
206  /* python adds a '/', prefix, so check for both */
207  if ((BLI_path_cmp(filepath_exc, filepath) == 0) ||
208  (ELEM(filepath_exc[0], '\\', '/') && BLI_path_cmp(filepath_exc + 1, filepath) == 0)) {
209  success = true;
210  }
211  }
212  }
213  }
214  else {
215  PyErr_NormalizeException(&exception, &value, (PyObject **)&tb);
216 
217  for (tb = (PyTracebackObject *)PySys_GetObject("last_traceback");
218  tb && (PyObject *)tb != Py_None;
219  tb = tb->tb_next) {
220  PyObject *coerce;
221  const char *tb_filepath = traceback_filepath(tb, &coerce);
222  const int match = ((BLI_path_cmp(tb_filepath, filepath) == 0) ||
223  (ELEM(tb_filepath[0], '\\', '/') &&
224  BLI_path_cmp(tb_filepath + 1, filepath) == 0));
225  Py_DECREF(coerce);
226 
227  if (match) {
228  success = true;
229  *r_lineno = *r_lineno_end = tb->tb_lineno;
230  /* used to break here, but better find the inner most line */
231  }
232  }
233  }
234 
235  PyErr_Restore(exception, value, (PyObject *)tb); /* takes away reference! */
236 
237  return success;
238 }
#define BLI_path_cmp
#define ELEM(...)
ATTR_WARN_UNUSED_RESULT const BMVert * v
static int parse_syntax_error(PyObject *err, PyObject **message, PyObject **filename, int *lineno, int *offset, int *end_lineno, int *end_offset, PyObject **text)
Definition: bpy_traceback.c:31
_Py_static_string(PyId_string, "<string>")
bool python_script_error_jump(const char *filepath, int *r_lineno, int *r_offset, int *r_lineno_end, int *r_offset_end)
static const char * traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
Definition: bpy_traceback.c:21
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
static FT_Error err