python: reimplement the babeltrace package as a bt2 wrapper
[babeltrace.git] / bindings / python / babeltrace / babeltrace / reader_event.py
diff --git a/bindings/python/babeltrace/babeltrace/reader_event.py b/bindings/python/babeltrace/babeltrace/reader_event.py
new file mode 100644 (file)
index 0000000..de2b23e
--- /dev/null
@@ -0,0 +1,351 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2013-2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import bt2
+import babeltrace.common as common
+import babeltrace.reader_field_definition as field_definition
+import datetime
+import collections
+
+def _create_event(event_notification, trace_handle=None, trace_collection=None):
+    event = Event.__new__(Event)
+    event._event_notification = event_notification
+    event._trace_handle = trace_handle
+    event._trace_collection = trace_collection
+    return event
+
+class Event(collections.Mapping):
+    """
+    An :class:`Event` object represents a trace event. :class:`Event`
+    objects are returned by :attr:`TraceCollection.events` and are
+    not meant to be instantiated by the user.
+
+    :class:`Event` has a :class:`dict`-like interface for accessing
+    an event's field value by field name:
+
+    .. code-block:: python
+
+       event['my_field']
+
+    If a field name exists in multiple scopes, the value of the first
+    field found is returned. The scopes are searched in the following
+    order:
+
+    1. Event fields (:attr:`babeltrace.common.CTFScope.EVENT_FIELDS`)
+    2. Event context (:attr:`babeltrace.common.CTFScope.EVENT_CONTEXT`)
+    3. Stream event context (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_CONTEXT`)
+    4. Event header (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_HEADER`)
+    5. Packet context (:attr:`babeltrace.common.CTFScope.STREAM_PACKET_CONTEXT`)
+    6. Packet header (:attr:`babeltrace.common.CTFScope.TRACE_PACKET_HEADER`)
+
+    It is still possible to obtain a field's value from a specific
+    scope using :meth:`field_with_scope`.
+
+    Field values are returned as native Python types, that is:
+
+    +-----------------------+----------------------------------+
+    | Field type            | Python type                      |
+    +=======================+==================================+
+    | Integer               | :class:`int`                     |
+    +-----------------------+----------------------------------+
+    | Floating point number | :class:`float`                   |
+    +-----------------------+----------------------------------+
+    | Enumeration           | :class:`str` (enumeration label) |
+    +-----------------------+----------------------------------+
+    | String                | :class:`str`                     |
+    +-----------------------+----------------------------------+
+    | Array                 | :class:`list` of native Python   |
+    |                       | objects                          |
+    +-----------------------+----------------------------------+
+    | Sequence              | :class:`list` of native Python   |
+    |                       | objects                          |
+    +-----------------------+----------------------------------+
+    | Structure             | :class:`dict` mapping field      |
+    |                       | names to native Python objects   |
+    +-----------------------+----------------------------------+
+
+    For example, printing the third element of a sequence named ``seq``
+    in a structure named ``my_struct`` of the ``event``'s field named
+    ``my_field`` is done this way:
+
+    .. code-block:: python
+
+       print(event['my_field']['my_struct']['seq'][2])
+    """
+
+    def __init__(self):
+        raise NotImplementedError("Event cannot be instantiated")
+
+    @property
+    def name(self):
+        """
+        Event name or ``None`` on error.
+        """
+
+        try:
+            return self._event_notification.event.name
+        except bt2.Error:
+            pass
+
+    def _clock_value(self):
+        cc_prio_map = self._event_notification.clock_class_priority_map
+        clock_class = cc_prio_map.highest_priority_clock_class
+        if not clock_class:
+            return
+
+        return self._event_notification.event.clock_value(clock_class)
+
+    @property
+    def cycles(self):
+        """
+        Event timestamp in cycles or -1 on error.
+        """
+
+        try:
+            clock_value = self._clock_value()
+        except bt2.Error:
+            return -1
+
+        if clock_value is not None:
+            return clock_value.cycles
+        else:
+            return -1
+
+    @property
+    def timestamp(self):
+        """
+        Event timestamp (nanoseconds since Epoch).
+        """
+
+        try:
+            clock_value = self._clock_value()
+        except bt2.Error:
+            raise RuntimeError("Failed to get event timestamp")
+
+        if clock_value is not None:
+            return clock_value.ns_from_epoch
+        else:
+            raise RuntimeError("Failed to get event timestamp")
+
+    @property
+    def datetime(self):
+        """
+        Event timestamp as a standard :class:`datetime.datetime`
+        object.
+
+        Note that the :class:`datetime.datetime` class' precision
+        is limited to microseconds, whereas :attr:`timestamp` provides
+        the event's timestamp with a nanosecond resolution.
+        """
+
+        return datetime.date.fromtimestamp(self.timestamp / 1E9)
+
+    def field_with_scope(self, field_name, scope):
+        """
+        Returns the value of a field named *field_name* within the
+        scope *scope*, or ``None`` if the field cannot be found.
+
+        *scope* must be one of :class:`babeltrace.common.CTFScope`
+        constants.
+        """
+
+        if scope not in _SCOPES:
+            raise ValueError("Invalid scope provided")
+
+        field = self._field_with_scope(field_name, scope)
+
+        if field is not None:
+            return field.value
+
+    def field_list_with_scope(self, scope):
+        """
+        Returns a list of field names in the scope *scope*.
+        """
+
+        if scope not in _SCOPES:
+            raise ValueError("Invalid scope provided")
+
+        field_names = []
+
+        for field in self._field_list_with_scope(scope):
+            field_names.append(field.name)
+
+        return field_names
+
+    @property
+    def handle(self):
+        """
+        :class:`TraceHandle` object containing this event, or ``None``
+        on error.
+        """
+
+        try:
+            return self._trace_handle
+        except AttributeError:
+            return None
+
+    @property
+    def trace_collection(self):
+        """
+        :class:`TraceCollection` object containing this event, or
+        ``None`` on error.
+        """
+
+        try:
+            return self._trace_collection
+        except AttributeError:
+            return
+
+    def __getitem__(self, field_name):
+        field = self._field(field_name)
+        if field is None:
+            raise KeyError(field_name)
+        return field.value
+
+    def __iter__(self):
+        for key in self.keys():
+            yield key
+
+    def __len__(self):
+        count = 0
+        for scope in _SCOPES:
+            scope_field = self._get_scope_field(scope)
+            if scope_field is not None and isinstance(scope_field,
+                                                      bt2._StructureField):
+                count += len(scope_field)
+        return count
+
+    def __contains__(self, field_name):
+        return self._field(field_name) is not None
+
+    def keys(self):
+        """
+        Returns the list of field names.
+
+        Note: field names are unique within the returned list, although
+        a field name could exist in multiple scopes. Use
+        :meth:`field_list_with_scope` to obtain the list of field names
+        of a given scope.
+        """
+
+        field_names = set()
+
+        for scope in _SCOPES:
+            for name in self.field_list_with_scope(scope):
+                field_names.add(name)
+
+        return list(field_names)
+
+    def get(self, field_name, default=None):
+        """
+        Returns the value of the field named *field_name*, or *default*
+        when not found.
+
+        See :class:`Event` note about how fields are retrieved by
+        name when multiple fields share the same name in different
+        scopes.
+        """
+
+        field = self._field(field_name)
+
+        if field is None:
+            return default
+
+        return field.value
+
+    def items(self):
+        """
+        Generates pairs of (field name, field value).
+
+        This method iterates :meth:`keys` to find field names, which
+        means some fields could be unavailable if other fields share
+        their names in scopes with higher priorities.
+        """
+
+        for field in self.keys():
+            yield (field, self[field])
+
+    def _get_scope_field(self, scope):
+        try:
+            event = self._event_notification.event
+            if scope is common.CTFScope.EVENT_FIELDS:
+                return event.payload_field
+
+            if scope is common.CTFScope.EVENT_CONTEXT:
+                return event.context_field
+
+            if scope is common.CTFScope.STREAM_EVENT_CONTEXT:
+                return event.stream_event_context_field
+
+            if scope is common.CTFScope.STREAM_EVENT_HEADER:
+                return event.header_field
+
+            if scope is common.CTFScope.STREAM_PACKET_CONTEXT:
+                return event.packet.context_field
+
+            if scope is common.CTFScope.TRACE_PACKET_HEADER:
+                return event.packet.header_field
+        except bt2.Error:
+            return
+
+        raise ValueError("Invalid scope provided")
+
+    def _field_with_scope(self, field_name, scope):
+        scope_field = self._get_scope_field(scope)
+        if scope_field is not None:
+            try:
+                bt2_field = scope_field[field_name]
+                if bt2_field is not None:
+                    return field_definition._Definition(scope, bt2_field,
+                                                        field_name)
+            except (KeyError, bt2.Error):
+                return None
+
+    def _field(self, field_name):
+        for scope in _SCOPES:
+            field = self._field_with_scope(field_name, scope)
+            if field is not None:
+                return field
+
+    def _field_list_with_scope(self, scope):
+        fields = []
+        scope_field = self._get_scope_field(scope)
+
+        if scope_field is None or not isinstance(scope_field,
+                                                 bt2._StructureField):
+            return fields
+
+        for name, field in scope_field.items():
+            fields.append(field_definition._Definition(scope, field, name))
+
+        return fields
+
+
+# Priority of the scopes when searching for event fields
+_SCOPES = [
+    common.CTFScope.EVENT_FIELDS,
+    common.CTFScope.EVENT_CONTEXT,
+    common.CTFScope.STREAM_EVENT_CONTEXT,
+    common.CTFScope.STREAM_EVENT_HEADER,
+    common.CTFScope.STREAM_PACKET_CONTEXT,
+    common.CTFScope.TRACE_PACKET_HEADER
+]
This page took 0.031499 seconds and 4 git commands to generate.