test_exceptions.cpp
Go to the documentation of this file.
1 /*
2  tests/test_custom-exceptions.cpp -- exception translation
3 
4  Copyright (c) 2016 Pim Schellart <P.Schellart@princeton.edu>
5 
6  All rights reserved. Use of this source code is governed by a
7  BSD-style license that can be found in the LICENSE file.
8 */
9 #include "test_exceptions.h"
10 
11 #include "local_bindings.h"
12 #include "pybind11_tests.h"
13 
14 #include <exception>
15 #include <stdexcept>
16 #include <utility>
17 
18 // A type that should be raised as an exception in Python
19 class MyException : public std::exception {
20 public:
21  explicit MyException(const char *m) : message{m} {}
22  const char *what() const noexcept override { return message.c_str(); }
23 
24 private:
25  std::string message = "";
26 };
27 
28 // A type that should be translated to a standard Python exception
29 class MyException2 : public std::exception {
30 public:
31  explicit MyException2(const char *m) : message{m} {}
32  const char *what() const noexcept override { return message.c_str(); }
33 
34 private:
35  std::string message = "";
36 };
37 
38 // A type that is not derived from std::exception (and is thus unknown)
39 class MyException3 {
40 public:
41  explicit MyException3(const char *m) : message{m} {}
42  virtual const char *what() const noexcept { return message.c_str(); }
43  // Rule of 5 BEGIN: to preempt compiler warnings.
44  MyException3(const MyException3 &) = default;
45  MyException3(MyException3 &&) = default;
46  MyException3 &operator=(const MyException3 &) = default;
47  MyException3 &operator=(MyException3 &&) = default;
48  virtual ~MyException3() = default;
49  // Rule of 5 END.
50 private:
51  std::string message = "";
52 };
53 
54 // A type that should be translated to MyException
55 // and delegated to its exception translator
56 class MyException4 : public std::exception {
57 public:
58  explicit MyException4(const char *m) : message{m} {}
59  const char *what() const noexcept override { return message.c_str(); }
60 
61 private:
62  std::string message = "";
63 };
64 
65 // Like the above, but declared via the helper function
66 class MyException5 : public std::logic_error {
67 public:
68  explicit MyException5(const std::string &what) : std::logic_error(what) {}
69 };
70 
71 // Inherits from MyException5
72 class MyException5_1 : public MyException5 {
74 };
75 
76 // Exception that will be caught via the module local translator.
77 class MyException6 : public std::exception {
78 public:
79  explicit MyException6(const char *m) : message{m} {}
80  const char *what() const noexcept override { return message.c_str(); }
81 
82 private:
83  std::string message = "";
84 };
85 
87  explicit PythonCallInDestructor(const py::dict &d) : d(d) {}
88  ~PythonCallInDestructor() { d["good"] = true; }
89 
90  py::dict d;
91 };
92 
94  explicit PythonAlreadySetInDestructor(const py::str &s) : s(s) {}
96  py::dict foo;
97  try {
98  // Assign to a py::object to force read access of nonexistent dict entry
99  py::object o = foo["bar"];
100  } catch (py::error_already_set &ex) {
101  ex.discard_as_unraisable(s);
102  }
103  }
104 
106 };
107 
108 std::string error_already_set_what(const py::object &exc_type, const py::object &exc_value) {
109  PyErr_SetObject(exc_type.ptr(), exc_value.ptr());
110  return py::error_already_set().what();
111 }
112 
113 TEST_SUBMODULE(exceptions, m) {
114  m.def("throw_std_exception",
115  []() { throw std::runtime_error("This exception was intentionally thrown."); });
116 
117  // make a new custom exception and use it as a translation target
118  static py::exception<MyException> ex(m, "MyException");
119  py::register_exception_translator([](std::exception_ptr p) {
120  try {
121  if (p) {
122  std::rethrow_exception(p);
123  }
124  } catch (const MyException &e) {
125  // Set MyException as the active python error
126  ex(e.what());
127  }
128  });
129 
130  // register new translator for MyException2
131  // no need to store anything here because this type will
132  // never by visible from Python
133  py::register_exception_translator([](std::exception_ptr p) {
134  try {
135  if (p) {
136  std::rethrow_exception(p);
137  }
138  } catch (const MyException2 &e) {
139  // Translate this exception to a standard RuntimeError
140  PyErr_SetString(PyExc_RuntimeError, e.what());
141  }
142  });
143 
144  // register new translator for MyException4
145  // which will catch it and delegate to the previously registered
146  // translator for MyException by throwing a new exception
147  py::register_exception_translator([](std::exception_ptr p) {
148  try {
149  if (p) {
150  std::rethrow_exception(p);
151  }
152  } catch (const MyException4 &e) {
153  throw MyException(e.what());
154  }
155  });
156 
157  // A simple exception translation:
158  auto ex5 = py::register_exception<MyException5>(m, "MyException5");
159  // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
160  py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
161 
162  // py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException")
163 
164  py::register_local_exception_translator([](std::exception_ptr p) {
165  try {
166  if (p) {
167  std::rethrow_exception(p);
168  }
169  } catch (const MyException6 &e) {
170  PyErr_SetString(PyExc_RuntimeError, e.what());
171  }
172  });
173 
174  m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
175  m.def("throws2",
176  []() { throw MyException2("this error should go to a standard Python exception"); });
177  m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
178  m.def("throws4", []() { throw MyException4("this error is rethrown"); });
179  m.def("throws5",
180  []() { throw MyException5("this is a helper-defined translated exception"); });
181  m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
182  m.def("throws6", []() { throw MyException6("MyException6 only handled in this module"); });
183  m.def("throws_logic_error", []() {
184  throw std::logic_error("this error should fall through to the standard handler");
185  });
186  m.def("throws_overflow_error", []() { throw std::overflow_error(""); });
187  m.def("throws_local_error", []() { throw LocalException("never caught"); });
188  m.def("throws_local_simple_error", []() { throw LocalSimpleException("this mod"); });
189  m.def("exception_matches", []() {
190  py::dict foo;
191  try {
192  // Assign to a py::object to force read access of nonexistent dict entry
193  py::object o = foo["bar"];
194  } catch (py::error_already_set &ex) {
195  if (!ex.matches(PyExc_KeyError)) {
196  throw;
197  }
198  return true;
199  }
200  return false;
201  });
202  m.def("exception_matches_base", []() {
203  py::dict foo;
204  try {
205  // Assign to a py::object to force read access of nonexistent dict entry
206  py::object o = foo["bar"];
207  } catch (py::error_already_set &ex) {
208  if (!ex.matches(PyExc_Exception)) {
209  throw;
210  }
211  return true;
212  }
213  return false;
214  });
215  m.def("modulenotfound_exception_matches_base", []() {
216  try {
217  // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
218  py::module_::import("nonexistent");
219  } catch (py::error_already_set &ex) {
220  if (!ex.matches(PyExc_ImportError)) {
221  throw;
222  }
223  return true;
224  }
225  return false;
226  });
227 
228  m.def("throw_already_set", [](bool err) {
229  if (err) {
230  PyErr_SetString(PyExc_ValueError, "foo");
231  }
232  try {
233  throw py::error_already_set();
234  } catch (const std::runtime_error &e) {
235  if ((err && e.what() != std::string("ValueError: foo"))
236  || (!err
237  && e.what()
238  != std::string("Internal error: pybind11::error_already_set called "
239  "while Python error indicator not set."))) {
240  PyErr_Clear();
241  throw std::runtime_error("error message mismatch");
242  }
243  }
244  PyErr_Clear();
245  if (err) {
246  PyErr_SetString(PyExc_ValueError, "foo");
247  }
248  throw py::error_already_set();
249  });
250 
251  m.def("python_call_in_destructor", [](const py::dict &d) {
252  bool retval = false;
253  try {
254  PythonCallInDestructor set_dict_in_destructor(d);
255  PyErr_SetString(PyExc_ValueError, "foo");
256  throw py::error_already_set();
257  } catch (const py::error_already_set &) {
258  retval = true;
259  }
260  return retval;
261  });
262 
263  m.def("python_alreadyset_in_destructor", [](const py::str &s) {
264  PythonAlreadySetInDestructor alreadyset_in_destructor(s);
265  return true;
266  });
267 
268  // test_nested_throws
269  m.def("try_catch",
270  [m](const py::object &exc_type, const py::function &f, const py::args &args) {
271  try {
272  f(*args);
273  } catch (py::error_already_set &ex) {
274  if (ex.matches(exc_type)) {
275  py::print(ex.what());
276  } else {
277  // Simply `throw;` also works and is better, but using `throw ex;`
278  // here to cover that situation (as observed in the wild).
279  throw ex; // Invokes the copy ctor.
280  }
281  }
282  });
283 
284  // Test repr that cannot be displayed
285  m.def("simple_bool_passthrough", [](bool x) { return x; });
286 
287  m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
288 
289  m.def("raise_from", []() {
290  PyErr_SetString(PyExc_ValueError, "inner");
291  py::raise_from(PyExc_ValueError, "outer");
292  throw py::error_already_set();
293  });
294 
295  m.def("raise_from_already_set", []() {
296  try {
297  PyErr_SetString(PyExc_ValueError, "inner");
298  throw py::error_already_set();
299  } catch (py::error_already_set &e) {
300  py::raise_from(e, PyExc_ValueError, "outer");
301  throw py::error_already_set();
302  }
303  });
304 
305  m.def("throw_nested_exception", []() {
306  try {
307  throw std::runtime_error("Inner Exception");
308  } catch (const std::runtime_error &) {
309  std::throw_with_nested(std::runtime_error("Outer Exception"));
310  }
311  });
312 
313  m.def("error_already_set_what", [](const py::object &exc_type, const py::object &exc_value) {
314  PyErr_SetObject(exc_type.ptr(), exc_value.ptr());
315  std::string what = py::error_already_set().what();
316  bool py_err_set_after_what = (PyErr_Occurred() != nullptr);
317  PyErr_Clear();
318  return py::make_tuple(std::move(what), py_err_set_after_what);
319  });
320 
321  m.def("test_cross_module_interleaved_error_already_set", []() {
322  auto cm = py::module_::import("cross_module_interleaved_error_already_set");
323  auto interleaved_error_already_set
324  = reinterpret_cast<void (*)()>(PyLong_AsVoidPtr(cm.attr("funcaddr").ptr()));
325  interleaved_error_already_set();
326  });
327 
328  m.def("test_error_already_set_double_restore", [](bool dry_run) {
329  PyErr_SetString(PyExc_ValueError, "Random error.");
330  py::error_already_set e;
331  e.restore();
332  PyErr_Clear();
333  if (!dry_run) {
334  e.restore();
335  }
336  });
337 }
Matrix3f m
static const double cm
MyException4(const char *m)
PythonCallInDestructor(const py::dict &d)
tuple make_tuple()
Definition: cast.h:1209
const char * what() const noexcept override
Definition: pytypes.h:2012
void raise_from(PyObject *type, const char *message)
Definition: pytypes.h:655
Definition: BFloat16.h:88
MyException2(const char *m)
EIGEN_STRONG_INLINE Packet4f print(const Packet4f &a)
void register_exception_translator(ExceptionTranslator &&translator)
Definition: pybind11.h:2482
MyException6(const char *m)
void foo(CV_QUALIFIER Matrix3d &m)
Point2(* f)(const Point3 &, OptionalJacobian< 2, 3 >)
MyException(const char *m)
Array< double, 1, 3 > e(1./3., 0.5, 2.)
RealScalar s
MyException3(const char *m)
TEST_SUBMODULE(exceptions, m)
std::string error_already_set_what(const py::object &exc_type, const py::object &exc_value)
const char * what() const noexcept override
const char * what() const noexcept override
float * p
PythonAlreadySetInDestructor(const py::str &s)
MyException5(const std::string &what)
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy x
virtual const char * what() const noexcept
std::string message
void register_local_exception_translator(ExceptionTranslator &&translator)
Definition: pybind11.h:2493
const char * what() const noexcept override


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:37:45