JeVois  1.20
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
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 
32 namespace
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  pytb->tb_lineno,
88  python_string_as_std_string(pytb->tb_frame->f_code->co_filename),
89  python_string_as_std_string(pytb->tb_frame->f_code->co_name)
90  };
91  output.push_back(entry);
92  }
93 
94 
95  traceback extract_traceback(boost::python::object py_traceback)
96  {
97  if (py_traceback.is_none()) return traceback();
98 
99  PyTracebackObject const * tb = reinterpret_cast<PyTracebackObject *>(py_traceback.ptr());
100  std::vector<traceback_step> result;
101  for (; tb != 0; tb = tb->tb_next) add_traceback_step(result, tb);
102  return result;
103  }
104 
105  std::string extract_exception_type(boost::python::object type)
106  {
107  if (PyExceptionClass_Check(type.ptr())) {
108  return PyExceptionClass_Name(type.ptr());
109  } else {
110  throw std::logic_error("Given type is not a standard python exception class");
111  }
112  }
113 
114  std::string extract_message(boost::python::object value)
115  {
116  return boost::python::extract<std::string>(boost::python::str(value));
117  }
118 
119  std::string generate_message(std::string const & type, std::string const & message, traceback const & traceback)
120  {
121  std::ostringstream result;
122  if (traceback.empty() == false) result << "Python traceback (most recent calls last):\n" << traceback;
123  result << type;
124  if (message.empty() == false) result << ": " << message;
125  return result.str();
126  }
127 
128  std::string make_syntax_error_message(boost::python::object error)
129  {
130  std::string const module_name = boost::python::extract<std::string>(error.attr("filename"));
131  std::string const code = boost::python::extract<std::string>(error.attr("text"));
132 
133  // for some reason, extract<long> does not work while a python error is set, so do it with CPython
134  long const line_number = python_integral_as_long(boost::python::object(error.attr("lineno")).ptr());
135  long const pos_in_line = python_integral_as_long(boost::python::object(error.attr("offset")).ptr());
136 
137  std::ostringstream message;
138  message << "In module \"" << module_name << "\", line " << line_number << ", position " << pos_in_line << ":\n";
139  message << "Offending code: " << code;
140  message << " " << std::string(pos_in_line-1, ' ') << "^";
141 
142  return message.str();
143  }
144 } // anonymous namespace
145 
146 std::string jevois::getPythonExceptionString(boost::python::error_already_set &)
147 {
148  // Get some info from the python exception:
149  PyObject *t, *v, *tb;
150 
151  try
152  {
153  PyErr_Fetch(&t, &v, &tb);
154  PyErr_NormalizeException(&t, &v, &tb);
155  }
156  catch (...) { return "Internal error trying to fetch exception data from Python"; }
157 
158  try
159  {
160  boost::python::object objtype = ::ptr_to_obj(t);
161  boost::python::object objvalue = ::ptr_to_obj(v);
162  boost::python::object objtraceback = ::ptr_to_obj(tb);
163 
164  std::string const type = extract_exception_type(objtype);
165  std::string const message = (type == "SyntaxError") ?
166  make_syntax_error_message(objvalue) : extract_message(objvalue);
167  traceback const traceback = extract_traceback(objtraceback);
168 
169  PyErr_Restore(t, v, tb);
170  clear_exception();
171 
172  return generate_message(type, message, traceback);
173  }
174  catch (...)
175  { PyErr_Restore(t, v, tb); clear_exception(); return "Internal error trying to fetch exception data from Python"; }
176 }
jevois::Range::operator<<
std::ostream & operator<<(std::ostream &out, Range< T > const &r)
Stream out as "[min ... max]".
jevois::getPythonExceptionString
std::string getPythonExceptionString(boost::python::error_already_set &)
Python exception translation to string so we can print the traceback to our serlog stream.
Definition: PythonException.C:146
PythonException.H