| 
									
										
										
										
											2011-02-23 10:52:22 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  |  * of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software Foundation, | 
					
						
							|  |  |  |  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 08:08:12 +11:00
										 |  |  | /** \file
 | 
					
						
							|  |  |  |  * \ingroup pythonintern | 
					
						
							| 
									
										
										
										
											2011-11-05 08:21:12 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This file contains utility functions for getting data from a python stack | 
					
						
							|  |  |  |  * trace. | 
					
						
							| 
									
										
										
										
											2011-02-27 20:10:08 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | #include <Python.h>
 | 
					
						
							|  |  |  | #include <frameobject.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-08 02:19:41 +00:00
										 |  |  | #include "BLI_path_util.h"
 | 
					
						
							| 
									
										
										
										
											2020-03-19 09:33:03 +01:00
										 |  |  | #include "BLI_utildefines.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-01 07:21:08 +10:00
										 |  |  | #ifdef WIN32
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | #  include "BLI_string.h" /* BLI_strcasecmp */
 | 
					
						
							| 
									
										
										
										
											2014-05-01 07:21:08 +10:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2012-03-08 02:19:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | #include "bpy_traceback.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-26 17:55:03 +00:00
										 |  |  | static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce) | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-03 17:35:31 +02:00
										 |  |  |   *coerce = PyUnicode_EncodeFSDefault(tb->tb_frame->f_code->co_filename); | 
					
						
							|  |  |  |   return PyBytes_AS_STRING(*coerce); | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-02 06:24:29 +10:00
										 |  |  | /* copied from pythonrun.c, 3.4.0 */ | 
					
						
							|  |  |  | _Py_static_string(PyId_string, "<string>"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | static int parse_syntax_error(PyObject *err, | 
					
						
							|  |  |  |                               PyObject **message, | 
					
						
							|  |  |  |                               PyObject **filename, | 
					
						
							|  |  |  |                               int *lineno, | 
					
						
							|  |  |  |                               int *offset, | 
					
						
							|  |  |  |                               PyObject **text) | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   long hold; | 
					
						
							|  |  |  |   PyObject *v; | 
					
						
							|  |  |  |   _Py_IDENTIFIER(msg); | 
					
						
							|  |  |  |   _Py_IDENTIFIER(filename); | 
					
						
							|  |  |  |   _Py_IDENTIFIER(lineno); | 
					
						
							|  |  |  |   _Py_IDENTIFIER(offset); | 
					
						
							|  |  |  |   _Py_IDENTIFIER(text); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *message = NULL; | 
					
						
							|  |  |  |   *filename = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* new style errors.  `err' is an instance */ | 
					
						
							|  |  |  |   *message = _PyObject_GetAttrId(err, &PyId_msg); | 
					
						
							|  |  |  |   if (!*message) { | 
					
						
							|  |  |  |     goto finally; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   v = _PyObject_GetAttrId(err, &PyId_filename); | 
					
						
							|  |  |  |   if (!v) { | 
					
						
							|  |  |  |     goto finally; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (v == Py_None) { | 
					
						
							|  |  |  |     Py_DECREF(v); | 
					
						
							|  |  |  |     *filename = _PyUnicode_FromId(&PyId_string); | 
					
						
							|  |  |  |     if (*filename == NULL) { | 
					
						
							|  |  |  |       goto finally; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_INCREF(*filename); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     *filename = v; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   v = _PyObject_GetAttrId(err, &PyId_lineno); | 
					
						
							|  |  |  |   if (!v) { | 
					
						
							|  |  |  |     goto finally; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   hold = PyLong_AsLong(v); | 
					
						
							|  |  |  |   Py_DECREF(v); | 
					
						
							|  |  |  |   if (hold < 0 && PyErr_Occurred()) { | 
					
						
							|  |  |  |     goto finally; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   *lineno = (int)hold; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   v = _PyObject_GetAttrId(err, &PyId_offset); | 
					
						
							|  |  |  |   if (!v) { | 
					
						
							|  |  |  |     goto finally; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (v == Py_None) { | 
					
						
							|  |  |  |     *offset = -1; | 
					
						
							|  |  |  |     Py_DECREF(v); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     hold = PyLong_AsLong(v); | 
					
						
							|  |  |  |     Py_DECREF(v); | 
					
						
							|  |  |  |     if (hold < 0 && PyErr_Occurred()) { | 
					
						
							|  |  |  |       goto finally; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *offset = (int)hold; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   v = _PyObject_GetAttrId(err, &PyId_text); | 
					
						
							|  |  |  |   if (!v) { | 
					
						
							|  |  |  |     goto finally; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (v == Py_None) { | 
					
						
							|  |  |  |     Py_DECREF(v); | 
					
						
							|  |  |  |     *text = NULL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     *text = v; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 1; | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | finally: | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   Py_XDECREF(*message); | 
					
						
							|  |  |  |   Py_XDECREF(*filename); | 
					
						
							|  |  |  |   return 0; | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | /* end copied function! */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void python_script_error_jump(const char *filepath, int *lineno, int *offset) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   PyObject *exception, *value; | 
					
						
							|  |  |  |   PyTracebackObject *tb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *lineno = -1; | 
					
						
							|  |  |  |   *offset = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   PyErr_Fetch(&exception, &value, (PyObject **)&tb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (exception && PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError)) { | 
					
						
							| 
									
										
										
										
											2020-07-19 17:12:48 +10:00
										 |  |  |     /* no trace-back available when `SyntaxError`.
 | 
					
						
							|  |  |  |      * python has no API's to this. reference #parse_syntax_error() from pythonrun.c */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     PyErr_NormalizeException(&exception, &value, (PyObject **)&tb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (value) { /* should always be true */ | 
					
						
							|  |  |  |       PyObject *message; | 
					
						
							|  |  |  |       PyObject *filename_py, *text_py; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (parse_syntax_error(value, &message, &filename_py, lineno, offset, &text_py)) { | 
					
						
							| 
									
										
										
										
											2021-02-13 22:57:01 +11:00
										 |  |  |         const char *filename = PyUnicode_AsUTF8(filename_py); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         /* python adds a '/', prefix, so check for both */ | 
					
						
							|  |  |  |         if ((BLI_path_cmp(filename, filepath) == 0) || | 
					
						
							| 
									
										
										
										
											2020-03-07 00:58:48 +11:00
										 |  |  |             (ELEM(filename[0], '\\', '/') && BLI_path_cmp(filename + 1, filepath) == 0)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           /* good */ | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           *lineno = -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							|  |  |  |         *lineno = -1; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-23 20:29:11 +10:00
										 |  |  |     PyErr_Restore(exception, value, (PyObject *)tb); /* takes away reference! */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     PyErr_NormalizeException(&exception, &value, (PyObject **)&tb); | 
					
						
							|  |  |  |     PyErr_Restore(exception, value, (PyObject *)tb); /* takes away reference! */ | 
					
						
							|  |  |  |     PyErr_Print(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (tb = (PyTracebackObject *)PySys_GetObject("last_traceback"); | 
					
						
							|  |  |  |          tb && (PyObject *)tb != Py_None; | 
					
						
							|  |  |  |          tb = tb->tb_next) { | 
					
						
							|  |  |  |       PyObject *coerce; | 
					
						
							|  |  |  |       const char *tb_filepath = traceback_filepath(tb, &coerce); | 
					
						
							|  |  |  |       const int match = ((BLI_path_cmp(tb_filepath, filepath) == 0) || | 
					
						
							| 
									
										
										
										
											2020-03-07 00:58:48 +11:00
										 |  |  |                          (ELEM(tb_filepath[0], '\\', '/') && | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |                           BLI_path_cmp(tb_filepath + 1, filepath) == 0)); | 
					
						
							|  |  |  |       Py_DECREF(coerce); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (match) { | 
					
						
							|  |  |  |         *lineno = tb->tb_lineno; | 
					
						
							|  |  |  |         /* used to break here, but better find the inner most line */ | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2011-02-22 11:32:29 +00:00
										 |  |  | } |