cppyabm  1.0.17
An agent-based library to integrate C++ and Python
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 
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:
15  explicit MyException(const char * m) : message{m} {}
16  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:
24  explicit MyException2(const char * m) : message{m} {}
25  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:
33  explicit MyException3(const char * m) : message{m} {}
34  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;
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:
50  explicit MyException4(const char * m) : message{m} {}
51  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:
60  explicit MyException5(const std::string &what) : std::logic_error(what) {}
61 };
62 
63 // Inherits from MyException5
64 class MyException5_1 : public MyException5 {
66 };
67 
69  PythonCallInDestructor(const py::dict &d) : d(d) {}
70  ~PythonCallInDestructor() { d["good"] = true; }
71 
72  py::dict d;
73 };
74 
75 
76 
78  PythonAlreadySetInDestructor(const py::str &s) : s(s) {}
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 
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 }
test_builtin_casters.x
x
Definition: test_builtin_casters.py:467
MyException3::MyException3
MyException3(MyException3 &&)=default
PythonAlreadySetInDestructor::~PythonAlreadySetInDestructor
~PythonAlreadySetInDestructor()
Definition: test_exceptions.cpp:79
MyException3
Definition: test_exceptions.cpp:31
PythonCallInDestructor::d
py::dict d
Definition: test_exceptions.cpp:72
MyException2
Definition: test_exceptions.cpp:22
MyException3::MyException3
MyException3(const char *m)
Definition: test_exceptions.cpp:33
PythonCallInDestructor::~PythonCallInDestructor
~PythonCallInDestructor()
Definition: test_exceptions.cpp:70
MyException5::MyException5
MyException5(const std::string &what)
Definition: test_exceptions.cpp:60
MyException4::what
const char * what() const noexcept override
Definition: test_exceptions.cpp:51
MyException3::operator=
MyException3 & operator=(const MyException3 &)=default
PythonAlreadySetInDestructor
Definition: test_exceptions.cpp:77
PythonAlreadySetInDestructor::PythonAlreadySetInDestructor
PythonAlreadySetInDestructor(const py::str &s)
Definition: test_exceptions.cpp:78
PythonCallInDestructor::PythonCallInDestructor
PythonCallInDestructor(const py::dict &d)
Definition: test_exceptions.cpp:69
MyException4::MyException4
MyException4(const char *m)
Definition: test_exceptions.cpp:50
PythonAlreadySetInDestructor::s
py::str s
Definition: test_exceptions.cpp:90
MyException3::~MyException3
virtual ~MyException3()=default
MyException::what
const char * what() const noexcept override
Definition: test_exceptions.cpp:16
MyException2::what
const char * what() const noexcept override
Definition: test_exceptions.cpp:25
MyException5
Definition: test_exceptions.cpp:58
MyException2::MyException2
MyException2(const char *m)
Definition: test_exceptions.cpp:24
MyException5_1
Definition: test_exceptions.cpp:64
MyException3::what
virtual const char * what() const noexcept
Definition: test_exceptions.cpp:34
pybind11_tests.h
args
Definition: pytypes.h:1366
TEST_SUBMODULE
TEST_SUBMODULE(exceptions, m)
Definition: test_exceptions.cpp:94
MyException3::MyException3
MyException3(const MyException3 &)=default
MyException
Definition: test_exceptions.cpp:13
register_exception_translator
void register_exception_translator(ExceptionTranslator &&translator)
Definition: pybind11.h:1991
MyException::MyException
MyException(const char *m)
Definition: test_exceptions.cpp:15
test_async.m
m
Definition: test_async.py:5
PythonCallInDestructor
Definition: test_exceptions.cpp:68
MyException3::operator=
MyException3 & operator=(MyException3 &&)=default
MyException4
Definition: test_exceptions.cpp:48
print
PYBIND11_NOINLINE void print(tuple args, dict kwargs)
Definition: pybind11.h:2056