μ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)
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
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 && e.what() != std::string("Unknown internal error occurred"))) {
232 PyErr_Clear();
233 throw std::runtime_error("error message mismatch");
234 }
235 }
236 PyErr_Clear();
237 if (err) {
238 PyErr_SetString(PyExc_ValueError, "foo");
239 }
240 throw py::error_already_set();
241 });
242
243 m.def("python_call_in_destructor", [](const py::dict &d) {
244 bool retval = false;
245 try {
246 PythonCallInDestructor set_dict_in_destructor(d);
247 PyErr_SetString(PyExc_ValueError, "foo");
248 throw py::error_already_set();
249 } catch (const py::error_already_set &) {
250 retval = true;
251 }
252 return retval;
253 });
254
255 m.def("python_alreadyset_in_destructor", [](const py::str &s) {
256 PythonAlreadySetInDestructor alreadyset_in_destructor(s);
257 return true;
258 });
259
260 // test_nested_throws
261 m.def("try_catch",
262 [m](const py::object &exc_type, const py::function &f, const py::args &args) {
263 try {
264 f(*args);
265 } catch (py::error_already_set &ex) {
266 if (ex.matches(exc_type)) {
267 py::print(ex.what());
268 } else {
269 throw;
270 }
271 }
272 });
273
274 // Test repr that cannot be displayed
275 m.def("simple_bool_passthrough", [](bool x) { return x; });
276
277 m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
278
279#if PY_VERSION_HEX >= 0x03030000
280
281 m.def("raise_from", []() {
282 PyErr_SetString(PyExc_ValueError, "inner");
283 py::raise_from(PyExc_ValueError, "outer");
284 throw py::error_already_set();
285 });
286
287 m.def("raise_from_already_set", []() {
288 try {
289 PyErr_SetString(PyExc_ValueError, "inner");
290 throw py::error_already_set();
291 } catch (py::error_already_set &e) {
292 py::raise_from(e, PyExc_ValueError, "outer");
293 throw py::error_already_set();
294 }
295 });
296
297 m.def("throw_nested_exception", []() {
298 try {
299 throw std::runtime_error("Inner Exception");
300 } catch (const std::runtime_error &) {
301 std::throw_with_nested(std::runtime_error("Outer Exception"));
302 }
303 });
304#endif
305}
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)