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
10 #include "pybind11_tests.h"
11
12 // A type that should be raised as an exception in Python
13 class MyException : public std::exception {
14 public:
MyException(const char * m)15 explicit MyException(const char * m) : message{m} {}
what() const16 const char * what() const noexcept override {return message.c_str();}
17 private:
18 std::string message = "";
19 };
20
21 // A type that should be translated to a standard Python exception
22 class MyException2 : public std::exception {
23 public:
MyException2(const char * m)24 explicit MyException2(const char * m) : message{m} {}
what() const25 const char * what() const noexcept override {return message.c_str();}
26 private:
27 std::string message = "";
28 };
29
30 // A type that is not derived from std::exception (and is thus unknown)
31 class MyException3 {
32 public:
MyException3(const char * m)33 explicit MyException3(const char * m) : message{m} {}
what() const34 virtual const char * what() const noexcept {return message.c_str();}
35 // Rule of 5 BEGIN: to preempt compiler warnings.
36 MyException3(const MyException3&) = default;
37 MyException3(MyException3&&) = default;
38 MyException3& operator=(const MyException3&) = default;
39 MyException3& operator=(MyException3&&) = default;
40 virtual ~MyException3() = default;
41 // Rule of 5 END.
42 private:
43 std::string message = "";
44 };
45
46 // A type that should be translated to MyException
47 // and delegated to its exception translator
48 class MyException4 : public std::exception {
49 public:
MyException4(const char * m)50 explicit MyException4(const char * m) : message{m} {}
what() const51 const char * what() const noexcept override {return message.c_str();}
52 private:
53 std::string message = "";
54 };
55
56
57 // Like the above, but declared via the helper function
58 class MyException5 : public std::logic_error {
59 public:
MyException5(const std::string & what)60 explicit MyException5(const std::string &what) : std::logic_error(what) {}
61 };
62
63 // Inherits from MyException5
64 class MyException5_1 : public MyException5 {
65 using MyException5::MyException5;
66 };
67
68 struct PythonCallInDestructor {
PythonCallInDestructorPythonCallInDestructor69 PythonCallInDestructor(const py::dict &d) : d(d) {}
~PythonCallInDestructorPythonCallInDestructor70 ~PythonCallInDestructor() { d["good"] = true; }
71
72 py::dict d;
73 };
74
75
76
77 struct PythonAlreadySetInDestructor {
PythonAlreadySetInDestructorPythonAlreadySetInDestructor78 PythonAlreadySetInDestructor(const py::str &s) : s(s) {}
~PythonAlreadySetInDestructorPythonAlreadySetInDestructor79 ~PythonAlreadySetInDestructor() {
80 py::dict foo;
81 try {
82 // Assign to a py::object to force read access of nonexistent dict entry
83 py::object o = foo["bar"];
84 }
85 catch (py::error_already_set& ex) {
86 ex.discard_as_unraisable(s);
87 }
88 }
89
90 py::str s;
91 };
92
93
TEST_SUBMODULE(exceptions,m)94 TEST_SUBMODULE(exceptions, m) {
95 m.def("throw_std_exception", []() {
96 throw std::runtime_error("This exception was intentionally thrown.");
97 });
98
99 // make a new custom exception and use it as a translation target
100 static py::exception<MyException> ex(m, "MyException");
101 py::register_exception_translator([](std::exception_ptr p) {
102 try {
103 if (p) std::rethrow_exception(p);
104 } catch (const MyException &e) {
105 // Set MyException as the active python error
106 ex(e.what());
107 }
108 });
109
110 // register new translator for MyException2
111 // no need to store anything here because this type will
112 // never by visible from Python
113 py::register_exception_translator([](std::exception_ptr p) {
114 try {
115 if (p) std::rethrow_exception(p);
116 } catch (const MyException2 &e) {
117 // Translate this exception to a standard RuntimeError
118 PyErr_SetString(PyExc_RuntimeError, e.what());
119 }
120 });
121
122 // register new translator for MyException4
123 // which will catch it and delegate to the previously registered
124 // translator for MyException by throwing a new exception
125 py::register_exception_translator([](std::exception_ptr p) {
126 try {
127 if (p) std::rethrow_exception(p);
128 } catch (const MyException4 &e) {
129 throw MyException(e.what());
130 }
131 });
132
133 // A simple exception translation:
134 auto ex5 = py::register_exception<MyException5>(m, "MyException5");
135 // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
136 py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
137
138 m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
139 m.def("throws2", []() { throw MyException2("this error should go to a standard Python exception"); });
140 m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
141 m.def("throws4", []() { throw MyException4("this error is rethrown"); });
142 m.def("throws5", []() { throw MyException5("this is a helper-defined translated exception"); });
143 m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
144 m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
145 m.def("throws_overflow_error", []() {throw std::overflow_error(""); });
146 m.def("exception_matches", []() {
147 py::dict foo;
148 try {
149 // Assign to a py::object to force read access of nonexistent dict entry
150 py::object o = foo["bar"];
151 }
152 catch (py::error_already_set& ex) {
153 if (!ex.matches(PyExc_KeyError)) throw;
154 return true;
155 }
156 return false;
157 });
158 m.def("exception_matches_base", []() {
159 py::dict foo;
160 try {
161 // Assign to a py::object to force read access of nonexistent dict entry
162 py::object o = foo["bar"];
163 }
164 catch (py::error_already_set &ex) {
165 if (!ex.matches(PyExc_Exception)) throw;
166 return true;
167 }
168 return false;
169 });
170 m.def("modulenotfound_exception_matches_base", []() {
171 try {
172 // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
173 py::module_::import("nonexistent");
174 }
175 catch (py::error_already_set &ex) {
176 if (!ex.matches(PyExc_ImportError)) throw;
177 return true;
178 }
179 return false;
180 });
181
182 m.def("throw_already_set", [](bool err) {
183 if (err)
184 PyErr_SetString(PyExc_ValueError, "foo");
185 try {
186 throw py::error_already_set();
187 } catch (const std::runtime_error& e) {
188 if ((err && e.what() != std::string("ValueError: foo")) ||
189 (!err && e.what() != std::string("Unknown internal error occurred")))
190 {
191 PyErr_Clear();
192 throw std::runtime_error("error message mismatch");
193 }
194 }
195 PyErr_Clear();
196 if (err)
197 PyErr_SetString(PyExc_ValueError, "foo");
198 throw py::error_already_set();
199 });
200
201 m.def("python_call_in_destructor", [](py::dict d) {
202 try {
203 PythonCallInDestructor set_dict_in_destructor(d);
204 PyErr_SetString(PyExc_ValueError, "foo");
205 throw py::error_already_set();
206 } catch (const py::error_already_set&) {
207 return true;
208 }
209 return false;
210 });
211
212 m.def("python_alreadyset_in_destructor", [](py::str s) {
213 PythonAlreadySetInDestructor alreadyset_in_destructor(s);
214 return true;
215 });
216
217 // test_nested_throws
218 m.def("try_catch", [m](py::object exc_type, py::function f, py::args args) {
219 try { f(*args); }
220 catch (py::error_already_set &ex) {
221 if (ex.matches(exc_type))
222 py::print(ex.what());
223 else
224 throw;
225 }
226 });
227
228 // Test repr that cannot be displayed
229 m.def("simple_bool_passthrough", [](bool x) {return x;});
230
231 }
232