knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
PythonError.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of KnowRob, please consult
3  * https://github.com/knowrob/knowrob for license details.
4  */
5 
6 #include <boost/python.hpp>
7 #include "knowrob/integration/python/PythonError.h"
8 #include "knowrob/Logger.h"
9 
10 using namespace knowrob;
11 
13  static const std::string unknown = "UnknownError";
14  exc_type = unknown;
15  exc_msg.clear();
16  exc_trace.clear();
17  exc_file = std::nullopt;
18  exc_line = std::nullopt;
19 }
20 
22  : KnowRobError(errorData.exc_type, errorData.exc_msg, errorData.exc_trace),
23  errorData_(errorData) {
24  if (errorData_.exc_file.has_value()) {
25  setFile(errorData_.exc_file.value());
26  }
27  if (errorData_.exc_line.has_value()) {
28  setLine(errorData_.exc_line.value());
29  }
30 }
31 
33  : PythonError(makeErrorData()) {
34 }
35 
37  using namespace boost::python;
38 
39  ErrorData errorData;
40  if (!PyErr_Occurred()) {
41  // no python error occurred
42  KB_WARN("PythonError was created even though no error occurred in Python!");
43  return errorData;
44  }
45 
46  // try to fetch the Python error
47  PyObject * py_type, *py_value, *py_traceback;
48  PyErr_Fetch(&py_type, &py_value, &py_traceback);
49  if (!py_type) {
50  KB_WARN("PythonError was created but PyErr_Fetch returned nullptr for error type.");
51  return errorData;
52  }
53  PyErr_NormalizeException(&py_type, &py_value, &py_traceback);
54  if (py_traceback != nullptr) {
55  PyException_SetTraceback(py_value, py_traceback);
56  }
57 
58  // Get Boost handles to the Python objects so we get an easier API.
59  handle<> h_type(py_type);
60  handle<> h_value(allow_null(py_value));
61  handle<> h_traceback(allow_null(py_traceback));
62 
63  // extract name of error type
64  auto e_type = extract<std::string>(object(h_type).attr("__name__"));
65  if (e_type.check()) {
66  errorData.exc_type = e_type();
67  } else {
68  errorData.exc_type = "UnknownError";
69  }
70 
71  // extract error msg by using __str__ method of h_value
72  if (h_value) {
73  auto e_message = extract<std::string>(object(h_value).attr("__str__")());
74  if (e_message.check()) {
75  errorData.exc_msg = e_message();
76  }
77  }
78 
79  // finally handle traceback if any
80  if (h_traceback) {
81  object o_traceback(h_traceback);
82  // extract the line number from the traceback
83  auto tb_lineno = extract<long>(o_traceback.
84  attr("tb_lineno"));
85  if (tb_lineno.check()) {
86  errorData.exc_line = tb_lineno;
87  }
88  // extract the file path from the traceback
89  auto e_file_path = extract<std::string>(o_traceback.
90  attr("tb_frame").
91  attr("f_code").
92  attr("co_filename"));
93  if (e_file_path.check()) {
94  errorData.exc_file = e_file_path();
95  }
96 
97  // Import the `traceback` module and use it to format the exception.
98  object format_tb = import("traceback").
99  attr("format_tb");
100  // Extract the formatted traceback using `format_tb`
101  object formatted_list = format_tb(o_traceback);
102  object formatted_traceback = str("\n").join(formatted_list).slice(0, -1);
103  auto str_traceback = extract<std::string>(formatted_traceback);
104  if (str_traceback.check()) {
105  errorData.exc_trace = str_traceback();
106  }
107  }
108 
109  return errorData;
110 }
#define KB_WARN
Definition: Logger.h:27
void setLine(int line)
Definition: KnowRobError.h:56
void setFile(std::string_view file)
Definition: KnowRobError.h:51
static ErrorData makeErrorData()
Definition: PythonError.cpp:36
ErrorData errorData_
Definition: PythonError.h:31
TermRule & string()
Definition: terms.cpp:63
std::optional< std::string > exc_file
Definition: PythonError.h:28
std::optional< int > exc_line
Definition: PythonError.h:29