Advertisement
dan-masek

Distinguish C++ exception from Python in Boost Python

Dec 3rd, 2018
343
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.57 KB | None | 0 0
  1. /*
  2. References:
  3. https://stackoverflow.com/questions/2261858/boostpython-export-custom-exception
  4. https://stackoverflow.com/questions/9620268/boost-python-custom-exception-class
  5. https://stackoverflow.com/questions/11448735/boostpython-export-custom-exception-and-inherit-from-pythons-exception
  6.  
  7. https://github.com/boostorg/python/blob/develop/src/errors.cpp#L19
  8. https://docs.python.org/2/c-api/exceptions.html
  9.  
  10. and probably some more...
  11. */
  12. // ============================================================================
  13. #include <boost/python.hpp>
  14. #include <boost/core/typeinfo.hpp>
  15. #include <boost/stacktrace.hpp>
  16. #include <boost/exception/all.hpp>
  17. #include <iostream>
  18. // ============================================================================
  19. namespace bp = boost::python;
  20. // ============================================================================
  21. typedef boost::error_info<struct tag_stacktrace, boost::stacktrace::stacktrace> traced;
  22.  
  23. template <class E>
  24. void throw_with_trace(const E& e)
  25. {
  26.     throw boost::enable_error_info(e)
  27.         << traced(boost::stacktrace::stacktrace());
  28. }
  29. // ============================================================================
  30. std::string format_pyerror()
  31. {
  32.     PyObject *exc, *val, *tb;
  33.     bp::object formatted_list, formatted;
  34.     PyErr_Fetch(&exc, &val, &tb);
  35.     PyErr_NormalizeException(&exc, &val, &tb);
  36.  
  37.     bp::handle<> hexc(exc), hval(bp::allow_null(val)), htb(bp::allow_null(tb));
  38.     bp::object traceback(bp::import("traceback"));
  39.     if (!tb) {
  40.         bp::object format_exception_only(traceback.attr("format_exception_only"));
  41.         formatted_list = format_exception_only(hexc, hval);
  42.     } else {
  43.         bp::object format_exception(traceback.attr("format_exception"));
  44.         formatted_list = format_exception(hexc, hval, htb);
  45.     }
  46.     formatted = bp::str("").join(formatted_list);
  47.     return bp::extract<std::string>(formatted);
  48. }
  49. // ============================================================================
  50. void raise_cpp_ex(bool with_trace)
  51. {
  52.     if (with_trace) {
  53.         throw_with_trace(std::runtime_error("Error from C++"));
  54.     } else {
  55.         throw std::runtime_error("Error from C++");
  56.     }
  57. }
  58.  
  59. void raise_cpp_ex2()
  60. {
  61.     bp::object main = bp::import("__main__");
  62.     bp::object globals = main.attr("__dict__");
  63.     bp::dict locals;
  64.  
  65.     bp::exec("import foo\nfoo.raise_cpp_ex(True)\n", globals, locals);
  66. }
  67.  
  68. void raise_python_ex()
  69. {
  70.     bp::object main = bp::import("__main__");
  71.     bp::object globals = main.attr("__dict__");
  72.     bp::dict locals;
  73.  
  74.     bp::exec("raise RuntimeError('Error from Python')\n", globals, locals);
  75. }
  76. // ============================================================================
  77. template<typename T, PyObject** E>
  78. void translator(T const& x)
  79. {
  80.     PyObject *args = PyDict_New();
  81.     PyDict_SetItemString(args, "message", PyString_FromString(x.what()));
  82.     PyDict_SetItemString(args, "origin", PyString_FromString("C++"));
  83.  
  84.     boost::core::typeinfo const & ti = BOOST_CORE_TYPEID(x);
  85.     PyDict_SetItemString(args, "type", PyString_FromString(boost::core::demangled_name(ti).c_str()));
  86.  
  87.     const boost::stacktrace::stacktrace* st = boost::get_error_info<traced>(x);
  88.     if (st) {
  89.         std::stringstream ss;
  90.         ss << *st;
  91.         PyDict_SetItemString(args, "trace", PyString_FromString(ss.str().c_str()));
  92.     }  
  93.  
  94.     PyErr_SetObject(*E, args);
  95. }
  96. // ============================================================================
  97. BOOST_PYTHON_MODULE(foo)
  98. {
  99.     bp::register_exception_translator<boost::bad_numeric_cast>(translator<boost::bad_numeric_cast, &PyExc_OverflowError>);
  100.     bp::register_exception_translator<std::out_of_range>(translator<std::out_of_range, &PyExc_IndexError>);
  101.     bp::register_exception_translator<std::invalid_argument>(translator<std::invalid_argument, &PyExc_ValueError>);
  102.     bp::register_exception_translator<std::exception>(translator<std::exception, &PyExc_RuntimeError>);
  103.  
  104.     bp::def("raise_cpp_ex", &raise_cpp_ex);
  105.     bp::def("raise_python_ex", &raise_python_ex);
  106.     bp::def("raise_cpp_ex2", &raise_cpp_ex2);
  107. }
  108. // ============================================================================
  109. void test(std::string const& code)
  110. {
  111.     try {
  112.         bp::object main = bp::import("__main__");
  113.         bp::object globals = main.attr("__dict__");
  114.         bp::dict locals;
  115.  
  116.         bp::exec(code.c_str(), globals, locals);
  117.     } catch (bp::error_already_set&) {
  118.         if (PyErr_Occurred()) {
  119.             std::cout << "Python error: " << format_pyerror() << std::endl;
  120.         }
  121.         PyErr_Clear();
  122.     }
  123. }
  124. // ============================================================================
  125. int main()
  126. {
  127.     if (!Py_IsInitialized()) {
  128.         Py_Initialize();
  129.     }
  130.  
  131.     initfoo();
  132.  
  133.     std::cout << "Error in Python code: missing function:\n\n";
  134.  
  135.     test("import foo\n"
  136.         "foo.test()\n");
  137.  
  138.     std::cout << "Exception raised in Python:\n\n";
  139.  
  140.     test("import foo\n"
  141.         "raise RuntimeError('Fake')\n");
  142.  
  143.     std::cout << "C++ exception:\n\n";
  144.  
  145.     test("import foo\n"
  146.         "foo.raise_cpp_ex(False)\n");
  147.  
  148.     std::cout << "C++ exception (with backtrace):\n\n";
  149.  
  150.     test("import foo\n"
  151.         "foo.raise_cpp_ex(True)\n");
  152.  
  153.     std::cout << "Exception from Python called in C++ code:\n\n";
  154.  
  155.     test("import foo\n"
  156.         "foo.raise_python_ex()\n");
  157.  
  158.     std::cout << "C++ exception (nested):\n\n";
  159.  
  160.     test("import foo\n"
  161.         "foo.raise_cpp_ex2()\n");
  162.  
  163.     Py_Finalize();
  164.     return 0;
  165. }
  166. // ============================================================================
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement