μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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
19class MyException : public std::exception {
20public:
21 explicit MyException(const char *m) : message{m} {}
22 const char *what() const noexcept override { return message.c_str(); }
23
24private:
25 std::string message = "";
26};
27
28// A type that should be translated to a standard Python exception
29class MyException2 : public std::exception {
30public:
31 explicit MyException2(const char *m) : message{m} {}
32 const char *what() const noexcept override { return message.c_str(); }
33
34private:
35 std::string message = "";
36};
37
38// A type that is not derived from std::exception (and is thus unknown)
39class MyException3 {
40public:
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;
46 MyException3 &operator=(const MyException3 &) = default;
48 virtual ~MyException3() = default;
49 // Rule of 5 END.
50private:
51 std::string message = "";
52};
53
54// A type that should be translated to MyException
55// and delegated to its exception translator
56class MyException4 : public std::exception {
57public:
58 explicit MyException4(const char *m) : message{m} {}
59 const char *what() const noexcept override { return message.c_str(); }
60
61private:
62 std::string message = "";
63};
64
65// Like the above, but declared via the helper function
66class MyException5 : public std::logic_error {
67public:
68 explicit MyException5(const std::string &what) : std::logic_error(what) {}
69};
70
71// Inherits from MyException5
72class MyException5_1 : public MyException5 {
74};
75
76// Exception that will be caught via the module local translator.
77class MyException6 : public std::exception {
78public:
79 explicit MyException6(const char *m) : message{m} {}
80 const char *what() const noexcept override { return message.c_str(); }
81
82private:
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
105 py::str s;
106};
107
108TEST_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}
const char * what() const noexcept override
std::string message
MyException2(const char *m)
std::string message
virtual ~MyException3()=default
MyException3(const char *m)
MyException3 & operator=(MyException3 &&)=default
MyException3 & operator=(const MyException3 &)=default
MyException3(const MyException3 &)=default
virtual const char * what() const noexcept
MyException3(MyException3 &&)=default
std::string message
MyException4(const char *m)
const char * what() const noexcept override
MyException5(const std::string &what)
const char * what() const noexcept override
std::string message
MyException6(const char *m)
const char * what() const noexcept override
MyException(const char *m)
std::string message
Definition: pytypes.h:1776
#define TEST_SUBMODULE(name, variable)
PythonAlreadySetInDestructor(const py::str &s)
PythonCallInDestructor(const py::dict &d)