X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=src%2Fbindings%2Fpython%2Fbt2%2Fbt2%2Fnative_bt_component_class.i;h=ed939937e6cade034a4665ff1b2c0adb82f45460;hb=2c1d3e9111d8f1b55361b9b880350c2680c90093;hp=93aaf1b7f9f4c65054e3d4addb23c5abfaf6172d;hpb=d24d56638469189904fb6ddbb3c725817b3e9417;p=babeltrace.git 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 93aaf1b7..ed939937 100644 --- a/src/bindings/python/bt2/bt2/native_bt_component_class.i +++ b/src/bindings/python/bt2/bt2/native_bt_component_class.i @@ -94,12 +94,12 @@ PyObject *lookup_cc_ptr_to_py_cls(const bt_component_class *bt_cc) static PyObject *py_mod_bt2 = NULL; static PyObject *py_mod_bt2_exc_error_type = NULL; +static PyObject *py_mod_bt2_exc_memory_error = NULL; static PyObject *py_mod_bt2_exc_try_again_type = NULL; static PyObject *py_mod_bt2_exc_stop_type = NULL; static PyObject *py_mod_bt2_exc_msg_iter_canceled_type = NULL; static PyObject *py_mod_bt2_exc_invalid_object_type = NULL; static PyObject *py_mod_bt2_exc_invalid_params_type = NULL; -static PyObject *py_mod_bt2_exc_unsupported_type = NULL; static void bt_bt2_cc_init_from_bt2(void) @@ -115,8 +115,11 @@ void bt_bt2_cc_init_from_bt2(void) py_mod_bt2 = PyImport_ImportModule("bt2"); BT_ASSERT(py_mod_bt2); py_mod_bt2_exc_error_type = - PyObject_GetAttrString(py_mod_bt2, "Error"); + PyObject_GetAttrString(py_mod_bt2, "_Error"); BT_ASSERT(py_mod_bt2_exc_error_type); + py_mod_bt2_exc_memory_error = + PyObject_GetAttrString(py_mod_bt2, "_MemoryError"); + BT_ASSERT(py_mod_bt2_exc_memory_error); py_mod_bt2_exc_try_again_type = PyObject_GetAttrString(py_mod_bt2, "TryAgain"); BT_ASSERT(py_mod_bt2_exc_try_again_type); @@ -129,9 +132,6 @@ void bt_bt2_cc_init_from_bt2(void) py_mod_bt2_exc_invalid_params_type = PyObject_GetAttrString(py_mod_bt2, "InvalidParams"); BT_ASSERT(py_mod_bt2_exc_invalid_params_type); - py_mod_bt2_exc_unsupported_type = - PyObject_GetAttrString(py_mod_bt2, "Unsupported"); - BT_ASSERT(py_mod_bt2_exc_unsupported_type); } static @@ -172,20 +172,181 @@ 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(int log_level) +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, + const char *module_name) { GString *gstr; - BT_ASSERT(PyErr_Occurred() != NULL); - gstr = bt_py_common_format_exception(BT_LOG_OUTPUT_LEVEL); + BT_ASSERT(PyErr_Occurred()); + gstr = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL); if (!gstr) { - /* bt_py_common_format_exception() logs errors */ + /* bt_py_common_format_current_exception() logs errors */ goto end; } BT_LOG_WRITE(log_level, BT_LOG_TAG, "%s", gstr->str); + if (append_error) { + restore_bt_error_and_append_current_exception_chain( + self_component_class, self_component, + self_message_iterator, module_name); + + } + end: if (gstr) { g_string_free(gstr, TRUE); @@ -193,19 +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, module_name); +} + +static +void loge_exception_message_iterator( + bt_self_message_iterator *self_message_iterator) { - log_exception(BT_LOG_ERROR); + log_exception_and_maybe_append_error(BT_LOG_ERROR, true, NULL, NULL, + self_message_iterator, NULL); } static inline void logw_exception(void) { - log_exception(BT_LOG_WARN); + log_exception_and_maybe_append_error(BT_LOG_WARNING, false, NULL, NULL, + NULL, NULL); } static inline -int py_exc_to_status(void) +int py_exc_to_status(bt_self_component_class *self_component_class, + bt_self_component *self_component, + bt_self_message_iterator *self_message_iterator, + const char *module_name) { int status = __BT_FUNC_STATUS_OK; PyObject *exc = PyErr_Occurred(); @@ -226,13 +400,18 @@ int py_exc_to_status(void) } else if (PyErr_GivenExceptionMatches(exc, py_mod_bt2_exc_invalid_params_type)) { status = __BT_FUNC_STATUS_INVALID_PARAMS; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_unsupported_type)) { - status = __BT_FUNC_STATUS_UNSUPPORTED; } else { /* Unknown exception: convert to general error */ - logw_exception(); - status = __BT_FUNC_STATUS_ERROR; + log_exception_and_maybe_append_error(BT_LOG_WARNING, true, + self_component_class, self_component, + self_message_iterator, module_name); + + if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_memory_error)) { + status = __BT_FUNC_STATUS_MEMORY_ERROR; + } else { + status = __BT_FUNC_STATUS_ERROR; + } } end: @@ -240,6 +419,25 @@ end: return status; } +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, NULL); +} + +static +int py_exc_to_status_component(bt_self_component *self_component) +{ + 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, NULL); +} + /* Component class proxy methods (delegate to the attached Python object) */ static @@ -294,18 +492,18 @@ bt_component_class_init_method_status component_class_init( /* * Do the equivalent of this: * - * py_comp = py_cls._init_from_native(py_comp_ptr, py_params_ptr) + * py_comp = py_cls._bt_init_from_native(py_comp_ptr, py_params_ptr) * - * _UserComponentType._init_from_native() calls the Python + * _UserComponentType._bt_init_from_native() calls the Python * component object's __init__() function. */ py_comp = PyObject_CallMethod(py_cls, - "_init_from_native", "(OO)", py_comp_ptr, py_params_ptr); + "_bt_init_from_native", "(OO)", py_comp_ptr, py_params_ptr); if (!py_comp) { - BT_LOGW("Failed to call Python class's _init_from_native() method: " + BT_LOGW("Failed to call Python class's _bt_init_from_native() method: " "py-cls-addr=%p", py_cls); - logw_exception(); - goto error; + status = py_exc_to_status_component(self_component); + goto end; } /* @@ -436,7 +634,7 @@ bt_bool component_class_can_seek_beginning( py_iter = bt_self_message_iterator_get_data(self_message_iterator); BT_ASSERT(py_iter); - py_result = PyObject_GetAttrString(py_iter, "_can_seek_beginning_from_native"); + py_result = PyObject_GetAttrString(py_iter, "_bt_can_seek_beginning_from_native"); BT_ASSERT(!py_result || PyBool_Check(py_result)); @@ -447,7 +645,7 @@ bt_bool component_class_can_seek_beginning( * Once can_seek_beginning can report errors, convert the * exception to a status. For now, log and return false; */ - loge_exception(); + loge_exception_message_iterator(self_message_iterator); PyErr_Clear(); } @@ -466,10 +664,10 @@ component_class_seek_beginning(bt_self_message_iterator *self_message_iterator) py_iter = bt_self_message_iterator_get_data(self_message_iterator); BT_ASSERT(py_iter); - py_result = PyObject_CallMethod(py_iter, "_seek_beginning_from_native", + 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(); + status = py_exc_to_status_message_iterator(self_message_iterator); Py_XDECREF(py_result); return status; } @@ -507,10 +705,10 @@ bt_component_class_port_connected_method_status component_class_port_connected( goto end; } py_method_result = PyObject_CallMethod(py_comp, - "_port_connected_from_native", "(OiO)", py_self_port_ptr, + "_bt_port_connected_from_native", "(OiO)", py_self_port_ptr, self_component_port_type, py_other_port_ptr); BT_ASSERT(!py_method_result || py_method_result == Py_None); - status = py_exc_to_status(); + status = py_exc_to_status_component(self_component); end: Py_XDECREF(py_self_port_ptr); @@ -603,9 +801,9 @@ component_class_sink_graph_is_configured( py_comp = bt_self_component_get_data(self_component); py_method_result = PyObject_CallMethod(py_comp, - "_graph_is_configured_from_native", NULL); + "_bt_graph_is_configured_from_native", NULL); BT_ASSERT(!py_method_result || py_method_result == Py_None); - status = py_exc_to_status(); + status = py_exc_to_status_component(self_component); Py_XDECREF(py_method_result); return status; } @@ -613,6 +811,7 @@ component_class_sink_graph_is_configured( static bt_component_class_query_method_status component_class_query( const bt_component_class *component_class, + bt_self_component_class *self_component_class, const bt_query_executor *query_executor, const char *object, const bt_value *params, bt_logging_level log_level, @@ -654,12 +853,12 @@ bt_component_class_query_method_status component_class_query( } py_results_addr = PyObject_CallMethod(py_cls, - "_query_from_native", "(OOOi)", py_query_exec_ptr, + "_bt_query_from_native", "(OOOi)", py_query_exec_ptr, py_object, py_params_ptr, (int) log_level); if (!py_results_addr) { - BT_LOGW("Failed to call Python class's _query_from_native() method: " + BT_LOGW("Failed to call Python class's _bt_query_from_native() method: " "py-cls-addr=%p", py_cls); - status = py_exc_to_status(); + status = py_exc_to_status_component_class(self_component_class); goto end; } @@ -696,8 +895,9 @@ bt_component_class_query_method_status component_class_source_query( { const bt_component_class_source *component_class_source = bt_self_component_class_source_as_component_class_source(self_component_class_source); const bt_component_class *component_class = bt_component_class_source_as_component_class_const(component_class_source); + bt_self_component_class *self_component_class = bt_self_component_class_source_as_self_component_class(self_component_class_source); - return component_class_query(component_class, query_executor, object, params, log_level, result); + return component_class_query(component_class, self_component_class, query_executor, object, params, log_level, result); } static @@ -710,8 +910,9 @@ bt_component_class_query_method_status component_class_filter_query( { const bt_component_class_filter *component_class_filter = bt_self_component_class_filter_as_component_class_filter(self_component_class_filter); const bt_component_class *component_class = bt_component_class_filter_as_component_class_const(component_class_filter); + bt_self_component_class *self_component_class = bt_self_component_class_filter_as_self_component_class(self_component_class_filter); - return component_class_query(component_class, query_executor, object, params, log_level, result); + return component_class_query(component_class, self_component_class, query_executor, object, params, log_level, result); } static @@ -724,8 +925,9 @@ bt_component_class_query_method_status component_class_sink_query( { const bt_component_class_sink *component_class_sink = bt_self_component_class_sink_as_component_class_sink(self_component_class_sink); const bt_component_class *component_class = bt_component_class_sink_as_component_class_const(component_class_sink); + bt_self_component_class *self_component_class = bt_self_component_class_sink_as_self_component_class(self_component_class_sink); - return component_class_query(component_class, query_executor, object, params, log_level, result); + return component_class_query(component_class, self_component_class, query_executor, object, params, log_level, result); } static @@ -750,19 +952,23 @@ component_class_message_iterator_init( py_comp_cls = PyObject_GetAttrString(py_comp, "__class__"); if (!py_comp_cls) { BT_LOGE_STR("Cannot get Python object's `__class__` attribute."); - goto error; + goto python_error; } py_iter_cls = PyObject_GetAttrString(py_comp_cls, "_iter_cls"); if (!py_iter_cls) { BT_LOGE_STR("Cannot get Python class's `_iter_cls` attribute."); - goto error; + goto python_error; } py_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_message_iterator), SWIGTYPE_p_bt_self_message_iterator, 0); if (!py_iter_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); + const char *err = "Failed to create a SWIG pointer object."; + + BT_LOGE_STR(err); + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR( + self_message_iterator, err); goto error; } @@ -777,8 +983,7 @@ component_class_message_iterator_init( if (!py_iter) { BT_LOGE("Failed to call Python class's __new__() method: " "py-cls-addr=%p", py_iter_cls); - loge_exception(); - goto error; + goto python_error; } /* @@ -797,16 +1002,19 @@ component_class_message_iterator_init( SWIG_as_voidptr(self_component_port_output), SWIGTYPE_p_bt_self_component_port_output, 0); if (!py_component_port_output_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); + const char *err = "Failed to create a SWIG pointer object."; + + BT_LOGE_STR(err); + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR( + self_message_iterator, err); goto error; } py_init_method_result = PyObject_CallMethod(py_iter, - "_init_from_native", "O", py_component_port_output_ptr); + "_bt_init_from_native", "O", py_component_port_output_ptr); if (!py_init_method_result) { - BT_LOGW_STR("User's __init__() method failed:"); - logw_exception(); - goto error; + BT_LOGE_STR("User's __init__() method failed:"); + goto python_error; } /* @@ -831,24 +1039,19 @@ component_class_message_iterator_init( py_iter = NULL; goto end; -error: - status = py_exc_to_status(); - if (status == __BT_FUNC_STATUS_OK) { - /* - * Looks like there wasn't any exception from the Python - * side, but we're still in an error state here. - */ - status = __BT_FUNC_STATUS_ERROR; - } +python_error: + /* Handling of errors that cause a Python exception to be set. */ + status = py_exc_to_status_message_iterator(self_message_iterator); + BT_ASSERT(status != __BT_FUNC_STATUS_OK); + goto end; - /* - * Clear any exception: we're returning a bad status anyway. If - * this call originated from Python, then the user gets an - * appropriate creation error. - */ - PyErr_Clear(); +error: + /* Handling of errors that don't cause a Python exception to be set. */ + status = __BT_FUNC_STATUS_ERROR; end: + BT_ASSERT(!PyErr_Occurred()); + Py_XDECREF(py_comp_cls); Py_XDECREF(py_iter_cls); Py_XDECREF(py_iter_ptr); @@ -925,9 +1128,9 @@ component_class_message_iterator_next( BT_ASSERT(py_message_iter); py_method_result = PyObject_CallMethod(py_message_iter, - "_next_from_native", NULL); + "_bt_next_from_native", NULL); if (!py_method_result) { - status = py_exc_to_status(); + status = py_exc_to_status_message_iterator(message_iterator); BT_ASSERT(status != __BT_FUNC_STATUS_OK); goto end; } @@ -961,7 +1164,7 @@ component_class_sink_consume(bt_self_component_sink *self_component_sink) BT_ASSERT(py_comp); py_method_result = PyObject_CallMethod(py_comp, "_consume", NULL); - status = py_exc_to_status(); + status = py_exc_to_status_component(self_component); if (!py_method_result && status == __BT_FUNC_STATUS_OK) { /* Pretty sure this should never happen, but just in case */ BT_LOGE("User's _consume() method failed without raising an exception: "