Scarab  2.8.1
Project 8 C++ Utility Library
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  virtual 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  virtual 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 private:
36  std::string message = "";
37 };
38 
39 // A type that should be translated to MyException
40 // and delegated to its exception translator
41 class MyException4 : public std::exception {
42 public:
43  explicit MyException4(const char * m) : message{m} {}
44  virtual const char * what() const noexcept override {return message.c_str();}
45 private:
46  std::string message = "";
47 };
48 
49 
50 // Like the above, but declared via the helper function
51 class MyException5 : public std::logic_error {
52 public:
53  explicit MyException5(const std::string &what) : std::logic_error(what) {}
54 };
55 
56 // Inherits from MyException5
57 class MyException5_1 : public MyException5 {
59 };
60 
62  PythonCallInDestructor(const py::dict &d) : d(d) {}
63  ~PythonCallInDestructor() { d["good"] = true; }
64 
66 };
67 
69  m.def("throw_std_exception", []() {
70  throw std::runtime_error("This exception was intentionally thrown.");
71  });
72 
73  // make a new custom exception and use it as a translation target
74  static py::exception<MyException> ex(m, "MyException");
75  py::register_exception_translator([](std::exception_ptr p) {
76  try {
77  if (p) std::rethrow_exception(p);
78  } catch (const MyException &e) {
79  // Set MyException as the active python error
80  ex(e.what());
81  }
82  });
83 
84  // register new translator for MyException2
85  // no need to store anything here because this type will
86  // never by visible from Python
87  py::register_exception_translator([](std::exception_ptr p) {
88  try {
89  if (p) std::rethrow_exception(p);
90  } catch (const MyException2 &e) {
91  // Translate this exception to a standard RuntimeError
92  PyErr_SetString(PyExc_RuntimeError, e.what());
93  }
94  });
95 
96  // register new translator for MyException4
97  // which will catch it and delegate to the previously registered
98  // translator for MyException by throwing a new exception
99  py::register_exception_translator([](std::exception_ptr p) {
100  try {
101  if (p) std::rethrow_exception(p);
102  } catch (const MyException4 &e) {
103  throw MyException(e.what());
104  }
105  });
106 
107  // A simple exception translation:
108  auto ex5 = py::register_exception<MyException5>(m, "MyException5");
109  // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
110  py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
111 
112  m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
113  m.def("throws2", []() { throw MyException2("this error should go to a standard Python exception"); });
114  m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
115  m.def("throws4", []() { throw MyException4("this error is rethrown"); });
116  m.def("throws5", []() { throw MyException5("this is a helper-defined translated exception"); });
117  m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
118  m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
119  m.def("exception_matches", []() {
120  py::dict foo;
121  try {
122  // Assign to a py::object to force read access of nonexistent dict entry
123  py::object o = foo["bar"];
124  }
125  catch (py::error_already_set& ex) {
126  if (!ex.matches(PyExc_KeyError)) throw;
127  return true;
128  }
129  return false;
130  });
131  m.def("exception_matches_base", []() {
132  py::dict foo;
133  try {
134  // Assign to a py::object to force read access of nonexistent dict entry
135  py::object o = foo["bar"];
136  }
137  catch (py::error_already_set &ex) {
138  if (!ex.matches(PyExc_Exception)) throw;
139  return true;
140  }
141  return false;
142  });
143  m.def("modulenotfound_exception_matches_base", []() {
144  try {
145  // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
146  py::module::import("nonexistent");
147  }
148  catch (py::error_already_set &ex) {
149  if (!ex.matches(PyExc_ImportError)) throw;
150  return true;
151  }
152  return false;
153  });
154 
155  m.def("throw_already_set", [](bool err) {
156  if (err)
157  PyErr_SetString(PyExc_ValueError, "foo");
158  try {
159  throw py::error_already_set();
160  } catch (const std::runtime_error& e) {
161  if ((err && e.what() != std::string("ValueError: foo")) ||
162  (!err && e.what() != std::string("Unknown internal error occurred")))
163  {
164  PyErr_Clear();
165  throw std::runtime_error("error message mismatch");
166  }
167  }
168  PyErr_Clear();
169  if (err)
170  PyErr_SetString(PyExc_ValueError, "foo");
171  throw py::error_already_set();
172  });
173 
174  m.def("python_call_in_destructor", [](py::dict d) {
175  try {
176  PythonCallInDestructor set_dict_in_destructor(d);
177  PyErr_SetString(PyExc_ValueError, "foo");
178  throw py::error_already_set();
179  } catch (const py::error_already_set&) {
180  return true;
181  }
182  return false;
183  });
184 
185  // test_nested_throws
186  m.def("try_catch", [m](py::object exc_type, py::function f, py::args args) {
187  try { f(*args); }
188  catch (py::error_already_set &ex) {
189  if (ex.matches(exc_type))
190  py::print(ex.what());
191  else
192  throw;
193  }
194  });
195 
196 }
virtual const char * what() const noexcept override
STL namespace.
MyException5(const std::string &what)
MyException(const char *m)
virtual const char * what() const noexcept override
PythonCallInDestructor(const py::dict &d)
def d(s)
Definition: mkdoc.py:69
void register_exception_translator(ExceptionTranslator &&translator)
Definition: pybind11.h:1751
MyException4(const char *m)
std::string message
bool matches(handle exc) const
Definition: pytypes.h:347
void print(Args &&...args)
Definition: pybind11.h:1849
#define TEST_SUBMODULE(name, variable)
virtual const char * what() const noexcept override
test_initializer exceptions("exceptions", test_submodule_exceptions)
virtual const char * what() const noexcept
MyException3(const char *m)
MyException2(const char *m)