test_exceptions.cpp 7.7 KB

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