test_exceptions.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. tests/test_custom-exceptions.cpp -- exception translation
  3. Copyright (c) 2016 Pim Schellart <[email protected]>
  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. 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. struct PythonCallInDestructor {
  51. PythonCallInDestructor(const py::dict &d) : d(d) {}
  52. ~PythonCallInDestructor() { d["good"] = true; }
  53. py::dict d;
  54. };
  55. TEST_SUBMODULE(exceptions, m) {
  56. m.def("throw_std_exception", []() {
  57. throw std::runtime_error("This exception was intentionally thrown.");
  58. });
  59. // make a new custom exception and use it as a translation target
  60. static py::exception<MyException> ex(m, "MyException");
  61. py::register_exception_translator([](std::exception_ptr p) {
  62. try {
  63. if (p) std::rethrow_exception(p);
  64. } catch (const MyException &e) {
  65. // Set MyException as the active python error
  66. ex(e.what());
  67. }
  68. });
  69. // register new translator for MyException2
  70. // no need to store anything here because this type will
  71. // never by visible from Python
  72. py::register_exception_translator([](std::exception_ptr p) {
  73. try {
  74. if (p) std::rethrow_exception(p);
  75. } catch (const MyException2 &e) {
  76. // Translate this exception to a standard RuntimeError
  77. PyErr_SetString(PyExc_RuntimeError, e.what());
  78. }
  79. });
  80. // register new translator for MyException4
  81. // which will catch it and delegate to the previously registered
  82. // translator for MyException by throwing a new exception
  83. py::register_exception_translator([](std::exception_ptr p) {
  84. try {
  85. if (p) std::rethrow_exception(p);
  86. } catch (const MyException4 &e) {
  87. throw MyException(e.what());
  88. }
  89. });
  90. // A simple exception translation:
  91. auto ex5 = py::register_exception<MyException5>(m, "MyException5");
  92. // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
  93. py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
  94. m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
  95. m.def("throws2", []() { throw MyException2("this error should go to a standard Python exception"); });
  96. m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
  97. m.def("throws4", []() { throw MyException4("this error is rethrown"); });
  98. m.def("throws5", []() { throw MyException5("this is a helper-defined translated exception"); });
  99. m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
  100. m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
  101. m.def("exception_matches", []() {
  102. py::dict foo;
  103. try { foo["bar"]; }
  104. catch (py::error_already_set& ex) {
  105. if (!ex.matches(PyExc_KeyError)) throw;
  106. }
  107. });
  108. m.def("throw_already_set", [](bool err) {
  109. if (err)
  110. PyErr_SetString(PyExc_ValueError, "foo");
  111. try {
  112. throw py::error_already_set();
  113. } catch (const std::runtime_error& e) {
  114. if ((err && e.what() != std::string("ValueError: foo")) ||
  115. (!err && e.what() != std::string("Unknown internal error occurred")))
  116. {
  117. PyErr_Clear();
  118. throw std::runtime_error("error message mismatch");
  119. }
  120. }
  121. PyErr_Clear();
  122. if (err)
  123. PyErr_SetString(PyExc_ValueError, "foo");
  124. throw py::error_already_set();
  125. });
  126. m.def("python_call_in_destructor", [](py::dict d) {
  127. try {
  128. PythonCallInDestructor set_dict_in_destructor(d);
  129. PyErr_SetString(PyExc_ValueError, "foo");
  130. throw py::error_already_set();
  131. } catch (const py::error_already_set&) {
  132. return true;
  133. }
  134. return false;
  135. });
  136. // test_nested_throws
  137. m.def("try_catch", [m](py::object exc_type, py::function f, py::args args) {
  138. try { f(*args); }
  139. catch (py::error_already_set &ex) {
  140. if (ex.matches(exc_type))
  141. py::print(ex.what());
  142. else
  143. throw;
  144. }
  145. });
  146. }