blob: 01efc700ee1bb80d87e6764e8ccc2cb34166fefd [file] [log] [blame]
Pim Schellart5a7d17f2016-06-17 17:35:59 -04001/*
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02002 tests/test_custom-exceptions.cpp -- exception translation
Pim Schellart5a7d17f2016-06-17 17:35:59 -04003
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
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020010#include "pybind11_tests.h"
Pim Schellart5a7d17f2016-06-17 17:35:59 -040011
12// A type that should be raised as an exeption in Python
13class MyException : public std::exception {
14public:
15 explicit MyException(const char * m) : message{m} {}
16 virtual const char * what() const noexcept override {return message.c_str();}
17private:
18 std::string message = "";
19};
20
21// A type that should be translated to a standard Python exception
22class MyException2 : public std::exception {
23public:
24 explicit MyException2(const char * m) : message{m} {}
25 virtual const char * what() const noexcept override {return message.c_str();}
26private:
27 std::string message = "";
28};
29
30// A type that is not derived from std::exception (and is thus unknown)
31class MyException3 {
32public:
33 explicit MyException3(const char * m) : message{m} {}
34 virtual const char * what() const noexcept {return message.c_str();}
35private:
36 std::string message = "";
37};
38
39// A type that should be translated to MyException
40// and delegated to its exception translator
41class MyException4 : public std::exception {
42public:
43 explicit MyException4(const char * m) : message{m} {}
44 virtual const char * what() const noexcept override {return message.c_str();}
45private:
46 std::string message = "";
47};
48
Jason Rhinelanderb3794f12016-09-16 02:04:15 -040049
50// Like the above, but declared via the helper function
51class MyException5 : public std::logic_error {
52public:
53 explicit MyException5(const std::string &what) : std::logic_error(what) {}
54};
55
56// Inherits from MyException5
57class MyException5_1 : public MyException5 {
58 using MyException5::MyException5;
59};
60
Pim Schellart5a7d17f2016-06-17 17:35:59 -040061void throws1() {
62 throw MyException("this error should go to a custom type");
63}
64
65void throws2() {
66 throw MyException2("this error should go to a standard Python exception");
67}
68
69void throws3() {
70 throw MyException3("this error cannot be translated");
71}
72
73void throws4() {
74 throw MyException4("this error is rethrown");
75}
76
Jason Rhinelanderb3794f12016-09-16 02:04:15 -040077void throws5() {
78 throw MyException5("this is a helper-defined translated exception");
79}
80
81void throws5_1() {
82 throw MyException5_1("MyException5 subclass");
83}
84
Pim Schellart5a7d17f2016-06-17 17:35:59 -040085void throws_logic_error() {
86 throw std::logic_error("this error should fall through to the standard handler");
87}
88
Roman Miroshnychenko83a8a972017-04-02 23:38:50 +030089// Test error_already_set::matches() method
90void exception_matches() {
91 py::dict foo;
92 try {
93 foo["bar"];
94 }
95 catch (py::error_already_set& ex) {
96 if (ex.matches(PyExc_KeyError))
97 ex.clear();
98 else
99 throw;
100 }
101}
102
Dean Moldovan135ba8d2016-09-10 11:58:02 +0200103struct PythonCallInDestructor {
104 PythonCallInDestructor(const py::dict &d) : d(d) {}
Jason Rhinelander3f1ff3f2016-12-12 17:42:52 -0500105 ~PythonCallInDestructor() { d["good"] = true; }
Dean Moldovan135ba8d2016-09-10 11:58:02 +0200106
107 py::dict d;
108};
109
Jason Rhinelander52f4be82016-09-03 14:54:22 -0400110test_initializer custom_exceptions([](py::module &m) {
Dean Moldovan83e328f2017-06-09 00:44:49 +0200111 m.def("throw_std_exception", []() {
112 throw std::runtime_error("This exception was intentionally thrown.");
113 });
114
Pim Schellart5a7d17f2016-06-17 17:35:59 -0400115 // make a new custom exception and use it as a translation target
116 static py::exception<MyException> ex(m, "MyException");
117 py::register_exception_translator([](std::exception_ptr p) {
118 try {
119 if (p) std::rethrow_exception(p);
120 } catch (const MyException &e) {
Jason Rhinelanderb3794f12016-09-16 02:04:15 -0400121 // Set MyException as the active python error
122 ex(e.what());
Pim Schellart5a7d17f2016-06-17 17:35:59 -0400123 }
124 });
125
126 // register new translator for MyException2
127 // no need to store anything here because this type will
128 // never by visible from Python
129 py::register_exception_translator([](std::exception_ptr p) {
130 try {
131 if (p) std::rethrow_exception(p);
132 } catch (const MyException2 &e) {
Jason Rhinelanderb3794f12016-09-16 02:04:15 -0400133 // Translate this exception to a standard RuntimeError
Pim Schellart5a7d17f2016-06-17 17:35:59 -0400134 PyErr_SetString(PyExc_RuntimeError, e.what());
135 }
136 });
137
138 // register new translator for MyException4
139 // which will catch it and delegate to the previously registered
140 // translator for MyException by throwing a new exception
141 py::register_exception_translator([](std::exception_ptr p) {
142 try {
143 if (p) std::rethrow_exception(p);
144 } catch (const MyException4 &e) {
145 throw MyException(e.what());
146 }
147 });
148
Jason Rhinelanderb3794f12016-09-16 02:04:15 -0400149 // A simple exception translation:
150 auto ex5 = py::register_exception<MyException5>(m, "MyException5");
151 // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
152 py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
153
Pim Schellart5a7d17f2016-06-17 17:35:59 -0400154 m.def("throws1", &throws1);
155 m.def("throws2", &throws2);
156 m.def("throws3", &throws3);
157 m.def("throws4", &throws4);
Jason Rhinelanderb3794f12016-09-16 02:04:15 -0400158 m.def("throws5", &throws5);
159 m.def("throws5_1", &throws5_1);
Pim Schellart5a7d17f2016-06-17 17:35:59 -0400160 m.def("throws_logic_error", &throws_logic_error);
Roman Miroshnychenko83a8a972017-04-02 23:38:50 +0300161 m.def("exception_matches", &exception_matches);
Pim Schellart5a7d17f2016-06-17 17:35:59 -0400162
Ivan Smirnov67b54892016-09-07 21:10:16 +0100163 m.def("throw_already_set", [](bool err) {
164 if (err)
165 PyErr_SetString(PyExc_ValueError, "foo");
166 try {
167 throw py::error_already_set();
168 } catch (const std::runtime_error& e) {
169 if ((err && e.what() != std::string("ValueError: foo")) ||
170 (!err && e.what() != std::string("Unknown internal error occurred")))
171 {
172 PyErr_Clear();
173 throw std::runtime_error("error message mismatch");
174 }
175 }
176 PyErr_Clear();
177 if (err)
178 PyErr_SetString(PyExc_ValueError, "foo");
179 throw py::error_already_set();
180 });
Dean Moldovan135ba8d2016-09-10 11:58:02 +0200181
182 m.def("python_call_in_destructor", [](py::dict d) {
183 try {
184 PythonCallInDestructor set_dict_in_destructor(d);
185 PyErr_SetString(PyExc_ValueError, "foo");
186 throw py::error_already_set();
187 } catch (const py::error_already_set&) {
188 return true;
189 }
190 return false;
191 });
Ivan Smirnov67b54892016-09-07 21:10:16 +0100192});