lsst.pex.exceptions  13.0-1-g52a7baa+8
 All Classes Namespaces Files Functions Variables Typedefs Macros Pages
exceptions.cc
Go to the documentation of this file.
1 #include "pybind11/pybind11.h"
2 
3 #include <sstream>
4 
7 
8 using namespace lsst::pex::exceptions;
9 
10 namespace py = pybind11;
11 
12 namespace lsst {
13 namespace pex {
14 namespace exceptions {
15 namespace {
16 // Helper function to show a Python warning when exception translation fails.
17 void tryLsstExceptionWarn(const char *message) {
18  // Try to warn that exception translation failed, if we fail, clear the exception raised by the
19  // warning attempt so we can raise a less-informative exception based on the original.
20  int s = PyErr_WarnEx(PyExc_Warning, message, 1);
21  if (s) {
22  PyErr_Clear();
23  }
24 }
25 
26 // Raise a Python exception that wraps the given C++ exception instance.
27 //
28 // Most of the work is delegated to the pure-Python function pex.exceptions.wrappers.translate(),
29 // which looks up the appropriate Python exception class from a dict that maps C++ exception
30 // types to their custom Python wrappers. Everything else here is basically just importing that
31 // module, preparing the arguments, and calling that function, along with the very verbose error
32 // handling required by the Python C API.
33 //
34 // If any point we fail to translate the exception, we print a Python warning and raise the built-in
35 // Python RuntimeError exception with the same message as the C++ exception.
36 void raiseLsstException(py::object &pyex) {
37  static auto module =
38  py::reinterpret_borrow<py::object>(PyImport_ImportModule("lsst.pex.exceptions.wrappers"));
39  if (!module.ptr()) {
40  tryLsstExceptionWarn("Failed to import C++ Exception wrapper module.");
41  } else {
42  static auto translate =
43  py::reinterpret_borrow<py::object>(PyObject_GetAttrString(module.ptr(), "translate"));
44  if (!translate.ptr()) {
45  tryLsstExceptionWarn("Failed to find translation function for C++ Exceptions.");
46  } else {
47  // Calling the Python translate() returns an instance of the appropriate Python
48  // exception that wraps the C++ exception instance that we give it.
49  auto instance = py::reinterpret_steal<py::object>(
50  PyObject_CallFunctionObjArgs(translate.ptr(), pyex.ptr(), NULL));
51  if (!instance.ptr()) {
52  // We actually expect a null return here, as translate() should raise an exception
53  tryLsstExceptionWarn("Failed to translate C++ Exception to Python.");
54  } else {
55  auto type = py::reinterpret_borrow<py::object>(PyObject_Type(instance.ptr()));
56  PyErr_SetObject(type.ptr(), instance.ptr());
57  }
58  }
59  }
60 }
61 } // <anonymous>
62 
63 PYBIND11_PLUGIN(exceptions) {
64  py::module mod("exceptions");
65 
66  py::class_<Tracepoint> clsTracepoint(mod, "Tracepoint");
67 
68  clsTracepoint.def(py::init<char const *, int, char const *, std::string const &>())
69  .def_readwrite("_file", &Tracepoint::_file)
70  .def_readwrite("_line", &Tracepoint::_line)
71  .def_readwrite("_func", &Tracepoint::_func)
72  .def_readwrite("_message", &Tracepoint::_message);
73 
74  py::class_<Exception> clsException(mod, "Exception");
75 
76  clsException.def(py::init<std::string const &>())
77  .def("addMessage", &Exception::addMessage)
78  .def("getTraceback", &Exception::getTraceback)
79  .def("addToStream", &Exception::addToStream)
80  .def("what", &Exception::what)
81  .def("getType", &Exception::getType)
82  .def("clone", &Exception::clone)
83  .def("asString",
84  [](Exception &self) -> std::string {
85  std::ostringstream stream;
86  self.addToStream(stream);
87  return stream.str();
88  })
89  .def("__repr__", [](Exception &self) -> std::string {
90  std::stringstream s;
91  s << "Exception('" << self.what() << "')";
92  return s.str();
93  });
94 
95  py::class_<LogicError, Exception> clsLogicError(mod, "LogicError");
96  clsLogicError.def(py::init<std::string const &>());
97 
98  py::class_<NotFoundError, Exception> clsNotFoundError(mod, "NotFoundError");
99  clsNotFoundError.def(py::init<std::string const &>());
100 
101  py::class_<RuntimeError, Exception> clsRuntimeError(mod, "RuntimeError");
102  clsRuntimeError.def(py::init<std::string const &>());
103 
104  py::class_<IoError, RuntimeError> clsIoError(mod, "IoError");
105  clsIoError.def(py::init<std::string const &>());
106 
107  py::class_<MemoryError, RuntimeError> clsMemoryError(mod, "MemoryError");
108  clsMemoryError.def(py::init<std::string const &>());
109 
110  py::class_<OverflowError, RuntimeError> clsOverflowError(mod, "OverflowError");
111  clsOverflowError.def(py::init<std::string const &>());
112 
113  py::class_<RangeError, RuntimeError> clsRangeError(mod, "RangeError");
114  clsRangeError.def(py::init<std::string const &>());
115 
116  py::class_<TimeoutError, RuntimeError> clsTimeoutError(mod, "TimeoutError");
117  clsTimeoutError.def(py::init<std::string const &>());
118 
119  py::class_<TypeError, RuntimeError> clsTypeError(mod, "TypeError");
120  clsTypeError.def(py::init<std::string const &>());
121 
122  py::class_<UnderflowError, RuntimeError> clsUnderflowError(mod, "UnderflowError");
123  clsUnderflowError.def(py::init<std::string const &>());
124 
125  py::class_<DomainError, LogicError> clsDomainError(mod, "DomainError");
126  clsDomainError.def(py::init<std::string const &>());
127 
128  py::class_<InvalidParameterError, LogicError> clsInvalidParameterError(mod, "InvalidParameterError");
129  clsInvalidParameterError.def(py::init<std::string const &>());
130 
131  py::class_<LengthError, LogicError> clsLengthError(mod, "LengthError");
132  clsLengthError.def(py::init<std::string const &>());
133 
134  py::class_<OutOfRangeError, LogicError> clsOutOfRangeError(mod, "OutOfRangeError");
135  clsOutOfRangeError.def(py::init<std::string const &>());
136 
137  py::register_exception_translator([](std::exception_ptr p) {
138  try {
139  if (p) std::rethrow_exception(p);
140  } catch (const Exception &e) {
141  py::object current_exception;
142  current_exception = py::cast(e.clone(), py::return_value_policy::take_ownership);
143  raiseLsstException(current_exception);
144  }
145  });
146 
147  return mod.ptr();
148 }
149 
150 } // exceptions
151 } // pex
152 } // lsst
void addMessage(char const *file, int line, char const *func, std::string const &message)
Add a tracepoint and a message to an exception before rethrowing it (access via LSST_EXCEPT_ADD).
Definition: Exception.cc:55
virtual char const * what(void) const
Return a character string summarizing this exception.
Definition: Exception.cc:112
PYBIND11_PLUGIN(exceptions)
Definition: exceptions.cc:63
virtual std::ostream & addToStream(std::ostream &stream) const
Add a text representation of this exception, including its traceback with messages, to a stream.
Definition: Exception.cc:93
Traceback const & getTraceback(void) const
Retrieve the list of tracepoints associated with an exception.
Definition: Exception.cc:89
virtual Exception * clone(void) const
Return a copy of the exception as an Exception*.
Definition: Exception.cc:120
virtual char const * getType(void) const
Return the fully-specified C++ type of a pointer to the exception.
Definition: Exception.cc:116