bt2: make bt2.Error wrap current thread's error
authorSimon Marchi <simon.marchi@efficios.com>
Wed, 17 Jul 2019 22:44:23 +0000 (18:44 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 24 Jul 2019 04:18:54 +0000 (00:18 -0400)
commitce4923b0c7a2de36eba95725334d251e9aa08aad
tree95dd78eb5861d8fd8b0b3ead4d24b62f50ce985b
parent416379bc0706641778bba7efbebaa5c6027e81b8
bt2: make bt2.Error wrap current thread's error

This patch makes the Python bindings integrate better with the
Babeltrace error framework.

When a Babeltrace API call fails, it returns an ERROR or MEMORY_ERROR
status and appends a cause to the current thread's error object.  When
that API call is made from the Python, we convert that to raising a
bt2.Error execption.  With this patch, we now steal the current thread's
error object and give it to the bt2.Error exception object.

The Python code can catch this exception and consult the error causes
in the error object:

    def _consume(self):
        try:
            something_that_raises_a_bt2_error()
        except bt2.Error as exc:
            for cause in exc:
                print(cause)

If the Python code catches the exception and does nothing else with
it (as in the example above), the exception object is destroyed,
destroying the wrapped bt_error object with it.  It can be seen as if
the Python code catches the error and recovers from it.

If the Python code lets the exception propagate back to the C code, like
this:

    def _consume(self):
        something_that_raises_a_bt2_error()

... the bt2.Error is converted back to an ERROR status.  But we now also
take back the bt_error object from the bt2.Error exception object and
restore it as the current thread's error object, so that the error is
propagated.  We also append a new cause to it, with the traceback of
where the bt2.Error exception was raised.

A more complex case is if the user raises their own exception from the
bt2.Error, using Python exception chaining:

    def _consume(self):
        try:
            something_that_raises_a_bt2_error()
        except bt2.Error as exc:
            raise MyOwnExceptionType from exc

In this case, we start by restoring the bt_error as the thread's error
object, to put things back as they were before we called Python.  We
then append one cause per exception in the chain, starting with the end
of the chain.  That is, one cause for the bt2.Error (with the traceback
to where it was raised)  and one cause for the MyOwnExceptionType
exception (with the traceback to where it was raised).

When it handles a bt2.Error, the Python code can obtain information
about the error, mainly to access the list of causes.  It does so by
accessing the bt2.Error as a sequence of causes.  Each cause kind (from
unknown actor, from component class actor, from component actor and from
message iterator actor) is represented by a class, that gives access to
the appropriate properties of the cause (e.g. component class name, port
name, etc).

Various design choices:

- Some causes have a `component_class_type` property, which returns the
type of component class in which the cause happened.  This property
returns a value of the `bt_component_class_type` enum.  The user can
compare the returned value against the properties of the newly
introduced ComponentClassType class.

- Because it's now non-trivial, the Error type was moved to the new
error.py file.

- Add BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET macro, which calls
bt_current_thread_move_error and then clears the passed lvalue. It's not
essential, but may help avoid mistakes, since once you moved back the
error into place, it's no longer yours.

- Removed the raise bt2.Error in message._create_from_ptr.  If we get an
unexpected message type, there's not a lot we can do, it's just a
programming error / bug that needs to be fixed.  If it happens, we will
get a KeyError with the key that was not found, so it should be pretty
obvious.

Change-Id: Ibb63d57838a248fadda9cb741d920538fe588680
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1738
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Tested-by: jenkins <jenkins@lttng.org>
23 files changed:
include/babeltrace2/current-thread.h
src/bindings/python/bt2/Makefile.am
src/bindings/python/bt2/bt2/__init__.py.in
src/bindings/python/bt2/bt2/component.py
src/bindings/python/bt2/bt2/error.py [new file with mode: 0644]
src/bindings/python/bt2/bt2/field_class.py
src/bindings/python/bt2/bt2/message.py
src/bindings/python/bt2/bt2/native_bt.i
src/bindings/python/bt2/bt2/native_bt_component_class.i
src/bindings/python/bt2/bt2/native_bt_error.i [new file with mode: 0644]
src/bindings/python/bt2/bt2/native_bt_graph.i
src/bindings/python/bt2/bt2/native_bt_trace.i
src/bindings/python/bt2/bt2/native_bt_trace_class.i
src/bindings/python/bt2/bt2/plugin.py
src/bindings/python/bt2/bt2/stream.py
src/bindings/python/bt2/bt2/trace_collection_message_iterator.py
src/bindings/python/bt2/bt2/utils.py
tests/bindings/python/bt2/test_component_class.py
tests/bindings/python/bt2/test_error.py [new file with mode: 0644]
tests/bindings/python/bt2/test_field_class.py
tests/bindings/python/bt2/test_plugin.py
tests/bindings/python/bt2/test_query_executor.py
tests/bindings/python/bt2/test_trace_collection_message_iterator.py
This page took 0.029426 seconds and 4 git commands to generate.