aa21627a27911d818de119b7ef026b2b54dbbef7
[blender.git] / source / blender / python / intern / bpy_traceback.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/python/intern/bpy_traceback.c
22  *  \ingroup pythonintern
23  */
24
25
26 #include <Python.h>
27 #include <frameobject.h>
28
29 #include "bpy_traceback.h"
30
31 static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
32 {
33         return PyBytes_AS_STRING((*coerce= PyUnicode_EncodeFSDefault(tb->tb_frame->f_code->co_filename)));
34 }
35
36 /* copied from pythonrun.c, 3.2.0 */
37 static int
38 parse_syntax_error(PyObject *err, PyObject **message, const char **filename,
39                                    int *lineno, int *offset, const char **text)
40 {
41         long hold;
42         PyObject *v;
43
44         /* old style errors */
45         if (PyTuple_Check(err))
46                 return PyArg_ParseTuple(err, "O(ziiz)", message, filename,
47                                                                 lineno, offset, text);
48
49         /* new style errors.  `err' is an instance */
50
51         if (! (v = PyObject_GetAttrString(err, "msg")))
52                 goto finally;
53         *message = v;
54
55         if (!(v = PyObject_GetAttrString(err, "filename")))
56                 goto finally;
57         if (v == Py_None)
58                 *filename = NULL;
59         else if (! (*filename = _PyUnicode_AsString(v)))
60                 goto finally;
61
62         Py_DECREF(v);
63         if (!(v = PyObject_GetAttrString(err, "lineno")))
64                 goto finally;
65         hold = PyLong_AsLong(v);
66         Py_DECREF(v);
67         v = NULL;
68         if (hold < 0 && PyErr_Occurred())
69                 goto finally;
70         *lineno = (int)hold;
71
72         if (!(v = PyObject_GetAttrString(err, "offset")))
73                 goto finally;
74         if (v == Py_None) {
75                 *offset = -1;
76                 Py_DECREF(v);
77                 v = NULL;
78         }
79         else {
80                 hold = PyLong_AsLong(v);
81                 Py_DECREF(v);
82                 v = NULL;
83                 if (hold < 0 && PyErr_Occurred())
84                         goto finally;
85                 *offset = (int)hold;
86         }
87
88         if (!(v = PyObject_GetAttrString(err, "text")))
89                 goto finally;
90         if (v == Py_None)
91                 *text = NULL;
92         else if (!PyUnicode_Check(v) ||
93                          !(*text = _PyUnicode_AsString(v)))
94                 goto finally;
95         Py_DECREF(v);
96         return 1;
97
98 finally:
99         Py_XDECREF(v);
100         return 0;
101 }
102 /* end copied function! */
103
104
105 void python_script_error_jump(const char *filepath, int *lineno, int *offset)
106 {
107         PyObject *exception, *value;
108         PyTracebackObject *tb;
109
110         *lineno= -1;
111         *offset= 0;
112
113         PyErr_Fetch(&exception, &value, (PyObject **)&tb);
114
115         if (exception && PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError)) {
116                 /* no traceback available when SyntaxError.
117                  * python has no api's to this. reference parse_syntax_error() from pythonrun.c */
118                 PyErr_NormalizeException(&exception, &value, (PyObject **)&tb);
119                 PyErr_Restore(exception, value, (PyObject *)tb);        /* takes away reference! */
120
121                 if (value) { /* should always be true */
122                         PyObject *message;
123                         const char *filename, *text;
124
125                         if (parse_syntax_error(value, &message, &filename, lineno, offset, &text)) {
126                                 /* python adds a '/', prefix, so check for both */
127                                 if ((strcmp(filename, filepath) == 0) ||
128                                         ((filename[0] == '\\' || filename[0] == '/') && strcmp(filename + 1, filepath) == 0)
129                                 ) {
130                                         /* good */
131                                 }
132                                 else {
133                                         *lineno= -1;
134                                 }
135                         }
136                         else {
137                                 *lineno= -1;
138                         }
139                 }
140         }
141         else {
142                 PyErr_NormalizeException(&exception, &value, (PyObject **)&tb);
143                 PyErr_Restore(exception, value, (PyObject *)tb);        /* takes away reference! */
144                 PyErr_Print();
145
146                 for (tb= (PyTracebackObject *)PySys_GetObject("last_traceback"); tb && (PyObject *)tb != Py_None; tb= tb->tb_next) {
147                         PyObject *coerce;
148                         const char *tb_filepath= traceback_filepath(tb, &coerce);
149                         const int match= strcmp(tb_filepath, filepath) != 0;
150                         Py_DECREF(coerce);
151
152                         if (match) {
153                                 *lineno= tb->tb_lineno;
154                                 break;
155                         }
156                 }
157         }
158 }