documentation - brief descriptions for bpy api files.
[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  * This file contains utility functions for getting data from a python stack
25  * trace.
26  */
27
28
29 #include <Python.h>
30 #include <frameobject.h>
31
32 #include "bpy_traceback.h"
33
34 static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
35 {
36         return PyBytes_AS_STRING((*coerce= PyUnicode_EncodeFSDefault(tb->tb_frame->f_code->co_filename)));
37 }
38
39 /* copied from pythonrun.c, 3.2.0 */
40 static int
41 parse_syntax_error(PyObject *err, PyObject **message, const char **filename,
42                                    int *lineno, int *offset, const char **text)
43 {
44         long hold;
45         PyObject *v;
46
47         /* old style errors */
48         if (PyTuple_Check(err))
49                 return PyArg_ParseTuple(err, "O(ziiz)", message, filename,
50                                                                 lineno, offset, text);
51
52         /* new style errors.  `err' is an instance */
53
54         if (! (v = PyObject_GetAttrString(err, "msg")))
55                 goto finally;
56         *message = v;
57
58         if (!(v = PyObject_GetAttrString(err, "filename")))
59                 goto finally;
60         if (v == Py_None)
61                 *filename = NULL;
62         else if (! (*filename = _PyUnicode_AsString(v)))
63                 goto finally;
64
65         Py_DECREF(v);
66         if (!(v = PyObject_GetAttrString(err, "lineno")))
67                 goto finally;
68         hold = PyLong_AsLong(v);
69         Py_DECREF(v);
70         v = NULL;
71         if (hold < 0 && PyErr_Occurred())
72                 goto finally;
73         *lineno = (int)hold;
74
75         if (!(v = PyObject_GetAttrString(err, "offset")))
76                 goto finally;
77         if (v == Py_None) {
78                 *offset = -1;
79                 Py_DECREF(v);
80                 v = NULL;
81         }
82         else {
83                 hold = PyLong_AsLong(v);
84                 Py_DECREF(v);
85                 v = NULL;
86                 if (hold < 0 && PyErr_Occurred())
87                         goto finally;
88                 *offset = (int)hold;
89         }
90
91         if (!(v = PyObject_GetAttrString(err, "text")))
92                 goto finally;
93         if (v == Py_None)
94                 *text = NULL;
95         else if (!PyUnicode_Check(v) ||
96                          !(*text = _PyUnicode_AsString(v)))
97                 goto finally;
98         Py_DECREF(v);
99         return 1;
100
101 finally:
102         Py_XDECREF(v);
103         return 0;
104 }
105 /* end copied function! */
106
107
108 void python_script_error_jump(const char *filepath, int *lineno, int *offset)
109 {
110         PyObject *exception, *value;
111         PyTracebackObject *tb;
112
113         *lineno= -1;
114         *offset= 0;
115
116         PyErr_Fetch(&exception, &value, (PyObject **)&tb);
117
118         if (exception && PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError)) {
119                 /* no traceback available when SyntaxError.
120                  * python has no api's to this. reference parse_syntax_error() from pythonrun.c */
121                 PyErr_NormalizeException(&exception, &value, (PyObject **)&tb);
122                 PyErr_Restore(exception, value, (PyObject *)tb);        /* takes away reference! */
123
124                 if (value) { /* should always be true */
125                         PyObject *message;
126                         const char *filename, *text;
127
128                         if (parse_syntax_error(value, &message, &filename, lineno, offset, &text)) {
129                                 /* python adds a '/', prefix, so check for both */
130                                 if ((strcmp(filename, filepath) == 0) ||
131                                         ((filename[0] == '\\' || filename[0] == '/') && strcmp(filename + 1, filepath) == 0)
132                                 ) {
133                                         /* good */
134                                 }
135                                 else {
136                                         *lineno= -1;
137                                 }
138                         }
139                         else {
140                                 *lineno= -1;
141                         }
142                 }
143         }
144         else {
145                 PyErr_NormalizeException(&exception, &value, (PyObject **)&tb);
146                 PyErr_Restore(exception, value, (PyObject *)tb);        /* takes away reference! */
147                 PyErr_Print();
148
149                 for (tb= (PyTracebackObject *)PySys_GetObject("last_traceback"); tb && (PyObject *)tb != Py_None; tb= tb->tb_next) {
150                         PyObject *coerce;
151                         const char *tb_filepath= traceback_filepath(tb, &coerce);
152                         const int match= strcmp(tb_filepath, filepath) != 0;
153                         Py_DECREF(coerce);
154
155                         if (match) {
156                                 *lineno= tb->tb_lineno;
157                                 break;
158                         }
159                 }
160         }
161 }