20#include <frameobject.h>
24#include <boost/python/object.hpp>
25#include <boost/python/str.hpp>
26#include <boost/python/extract.hpp>
34 std::string python_string_as_std_string(PyObject* obj)
36#if PY_MAJOR_VERSION == 2
37 return PyString_AsString(obj);
39 return PyUnicode_AsUTF8(obj);
43 long python_integral_as_long(PyObject* obj)
45#if PY_MAJOR_VERSION == 2
46 return PyInt_AsLong(obj);
48 return PyLong_AsLong(obj);
52 void clear_exception()
57 boost::python::object ptr_to_obj(PyObject * ptr)
59 if (ptr)
return boost::python::object(boost::python::handle<>(boost::python::borrowed(ptr)));
60 else return boost::python::object();
66 std::string file_name;
70 std::ostream & operator<<(std::ostream& stream, traceback_step
const & info)
72 stream <<
"File " << info.file_name <<
"\nLine " << info.line_number <<
", in " << info.source <<
":";
76 typedef std::vector<traceback_step> traceback;
78 std::ostream & operator<<(std::ostream & stream, traceback
const & tb)
80 for (traceback_step
const & step : tb) stream << step <<
"\n";
84 void add_traceback_step(std::vector<traceback_step> & output, PyTracebackObject
const * pytb)
86 traceback_step entry =
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)
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)
99 output.push_back(entry);
103 traceback extract_traceback(boost::python::object py_traceback)
105 if (py_traceback.is_none())
return traceback();
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);
113 std::string extract_exception_type(boost::python::object type)
115 if (PyExceptionClass_Check(type.ptr())) {
116 return PyExceptionClass_Name(type.ptr());
118 throw std::logic_error(
"Given type is not a standard python exception class");
122 std::string extract_message(boost::python::object value)
124 return boost::python::extract<std::string>(boost::python::str(value));
127 std::string generate_message(std::string
const & type, std::string
const & message, traceback
const & traceback)
129 std::ostringstream result;
130 if (traceback.empty() ==
false) result <<
"Python traceback (most recent calls last):\n" << traceback;
132 if (message.empty() ==
false) result <<
": " << message;
136 std::string make_syntax_error_message(boost::python::object error)
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"));
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());
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,
' ') <<
"^";
150 return message.str();
157 PyObject *t, *v, *tb;
161 PyErr_Fetch(&t, &v, &tb);
162 PyErr_NormalizeException(&t, &v, &tb);
164 catch (...) {
return "Internal error trying to fetch exception data from Python"; }
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);
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);
177 PyErr_Restore(t, v, tb);
180 return generate_message(type, message, traceback);
183 { PyErr_Restore(t, v, tb); clear_exception();
return "Internal error trying to fetch exception data from Python"; }
std::string getPythonExceptionString(boost::python::error_already_set &)
Python exception translation to string so we can print the traceback to our serlog stream.