From 8e7588671fab86aac1d4ed52de1839e3ac7f038a Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Tue, 23 Sep 2014 02:20:41 +0000 Subject: [PATCH] [Cython/Reactor] Print messages from exceptions raised in Python callbacks Unlike the normal case for CallbackError, where the error message is visible when it is re-raised in Python, errors occuring during reactor network integration aren't necessarily terminal, so they can't simply be re-raised after returning to Python. But we still want to display the information they contain, so we need to extract that information from the Python exception objects in the what() function that is called by cvodes_rhs. --- include/cantera/base/ctexceptions.h | 2 +- include/cantera/cython/funcWrapper.h | 44 ++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/include/cantera/base/ctexceptions.h b/include/cantera/base/ctexceptions.h index ede39719a..00989ba61 100644 --- a/include/cantera/base/ctexceptions.h +++ b/include/cantera/base/ctexceptions.h @@ -136,10 +136,10 @@ protected: //! The name of the procedure where the exception occurred std::string procedure_; + mutable std::string formattedMessage_; //!< Formatted message returned by what() private: std::string msg_; //!< Message associated with the exception - mutable std::string formattedMessage_; //!< Formatted message returned by what() bool saved_; //!< Exception has already been saved to Cantera's error stack }; diff --git a/include/cantera/cython/funcWrapper.h b/include/cantera/cython/funcWrapper.h index aa458da12..804b2d80e 100644 --- a/include/cantera/cython/funcWrapper.h +++ b/include/cantera/cython/funcWrapper.h @@ -3,6 +3,7 @@ #include "cantera/numerics/Func1.h" #include "cantera/base/ctexceptions.h" +#include #include "Python.h" @@ -16,7 +17,46 @@ public: CallbackError(void* type, void* value) : m_type((PyObject*) type), m_value((PyObject*) value) - {} + { + } + const char* what() const throw() { + formattedMessage_ = "\n" + std::string(71, '*') + "\n"; + formattedMessage_ += "Exception raised in Python callback function:\n"; + + PyObject* name = PyObject_GetAttrString(m_type, "__name__"); + PyObject* value_str = PyObject_Str(m_value); + + #if PY_MAJOR_VERSION > 2 + PyObject* name_bytes = PyUnicode_AsASCIIString(name); + PyObject* value_bytes = PyUnicode_AsASCIIString(value_str); + #else + PyObject* name_bytes = PyObject_Bytes(name); + PyObject* value_bytes = PyObject_Bytes(value_str); + #endif + + if (name_bytes) { + formattedMessage_ += PyBytes_AsString(name_bytes); + Py_DECREF(name_bytes); + } else { + formattedMessage_ += ""; + } + + formattedMessage_ += ": "; + + if (value_bytes) { + formattedMessage_ += PyBytes_AsString(value_bytes); + Py_DECREF(value_bytes); + } else { + formattedMessage_ += ""; + } + + Py_XDECREF(name); + Py_XDECREF(value_str); + + formattedMessage_ += "\n" + std::string(71, '*') + "\n"; + return formattedMessage_.c_str(); + } + PyObject* m_type; PyObject* m_value; }; @@ -57,7 +97,7 @@ inline int translate_exception() // current one. throw; } - } catch (CallbackError& exn) { + } catch (const CallbackError& exn) { // Re-raise a Python exception generated in a callback PyErr_SetObject(exn.m_type, exn.m_value); } catch (const std::out_of_range& exn) {