X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=blobdiff_plain;f=src%2Fbindings%2Fpython%2Fbt2%2Fbt2%2Fnative_bt_component_class.i;h=b9e91a4af86e83c9864bb89b8fc1998865aeb909;hp=0b679ec8359a646d000e87672babc4b33adb210e;hb=ce4923b0c7a2de36eba95725334d251e9aa08aad;hpb=416379bc0706641778bba7efbebaa5c6027e81b8 diff --git a/src/bindings/python/bt2/bt2/native_bt_component_class.i b/src/bindings/python/bt2/bt2/native_bt_component_class.i index 0b679ec8..b9e91a4a 100644 --- a/src/bindings/python/bt2/bt2/native_bt_component_class.i +++ b/src/bindings/python/bt2/bt2/native_bt_component_class.i @@ -172,12 +172,162 @@ void native_comp_class_dtor(void) { } } +static +void restore_current_thread_error_and_append_exception_chain_recursive( + PyObject *py_exc_value, + bt_self_component_class *self_component_class, + bt_self_component *self_component, + bt_self_message_iterator *self_message_iterator, + const char *module_name) +{ + PyObject *py_exc_cause_value; + PyObject *py_exc_type = NULL; + PyObject *py_exc_tb = NULL; + GString *gstr = NULL; + + /* If this exception has a cause, handle that one first. */ + py_exc_cause_value = PyException_GetCause(py_exc_value); + if (py_exc_cause_value) { + restore_current_thread_error_and_append_exception_chain_recursive( + py_exc_cause_value, self_component_class, + self_component, self_message_iterator, module_name); + } + + /* + * If the raised exception is a bt2.Error, restore the wrapped error. + */ + if (PyErr_GivenExceptionMatches(py_exc_value, py_mod_bt2_exc_error_type)) { + PyObject *py_error_swig_ptr; + const bt_error *error; + int ret; + + /* + * We never raise a bt2.Error with a cause: it should be the + * end of the chain. + */ + BT_ASSERT(!py_exc_cause_value); + + /* + * We steal the error object from the exception, to move + * it back as the current thread's error. + */ + py_error_swig_ptr = PyObject_GetAttrString(py_exc_value, "_ptr"); + BT_ASSERT(py_error_swig_ptr); + + ret = PyObject_SetAttrString(py_exc_value, "_ptr", Py_None); + BT_ASSERT(ret == 0); + + ret = SWIG_ConvertPtr(py_error_swig_ptr, (void **) &error, + SWIGTYPE_p_bt_error, 0); + BT_ASSERT(ret == 0); + + BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(error); + + Py_DECREF(py_error_swig_ptr); + } + + py_exc_type = PyObject_Type(py_exc_value); + py_exc_tb = PyException_GetTraceback(py_exc_value); + + gstr = bt_py_common_format_exception(py_exc_type, py_exc_value, + py_exc_tb, BT_LOG_OUTPUT_LEVEL, false); + if (!gstr) { + /* bt_py_common_format_exception has already warned. */ + goto end; + } + + if (self_component_class) { + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS( + self_component_class, "%s", gstr->str); + } else if (self_component) { + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT( + self_component, "%s", gstr->str); + } else if (self_message_iterator) { + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR( + self_message_iterator, "%s", gstr->str); + } else { + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( + module_name, "%s", gstr->str); + } + +end: + if (gstr) { + g_string_free(gstr, TRUE); + } + + Py_XDECREF(py_exc_cause_value); + Py_XDECREF(py_exc_type); + Py_XDECREF(py_exc_tb); +} + +/* + * If you have the following code: + * + * try: + * try: + * something_that_raises_bt2_error() + * except bt2.Error as e1: + * raise ValueError from e1 + * except ValueError as e2: + * raise TypeError from e2 + * + * We will have the following exception chain: + * + * TypeError -> ValueError -> bt2.Error + * + * Where the TypeError is the current exception (obtained from PyErr_Fetch). + * + * The bt2.Error contains a `struct bt_error *` that used to be the current + * thread's error, at the moment the exception was raised. + * + * This function gets to the bt2.Error and restores the wrapped + * `struct bt_error *` as the current thread's error. + * + * Then, for each exception in the chain, starting with the oldest one, it adds + * an error cause to the current thread's error. + */ +static +void restore_bt_error_and_append_current_exception_chain( + bt_self_component_class *self_component_class, + bt_self_component *self_component, + bt_self_message_iterator *self_message_iterator, + const char *module_name) +{ + BT_ASSERT(PyErr_Occurred()); + + /* Used to access and restore the current exception. */ + PyObject *py_exc_type; + PyObject *py_exc_value; + PyObject *py_exc_tb; + + /* Fetch and normalize the Python exception. */ + PyErr_Fetch(&py_exc_type, &py_exc_value, &py_exc_tb); + PyErr_NormalizeException(&py_exc_type, &py_exc_value, &py_exc_tb); + BT_ASSERT(py_exc_type); + BT_ASSERT(py_exc_value); + BT_ASSERT(py_exc_tb); + + /* + * Set the exception's traceback so it's possible to get it using + * PyException_GetTraceback in + * restore_current_thread_error_and_append_exception_chain_recursive. + */ + PyException_SetTraceback(py_exc_value, py_exc_tb); + + restore_current_thread_error_and_append_exception_chain_recursive(py_exc_value, + self_component_class, self_component, self_message_iterator, + module_name); + + PyErr_Restore(py_exc_type, py_exc_value, py_exc_tb); +} + static inline void log_exception_and_maybe_append_error(int log_level, bool append_error, bt_self_component_class *self_component_class, bt_self_component *self_component, - bt_self_message_iterator *self_message_iterator) + bt_self_message_iterator *self_message_iterator, + const char *module_name) { GString *gstr; @@ -191,19 +341,10 @@ void log_exception_and_maybe_append_error(int log_level, BT_LOG_WRITE(log_level, BT_LOG_TAG, "%s", gstr->str); if (append_error) { - if (self_component_class) { - BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS( - self_component_class, "%s", gstr->str); - } else if (self_component) { - BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT( - self_component, "%s", gstr->str); - } else if (self_message_iterator) { - BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR( - self_message_iterator, "%s", gstr->str); - } else { - BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( - "Python", "%s", gstr->str); - } + restore_bt_error_and_append_current_exception_chain( + self_component_class, self_component, + self_message_iterator, module_name); + } end: @@ -213,28 +354,32 @@ end: } static inline -void loge_exception(void) +void loge_exception(const char *module_name) { - log_exception_and_maybe_append_error(BT_LOG_ERROR, true, NULL, NULL, NULL); + log_exception_and_maybe_append_error(BT_LOG_ERROR, true, NULL, NULL, + NULL, module_name); } static void loge_exception_message_iterator( bt_self_message_iterator *self_message_iterator) { - log_exception_and_maybe_append_error(BT_LOG_ERROR, true, NULL, NULL, self_message_iterator); + log_exception_and_maybe_append_error(BT_LOG_ERROR, true, NULL, NULL, + self_message_iterator, NULL); } static inline void logw_exception(void) { - log_exception_and_maybe_append_error(BT_LOG_WARNING, false, NULL, NULL, NULL); + log_exception_and_maybe_append_error(BT_LOG_WARNING, false, NULL, NULL, + NULL, NULL); } static inline int py_exc_to_status(bt_self_component_class *self_component_class, bt_self_component *self_component, - bt_self_message_iterator *self_message_iterator) + bt_self_message_iterator *self_message_iterator, + const char *module_name) { int status = __BT_FUNC_STATUS_OK; PyObject *exc = PyErr_Occurred(); @@ -262,7 +407,7 @@ int py_exc_to_status(bt_self_component_class *self_component_class, /* Unknown exception: convert to general error */ log_exception_and_maybe_append_error(BT_LOG_WARNING, true, self_component_class, self_component, - self_message_iterator); + self_message_iterator, module_name); status = __BT_FUNC_STATUS_ERROR; } @@ -274,20 +419,20 @@ end: static int py_exc_to_status_component_class(bt_self_component_class *self_component_class) { - return py_exc_to_status(self_component_class, NULL, NULL); + return py_exc_to_status(self_component_class, NULL, NULL, NULL); } static int py_exc_to_status_component(bt_self_component *self_component) { - return py_exc_to_status(NULL, self_component, NULL); + return py_exc_to_status(NULL, self_component, NULL, NULL); } static int py_exc_to_status_message_iterator( bt_self_message_iterator *self_message_iterator) { - return py_exc_to_status(NULL, NULL, self_message_iterator); + return py_exc_to_status(NULL, NULL, self_message_iterator, NULL); } /* Component class proxy methods (delegate to the attached Python object) */ @@ -519,7 +664,7 @@ component_class_seek_beginning(bt_self_message_iterator *self_message_iterator) py_result = PyObject_CallMethod(py_iter, "_bt_seek_beginning_from_native", NULL); BT_ASSERT(!py_result || py_result == Py_None); - status = py_exc_to_status_message_iterator(self_message_iterator); + status = py_exc_to_status_message_iterator(self_message_iterator); Py_XDECREF(py_result); return status; }