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 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)
91 output.push_back(entry);
95 traceback extract_traceback(boost::python::object py_traceback)
97 if (py_traceback.is_none())
return traceback();
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);
105 std::string extract_exception_type(boost::python::object type)
107 if (PyExceptionClass_Check(type.ptr())) {
108 return PyExceptionClass_Name(type.ptr());
110 throw std::logic_error(
"Given type is not a standard python exception class");
114 std::string extract_message(boost::python::object value)
116 return boost::python::extract<std::string>(boost::python::str(value));
119 std::string generate_message(std::string
const & type, std::string
const & message, traceback
const & traceback)
121 std::ostringstream result;
122 if (traceback.empty() ==
false) result <<
"Python traceback (most recent calls last):\n" << traceback;
124 if (message.empty() ==
false) result <<
": " << message;
128 std::string make_syntax_error_message(boost::python::object error)
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"));
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());
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,
' ') <<
"^";
142 return message.str();
149 PyObject *t, *v, *tb;
153 PyErr_Fetch(&t, &v, &tb);
154 PyErr_NormalizeException(&t, &v, &tb);
156 catch (...) {
return "Internal error trying to fetch exception data from Python"; }
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);
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);
169 PyErr_Restore(t, v, tb);
172 return generate_message(type, message, traceback);
175 { PyErr_Restore(t, v, tb); clear_exception();
return "Internal error trying to fetch exception data from Python"; }