JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
PythonException.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4// California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5//
6// This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7// redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8// Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10// License for more details. You should have received a copy of the GNU General Public License along with this program;
11// if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12//
13// Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14// Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16/*! \file */
17
19#include <Python.h>
20#include <frameobject.h>
21#include <sstream>
22#include <vector>
23
24#include <boost/python/object.hpp>
25#include <boost/python/str.hpp>
26#include <boost/python/extract.hpp>
27
28// This code inspired from:
29// https://github.com/abingham/boost_python_exception/tree/master/src/boost_python_exception/auto_translation
30
31
32namespace
33{
34 std::string python_string_as_std_string(PyObject* obj)
35 {
36#if PY_MAJOR_VERSION == 2
37 return PyString_AsString(obj);
38#else
39 return PyUnicode_AsUTF8(obj);
40#endif
41 }
42
43 long python_integral_as_long(PyObject* obj)
44 {
45#if PY_MAJOR_VERSION == 2
46 return PyInt_AsLong(obj);
47#else
48 return PyLong_AsLong(obj);
49#endif
50 }
51
52 void clear_exception()
53 {
54 PyErr_Clear();
55 }
56
57 boost::python::object ptr_to_obj(PyObject * ptr)
58 {
59 if (ptr) return boost::python::object(boost::python::handle<>(boost::python::borrowed(ptr)));
60 else return boost::python::object();
61 }
62
63 struct traceback_step
64 {
65 int line_number;
66 std::string file_name;
67 std::string source;
68 };
69
70 std::ostream & operator<<(std::ostream& stream, traceback_step const & info)
71 {
72 stream << "File " << info.file_name << "\nLine " << info.line_number << ", in " << info.source << ":";
73 return stream;
74 }
75
76 typedef std::vector<traceback_step> traceback;
77
78 std::ostream & operator<<(std::ostream & stream, traceback const & tb)
79 {
80 for (traceback_step const & step : tb) stream << step << "\n";
81 return stream;
82 }
83
84 void add_traceback_step(std::vector<traceback_step> & output, PyTracebackObject const * pytb)
85 {
86 traceback_step entry =
87 {
88#if JEVOIS_PYTHON_MAJOR == 3 && JEVOIS_PYTHON_MINOR >= 10
89 PyFrame_GetLineNumber(pytb->tb_frame),
90 python_string_as_std_string(PyFrame_GetCode(pytb->tb_frame)->co_filename),
91 python_string_as_std_string(PyFrame_GetCode(pytb->tb_frame)->co_name)
92#else
93 pytb->tb_lineno,
94 python_string_as_std_string(pytb->tb_frame->f_code->co_filename),
95 python_string_as_std_string(pytb->tb_frame->f_code->co_name)
96#endif
97 };
98
99 output.push_back(entry);
100 }
101
102
103 traceback extract_traceback(boost::python::object py_traceback)
104 {
105 if (py_traceback.is_none()) return traceback();
106
107 PyTracebackObject const * tb = reinterpret_cast<PyTracebackObject *>(py_traceback.ptr());
108 std::vector<traceback_step> result;
109 for (; tb != 0; tb = tb->tb_next) add_traceback_step(result, tb);
110 return result;
111 }
112
113 std::string extract_exception_type(boost::python::object type)
114 {
115 if (PyExceptionClass_Check(type.ptr())) {
116 return PyExceptionClass_Name(type.ptr());
117 } else {
118 throw std::logic_error("Given type is not a standard python exception class");
119 }
120 }
121
122 std::string extract_message(boost::python::object value)
123 {
124 return boost::python::extract<std::string>(boost::python::str(value));
125 }
126
127 std::string generate_message(std::string const & type, std::string const & message, traceback const & traceback)
128 {
129 std::ostringstream result;
130 if (traceback.empty() == false) result << "Python traceback (most recent calls last):\n" << traceback;
131 result << type;
132 if (message.empty() == false) result << ": " << message;
133 return result.str();
134 }
135
136 std::string make_syntax_error_message(boost::python::object error)
137 {
138 std::string const module_name = boost::python::extract<std::string>(error.attr("filename"));
139 std::string const code = boost::python::extract<std::string>(error.attr("text"));
140
141 // for some reason, extract<long> does not work while a python error is set, so do it with CPython
142 long const line_number = python_integral_as_long(boost::python::object(error.attr("lineno")).ptr());
143 long const pos_in_line = python_integral_as_long(boost::python::object(error.attr("offset")).ptr());
144
145 std::ostringstream message;
146 message << "In module \"" << module_name << "\", line " << line_number << ", position " << pos_in_line << ":\n";
147 message << "Offending code: " << code;
148 message << " " << std::string(pos_in_line-1, ' ') << "^";
149
150 return message.str();
151 }
152} // anonymous namespace
153
154std::string jevois::getPythonExceptionString(boost::python::error_already_set &)
155{
156 // Get some info from the python exception:
157 PyObject *t, *v, *tb;
158
159 try
160 {
161 PyErr_Fetch(&t, &v, &tb);
162 PyErr_NormalizeException(&t, &v, &tb);
163 }
164 catch (...) { return "Internal error trying to fetch exception data from Python"; }
165
166 try
167 {
168 boost::python::object objtype = ::ptr_to_obj(t);
169 boost::python::object objvalue = ::ptr_to_obj(v);
170 boost::python::object objtraceback = ::ptr_to_obj(tb);
171
172 std::string const type = extract_exception_type(objtype);
173 std::string const message = (type == "SyntaxError") ?
174 make_syntax_error_message(objvalue) : extract_message(objvalue);
175 traceback const traceback = extract_traceback(objtraceback);
176
177 PyErr_Restore(t, v, tb);
178 clear_exception();
179
180 return generate_message(type, message, traceback);
181 }
182 catch (...)
183 { PyErr_Restore(t, v, tb); clear_exception(); return "Internal error trying to fetch exception data from Python"; }
184}
std::string getPythonExceptionString(boost::python::error_already_set &)
Python exception translation to string so we can print the traceback to our serlog stream.