You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
5.8 KiB

2 months ago
  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 exeption in Python
  9. class MyException : public std::exception {
  10. public:
  11. explicit MyException(const char * m) : message{m} {}
  12. virtual 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. virtual 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. private:
  30. std::string message = "";
  31. };
  32. // A type that should be translated to MyException
  33. // and delegated to its exception translator
  34. class MyException4 : public std::exception {
  35. public:
  36. explicit MyException4(const char * m) : message{m} {}
  37. virtual const char * what() const noexcept override {return message.c_str();}
  38. private:
  39. std::string message = "";
  40. };
  41. // Like the above, but declared via the helper function
  42. class MyException5 : public std::logic_error {
  43. public:
  44. explicit MyException5(const std::string &what) : std::logic_error(what) {}
  45. };
  46. // Inherits from MyException5
  47. class MyException5_1 : public MyException5 {
  48. using MyException5::MyException5;
  49. };
  50. void throws1() {
  51. throw MyException("this error should go to a custom type");
  52. }
  53. void throws2() {
  54. throw MyException2("this error should go to a standard Python exception");
  55. }
  56. void throws3() {
  57. throw MyException3("this error cannot be translated");
  58. }
  59. void throws4() {
  60. throw MyException4("this error is rethrown");
  61. }
  62. void throws5() {
  63. throw MyException5("this is a helper-defined translated exception");
  64. }
  65. void throws5_1() {
  66. throw MyException5_1("MyException5 subclass");
  67. }
  68. void throws_logic_error() {
  69. throw std::logic_error("this error should fall through to the standard handler");
  70. }
  71. // Test error_already_set::matches() method
  72. void exception_matches() {
  73. py::dict foo;
  74. try {
  75. foo["bar"];
  76. }
  77. catch (py::error_already_set& ex) {
  78. if (ex.matches(PyExc_KeyError))
  79. ex.clear();
  80. else
  81. throw;
  82. }
  83. }
  84. struct PythonCallInDestructor {
  85. PythonCallInDestructor(const py::dict &d) : d(d) {}
  86. ~PythonCallInDestructor() { d["good"] = true; }
  87. py::dict d;
  88. };
  89. test_initializer custom_exceptions([](py::module &m) {
  90. m.def("throw_std_exception", []() {
  91. throw std::runtime_error("This exception was intentionally thrown.");
  92. });
  93. // make a new custom exception and use it as a translation target
  94. static py::exception<MyException> ex(m, "MyException");
  95. py::register_exception_translator([](std::exception_ptr p) {
  96. try {
  97. if (p) std::rethrow_exception(p);
  98. } catch (const MyException &e) {
  99. // Set MyException as the active python error
  100. ex(e.what());
  101. }
  102. });
  103. // register new translator for MyException2
  104. // no need to store anything here because this type will
  105. // never by visible from Python
  106. py::register_exception_translator([](std::exception_ptr p) {
  107. try {
  108. if (p) std::rethrow_exception(p);
  109. } catch (const MyException2 &e) {
  110. // Translate this exception to a standard RuntimeError
  111. PyErr_SetString(PyExc_RuntimeError, e.what());
  112. }
  113. });
  114. // register new translator for MyException4
  115. // which will catch it and delegate to the previously registered
  116. // translator for MyException by throwing a new exception
  117. py::register_exception_translator([](std::exception_ptr p) {
  118. try {
  119. if (p) std::rethrow_exception(p);
  120. } catch (const MyException4 &e) {
  121. throw MyException(e.what());
  122. }
  123. });
  124. // A simple exception translation:
  125. auto ex5 = py::register_exception<MyException5>(m, "MyException5");
  126. // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
  127. py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
  128. m.def("throws1", &throws1);
  129. m.def("throws2", &throws2);
  130. m.def("throws3", &throws3);
  131. m.def("throws4", &throws4);
  132. m.def("throws5", &throws5);
  133. m.def("throws5_1", &throws5_1);
  134. m.def("throws_logic_error", &throws_logic_error);
  135. m.def("exception_matches", &exception_matches);
  136. m.def("throw_already_set", [](bool err) {
  137. if (err)
  138. PyErr_SetString(PyExc_ValueError, "foo");
  139. try {
  140. throw py::error_already_set();
  141. } catch (const std::runtime_error& e) {
  142. if ((err && e.what() != std::string("ValueError: foo")) ||
  143. (!err && e.what() != std::string("Unknown internal error occurred")))
  144. {
  145. PyErr_Clear();
  146. throw std::runtime_error("error message mismatch");
  147. }
  148. }
  149. PyErr_Clear();
  150. if (err)
  151. PyErr_SetString(PyExc_ValueError, "foo");
  152. throw py::error_already_set();
  153. });
  154. m.def("python_call_in_destructor", [](py::dict d) {
  155. try {
  156. PythonCallInDestructor set_dict_in_destructor(d);
  157. PyErr_SetString(PyExc_ValueError, "foo");
  158. throw py::error_already_set();
  159. } catch (const py::error_already_set&) {
  160. return true;
  161. }
  162. return false;
  163. });
  164. });