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 TEST_SUBMODULE(exceptions, m) {
109  m.def("throw_std_exception",
110  []() { throw std::runtime_error("This exception was intentionally thrown."); });
111 
112  // make a new custom exception and use it as a translation target
113  static py::exception<MyException> ex(m, "MyException");
114  py::register_exception_translator([](std::exception_ptr p) {
115  try {
116  if (p) {
117  std::rethrow_exception(p);
118  }
119  } catch (const MyException &e) {
120  // Set MyException as the active python error
121  ex(e.what());
122  }
123  });
124 
125  // register new translator for MyException2
126  // no need to store anything here because this type will
127  // never by visible from Python
128  py::register_exception_translator([](std::exception_ptr p) {
129  try {
130  if (p) {
131  std::rethrow_exception(p);
132  }
133  } catch (const MyException2 &e) {
134  // Translate this exception to a standard RuntimeError
135  PyErr_SetString(PyExc_RuntimeError, e.what());
136  }
137  });
138 
139  // register new translator for MyException4
140  // which will catch it and delegate to the previously registered
141  // translator for MyException by throwing a new exception
142  py::register_exception_translator([](std::exception_ptr p) {
143  try {
144  if (p) {
145  std::rethrow_exception(p);
146  }
147  } catch (const MyException4 &e) {
148  throw MyException(e.what());
149  }
150  });
151 
152  // A simple exception translation:
153  auto ex5 = py::register_exception<MyException5>(m, "MyException5");
154  // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
155  py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
156 
157  // py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException")
158 
159  py::register_local_exception_translator([](std::exception_ptr p) {
160  try {
161  if (p) {
162  std::rethrow_exception(p);
163  }
164  } catch (const MyException6 &e) {
165  PyErr_SetString(PyExc_RuntimeError, e.what());
166  }
167  });
168 
169  m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
170  m.def("throws2",
171  []() { throw MyException2("this error should go to a standard Python exception"); });
172  m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
173  m.def("throws4", []() { throw MyException4("this error is rethrown"); });
174  m.def("throws5",
175  []() { throw MyException5("this is a helper-defined translated exception"); });
176  m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
177  m.def("throws6", []() { throw MyException6("MyException6 only handled in this module"); });
178  m.def("throws_logic_error", []() {
179  throw std::logic_error("this error should fall through to the standard handler");
180  });
181  m.def("throws_overflow_error", []() { throw std::overflow_error(""); });
182  m.def("throws_local_error", []() { throw LocalException("never caught"); });
183  m.def("throws_local_simple_error", []() { throw LocalSimpleException("this mod"); });
184  m.def("exception_matches", []() {
185  py::dict foo;
186  try {
187  // Assign to a py::object to force read access of nonexistent dict entry
188  py::object o = foo["bar"];
189  } catch (py::error_already_set &ex) {
190  if (!ex.matches(PyExc_KeyError)) {
191  throw;
192  }
193  return true;
194  }
195  return false;
196  });
197  m.def("exception_matches_base", []() {
198  py::dict foo;
199  try {
200  // Assign to a py::object to force read access of nonexistent dict entry
201  py::object o = foo["bar"];
202  } catch (py::error_already_set &ex) {
203  if (!ex.matches(PyExc_Exception)) {
204  throw;
205  }
206  return true;
207  }
208  return false;
209  });
210  m.def("modulenotfound_exception_matches_base", []() {
211  try {
212  // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
213  py::module_::import("nonexistent");
214  } catch (py::error_already_set &ex) {
215  if (!ex.matches(PyExc_ImportError)) {
216  throw;
217  }
218  return true;
219  }
220  return false;
221  });
222 
223  m.def("throw_already_set", [](bool err) {
224  if (err) {
225  PyErr_SetString(PyExc_ValueError, "foo");
226  }
227  try {
228  throw py::error_already_set();
229  } catch (const std::runtime_error &e) {
230  if ((err && e.what() != std::string("ValueError: foo"))
231  || (!err
232  && e.what()
233  != std::string("Internal error: pybind11::error_already_set called "
234  "while Python error indicator not set."))) {
235  PyErr_Clear();
236  throw std::runtime_error("error message mismatch");
237  }
238  }
239  PyErr_Clear();
240  if (err) {
241  PyErr_SetString(PyExc_ValueError, "foo");
242  }
243  throw py::error_already_set();
244  });
245 
246  m.def("python_call_in_destructor", [](const py::dict &d) {
247  bool retval = false;
248  try {
249  PythonCallInDestructor set_dict_in_destructor(d);
250  PyErr_SetString(PyExc_ValueError, "foo");
251  throw py::error_already_set();
252  } catch (const py::error_already_set &) {
253  retval = true;
254  }
255  return retval;
256  });
257 
258  m.def("python_alreadyset_in_destructor", [](const py::str &s) {
259  PythonAlreadySetInDestructor alreadyset_in_destructor(s);
260  return true;
261  });
262 
263  // test_nested_throws
264  m.def("try_catch",
265  [m](const py::object &exc_type, const py::function &f, const py::args &args) {
266  try {
267  f(*args);
268  } catch (py::error_already_set &ex) {
269  if (ex.matches(exc_type)) {
270  py::print(ex.what());
271  } else {
272  // Simply `throw;` also works and is better, but using `throw ex;`
273  // here to cover that situation (as observed in the wild).
274  throw ex; // Invokes the copy ctor.
275  }
276  }
277  });
278 
279  // Test repr that cannot be displayed
280  m.def("simple_bool_passthrough", [](bool x) { return x; });
281 
282  m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
283 
284  m.def("raise_from", []() {
285  PyErr_SetString(PyExc_ValueError, "inner");
286  py::raise_from(PyExc_ValueError, "outer");
287  throw py::error_already_set();
288  });
289 
290  m.def("raise_from_already_set", []() {
291  try {
292  PyErr_SetString(PyExc_ValueError, "inner");
293  throw py::error_already_set();
294  } catch (py::error_already_set &e) {
295  py::raise_from(e, PyExc_ValueError, "outer");
296  throw py::error_already_set();
297  }
298  });
299 
300  m.def("throw_nested_exception", []() {
301  try {
302  throw std::runtime_error("Inner Exception");
303  } catch (const std::runtime_error &) {
304  std::throw_with_nested(std::runtime_error("Outer Exception"));
305  }
306  });
307 
308  m.def("error_already_set_what", [](const py::object &exc_type, const py::object &exc_value) {
309  PyErr_SetObject(exc_type.ptr(), exc_value.ptr());
310  std::string what = py::error_already_set().what();
311  bool py_err_set_after_what = (PyErr_Occurred() != nullptr);
312  PyErr_Clear();
313  return py::make_tuple(std::move(what), py_err_set_after_what);
314  });
315 
316  m.def("test_cross_module_interleaved_error_already_set", []() {
317  auto cm = py::module_::import("cross_module_interleaved_error_already_set");
318  auto interleaved_error_already_set
319  = reinterpret_cast<void (*)()>(PyLong_AsVoidPtr(cm.attr("funcaddr").ptr()));
320  interleaved_error_already_set();
321  });
322 
323  m.def("test_error_already_set_double_restore", [](bool dry_run) {
324  PyErr_SetString(PyExc_ValueError, "Random error.");
325  py::error_already_set e;
326  e.restore();
327  PyErr_Clear();
328  if (!dry_run) {
329  e.restore();
330  }
331  });
332 
333  // https://github.com/pybind/pybind11/issues/4075
334  m.def("test_pypy_oserror_normalization", []() {
335  try {
336  py::module_::import("io").attr("open")("this_filename_must_not_exist", "r");
337  } catch (const py::error_already_set &e) {
338  return py::str(e.what()); // str must be built before e goes out of scope.
339  }
340  return py::str("UNEXPECTED");
341  });
342 
343  m.def("test_fn_cast_int", [](const py::function &fn) {
344  // function returns None instead of int, should give a useful error message
345  fn().cast<int>();
346  });
347 }
MyException3::what
virtual const char * what() const noexcept
Definition: test_exceptions.cpp:42
Eigen::internal::print
EIGEN_STRONG_INLINE Packet4f print(const Packet4f &a)
Definition: NEON/PacketMath.h:3115
MyException4::message
std::string message
Definition: test_exceptions.cpp:62
s
RealScalar s
Definition: level1_cplx_impl.h:126
e
Array< double, 1, 3 > e(1./3., 0.5, 2.)
d
static const double d[K][N]
Definition: igam.h:11
fn
static double fn[10]
Definition: fresnl.c:103
MyException::message
std::string message
Definition: test_exceptions.cpp:25
x
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
Definition: gnuplot_common_settings.hh:12
MyException2::message
std::string message
Definition: test_exceptions.cpp:35
test_exceptions.h
MyException3
Definition: test_exceptions.cpp:39
PythonCallInDestructor
Definition: test_exceptions.cpp:86
PythonAlreadySetInDestructor::s
py::str s
Definition: test_exceptions.cpp:105
PythonAlreadySetInDestructor
Definition: test_exceptions.cpp:93
PythonCallInDestructor::d
py::dict d
Definition: test_exceptions.cpp:90
register_exception_translator
void register_exception_translator(ExceptionTranslator &&translator)
Definition: pybind11.h:2512
MyException::MyException
MyException(const char *m)
Definition: test_exceptions.cpp:21
process_shonan_timing_results.args
args
Definition: process_shonan_timing_results.py:163
MyException3::message
std::string message
Definition: test_exceptions.cpp:51
MyException2::MyException2
MyException2(const char *m)
Definition: test_exceptions.cpp:31
raise_from
void raise_from(PyObject *type, const char *message)
Definition: pytypes.h:780
MyException6
Definition: test_exceptions.cpp:77
MyException5_1
Definition: test_exceptions.cpp:72
MyException6::what
const char * what() const noexcept override
Definition: test_exceptions.cpp:80
make_tuple
tuple make_tuple()
Definition: cast.h:1248
foo
void foo(CV_QUALIFIER Matrix3d &m)
Definition: block_nonconst_ctor_on_const_xpr_0.cpp:11
shared_exception
Definition: test_exceptions.h:8
MyException3::MyException3
MyException3(const char *m)
Definition: test_exceptions.cpp:41
MyException2::what
const char * what() const noexcept override
Definition: test_exceptions.cpp:32
MyException2
Definition: test_exceptions.cpp:29
m
Matrix3f m
Definition: AngleAxis_mimic_euler.cpp:1
MyException6::message
std::string message
Definition: test_exceptions.cpp:83
register_local_exception_translator
void register_local_exception_translator(ExceptionTranslator &&translator)
Definition: pybind11.h:2523
MyException5
Definition: test_exceptions.cpp:66
MyException3::operator=
MyException3 & operator=(const MyException3 &)=default
tree::f
Point2(* f)(const Point3 &, OptionalJacobian< 2, 3 >)
Definition: testExpression.cpp:218
PythonAlreadySetInDestructor::~PythonAlreadySetInDestructor
~PythonAlreadySetInDestructor()
Definition: test_exceptions.cpp:95
MyException3::~MyException3
virtual ~MyException3()=default
LocalException
Definition: local_bindings.h:40
MyException
Definition: test_exceptions.cpp:19
pybind11_tests.h
std
Definition: BFloat16.h:88
args
Definition: pytypes.h:2163
p
float * p
Definition: Tutorial_Map_using.cpp:9
TEST_SUBMODULE
TEST_SUBMODULE(exceptions, m)
Definition: test_exceptions.cpp:108
cm
static const double cm
Definition: TimeOfArrivalExample.cpp:34
PythonCallInDestructor::~PythonCallInDestructor
~PythonCallInDestructor()
Definition: test_exceptions.cpp:88
MyException::what
const char * what() const noexcept override
Definition: test_exceptions.cpp:22
MyException5::MyException5
MyException5(const std::string &what)
Definition: test_exceptions.cpp:68
MyException6::MyException6
MyException6(const char *m)
Definition: test_exceptions.cpp:79
MyException4::MyException4
MyException4(const char *m)
Definition: test_exceptions.cpp:58
PythonAlreadySetInDestructor::PythonAlreadySetInDestructor
PythonAlreadySetInDestructor(const py::str &s)
Definition: test_exceptions.cpp:94
MyException4::what
const char * what() const noexcept override
Definition: test_exceptions.cpp:59
gtsam.examples.ShonanAveragingCLI.str
str
Definition: ShonanAveragingCLI.py:115
LocalSimpleException
Definition: local_bindings.h:50
MyException4
Definition: test_exceptions.cpp:56
PythonCallInDestructor::PythonCallInDestructor
PythonCallInDestructor(const py::dict &d)
Definition: test_exceptions.cpp:87
local_bindings.h


gtsam
Author(s):
autogenerated on Tue Jun 25 2024 03:05:28