From 811644b8fe5fb9946972a7ace9df02ed872f448a Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Thu, 15 Jun 2017 14:46:40 -0400 Subject: [PATCH] Update Python bindings and tests to match the latest API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- bindings/Makefile.am | 3 +- bindings/python/bt2/Makefile.am | 78 +- bindings/python/bt2/__init__.py.in | 83 +- bindings/python/bt2/clock_class.py | 62 +- .../python/bt2/clock_class_priority_map.py | 129 ++ bindings/python/bt2/component.py | 581 +++++---- bindings/python/bt2/connection.py | 110 ++ bindings/python/bt2/ctf_writer.py | 27 +- bindings/python/bt2/event.py | 18 +- bindings/python/bt2/event_class.py | 148 ++- bindings/python/bt2/field_types.py | 119 +- bindings/python/bt2/fields.py | 77 +- bindings/python/bt2/graph.py | 193 +++ bindings/python/bt2/logging.c | 26 + bindings/python/bt2/logging.h | 31 + bindings/python/bt2/logging.py | 59 + bindings/python/bt2/native_bt.i | 97 +- bindings/python/bt2/native_btccpriomap.i | 54 + bindings/python/bt2/native_btclockclass.i | 7 +- bindings/python/bt2/native_btcomponent.i | 165 +-- bindings/python/bt2/native_btcomponentclass.i | 1139 +++++++---------- bindings/python/bt2/native_btconnection.i | 113 ++ bindings/python/bt2/native_btctfwriter.i | 7 - bindings/python/bt2/native_btevent.i | 8 +- bindings/python/bt2/native_bteventclass.i | 45 +- bindings/python/bt2/native_btfields.i | 10 +- bindings/python/bt2/native_btft.i | 60 +- bindings/python/bt2/native_btgraph.i | 262 ++++ bindings/python/bt2/native_btlogging.i | 43 + bindings/python/bt2/native_btnotification.i | 92 +- bindings/python/bt2/native_btnotifiter.i | 48 +- bindings/python/bt2/native_btpacket.i | 4 - bindings/python/bt2/native_btplugin.i | 117 +- bindings/python/bt2/native_btport.i | 60 + bindings/python/bt2/native_btref.i | 4 - bindings/python/bt2/native_btstream.i | 8 +- bindings/python/bt2/native_btstreamclass.i | 18 +- bindings/python/bt2/native_bttrace.i | 114 +- bindings/python/bt2/native_btvalues.i | 27 +- bindings/python/bt2/native_btversion.i | 33 + bindings/python/bt2/notification.py | 392 +++++- bindings/python/bt2/notification_iterator.py | 94 +- bindings/python/bt2/object.py | 11 +- bindings/python/bt2/packet.py | 2 +- bindings/python/bt2/plugin.py | 182 +-- bindings/python/bt2/port.py | 160 +++ bindings/python/bt2/stream.py | 11 +- bindings/python/bt2/stream_class.py | 58 +- bindings/python/bt2/trace.py | 72 +- bindings/python/bt2/utils.py | 3 +- bindings/python/bt2/values.py | 38 +- configure.ac | 13 +- tests/bindings/python/bt2/.coveragerc | 7 + tests/bindings/python/bt2/Makefile.am | 35 +- tests/bindings/python/bt2/test_clock_class.py | 42 +- .../bt2/test_clock_class_priority_map.py | 139 ++ .../python/bt2/test_comp_notif_iter.py | 861 ------------- tests/bindings/python/bt2/test_component.py | 121 ++ .../python/bt2/test_component_class.py | 323 +++++ tests/bindings/python/bt2/test_connection.py | 392 ++++++ .../python/bt2/test_ctf_writer_clock.py | 3 + tests/bindings/python/bt2/test_event.py | 49 +- tests/bindings/python/bt2/test_event_class.py | 94 +- tests/bindings/python/bt2/test_field_types.py | 37 +- tests/bindings/python/bt2/test_fields.py | 150 ++- tests/bindings/python/bt2/test_graph.py | 471 +++++++ .../bindings/python/bt2/test_notification.py | 566 ++++++++ .../python/bt2/test_notification_iterator.py | 120 ++ tests/bindings/python/bt2/test_packet.py | 3 + tests/bindings/python/bt2/test_plugin.py | 104 ++ tests/bindings/python/bt2/test_port.py | 888 +++++++++++++ tests/bindings/python/bt2/test_stream.py | 22 +- .../bindings/python/bt2/test_stream_class.py | 37 +- tests/bindings/python/bt2/test_trace.py | 57 +- tests/bindings/python/bt2/test_values.py | 28 +- tests/bindings/python/bt2/testall.sh.in | 3 + tests/utils/Makefile.am | 1 + 77 files changed, 7101 insertions(+), 2767 deletions(-) create mode 100644 bindings/python/bt2/clock_class_priority_map.py create mode 100644 bindings/python/bt2/connection.py create mode 100644 bindings/python/bt2/graph.py create mode 100644 bindings/python/bt2/logging.c create mode 100644 bindings/python/bt2/logging.h create mode 100644 bindings/python/bt2/logging.py create mode 100644 bindings/python/bt2/native_btccpriomap.i create mode 100644 bindings/python/bt2/native_btconnection.i create mode 100644 bindings/python/bt2/native_btgraph.i create mode 100644 bindings/python/bt2/native_btlogging.i create mode 100644 bindings/python/bt2/native_btport.i create mode 100644 bindings/python/bt2/native_btversion.i create mode 100644 bindings/python/bt2/port.py create mode 100644 tests/bindings/python/bt2/test_clock_class_priority_map.py delete mode 100644 tests/bindings/python/bt2/test_comp_notif_iter.py create mode 100644 tests/bindings/python/bt2/test_component.py create mode 100644 tests/bindings/python/bt2/test_component_class.py create mode 100644 tests/bindings/python/bt2/test_connection.py create mode 100644 tests/bindings/python/bt2/test_graph.py create mode 100644 tests/bindings/python/bt2/test_notification.py create mode 100644 tests/bindings/python/bt2/test_notification_iterator.py create mode 100644 tests/bindings/python/bt2/test_plugin.py create mode 100644 tests/bindings/python/bt2/test_port.py diff --git a/bindings/Makefile.am b/bindings/Makefile.am index 6341f126..773e43ad 100644 --- a/bindings/Makefile.am +++ b/bindings/Makefile.am @@ -1,4 +1,3 @@ if ENABLE_PYTHON_BINDINGS -#Disabled temporarily (work in progress) -#SUBDIRS = python +SUBDIRS = python endif diff --git a/bindings/python/bt2/Makefile.am b/bindings/python/bt2/Makefile.am index 4a96fdb3..7b20517f 100644 --- a/bindings/python/bt2/Makefile.am +++ b/bindings/python/bt2/Makefile.am @@ -3,44 +3,55 @@ NATIVE_MODULE = native_bt # interface dependencies (without `native_bt` prefix and `.i` extension) NATIVE_MODULE_DEPS = \ + ccpriomap \ clockclass \ - eventclass \ + component \ + componentclass \ + connection \ + ctfwriter \ event \ + eventclass \ fields \ ft \ + graph \ + logging \ + notification \ + notifiter \ packet \ + plugin \ + port \ ref \ - streamclass \ stream \ + streamclass \ trace \ values \ - ctfwriter \ - componentclass \ - component \ - notification \ - notifiter \ - plugin + version # Python modules (without `.py` extension) -EXTRA_MODULES = \ - clock_class \ - event_class \ - event \ - fields \ - field_types \ - object \ - packet \ - stream_class \ - stream \ - trace \ - utils \ - values \ - ctf_writer \ - component \ - notification \ - notification_iterator \ - plugin \ - py_plugin +EXTRA_MODULES = \ + clock_class \ + clock_class_priority_map \ + component \ + connection \ + ctf_writer \ + event \ + event_class \ + field_types \ + fields \ + graph \ + logging \ + notification \ + notification_iterator \ + object \ + packet \ + plugin \ + port \ + py_plugin \ + stream \ + stream_class \ + trace \ + utils \ + values # automatically generated file lists EXTRA_MODULES_PY = $(addprefix $(srcdir)/,$(addsuffix .py,$(EXTRA_MODULES))) @@ -64,19 +75,20 @@ $(NATIVE_MODULE_C): $(NATIVE_MODULE_I) $(NATIVE_MODULE_DEPS_I) $(SWIG) -python -Wall -I$(srcdir) -I$(top_srcdir)/include -module $(NATIVE_MODULE) -outcurrentdir $(NATIVE_MODULE_I) # native_bt module -_native_bt_la_SOURCES = native_bt_wrap.c +_native_bt_la_SOURCES = logging.h logging.c +nodist__native_bt_la_SOURCES = native_bt_wrap.c _native_bt_la_LDFLAGS = -module -_native_bt_la_CFLAGS = $(GLIB_CFLAGS) $(PYTHON_INCLUDE) -I$(srcdir) $(AM_CFLAGS) -_native_bt_la_LIBS = $(GLIB_LIBS) +_native_bt_la_CFLAGS = $(PYTHON_INCLUDE) -I$(srcdir) $(AM_CFLAGS) _native_bt_la_LIBADD = \ $(top_builddir)/lib/libbabeltrace.la \ - $(top_builddir)/formats/ctf/libbabeltrace-ctf.la + $(top_builddir)/logging/libbabeltrace-logging.la \ + $(top_builddir)/common/libbabeltrace-common.la # extra module sources -> build directory all-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ for file in $(EXTRA_MODULES_PY); do \ - cp -f $(srcdir)/$$file $(builddir); \ + cp -f "$(srcdir)/$$file" "$(builddir)"; \ done; \ fi @@ -84,7 +96,7 @@ all-local: clean-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ for file in $(EXTRA_MODULES_PY); do \ - rm -f $(srcdir)/$$file $(builddir); \ + rm -f "$(srcdir)/$$file" "$(builddir)"; \ done; \ fi diff --git a/bindings/python/bt2/__init__.py.in b/bindings/python/bt2/__init__.py.in index ba196289..ebab24ab 100644 --- a/bindings/python/bt2/__init__.py.in +++ b/bindings/python/bt2/__init__.py.in @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -24,18 +24,59 @@ __version__ = '@PACKAGE_VERSION@' from bt2.clock_class import * +from bt2.clock_class import _ClockValue +from bt2.clock_class_priority_map import * from bt2.component import * +from bt2.component import _FilterComponent +from bt2.component import _GenericFilterComponentClass +from bt2.component import _GenericSinkComponentClass +from bt2.component import _GenericSourceComponentClass +from bt2.component import _SinkComponent +from bt2.component import _SourceComponent +from bt2.component import _UserFilterComponent +from bt2.component import _UserSinkComponent +from bt2.component import _UserSourceComponent +from bt2.connection import * +from bt2.connection import _Connection +from bt2.connection import _PrivateConnection from bt2.ctf_writer import * +from bt2.ctf_writer import _CtfWriterStream +from bt2.event import _Event from bt2.event_class import * from bt2.field_types import * +from bt2.field_types import _FieldType from bt2.fields import * +from bt2.fields import _ArrayField +from bt2.fields import _EnumerationField +from bt2.fields import _Field +from bt2.fields import _FloatingPointNumberField +from bt2.fields import _IntegerField +from bt2.fields import _SequenceField +from bt2.fields import _StringField +from bt2.fields import _StructureField +from bt2.fields import _VariantField +from bt2.graph import * +from bt2.logging import * from bt2.notification import * +from bt2.notification import _DiscardedEventsNotification +from bt2.notification import _DiscardedPacketsNotification from bt2.notification_iterator import * +from bt2.notification_iterator import _UserNotificationIterator +from bt2.packet import _Packet from bt2.plugin import * +from bt2.port import * +from bt2.port import _InputPort +from bt2.port import _OutputPort +from bt2.port import _Port +from bt2.port import _PrivateInputPort +from bt2.port import _PrivateOutputPort +from bt2.port import _PrivatePort from bt2.py_plugin import * +from bt2.stream import _Stream from bt2.stream_class import * from bt2.trace import * from bt2.values import * +from bt2.values import _Value class Error(Exception): @@ -46,11 +87,11 @@ class CreationError(Error): pass -class FrozenError(Error): +class Frozen(Error): pass -class NoSuchPluginError(Error): +class NoSuchPlugin(Error): pass @@ -58,6 +99,10 @@ class UnsupportedFeature(Exception): pass +class NoSinkComponent(Exception): + pass + + class TryAgain(Exception): pass @@ -66,12 +111,42 @@ class Stop(StopIteration): pass -class IncompleteUserClassError(Error): +class PortConnectionRefused(Exception): + pass + + +class IncompleteUserClass(Error): pass +class GraphCanceled(Exception): + pass + + +class NotificationIteratorCanceled(Exception): + pass + + +class ConnectionEnded(Exception): + pass + + +class _ListenerHandle: + def __init__(self, listener_id, obj): + self._listener_id = listener_id + self._obj = obj + + import bt2.native_bt as _native_bt import atexit atexit.register(_native_bt.py3_cc_exit_handler) +version = (_native_bt.version_get_major(), _native_bt.version_get_minor(), + _native_bt.version_get_patch(), _native_bt.version_get_extra()) _native_bt.py3_cc_init_from_bt2() +del _native_bt + +try: + del native_bt +except: + pass diff --git a/bindings/python/bt2/clock_class.py b/bindings/python/bt2/clock_class.py index adfba448..24b1db68 100644 --- a/bindings/python/bt2/clock_class.py +++ b/bindings/python/bt2/clock_class.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,7 @@ from bt2 import native_bt, object, utils import uuid as uuidp +import numbers import bt2 @@ -40,6 +41,9 @@ class ClockClassOffset: def cycles(self): return self._cycles + def __hash__(self): + return hash((self.seconds, self.cycles)) + def __eq__(self, other): if not isinstance(other, self.__class__): # not comparing apples to apples @@ -49,10 +53,11 @@ class ClockClassOffset: class ClockClass(object._Object): - def __init__(self, name, description=None, frequency=None, precision=None, + def __init__(self, name, frequency, description=None, precision=None, offset=None, is_absolute=None, uuid=None): utils._check_str(name) - ptr = native_bt.ctf_clock_class_create(name) + utils._check_uint64(frequency) + ptr = native_bt.ctf_clock_class_create(name, frequency) if ptr is None: raise bt2.CreationError('cannot create clock class object') @@ -113,10 +118,21 @@ class ClockClass(object._Object): memo[id(self)] = cpy return cpy + def __hash__(self): + return hash(( + self.name, + self.description, + self.frequency, + self.precision, + self.offset.seconds, + self.offset.cycles, + self.is_absolute, + self.uuid)) + @property def name(self): name = native_bt.ctf_clock_class_get_name(self._ptr) - utils._handle_ptr(name, "cannot get clock class object's name") + assert(name is not None) return name @name.setter @@ -127,8 +143,7 @@ class ClockClass(object._Object): @property def description(self): - description = native_bt.ctf_clock_class_get_description(self._ptr) - return description + return native_bt.ctf_clock_class_get_description(self._ptr) @description.setter def description(self, description): @@ -139,10 +154,7 @@ class ClockClass(object._Object): @property def frequency(self): frequency = native_bt.ctf_clock_class_get_frequency(self._ptr) - - if utils._is_m1ull(frequency): - raise bt2.Error("cannot get clock class object's frequency") - + assert(frequency >= 1) return frequency @frequency.setter @@ -154,10 +166,7 @@ class ClockClass(object._Object): @property def precision(self): precision = native_bt.ctf_clock_class_get_precision(self._ptr) - - if utils._is_m1ull(precision): - raise bt2.Error("cannot get clock class object's precision") - + assert(precision >= 0) return precision @precision.setter @@ -169,9 +178,9 @@ class ClockClass(object._Object): @property def offset(self): ret, offset_s = native_bt.ctf_clock_class_get_offset_s(self._ptr) - utils._handle_ret(ret, "cannot get clock class object's offset (seconds)") + assert(ret == 0) ret, offset_cycles = native_bt.ctf_clock_class_get_offset_cycles(self._ptr) - utils._handle_ret(ret, "cannot get clock class object's offset (cycles)") + assert(ret == 0) return ClockClassOffset(offset_s, offset_cycles) @offset.setter @@ -184,8 +193,8 @@ class ClockClass(object._Object): @property def is_absolute(self): - is_absolute = native_bt.ctf_clock_class_get_is_absolute(self._ptr) - utils._handle_ret(is_absolute, "cannot get clock class object's absoluteness") + is_absolute = native_bt.ctf_clock_class_is_absolute(self._ptr) + assert(is_absolute >= 0) return is_absolute > 0 @is_absolute.setter @@ -199,7 +208,7 @@ class ClockClass(object._Object): uuid_bytes = native_bt.ctf_clock_class_get_uuid(self._ptr) if uuid_bytes is None: - raise bt2.Error("cannot get clock class object's UUID") + return return uuidp.UUID(bytes=uuid_bytes) @@ -209,7 +218,7 @@ class ClockClass(object._Object): ret = native_bt.ctf_clock_class_set_uuid(self._ptr, uuid.bytes) utils._handle_ret(ret, "cannot set clock class object's UUID") - def create_clock_value(self, cycles): + def __call__(self, cycles): return _ClockValue(self._ptr, cycles) @@ -231,13 +240,13 @@ class _ClockValue(object._Object): @property def clock_class(self): ptr = native_bt.ctf_clock_value_get_class(self._ptr) - utils._handle_ptr(ptr, "cannot get clock value object's clock class object") + assert(ptr) return ClockClass._create_from_ptr(ptr) @property def cycles(self): ret, cycles = native_bt.ctf_clock_value_get_value(self._ptr) - utils._handle_ret(ret, "cannot get clock value object's cycles") + assert(ret == 0) return cycles @property @@ -247,6 +256,9 @@ class _ClockValue(object._Object): return ns def __eq__(self, other): + if isinstance(other, numbers.Integral): + return int(other) == self.cycles + if not isinstance(other, self.__class__): # not comparing apples to apples return False @@ -254,12 +266,12 @@ class _ClockValue(object._Object): if self.addr == other.addr: return True - self_props = self.clock_class.addr, self.cycles - other_props = other.clock_class.addr, other.cycles + self_props = self.clock_class, self.cycles + other_props = other.clock_class, other.cycles return self_props == other_props def __copy__(self): - return self.clock_class.create_clock_value(self.cycles) + return self.clock_class(self.cycles) def __deepcopy__(self, memo): cpy = self.__copy__() diff --git a/bindings/python/bt2/clock_class_priority_map.py b/bindings/python/bt2/clock_class_priority_map.py new file mode 100644 index 00000000..af4ac26f --- /dev/null +++ b/bindings/python/bt2/clock_class_priority_map.py @@ -0,0 +1,129 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# 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. + +from bt2 import native_bt, object, utils +import collections.abc +import bt2.clock_class +import copy +import bt2 + + +class _ClockClassIterator(collections.abc.Iterator): + def __init__(self, cc_prio_map): + self._cc_prio_map = cc_prio_map + self._at = 0 + + def __next__(self): + if self._at == len(self._cc_prio_map): + raise StopIteration + + cc_ptr = native_bt.clock_class_priority_map_get_clock_class_by_index(self._cc_prio_map._ptr, + self._at) + assert(cc_ptr) + clock_class = bt2.ClockClass._create_from_ptr(cc_ptr) + self._at += 1 + return clock_class + + +class ClockClassPriorityMap(object._Object, collections.abc.MutableMapping): + def __init__(self, clock_class_priorities=None): + ptr = native_bt.clock_class_priority_map_create() + + if ptr is None: + raise bt2.CreationError('cannot create clock class priority map object') + + super().__init__(ptr) + + if clock_class_priorities is not None: + for clock_class, priority in clock_class_priorities.items(): + self[clock_class] = priority + + def __getitem__(self, key): + utils._check_type(key, bt2.ClockClass) + ret, prio = native_bt.clock_class_priority_map_get_clock_class_priority(self._ptr, + key._ptr) + + if ret != 0: + raise KeyError(key) + + return prio + + def __len__(self): + count = native_bt.clock_class_priority_map_get_clock_class_count(self._ptr) + assert(count >= 0) + return count + + def __delitem__(self): + raise NotImplementedError + + def __setitem__(self, key, value): + utils._check_type(key, bt2.ClockClass) + utils._check_uint64(value) + ret = native_bt.clock_class_priority_map_add_clock_class(self._ptr, + key._ptr, + value) + utils._handle_ret(ret, "cannot set clock class's priority in clock class priority map object") + + def __iter__(self): + return _ClockClassIterator(self) + + @property + def highest_priority_clock_class(self): + cc_ptr = native_bt.clock_class_priority_map_get_highest_priority_clock_class(self._ptr) + + if cc_ptr is None: + return + + return bt2.ClockClass._create_from_ptr(cc_ptr) + + def _get_prios(self): + prios = {} + + for clock_class, prio in self.items(): + prios[clock_class] = prio + + return prios + + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + return self._get_prios() == other._get_prios() + + def _copy(self, cc_copy_func): + cpy = ClockClassPriorityMap() + + for clock_class, prio in self.items(): + cpy[cc_copy_func(clock_class)] = prio + + return cpy + + def __copy__(self): + return self._copy(lambda obj: obj) + + def __deepcopy__(self, memo): + cpy = self._copy(copy.deepcopy) + memo[id(self)] = cpy + return cpy diff --git a/bindings/python/bt2/component.py b/bindings/python/bt2/component.py index 6ee1a145..927fd061 100644 --- a/bindings/python/bt2/component.py +++ b/bindings/python/bt2/component.py @@ -24,8 +24,15 @@ from bt2 import native_bt, object, utils import bt2.notification_iterator import collections.abc import bt2.values +import traceback +import bt2.port import sys import bt2 +import os + + +_env_var = os.environ.get('BABELTRACE_PYTHON_BT2_NO_TRACEBACK') +_NO_PRINT_TRACEBACK = _env_var == '1' # This class wraps a component class pointer. This component class could @@ -35,7 +42,9 @@ import bt2 class _GenericComponentClass(object._Object): @property def name(self): - return native_bt.component_class_get_name(self._ptr) + name = native_bt.component_class_get_name(self._ptr) + assert(name is not None) + return name @property def description(self): @@ -48,17 +57,15 @@ class _GenericComponentClass(object._Object): def query(self, obj, params=None): return _query(self._ptr, obj, params) - def __call__(self, params=None, name=None): - params = bt2.create_value(params) - comp_ptr = native_bt.component_create_with_init_method_data(self._ptr, - name, - params._ptr, - None) - - if comp_ptr is None: - raise bt2.CreationError('cannot create component object') + def __eq__(self, other): + if not isinstance(other, _GenericComponentClass): + try: + if not issubclass(other, _UserComponent): + return False + except TypeError: + return False - return _create_generic_component_from_ptr(comp_ptr) + return self.addr == other.addr class _GenericSourceComponentClass(_GenericComponentClass): @@ -73,94 +80,175 @@ class _GenericSinkComponentClass(_GenericComponentClass): pass +def _handle_component_status(status, gen_error_msg): + if status == native_bt.COMPONENT_STATUS_END: + raise bt2.Stop + elif status == native_bt.COMPONENT_STATUS_AGAIN: + raise bt2.TryAgain + elif status == native_bt.COMPONENT_STATUS_UNSUPPORTED: + raise bt2.UnsupportedFeature + elif status == native_bt.COMPONENT_STATUS_REFUSE_PORT_CONNECTION: + raise bt2.PortConnectionRefused + elif status == native_bt.COMPONENT_STATUS_GRAPH_IS_CANCELED: + raise bt2.GraphCanceled + elif status < 0: + raise bt2.Error(gen_error_msg) + + +class _PortIterator(collections.abc.Iterator): + def __init__(self, comp_ports): + self._comp_ports = comp_ports + self._at = 0 + + def __next__(self): + if self._at == len(self._comp_ports): + raise StopIteration + + comp_ports = self._comp_ports + comp_ptr = comp_ports._component._ptr + port_ptr = comp_ports._get_port_at_index_fn(comp_ptr, self._at) + assert(port_ptr) + + if comp_ports._is_private: + port_pub_ptr = native_bt.port_from_private_port(port_ptr) + name = native_bt.port_get_name(port_pub_ptr) + native_bt.put(port_pub_ptr) + else: + name = native_bt.port_get_name(port_ptr) + + assert(name is not None) + native_bt.put(port_ptr) + self._at += 1 + return name + + +class _ComponentPorts(collections.abc.Mapping): + def __init__(self, is_private, component, + get_port_by_name_fn, get_port_at_index_fn, + get_port_count_fn): + self._is_private = is_private + self._component = component + self._get_port_by_name_fn = get_port_by_name_fn + self._get_port_at_index_fn = get_port_at_index_fn + self._get_port_count_fn = get_port_count_fn + + def __getitem__(self, key): + utils._check_str(key) + port_ptr = self._get_port_by_name_fn(self._component._ptr, key) + + if port_ptr is None: + raise KeyError(key) + + if self._is_private: + return bt2.port._create_private_from_ptr(port_ptr) + else: + return bt2.port._create_from_ptr(port_ptr) + + def __len__(self): + if self._is_private: + pub_ptr = native_bt.component_from_private_component(self._component._ptr) + count = self._get_port_count_fn(pub_ptr) + native_bt.put(pub_ptr) + else: + count = self._get_port_count_fn(self._component._ptr) + + assert(count >= 0) + return count + + def __iter__(self): + return _PortIterator(self) + + # This class holds the methods which are common to both generic # component objects and Python user component objects. They use the # internal native _ptr, however it was set, to call native API # functions. -class _CommonComponentMethods: +class _Component: @property def name(self): - return native_bt.component_get_name(self._ptr) + name = native_bt.component_get_name(self._ptr) + assert(name is not None) + return name + + @property + def graph(self): + ptr = native_bt.component_get_graph(self._ptr) + assert(ptr) + return bt2.Graph._create_from_ptr(ptr) @property def component_class(self): cc_ptr = native_bt.component_get_class(self._ptr) - utils._handle_ptr(cc_ptr, "cannot get component object's class object") + assert(cc_ptr) return _create_generic_component_class_from_ptr(cc_ptr) - def _handle_status(self, status, gen_error_msg): - if status == native_bt.COMPONENT_STATUS_END: - raise bt2.Stop - elif status == native_bt.COMPONENT_STATUS_AGAIN: - raise bt2.TryAgain - elif status == native_bt.COMPONENT_STATUS_UNSUPPORTED: - raise bt2.UnsupportedFeature - elif status < 0: - raise bt2.Error(gen_error_msg) - - -class _CommonSourceComponentMethods(_CommonComponentMethods): - def create_notification_iterator(self): - iter_ptr = native_bt.component_source_create_notification_iterator_with_init_method_data(self._ptr, None) - - if iter_ptr is None: - raise bt2.CreationError('cannot create notification iterator object') - - return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(iter_ptr) + def __eq__(self, other): + if not hasattr(other, 'addr'): + return False + return self.addr == other.addr -class _CommonFilterComponentMethods(_CommonComponentMethods): - def create_notification_iterator(self): - iter_ptr = native_bt.component_filter_create_notification_iterator_with_init_method_data(self._ptr, None) - if iter_ptr is None: - raise bt2.CreationError('cannot create notification iterator object') - - return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(iter_ptr) +class _SourceComponent(_Component): + pass - def add_notification_iterator(self, notif_iter): - utils._check_type(notif_iter, bt2.notification_iterator._GenericNotificationIteratorMethods) - status = native_bt.component_filter_add_iterator(self._ptr, notif_iter._ptr) - self._handle_status(status, 'unexpected error: cannot add notification iterator to filter component object') +class _FilterComponent(_Component): + pass -class _CommonSinkComponentMethods(_CommonComponentMethods): - def add_notification_iterator(self, notif_iter): - utils._check_type(notif_iter, bt2.notification_iterator._GenericNotificationIteratorMethods) - status = native_bt.component_sink_add_iterator(self._ptr, notif_iter._ptr) - self._handle_status(status, 'unexpected error: cannot add notification iterator to sink component object') - def consume(self): - status = native_bt.component_sink_consume(self._ptr) - self._handle_status(status, 'unexpected error: cannot consume sink component object') +class _SinkComponent(_Component): + pass # This is analogous to _GenericSourceComponentClass, but for source # component objects. -class _GenericSourceComponent(object._Object, _CommonSourceComponentMethods): - pass +class _GenericSourceComponent(object._Object, _SourceComponent): + @property + def output_ports(self): + return _ComponentPorts(False, self, + native_bt.component_source_get_output_port_by_name, + native_bt.component_source_get_output_port_by_index, + native_bt.component_source_get_output_port_count) # This is analogous to _GenericFilterComponentClass, but for filter # component objects. -class _GenericFilterComponent(object._Object, _CommonFilterComponentMethods): - pass +class _GenericFilterComponent(object._Object, _FilterComponent): + @property + def output_ports(self): + return _ComponentPorts(False, self, + native_bt.component_filter_get_output_port_by_name, + native_bt.component_filter_get_output_port_by_index, + native_bt.component_filter_get_output_port_count) + + @property + def input_ports(self): + return _ComponentPorts(False, self, + native_bt.component_filter_get_input_port_by_name, + native_bt.component_filter_get_input_port_by_index, + native_bt.component_filter_get_input_port_count) # This is analogous to _GenericSinkComponentClass, but for sink # component objects. -class _GenericSinkComponent(object._Object, _CommonSinkComponentMethods): - pass +class _GenericSinkComponent(object._Object, _SinkComponent): + @property + def input_ports(self): + return _ComponentPorts(False, self, + native_bt.component_sink_get_input_port_by_name, + native_bt.component_sink_get_input_port_by_index, + native_bt.component_sink_get_input_port_count) -_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS = { +_COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS = { native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponent, native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponent, native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponent, } -_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_CLS = { +_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS = { native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponentClass, native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponentClass, native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponentClass, @@ -169,12 +257,12 @@ _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_CLS = { def _create_generic_component_from_ptr(ptr): comp_cls_type = native_bt.component_get_class_type(ptr) - return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS[comp_cls_type]._create_from_ptr(ptr) + return _COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS[comp_cls_type]._create_from_ptr(ptr) def _create_generic_component_class_from_ptr(ptr): comp_cls_type = native_bt.component_class_get_type(ptr) - return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_CLS[comp_cls_type]._create_from_ptr(ptr) + return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS[comp_cls_type]._create_from_ptr(ptr) def _trim_docstring(docstring): @@ -212,22 +300,19 @@ def _query(comp_cls_ptr, obj, params): params_ptr = params._ptr results_ptr = native_bt.component_class_query(comp_cls_ptr, obj, - params_ptr) + params_ptr) if results_ptr is None: raise bt2.Error('cannot query info with object "{}"'.format(obj)) - if results_ptr == native_bt.value_null: - return - return bt2.values._create_from_ptr(results_ptr) # Metaclass for component classes defined by Python code. # # The Python user can create a standard Python class which inherits one -# of the three base classes (UserSourceComponent, UserFilterComponent, -# or UserSinkComponent). Those base classes set this class +# of the three base classes (_UserSourceComponent, _UserFilterComponent, +# or _UserSinkComponent). Those base classes set this class # (_UserComponentType) as their metaclass. # # Once the body of a user-defined component class is executed, this @@ -260,29 +345,29 @@ def _query(comp_cls_ptr, obj, params): # def __init__(self, params, name, something_else): # ... # -# The user-defined component class can also have a _destroy() method +# The user-defined component class can also have a _finalize() method # (do NOT use __del__()) to be notified when the component object is -# (really) destroyed. +# finalized. # # User-defined source and filter component classes must use the # `notification_iterator_class` class parameter to specify the # notification iterator class to use for this component class: # -# class MyNotificationIterator(bt2.UserNotificationIterator): +# class MyNotificationIterator(bt2._UserNotificationIterator): # ... # -# class MySource(bt2.UserSourceComponent, +# class MySource(bt2._UserSourceComponent, # notification_iterator_class=MyNotificationIterator): # ... # # This notification iterator class must inherit -# bt2.UserNotificationIterator, and it must define the _get() and +# bt2._UserNotificationIterator, and it must define the _get() and # _next() methods. The notification iterator class can also define an # __init__() method: this method has access to the original Python # component object which was used to create it as the `component` -# property. The notification iterator class can also define a _destroy() -# method (again, do NOT use __del__()): this is called when the -# notification iterator is (really) destroyed. +# property. The notification iterator class can also define a +# _finalize() method (again, do NOT use __del__()): this is called when +# the notification iterator is (really) destroyed. # # When the user-defined class is destroyed, this metaclass's __del__() # method is called: the native BT component class pointer is put (not @@ -297,14 +382,24 @@ class _UserComponentType(type): super().__init__(class_name, bases, namespace) # skip our own bases; they are never directly instantiated by the user - if class_name in ('_UserComponent', 'UserSourceComponent', 'UserFilterComponent', 'UserSinkComponent'): + own_bases = ( + '_UserComponent', + '_UserFilterSinkComponent', + '_UserSourceComponent', + '_UserFilterComponent', + '_UserSinkComponent', + ) + + if class_name in own_bases: return comp_cls_name = kwargs.get('name', class_name) + utils._check_str(comp_cls_name) comp_cls_descr = None comp_cls_help = None if hasattr(cls, '__doc__') and cls.__doc__ is not None: + utils._check_str(cls.__doc__) docstring = _trim_docstring(cls.__doc__) lines = docstring.splitlines() @@ -316,101 +411,64 @@ class _UserComponentType(type): iter_cls = kwargs.get('notification_iterator_class') - if UserSourceComponent in bases: + if _UserSourceComponent in bases: _UserComponentType._set_iterator_class(cls, iter_cls) - has_seek_time = _UserComponentType._has_seek_to_time_method(cls._iter_cls) cc_ptr = native_bt.py3_component_class_source_create(cls, comp_cls_name, comp_cls_descr, - comp_cls_help, - has_seek_time) - elif UserFilterComponent in bases: + comp_cls_help) + elif _UserFilterComponent in bases: _UserComponentType._set_iterator_class(cls, iter_cls) - has_seek_time = _UserComponentType._has_seek_to_time_method(cls._iter_cls) cc_ptr = native_bt.py3_component_class_filter_create(cls, comp_cls_name, comp_cls_descr, - comp_cls_help, - has_seek_time) - elif UserSinkComponent in bases: + comp_cls_help) + elif _UserSinkComponent in bases: if not hasattr(cls, '_consume'): - raise bt2.IncompleteUserClassError("cannot create component class '{}': missing a _consume() method".format(class_name)) + raise bt2.IncompleteUserClass("cannot create component class '{}': missing a _consume() method".format(class_name)) cc_ptr = native_bt.py3_component_class_sink_create(cls, comp_cls_name, comp_cls_descr, comp_cls_help) else: - raise bt2.IncompleteUserClassError("cannot find a known component class base in the bases of '{}'".format(class_name)) + raise bt2.IncompleteUserClass("cannot find a known component class base in the bases of '{}'".format(class_name)) if cc_ptr is None: raise bt2.CreationError("cannot create component class '{}'".format(class_name)) cls._cc_ptr = cc_ptr - def __call__(cls, *args, **kwargs): - # create instance + def _init_from_native(cls, comp_ptr, params_ptr): + # create instance, not user-initialized yet self = cls.__new__(cls) - # assign native component pointer received from caller - self._ptr = kwargs.get('__comp_ptr') - name = kwargs.get('name') - - if self._ptr is None: - # called from Python code - self._belongs_to_native_component = False - - # py3_component_create() will call self.__init__() with the - # desired arguments and keyword arguments. This is needed - # because functions such as - # bt_component_sink_set_minimum_input_count() can only be - # called _during_ the bt_component_create() call (in - # Python words: during self.__init__()). - # - # The arguments and keyword arguments to use for - # self.__init__() are put in the object itself to find them - # from the bt_component_create() function. - self._init_args = args - self._init_kwargs = kwargs - native_bt.py3_component_create(cls._cc_ptr, self, name) - - # At this point, self._ptr should be set to non-None. If - # it's not, an error occured during the - # native_bt.py3_component_create() call. We consider this a - # creation error. - if self._ptr is None: - raise bt2.CreationError("cannot create component object from component class '{}'".format(cls.__name__)) - else: - # Called from non-Python code (within - # bt_component_create()): call __init__() here, after - # removing the __comp_ptr keyword argument which is just for - # this __call__() method. - self._belongs_to_native_component = True - del kwargs['__comp_ptr'] + # pointer to native private component object (weak/borrowed) + self._ptr = comp_ptr - # inject `name` into the keyword arguments - kwargs['name'] = self.name - self.__init__(*args, **kwargs) + # call user's __init__() method + if params_ptr is not None: + native_bt.get(params_ptr) + params = bt2.values._create_from_ptr(params_ptr) + else: + params = None + self.__init__(params) return self - @staticmethod - def _has_seek_to_time_method(iter_cls): - return hasattr(iter_cls, '_seek_to_time') + def __call__(cls, *args, **kwargs): + raise bt2.Error('cannot directly instantiate a user component from a Python module') @staticmethod def _set_iterator_class(cls, iter_cls): if iter_cls is None: - raise bt2.IncompleteUserClassError("cannot create component class '{}': missing notification iterator class".format(cls.__name__)) + raise bt2.IncompleteUserClass("cannot create component class '{}': missing notification iterator class".format(cls.__name__)) - if not issubclass(iter_cls, bt2.notification_iterator.UserNotificationIterator): - raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class does not inherit bt2.UserNotificationIterator".format(cls.__name__)) + if not issubclass(iter_cls, bt2.notification_iterator._UserNotificationIterator): + raise bt2.IncompleteUserClass("cannot create component class '{}': notification iterator class does not inherit bt2._UserNotificationIterator".format(cls.__name__)) - if not hasattr(iter_cls, '_get'): - raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class is missing a _get() method".format(cls.__name__)) - - if not hasattr(iter_cls, '_next'): - raise bt2.IncompleteUserClassError("cannot create component class '{}': notification iterator class is missing a _next() method".format(cls.__name__)) + if not hasattr(iter_cls, '__next__'): + raise bt2.IncompleteUserClass("cannot create component class '{}': notification iterator class is missing a __next__() method".format(cls.__name__)) cls._iter_cls = iter_cls @@ -430,131 +488,206 @@ class _UserComponentType(type): def addr(cls): return int(cls._cc_ptr) - def query(cls, action, params=None): - return _query(cls._cc_ptr, action, params) + def query(cls, obj, params=None): + return _query(cls._cc_ptr, obj, params) - def _query_from_bt(cls, action, params): + def _query_from_native(cls, obj, params_ptr): # this can raise, in which case the native call to # bt_component_class_query() returns NULL - results = cls._query(action, params) - results = bt2.create_value(results) + if params_ptr is not None: + native_bt.get(params_ptr) + params = bt2.values._create_from_ptr(params_ptr) + else: + params = None + + try: + results = cls._query(obj, params) + except: + if not _NO_PRINT_TRACEBACK: + traceback.print_exc() + + return + + if results is NotImplemented: + return results + + try: + results = bt2.create_value(results) + except: + if not _NO_PRINT_TRACEBACK: + traceback.print_exc() + + return if results is None: results_addr = int(native_bt.value_null) else: - # steal the underlying native value object for the caller + # return new reference + results._get() results_addr = int(results._ptr) - results._ptr = None return results_addr - @staticmethod - def _query(action, params): + @classmethod + def _query(cls, obj, params): # BT catches this and returns NULL to the user - raise NotImplementedError + return NotImplemented + + def __eq__(self, other): + if not hasattr(other, 'addr'): + return False + + return self.addr == other.addr def __del__(cls): if hasattr(cls, '_cc_ptr'): native_bt.put(cls._cc_ptr) -class _ComponentInputNotificationIterators(collections.abc.Sequence): - def __init__(self, comp, count_func, get_func): - self._comp = comp - self._count_func = count_func - self._get_func = get_func - - def __len__(self): - status, count = self._count_func(self._comp._ptr) - utils._handle_ret(status, "cannot get component object's input notification iterator count") - return count - - def __getitem__(self, index): - utils._check_uint64(index) - - if index >= len(self): - raise IndexError +class _UserComponent(metaclass=_UserComponentType): + @property + def name(self): + pub_ptr = native_bt.component_from_private_component(self._ptr) + name = native_bt.component_get_name(pub_ptr) + native_bt.put(pub_ptr) + assert(name is not None) + return name - notif_iter_ptr = self._get_func(self._comp._ptr, index) - utils._handle_ptr(notif_iter_ptr, "cannot get component object's input notification iterator") - return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr) + @property + def graph(self): + pub_ptr = native_bt.component_from_private_component(self._ptr) + ptr = native_bt.component_get_graph(pub_ptr) + native_bt.put(pub_ptr) + assert(ptr) + return bt2.Graph._create_from_ptr(ptr) + @property + def component_class(self): + pub_ptr = native_bt.component_from_private_component(self._ptr) + cc_ptr = native_bt.component_get_class(pub_ptr) + native_bt.put(pub_ptr) + assert(cc_ptr) + return _create_generic_component_class_from_ptr(cc_ptr) -class _UserComponent(metaclass=_UserComponentType): @property def addr(self): return int(self._ptr) - def __init__(self, *args, **kwargs): + def __init__(self, params=None): pass - def _destroy(self): + def _finalize(self): pass - def __del__(self): - if not self._belongs_to_native_component: - self._belongs_to_native_component = True - native_bt.py3_component_on_del(self) - native_bt.put(self._ptr) - - -class UserSourceComponent(_UserComponent, _CommonSourceComponentMethods): - pass - + def _accept_port_connection(self, port, other_port): + return True -class UserFilterComponent(_UserComponent, _CommonFilterComponentMethods): - def _set_minimum_input_notification_iterator_count(self, count): - utils._check_uint64(count) - status = native_bt.component_filter_set_minimum_input_count(self._ptr, count) - self._handle_status(status, "unexpected error: cannot set filter component object's minimum input notification iterator count") + def _accept_port_connection_from_native(self, port_ptr, other_port_ptr): + native_bt.get(port_ptr) + native_bt.get(other_port_ptr) + port = bt2.port._create_private_from_ptr(port_ptr) + other_port = bt2.port._create_from_ptr(other_port_ptr) + res = self._accept_port_connection(port, other_port_ptr) - _minimum_input_notification_iterator_count = property(fset=_set_minimum_input_notification_iterator_count) + if type(res) is not bool: + raise TypeError("'{}' is not a 'bool' object") - def _set_maximum_input_notification_iterator_count(self, count): - utils._check_uint64(count) - status = native_bt.component_filter_set_maximum_input_count(self._ptr, count) - self._handle_status(status, "unexpected error: cannot set filter component object's maximum input notification iterator count") + return res - _maximum_input_notification_iterator_count = property(fset=_set_maximum_input_notification_iterator_count) + def _port_connected(self, port, other_port): + pass - @property - def _input_notification_iterators(self): - return _ComponentInputNotificationIterators(self, - native_bt.component_filter_get_input_count, - native_bt.component_filter_get_input_iterator_private) + def _port_connected_from_native(self, port_ptr, other_port_ptr): + native_bt.get(port_ptr) + native_bt.get(other_port_ptr) + port = bt2.port._create_private_from_ptr(port_ptr) + other_port = bt2.port._create_from_ptr(other_port_ptr) - def _add_iterator_from_bt(self, notif_iter_ptr): - notif_iter = bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr) - self._add_notification_iterator(notif_iter) + try: + self._port_connected(port, other_port_ptr) + except: + if not _NO_PRINT_TRACEBACK: + traceback.print_exc() - def _add_notification_iterator(self, notif_iter): + def _port_disconnected(self, port): pass + def _port_disconnected_from_native(self, port_ptr): + native_bt.get(port_ptr) + port = bt2.port._create_private_from_ptr(port_ptr) -class UserSinkComponent(_UserComponent, _CommonSinkComponentMethods): - def _set_minimum_input_notification_iterator_count(self, count): - utils._check_uint64(count) - status = native_bt.component_sink_set_minimum_input_count(self._ptr, count) - self._handle_status(status, "unexpected error: cannot set sink component object's minimum input notification iterator count") - - _minimum_input_notification_iterator_count = property(fset=_set_minimum_input_notification_iterator_count) - - def _set_maximum_input_notification_iterator_count(self, count): - utils._check_uint64(count) - status = native_bt.component_sink_set_maximum_input_count(self._ptr, count) - self._handle_status(status, "unexpected error: cannot set sink component object's maximum input notification iterator count") + try: + self._port_disconnected(port) + except: + if not _NO_PRINT_TRACEBACK: + traceback.print_exc() - _maximum_input_notification_iterator_count = property(fset=_set_maximum_input_notification_iterator_count) +class _UserSourceComponent(_UserComponent, _SourceComponent): @property - def _input_notification_iterators(self): - return _ComponentInputNotificationIterators(self, - native_bt.component_sink_get_input_count, - native_bt.component_sink_get_input_iterator_private) - - def _add_iterator_from_bt(self, notif_iter_ptr): - notif_iter = bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr) - self._add_notification_iterator(notif_iter) + def _output_ports(self): + return _ComponentPorts(True, self, + native_bt.private_component_source_get_output_private_port_by_name, + native_bt.private_component_source_get_output_private_port_by_index, + native_bt.component_source_get_output_port_count) + + def _add_output_port(self, name): + utils._check_str(name) + fn = native_bt.private_component_source_add_output_private_port + comp_status, priv_port_ptr = fn(self._ptr, name, None) + _handle_component_status(comp_status, + 'cannot add output port to source component object') + assert(priv_port_ptr) + return bt2.port._create_private_from_ptr(priv_port_ptr) + + +class _UserFilterComponent(_UserComponent, _FilterComponent): + @property + def _output_ports(self): + return _ComponentPorts(True, self, + native_bt.private_component_filter_get_output_private_port_by_name, + native_bt.private_component_filter_get_output_private_port_by_index, + native_bt.component_filter_get_output_port_count) - def _add_notification_iterator(self, notif_iter): - pass + @property + def _input_ports(self): + return _ComponentPorts(True, self, + native_bt.private_component_filter_get_input_private_port_by_name, + native_bt.private_component_filter_get_input_private_port_by_index, + native_bt.component_filter_get_input_port_count) + + def _add_output_port(self, name): + utils._check_str(name) + fn = native_bt.private_component_filter_add_output_private_port + comp_status, priv_port_ptr = fn(self._ptr, name, None) + _handle_component_status(comp_status, + 'cannot add output port to filter component object') + assert(priv_port_ptr) + return bt2.port._create_private_from_ptr(priv_port_ptr) + + def _add_input_port(self, name): + utils._check_str(name) + fn = native_bt.private_component_filter_add_input_private_port + comp_status, priv_port_ptr = fn(self._ptr, name, None) + _handle_component_status(comp_status, + 'cannot add input port to filter component object') + assert(priv_port_ptr) + return bt2.port._create_private_from_ptr(priv_port_ptr) + + +class _UserSinkComponent(_UserComponent, _SinkComponent): + @property + def _input_ports(self): + return _ComponentPorts(True, self, + native_bt.private_component_sink_get_input_private_port_by_name, + native_bt.private_component_sink_get_input_private_port_by_index, + native_bt.component_sink_get_input_port_count) + + def _add_input_port(self, name): + utils._check_str(name) + fn = native_bt.private_component_sink_add_input_private_port + comp_status, priv_port_ptr = fn(self._ptr, name, None) + _handle_component_status(comp_status, + 'cannot add input port to sink component object') + assert(priv_port_ptr) + return bt2.port._create_private_from_ptr(priv_port_ptr) diff --git a/bindings/python/bt2/connection.py b/bindings/python/bt2/connection.py new file mode 100644 index 00000000..09092af0 --- /dev/null +++ b/bindings/python/bt2/connection.py @@ -0,0 +1,110 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# 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. + +from bt2 import native_bt, object, utils +import bt2.notification_iterator +import collections.abc +import bt2.port +import copy +import bt2 + + +def _handle_status(status, gen_error_msg): + if status == native_bt.CONNECTION_STATUS_GRAPH_IS_CANCELED: + raise bt2.GraphCanceled + elif status == native_bt.CONNECTION_STATUS_IS_ENDED: + raise bt2.ConnectionEnded + elif status < 0: + raise bt2.Error(gen_error_msg) + + +def _create_private_from_ptr(ptr): + obj = _PrivateConnection._create_from_ptr(ptr) + obj._pub_ptr = native_bt.connection_from_private_connection(ptr) + assert(obj._pub_ptr) + return obj + + +class _Connection(object._Object): + @staticmethod + def _downstream_port(ptr): + port_ptr = native_bt.connection_get_downstream_port(ptr) + utils._handle_ptr(port_ptr, "cannot get connection object's downstream port object") + return bt2.port._create_from_ptr(port_ptr) + + @staticmethod + def _upstream_port(ptr): + port_ptr = native_bt.connection_get_upstream_port(ptr) + utils._handle_ptr(port_ptr, "cannot get connection object's upstream port object") + return bt2.port._create_from_ptr(port_ptr) + + @property + def downstream_port(self): + return self._downstream_port(self._ptr) + + @property + def upstream_port(self): + return self._upstream_port(self._ptr) + + @staticmethod + def _is_ended(ptr): + return native_bt.connection_is_ended(ptr) == 1 + + @property + def is_ended(self): + return self._is_ended(self._ptr) + + def __eq__(self, other): + if type(other) not in (_Connection, _PrivateConnection): + return False + + return self.addr == other.addr + + +class _PrivateConnection(object._PrivateObject, _Connection): + def create_notification_iterator(self, notification_types=None): + if notification_types is None: + notif_types = None + else: + for notif_cls in notification_types: + if notif_cls not in bt2.notification._NOTIF_TYPE_TO_CLS.values(): + raise ValueError("'{}' is not a notification class".format(notif_cls)) + + notif_types = [notif_cls._TYPE for notif_cls in notification_types] + + status, notif_iter_ptr = native_bt.py3_create_notif_iter(int(self._ptr), + notif_types) + _handle_status(status, 'cannot create notification iterator object') + assert(notif_iter_ptr) + return bt2.notification_iterator._GenericNotificationIterator._create_from_ptr(notif_iter_ptr) + + @property + def is_ended(self): + return self._is_ended(self._pub_ptr) + + @property + def downstream_port(self): + return self._downstream_port(self._pub_ptr) + + @property + def upstream_port(self): + return self._upstream_port(self._pub_ptr) diff --git a/bindings/python/bt2/ctf_writer.py b/bindings/python/bt2/ctf_writer.py index 5a167cb2..0bbabaf4 100644 --- a/bindings/python/bt2/ctf_writer.py +++ b/bindings/python/bt2/ctf_writer.py @@ -98,7 +98,7 @@ class CtfWriterClock(object._Object): @property def name(self): name = native_bt.ctf_clock_get_name(self._ptr) - utils._handle_ptr(name, "cannot get CTF writer clock object's name") + assert(name is not None) return name @property @@ -115,10 +115,7 @@ class CtfWriterClock(object._Object): @property def frequency(self): frequency = native_bt.ctf_clock_get_frequency(self._ptr) - - if utils._is_m1ull(frequency): - raise bt2.Error("cannot get CTF writer clock object's frequency") - + assert(frequency >= 1) return frequency @frequency.setter @@ -130,10 +127,7 @@ class CtfWriterClock(object._Object): @property def precision(self): precision = native_bt.ctf_clock_get_precision(self._ptr) - - if utils._is_m1ull(precision): - raise bt2.Error("cannot get CTF writer clock object's precision") - + assert(precision >= 0) return precision @precision.setter @@ -145,9 +139,9 @@ class CtfWriterClock(object._Object): @property def offset(self): ret, offset_s = native_bt.ctf_clock_get_offset_s(self._ptr) - utils._handle_ret(ret, "cannot get CTF writer clock object's offset (seconds)") + assert(ret == 0) ret, offset_cycles = native_bt.ctf_clock_get_offset(self._ptr) - utils._handle_ret(ret, "cannot get CTF writer clock object's offset (cycles)") + assert(ret == 0) return bt2.ClockClassOffset(offset_s, offset_cycles) @offset.setter @@ -161,7 +155,7 @@ class CtfWriterClock(object._Object): @property def is_absolute(self): is_absolute = native_bt.ctf_clock_get_is_absolute(self._ptr) - utils._handle_ret(is_absolute, "cannot get CTF writer clock object's absoluteness") + assert(is_absolute >= 0) return is_absolute > 0 @is_absolute.setter @@ -173,10 +167,7 @@ class CtfWriterClock(object._Object): @property def uuid(self): uuid_bytes = native_bt.ctf_clock_get_uuid(self._ptr) - - if uuid_bytes is None: - raise bt2.Error("cannot get CTF writer clock object's UUID") - + assert(uuid_bytes is not None) return uuidp.UUID(bytes=uuid_bytes) @uuid.setter @@ -305,13 +296,13 @@ class CtfWriter(object._Object): @property def trace(self): trace_ptr = native_bt.ctf_writer_get_trace(self._ptr) - utils._handle_ptr(name, "cannot get CTF writer object's trace class") + assert(trace_ptr) return bt2.Trace._create_from_ptr(trace_ptr) @property def metadata_string(self): metadata_string = native_bt.ctf_writer_get_metadata_string(self._ptr) - utils._handle_ptr(metadata_string, "cannot get CTF writer object's metadata string") + assert(metadata_string is not None) return metadata_string def flush_metadata(self): diff --git a/bindings/python/bt2/event.py b/bindings/python/bt2/event.py index 1ca642c4..2e11de08 100644 --- a/bindings/python/bt2/event.py +++ b/bindings/python/bt2/event.py @@ -143,7 +143,7 @@ class _Event(object._Object): @property def payload_field(self): - field_ptr = native_bt.ctf_event_get_payload_field(self._ptr) + field_ptr = native_bt.ctf_event_get_event_payload(self._ptr) if field_ptr is None: return @@ -158,7 +158,7 @@ class _Event(object._Object): utils._check_type(payload, bt2.fields._Field) payload_ptr = payload._ptr - ret = native_bt.ctf_event_set_payload_field(self._ptr, payload_ptr) + ret = native_bt.ctf_event_set_event_payload(self._ptr, payload_ptr) utils._handle_ret(ret, "cannot set event object's payload field") def _get_clock_value_cycles(self, clock_class_ptr): @@ -173,7 +173,7 @@ class _Event(object._Object): utils._handle_ret(ret, "cannot get clock value object's cycles") return cycles - def get_clock_value(self, clock_class): + def clock_value(self, clock_class): utils._check_type(clock_class, bt2.ClockClass) clock_value_ptr = native_bt.ctf_event_get_clock_value(self._ptr, clock_class._ptr) @@ -184,7 +184,7 @@ class _Event(object._Object): clock_value = bt2.clock_class._create_clock_value_from_ptr(clock_value_ptr) return clock_value - def set_clock_value(self, clock_value): + def add_clock_value(self, clock_value): utils._check_type(clock_value, bt2.clock_class._ClockValue) ret = native_bt.ctf_event_set_clock_value(self._ptr, clock_value._ptr) @@ -293,12 +293,16 @@ class _Event(object._Object): cpy.context_field = copy_func(self.context_field) cpy.payload_field = copy_func(self.payload_field) - # copy known clock values + # Copy known clock value references. It's not necessary to copy + # clock class or clock value objects because once a clock value + # is created from a clock class, the clock class is frozen. + # Thus even if we copy the clock class, the user cannot modify + # it, therefore it's useless to copy it. for clock_class in self._clock_classes: - clock_value = self.get_clock_value(clock_class) + clock_value = self.clock_value(clock_class) if clock_value is not None: - cpy.set_clock_value(clock_value) + cpy.add_clock_value(clock_value) return cpy diff --git a/bindings/python/bt2/event_class.py b/bindings/python/bt2/event_class.py index 6d82b64e..fb527ad7 100644 --- a/bindings/python/bt2/event_class.py +++ b/bindings/python/bt2/event_class.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -29,58 +29,29 @@ import copy import bt2 -class _EventClassAttributesIterator(collections.abc.Iterator): - def __init__(self, attributes): - self._attributes = attributes - self._at = 0 - - def __next__(self): - if self._at == len(self._attributes): - raise StopIteration - - name = native_bt.ctf_event_class_get_attribute_name(self._attributes._event_class_ptr, - self._at) - utils._handle_ptr("cannot get event class object's attribute name") - self._at += 1 - return name - - -class _EventClassAttributes(collections.abc.MutableMapping): - def __init__(self, event_class_ptr): - self._event_class_ptr = event_class_ptr - - def __getitem__(self, key): - utils._check_str(key) - value_ptr = native_bt.ctf_event_class_get_attribute_value_by_name(self._event_class_ptr, - key) - - if value_ptr is None: - raise KeyError(key) - - return bt2.values._create_from_ptr(value_ptr) - - def __setitem__(self, key, value): - utils._check_str(key) - value = bt2.create_value(value) - ret = native_bt.ctf_event_class_set_attribute(self._event_class_ptr, key, - value._ptr) - utils._handle_ret(ret, "cannot set event class object's attribute") - - def __delitem__(self, key): - raise NotImplementedError - - def __len__(self): - count = native_bt.ctf_event_class_get_attribute_count(self._event_class_ptr) - utils._handle_ret(count, "cannot get event class object's attribute count") - return count - - def __iter__(self): - return _EventClassAttributesIterator(self) +class EventClassLogLevel: + UNKNOWN = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN + UNSPECIFIED = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED + EMERGENCY = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY + ALERT = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_ALERT + CRITICAL = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL + ERROR = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_ERROR + WARNING = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_WARNING + NOTICE = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_NOTICE + INFO = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_INFO + DEBUG_SYSTEM = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM + DEBUG_PROGRAM = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM + DEBUG_PROCESS = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS + DEBUG_MODULE = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE + DEBUG_UNIT = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT + DEBUG_FUNCTION = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION + DEBUG_LINE = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE + DEBUG = native_bt.CTF_EVENT_CLASS_LOG_LEVEL_DEBUG class EventClass(object._Object): - def __init__(self, name, id=None, context_field_type=None, - payload_field_type=None, attributes=None): + def __init__(self, name, id=None, log_level=None, emf_uri=None, + context_field_type=None, payload_field_type=None): utils._check_str(name) ptr = native_bt.ctf_event_class_create(name) @@ -92,16 +63,18 @@ class EventClass(object._Object): if id is not None: self.id = id + if log_level is not None: + self.log_level = log_level + + if emf_uri is not None: + self.emf_uri = emf_uri + if context_field_type is not None: self.context_field_type = context_field_type if payload_field_type is not None: self.payload_field_type = payload_field_type - if attributes is not None: - for name, value in attributes.items(): - self.attributes[name] = value - @property def stream_class(self): sc_ptr = native_bt.ctf_event_class_get_stream_class(self._ptr) @@ -109,10 +82,6 @@ class EventClass(object._Object): if sc_ptr is not None: return bt2.StreamClass._create_from_ptr(sc_ptr) - @property - def attributes(self): - return _EventClassAttributes(self._ptr) - @property def name(self): return native_bt.ctf_event_class_get_name(self._ptr) @@ -120,11 +89,7 @@ class EventClass(object._Object): @property def id(self): id = native_bt.ctf_event_class_get_id(self._ptr) - - if utils._is_m1ull(id): - raise bt2.Error("cannot get event class object's ID") - - return id + return id if id >= 0 else None @id.setter def id(self, id): @@ -132,6 +97,48 @@ class EventClass(object._Object): ret = native_bt.ctf_event_class_set_id(self._ptr, id) utils._handle_ret(ret, "cannot set event class object's ID") + @property + def log_level(self): + log_level = native_bt.ctf_event_class_get_log_level(self._ptr) + return log_level if log_level >= 0 else None + + @log_level.setter + def log_level(self, log_level): + log_levels = ( + EventClassLogLevel.UNSPECIFIED, + EventClassLogLevel.EMERGENCY, + EventClassLogLevel.ALERT, + EventClassLogLevel.CRITICAL, + EventClassLogLevel.ERROR, + EventClassLogLevel.WARNING, + EventClassLogLevel.NOTICE, + EventClassLogLevel.INFO, + EventClassLogLevel.DEBUG_SYSTEM, + EventClassLogLevel.DEBUG_PROGRAM, + EventClassLogLevel.DEBUG_PROCESS, + EventClassLogLevel.DEBUG_MODULE, + EventClassLogLevel.DEBUG_UNIT, + EventClassLogLevel.DEBUG_FUNCTION, + EventClassLogLevel.DEBUG_LINE, + EventClassLogLevel.DEBUG, + ) + + if log_level not in log_levels: + raise ValueError("'{}' is not a valid log level".format(log_level)) + + ret = native_bt.ctf_event_class_set_log_level(self._ptr, log_level) + utils._handle_ret(ret, "cannot set event class object's log level") + + @property + def emf_uri(self): + return native_bt.ctf_event_class_get_emf_uri(self._ptr) + + @emf_uri.setter + def emf_uri(self, emf_uri): + utils._check_str(emf_uri) + ret = native_bt.ctf_event_class_set_emf_uri(self._ptr, emf_uri) + utils._handle_ret(ret, "cannot set event class object's EMF URI") + @property def context_field_type(self): ft_ptr = native_bt.ctf_event_class_get_context_type(self._ptr) @@ -187,19 +194,19 @@ class EventClass(object._Object): if self.addr == other.addr: return True - self_attributes = {name: val for name, val in self.attributes.items()} - other_attributes = {name: val for name, val in other.attributes.items()} self_props = ( - self_attributes, self.name, self.id, + self.log_level, + self.emf_uri, self.context_field_type, self.payload_field_type ) other_props = ( - other_attributes, other.name, other.id, + other.log_level, + other.emf_uri, other.context_field_type, other.payload_field_type ) @@ -209,8 +216,11 @@ class EventClass(object._Object): cpy = EventClass(self.name) cpy.id = self.id - for name, value in self.attributes.items(): - cpy.attributes[name] = value + if self.log_level is not None: + cpy.log_level = self.log_level + + if self.emf_uri is not None: + cpy.emf_uri = self.emf_uri cpy.context_field_type = ft_copy_func(self.context_field_type) cpy.payload_field_type = ft_copy_func(self.payload_field_type) diff --git a/bindings/python/bt2/field_types.py b/bindings/python/bt2/field_types.py index 5268836c..3a4e820f 100644 --- a/bindings/python/bt2/field_types.py +++ b/bindings/python/bt2/field_types.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -103,7 +103,7 @@ class _AlignmentProp: @property def alignment(self): alignment = native_bt.ctf_field_type_get_alignment(self._ptr) - utils._handle_ret(alignment, "cannot get field type object's alignment") + assert(alignment >= 0) return alignment @alignment.setter @@ -117,7 +117,7 @@ class _ByteOrderProp: @property def byte_order(self): bo = native_bt.ctf_field_type_get_byte_order(self._ptr) - utils._handle_ret(bo, "cannot get field type object's byte order") + assert(bo >= 0) return bo @byte_order.setter @@ -162,26 +162,25 @@ class IntegerFieldType(_FieldType, _AlignmentProp, _ByteOrderProp): @property def size(self): size = native_bt.ctf_field_type_integer_get_size(self._ptr) - utils._handle_ret(size, "cannot get integer field type object's size") + assert(size >= 1) return size @property def is_signed(self): - is_signed = native_bt.ctf_field_type_integer_get_signed(self._ptr) - utils._handle_ret(is_signed, - "cannot get integer field type object's signedness") + is_signed = native_bt.ctf_field_type_integer_is_signed(self._ptr) + assert(is_signed >= 0) return is_signed > 0 @is_signed.setter def is_signed(self, is_signed): utils._check_bool(is_signed) - ret = native_bt.ctf_field_type_integer_set_signed(self._ptr, int(is_signed)) + ret = native_bt.ctf_field_type_integer_set_is_signed(self._ptr, int(is_signed)) utils._handle_ret(ret, "cannot set integer field type object's signedness") @property def base(self): base = native_bt.ctf_field_type_integer_get_base(self._ptr) - utils._handle_ret(base, "cannot get integer field type object's base") + assert(base >= 0) return base @base.setter @@ -193,7 +192,7 @@ class IntegerFieldType(_FieldType, _AlignmentProp, _ByteOrderProp): @property def encoding(self): encoding = native_bt.ctf_field_type_integer_get_encoding(self._ptr) - utils._handle_ret(encoding, "cannot get integer field type object's encoding") + assert(encoding >= 0) return encoding @encoding.setter @@ -205,7 +204,10 @@ class IntegerFieldType(_FieldType, _AlignmentProp, _ByteOrderProp): @property def mapped_clock_class(self): ptr = native_bt.ctf_field_type_integer_get_mapped_clock_class(self._ptr) - utils._handle_ptr(ptr, "cannot get integer field type object's mapped clock class") + + if ptr is None: + return + return bt2.ClockClass._create_from_ptr(ptr) @mapped_clock_class.setter @@ -239,7 +241,7 @@ class FloatingPointNumberFieldType(_FieldType, _AlignmentProp, _ByteOrderProp): @property def exponent_size(self): exp_size = native_bt.ctf_field_type_floating_point_get_exponent_digits(self._ptr) - utils._handle_ret(exp_size, "cannot get floating point number field type object's exponent size") + assert(exp_size >= 0) return exp_size @exponent_size.setter @@ -250,9 +252,9 @@ class FloatingPointNumberFieldType(_FieldType, _AlignmentProp, _ByteOrderProp): @property def mantissa_size(self): - exp_size = native_bt.ctf_field_type_floating_point_get_mantissa_digits(self._ptr) - utils._handle_ret(exp_size, "cannot get floating point number field type object's mantissa size") - return exp_size + mant_size = native_bt.ctf_field_type_floating_point_get_mantissa_digits(self._ptr) + assert(mant_size >= 0) + return mant_size @mantissa_size.setter def mantissa_size(self, mantissa_size): @@ -302,7 +304,7 @@ class _EnumerationFieldTypeMappingIterator(object._Object, else: ret, name, lower, upper = native_bt.ctf_field_type_enumeration_mapping_iterator_get_unsigned(self._ptr) - utils._handle_ret(ret, "cannot get enumeration field type mapping iterator object's current mapping") + assert(ret == 0) mapping = _EnumerationFieldTypeMapping(name, lower, upper) ret = native_bt.ctf_field_type_enumeration_mapping_iterator_next(self._ptr) @@ -333,7 +335,7 @@ class EnumerationFieldType(IntegerFieldType, collections.abc.Sequence): @property def integer_field_type(self): ptr = native_bt.ctf_field_type_enumeration_get_container_type(self._ptr) - utils._handle_ptr(ptr, "cannot get enumeration field type object's integer field type") + assert(ptr) return _create_from_ptr(ptr) @property @@ -390,7 +392,7 @@ class EnumerationFieldType(IntegerFieldType, collections.abc.Sequence): def __len__(self): count = native_bt.ctf_field_type_enumeration_get_mapping_count(self._ptr) - utils._handle_ret(count, "cannot get enumeration field type object's mapping count") + assert(count >= 0) return count def __getitem__(self, index): @@ -405,7 +407,7 @@ class EnumerationFieldType(IntegerFieldType, collections.abc.Sequence): get_fn = native_bt.ctf_field_type_enumeration_get_mapping_unsigned ret, name, lower, upper = get_fn(self._ptr, index) - utils._handle_ret(ret, "cannot get enumeration field type object's mapping") + assert(ret == 0) return _EnumerationFieldTypeMapping(name, lower, upper) def _get_mapping_iter(self, iter_ptr): @@ -433,7 +435,7 @@ class EnumerationFieldType(IntegerFieldType, collections.abc.Sequence): upper = lower if self.is_signed: - add_fn = native_bt.ctf_field_type_enumeration_add_mapping + add_fn = native_bt.ctf_field_type_enumeration_add_mapping_signed utils._check_int64(lower) utils._check_int64(upper) else: @@ -465,7 +467,7 @@ class StringFieldType(_FieldType): @property def encoding(self): encoding = native_bt.ctf_field_type_string_get_encoding(self._ptr) - utils._handle_ret(encoding, "cannot get string field type object's encoding") + assert(encoding >= 0) return encoding @encoding.setter @@ -478,7 +480,7 @@ class StringFieldType(_FieldType): class _FieldContainer(collections.abc.Mapping): def __len__(self): count = self._count() - utils._handle_ret(count, "cannot get {} field type object's field count".format(self._NAME.lower())) + assert(count >= 0) return count def __getitem__(self, key): @@ -521,8 +523,10 @@ class _StructureFieldTypeFieldIterator(collections.abc.Iterator): if self._at == len(self._struct_field_type): raise StopIteration - ret, name, field_type_ptr = native_bt.ctf_field_type_structure_get_field(self._struct_field_type._ptr, self._at) - utils._handle_ret(ret, "cannot get structure field type object's field") + get_ft_by_index = native_bt.ctf_field_type_structure_get_field_by_index + ret, name, field_type_ptr = get_ft_by_index(self._struct_field_type._ptr, + self._at) + assert(ret == 0) native_bt.put(field_type_ptr) self._at += 1 return name @@ -551,8 +555,11 @@ class StructureFieldType(_FieldType, _FieldContainer, _AlignmentProp): name) def _at(self, index): - ret, name, field_type_ptr = native_bt.ctf_field_type_structure_get_field(self._ptr, index) - utils._handle_ret(ret, "cannot get structure field type object's field") + if index < 0 or index >= len(self): + raise IndexError + + ret, name, field_type_ptr = native_bt.ctf_field_type_structure_get_field_by_index(self._ptr, index) + assert(ret == 0) return _create_from_ptr(field_type_ptr) @@ -569,8 +576,9 @@ class _VariantFieldTypeFieldIterator(collections.abc.Iterator): if self._at == len(self._variant_field_type): raise StopIteration - ret, name, field_type_ptr = native_bt.ctf_field_type_variant_get_field(self._variant_field_type._ptr, self._at) - utils._handle_ret(ret, "cannot get variant field type object's field") + ret, name, field_type_ptr = native_bt.ctf_field_type_variant_get_field_by_index(self._variant_field_type._ptr, + self._at) + assert(ret == 0) native_bt.put(field_type_ptr) self._at += 1 return name @@ -580,16 +588,24 @@ class VariantFieldType(_FieldType, _FieldContainer, _AlignmentProp): _NAME = 'Variant' _ITER_CLS = _VariantFieldTypeFieldIterator - def __init__(self, tag_name): + def __init__(self, tag_name, tag_field_type=None): utils._check_str(tag_name) - ptr = native_bt.ctf_field_type_variant_create(None, tag_name) + + if tag_field_type is None: + tag_ft_ptr = None + else: + utils._check_type(tag_field_type, EnumerationFieldType) + tag_ft_ptr = tag_field_type._ptr + + ptr = native_bt.ctf_field_type_variant_create(tag_ft_ptr, + tag_name) self._check_create_status(ptr) super().__init__(ptr) @property def tag_name(self): tag_name = native_bt.ctf_field_type_variant_get_tag_name(self._ptr) - utils._handle_ptr(tag_name, "cannot get variant field type object's tag name") + assert(tag_name is not None) return tag_name @tag_name.setter @@ -598,6 +614,15 @@ class VariantFieldType(_FieldType, _FieldContainer, _AlignmentProp): ret = native_bt.ctf_field_type_variant_set_tag_name(self._ptr, tag_name) utils._handle_ret(ret, "cannot set variant field type object's tag name") + @property + def tag_field_type(self): + ft_ptr = native_bt.ctf_field_type_variant_get_tag_type(self._ptr) + + if ft_ptr is None: + return + + return _create_from_ptr(ft_ptr) + def _count(self): return native_bt.ctf_field_type_variant_get_field_count(self._ptr) @@ -608,8 +633,11 @@ class VariantFieldType(_FieldType, _FieldContainer, _AlignmentProp): return native_bt.ctf_field_type_variant_add_field(self._ptr, ptr, name) def _at(self, index): - ret, name, field_type_ptr = native_bt.ctf_field_type_variant_get_field(self._ptr, index) - utils._handle_ret(ret, "cannot get variant field type object's field") + if index < 0 or index >= len(self): + raise IndexError + + ret, name, field_type_ptr = native_bt.ctf_field_type_variant_get_field_by_index(self._ptr, index) + assert(ret == 0) return _create_from_ptr(field_type_ptr) @@ -626,13 +654,13 @@ class ArrayFieldType(_FieldType): @property def length(self): length = native_bt.ctf_field_type_array_get_length(self._ptr) - utils._handle_ret(length, "cannot get array field type object's length") + assert(length >= 0) return length @property def element_field_type(self): ptr = native_bt.ctf_field_type_array_get_element_type(self._ptr) - utils._handle_ptr(ptr, "cannot get array field type object's element field type") + assert(ptr) return _create_from_ptr(ptr) @@ -650,24 +678,23 @@ class SequenceFieldType(_FieldType): @property def length_name(self): length_name = native_bt.ctf_field_type_sequence_get_length_field_name(self._ptr) - utils._handle_ptr(length_name, "cannot get sequence field type object's length name") + assert(length_name is not None) return length_name @property def element_field_type(self): ptr = native_bt.ctf_field_type_sequence_get_element_type(self._ptr) - utils._handle_ptr(ptr, "cannot get sequence field type object's element field type") + assert(ptr) return _create_from_ptr(ptr) _TYPE_ID_TO_OBJ = { - native_bt.CTF_TYPE_ID_INTEGER: IntegerFieldType, - native_bt.CTF_TYPE_ID_FLOAT: FloatingPointNumberFieldType, - native_bt.CTF_TYPE_ID_ENUM: EnumerationFieldType, - native_bt.CTF_TYPE_ID_STRING: StringFieldType, - native_bt.CTF_TYPE_ID_STRUCT: StructureFieldType, - native_bt.CTF_TYPE_ID_ARRAY: ArrayFieldType, - native_bt.CTF_TYPE_ID_SEQUENCE: SequenceFieldType, - native_bt.CTF_TYPE_ID_VARIANT: VariantFieldType, - native_bt.CTF_TYPE_ID_UNTAGGED_VARIANT: VariantFieldType, + native_bt.CTF_FIELD_TYPE_ID_INTEGER: IntegerFieldType, + native_bt.CTF_FIELD_TYPE_ID_FLOAT: FloatingPointNumberFieldType, + native_bt.CTF_FIELD_TYPE_ID_ENUM: EnumerationFieldType, + native_bt.CTF_FIELD_TYPE_ID_STRING: StringFieldType, + native_bt.CTF_FIELD_TYPE_ID_STRUCT: StructureFieldType, + native_bt.CTF_FIELD_TYPE_ID_ARRAY: ArrayFieldType, + native_bt.CTF_FIELD_TYPE_ID_SEQUENCE: SequenceFieldType, + native_bt.CTF_FIELD_TYPE_ID_VARIANT: VariantFieldType, } diff --git a/bindings/python/bt2/fields.py b/bindings/python/bt2/fields.py index c63cf357..44d6a386 100644 --- a/bindings/python/bt2/fields.py +++ b/bindings/python/bt2/fields.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -277,7 +277,10 @@ class _IntegerField(_IntegralField): else: ret, value = native_bt.ctf_field_unsigned_integer_get_value(self._ptr) - utils._handle_ret(ret, "cannot get integer field object's value") + if ret < 0: + # field is not set + return + return value @value.setter @@ -304,7 +307,11 @@ class _FloatingPointNumberField(_RealField): @property def value(self): ret, value = native_bt.ctf_field_floating_point_get_value(self._ptr) - utils._handle_ret(ret, "cannot get floating point number field object's value") + + if ret < 0: + # field is not set + return + return value @value.setter @@ -320,8 +327,7 @@ class _EnumerationField(_IntegerField): @property def integer_field(self): int_field_ptr = native_bt.ctf_field_enumeration_get_container(self._ptr) - utils._handle_ptr(int_field_ptr, - "cannot get enumeration field object's underlying integer field") + assert(int_field_ptr) return _create_from_ptr(int_field_ptr) @property @@ -334,8 +340,10 @@ class _EnumerationField(_IntegerField): @property def mappings(self): - iter_ptr = bt_ctf_field_enumeration_get_mappings(self._ptr) - return self.field_type._get_mapping_iter(iter_ptr) + iter_ptr = native_bt.ctf_field_enumeration_get_mappings(self._ptr) + assert(iter_ptr) + return bt2.field_types._EnumerationFieldTypeMappingIterator(iter_ptr, + self.field_type.is_signed) @functools.total_ordering @@ -354,7 +362,11 @@ class _StringField(_Field, collections.abc.Sequence): @property def value(self): value = native_bt.ctf_field_string_get_value(self._ptr) - utils._handle_ptr(value, "cannot get string field object's value") + + if value is None: + # field is not set + return + return value @value.setter @@ -402,7 +414,7 @@ class _ContainerField(_Field): def __len__(self): count = self._count() - utils._handle_ret(count, "cannot get {} field object's field count".format(self._NAME.lower())) + assert(count >= 0) return count def __delitem__(self, index): @@ -417,7 +429,7 @@ class _StructureField(_ContainerField, collections.abc.MutableMapping): def __getitem__(self, key): utils._check_str(key) - ptr = native_bt.ctf_field_structure_get_field(self._ptr, key) + ptr = native_bt.ctf_field_structure_get_field_by_name(self._ptr, key) if ptr is None: raise KeyError(key) @@ -441,8 +453,12 @@ class _StructureField(_ContainerField, collections.abc.MutableMapping): def at_index(self, index): utils._check_uint64(index) + + if index >= len(self): + raise IndexError + field_ptr = native_bt.ctf_field_structure_get_field_by_index(self._ptr, index) - utils._handle_ptr(field_ptr, "cannot get structure field object's field") + assert(field_ptr) return _create_from_ptr(field_ptr) def __iter__(self): @@ -474,7 +490,10 @@ class _VariantField(_Field): @property def tag_field(self): field_ptr = native_bt.ctf_field_variant_get_tag(self._ptr) - utils._handle_ptr(field_ptr, "cannot get variant field object's tag field") + + if field_ptr is None: + return + return _create_from_ptr(field_ptr) @property @@ -484,7 +503,9 @@ class _VariantField(_Field): def field(self, tag_field=None): if tag_field is None: field_ptr = native_bt.ctf_field_variant_get_current_field(self._ptr) - utils._handle_ptr(field_ptr, "cannot get variant field object's selected field") + + if field_ptr is None: + return else: utils._check_type(tag_field, _EnumerationField) field_ptr = native_bt.ctf_field_variant_get_field(self._ptr, tag_field._ptr) @@ -493,7 +514,16 @@ class _VariantField(_Field): return _create_from_ptr(field_ptr) def __eq__(self, other): - return self.field == other + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + return self.selected_field == other.selected_field + + def __bool__(self): + return bool(self.selected_field) class _ArraySequenceField(_ContainerField, collections.abc.MutableSequence): @@ -507,7 +537,7 @@ class _ArraySequenceField(_ContainerField, collections.abc.MutableSequence): raise IndexError('{} field object index is out of range'.format(self._NAME)) field_ptr = self._get_field_ptr_at_index(index) - utils._handle_ptr(field_ptr, "cannot get {} field object's field".format(self._NAME)) + assert(field_ptr) return _create_from_ptr(field_ptr) def __setitem__(self, index, value): @@ -575,13 +605,12 @@ class _SequenceField(_ArraySequenceField): _TYPE_ID_TO_OBJ = { - native_bt.CTF_TYPE_ID_INTEGER: _IntegerField, - native_bt.CTF_TYPE_ID_FLOAT: _FloatingPointNumberField, - native_bt.CTF_TYPE_ID_ENUM: _EnumerationField, - native_bt.CTF_TYPE_ID_STRING: _StringField, - native_bt.CTF_TYPE_ID_STRUCT: _StructureField, - native_bt.CTF_TYPE_ID_ARRAY: _ArrayField, - native_bt.CTF_TYPE_ID_SEQUENCE: _SequenceField, - native_bt.CTF_TYPE_ID_VARIANT: _VariantField, - native_bt.CTF_TYPE_ID_UNTAGGED_VARIANT: _VariantField, + native_bt.CTF_FIELD_TYPE_ID_INTEGER: _IntegerField, + native_bt.CTF_FIELD_TYPE_ID_FLOAT: _FloatingPointNumberField, + native_bt.CTF_FIELD_TYPE_ID_ENUM: _EnumerationField, + native_bt.CTF_FIELD_TYPE_ID_STRING: _StringField, + native_bt.CTF_FIELD_TYPE_ID_STRUCT: _StructureField, + native_bt.CTF_FIELD_TYPE_ID_ARRAY: _ArrayField, + native_bt.CTF_FIELD_TYPE_ID_SEQUENCE: _SequenceField, + native_bt.CTF_FIELD_TYPE_ID_VARIANT: _VariantField, } diff --git a/bindings/python/bt2/graph.py b/bindings/python/bt2/graph.py new file mode 100644 index 00000000..56148395 --- /dev/null +++ b/bindings/python/bt2/graph.py @@ -0,0 +1,193 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# 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. + +from bt2 import native_bt, object, utils +import bt2.connection +import bt2.component +import functools +import bt2.port +import bt2 + + +class GraphListenerType: + PORT_ADDED = 0 + PORT_REMOVED = 1 + PORTS_CONNECTED = 2 + PORTS_DISCONNECTED = 3 + + +def _graph_port_added_listener_from_native(user_listener, port_ptr): + try: + port = bt2.port._create_from_ptr(port_ptr) + port._get() + user_listener(port) + except: + pass + + +def _graph_port_removed_listener_from_native(user_listener, port_ptr): + try: + port = bt2.port._create_from_ptr(port_ptr) + port._get() + user_listener(port) + except: + pass + + +def _graph_ports_connected_listener_from_native(user_listener, + upstream_port_ptr, + downstream_port_ptr): + try: + upstream_port = bt2.port._create_from_ptr(upstream_port_ptr) + upstream_port._get() + downstream_port = bt2.port._create_from_ptr(downstream_port_ptr) + downstream_port._get() + user_listener(upstream_port, downstream_port) + except: + pass + + +def _graph_ports_disconnected_listener_from_native(user_listener, + upstream_comp_ptr, + downstream_comp_ptr, + upstream_port_ptr, + downstream_port_ptr): + try: + upstream_comp = bt2.component._create_generic_component_from_ptr(upstream_comp_ptr) + upstream_comp._get() + downstream_comp = bt2.component._create_generic_component_from_ptr(downstream_comp_ptr) + downstream_comp._get() + upstream_port = bt2.port._create_from_ptr(upstream_port_ptr) + upstream_port._get() + downstream_port = bt2.port._create_from_ptr(downstream_port_ptr) + downstream_port._get() + user_listener(upstream_comp, downstream_comp, upstream_port, + downstream_port) + except: + pass + + +class Graph(object._Object): + def __init__(self): + ptr = native_bt.graph_create() + + if ptr is None: + raise bt2.CreationError('cannot create graph object') + + super().__init__(ptr) + + def _handle_status(self, status, gen_error_msg): + if status == native_bt.GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: + raise bt2.PortConnectionRefused + elif status == native_bt.GRAPH_STATUS_CANCELED: + raise bt2.GraphCanceled + elif status == native_bt.GRAPH_STATUS_END: + raise bt2.Stop + elif status == native_bt.GRAPH_STATUS_AGAIN: + raise bt2.TryAgain + elif status == native_bt.GRAPH_STATUS_NO_SINK: + raise bt2.NoSinkComponent + elif status < 0: + raise bt2.Error(gen_error_msg) + + def add_component(self, component_class, name, params=None): + if isinstance(component_class, bt2.component._GenericComponentClass): + cc_ptr = component_class._ptr + elif issubclass(component_class, bt2.component._UserComponent): + cc_ptr = component_class._cc_ptr + else: + raise TypeError("'{}' is not a component class".format(component_class.__class__.__name__)) + + utils._check_str(name) + params = bt2.create_value(params) + + if params is None: + params_ptr = None + else: + params_ptr = params._ptr + + status, comp_ptr = native_bt.graph_add_component(self._ptr, cc_ptr, + name, params_ptr) + self._handle_status(status, 'cannot add component to graph') + assert(comp_ptr) + return bt2.component._create_generic_component_from_ptr(comp_ptr) + + def connect_ports(self, upstream_port, downstream_port): + utils._check_type(upstream_port, bt2.port._OutputPort) + utils._check_type(downstream_port, bt2.port._InputPort) + status, conn_ptr = native_bt.graph_connect_ports(self._ptr, + upstream_port._ptr, + downstream_port._ptr) + self._handle_status(status, 'cannot connect component ports within graph') + assert(conn_ptr) + return bt2.connection._Connection._create_from_ptr(conn_ptr) + + def add_listener(self, listener_type, listener): + if not hasattr(listener, '__call__'): + raise TypeError("'listener' parameter is not callable") + + if listener_type == GraphListenerType.PORT_ADDED: + fn = native_bt.py3_graph_add_port_added_listener + listener_from_native = functools.partial(_graph_port_added_listener_from_native, + listener) + elif listener_type == GraphListenerType.PORT_REMOVED: + fn = native_bt.py3_graph_add_port_removed_listener + listener_from_native = functools.partial(_graph_port_removed_listener_from_native, + listener) + elif listener_type == GraphListenerType.PORTS_CONNECTED: + fn = native_bt.py3_graph_add_ports_connected_listener + listener_from_native = functools.partial(_graph_ports_connected_listener_from_native, + listener) + elif listener_type == GraphListenerType.PORTS_DISCONNECTED: + fn = native_bt.py3_graph_add_ports_disconnected_listener + listener_from_native = functools.partial(_graph_ports_disconnected_listener_from_native, + listener) + else: + raise TypeError + + listener_id = fn(self._ptr, listener_from_native) + utils._handle_ret(listener_id, 'cannot add listener to graph object') + return bt2._ListenerHandle(listener_id, self) + + def run(self): + status = native_bt.graph_run(self._ptr) + + if status == native_bt.GRAPH_STATUS_END: + return + + self._handle_status(status, 'graph object stopped running because of an unexpected error') + + def cancel(self): + status = native_bt.graph_cancel(self._ptr) + self._handle_status(status, 'cannot cancel graph object') + + @property + def is_canceled(self): + is_canceled = native_bt.graph_is_canceled(self._ptr) + assert(is_canceled >= 0) + return is_canceled > 0 + + def __eq__(self, other): + if type(other) is not type(self): + return False + + return self.addr == other.addr diff --git a/bindings/python/bt2/logging.c b/bindings/python/bt2/logging.c new file mode 100644 index 00000000..52d531c7 --- /dev/null +++ b/bindings/python/bt2/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level +#include + +BT_LOG_INIT_LOG_LEVEL(bt_cli_log_level, "BABELTRACE_CLI_LOG_LEVEL"); diff --git a/bindings/python/bt2/logging.h b/bindings/python/bt2/logging.h new file mode 100644 index 00000000..ccc6c527 --- /dev/null +++ b/bindings/python/bt2/logging.h @@ -0,0 +1,31 @@ +#ifndef CLI_LOGGING_H +#define CLI_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level +#include + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_cli_log_level); + +#endif /* CLI_LOGGING_H */ diff --git a/bindings/python/bt2/logging.py b/bindings/python/bt2/logging.py new file mode 100644 index 00000000..51d898cc --- /dev/null +++ b/bindings/python/bt2/logging.py @@ -0,0 +1,59 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# 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. + +from bt2 import native_bt, object, utils +import bt2 + + +class LoggingLevel: + VERBOSE = native_bt.LOGGING_LEVEL_VERBOSE + DEBUG = native_bt.LOGGING_LEVEL_DEBUG + INFO = native_bt.LOGGING_LEVEL_INFO + WARN = native_bt.LOGGING_LEVEL_WARN + ERROR = native_bt.LOGGING_LEVEL_ERROR + FATAL = native_bt.LOGGING_LEVEL_FATAL + NONE = native_bt.LOGGING_LEVEL_NONE + + +def get_minimal_logging_level(): + return native_bt.logging_get_minimal_level() + + +def get_global_logging_level(): + return native_bt.logging_get_global_level() + + +def set_global_logging_level(level): + levels = ( + LoggingLevel.VERBOSE, + LoggingLevel.DEBUG, + LoggingLevel.INFO, + LoggingLevel.WARN, + LoggingLevel.ERROR, + LoggingLevel.FATAL, + LoggingLevel.NONE, + ) + + if level not in levels: + raise TypeError("'{}' is not a valid logging level".format(level)) + + return native_bt.logging_set_global_level(level) diff --git a/bindings/python/bt2/native_bt.i b/bindings/python/bt2/native_bt.i index 88953af5..0852b743 100644 --- a/bindings/python/bt2/native_bt.i +++ b/bindings/python/bt2/native_bt.i @@ -22,13 +22,20 @@ * THE SOFTWARE. */ +#ifndef SWIGPYTHON +# error Unsupported output language +#endif + %{ +#define BT_LOG_TAG "PY-NATIVE" +#include "logging.h" + +#include + typedef const unsigned char *BTUUID; %} -#ifndef SWIGPYTHON -# error Unsupported output language -#endif +typedef int bt_bool; /* For uint*_t/int*_t */ %include "stdint.i" @@ -69,6 +76,72 @@ typedef const unsigned char *BTUUID; } } +/* Output argument typemap for component output (always appends) */ +%typemap(in, numinputs=0) struct bt_component **BTOUTCOMP (struct bt_component *temp_comp = NULL) { + $1 = &temp_comp; +} + +%typemap(argout) struct bt_component **BTOUTCOMP { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p_bt_component, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for connection output (always appends) */ +%typemap(in, numinputs=0) struct bt_connection **BTOUTCONN (struct bt_connection *temp_conn = NULL) { + $1 = &temp_conn; +} + +%typemap(argout) struct bt_connection **BTOUTCONN { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p_bt_connection, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for private port output (always appends) */ +%typemap(in, numinputs=0) struct bt_private_port **BTOUTPRIVPORT (struct bt_private_port *temp_priv_port = NULL) { + $1 = &temp_priv_port; +} + +%typemap(argout) struct bt_private_port **BTOUTPRIVPORT { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p_bt_private_port, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for initialized uint64_t output parameter (always appends) */ +%typemap(in, numinputs=0) uint64_t *OUTPUTINIT (uint64_t temp = -1ULL) { + $1 = &temp; +} + +%typemap(argout) uint64_t *OUTPUTINIT { + $result = SWIG_Python_AppendOutput(resultobj, SWIG_From_unsigned_SS_long_SS_long((*$1))); +} + +/* Output argument typemap for initialized unsigned int output parameter (always appends) */ +%typemap(in, numinputs=0) unsigned int *OUTPUTINIT (unsigned int temp = -1) { + $1 = &temp; +} + +%typemap(argout) unsigned int *OUTPUTINIT { + $result = SWIG_Python_AppendOutput(resultobj, SWIG_From_unsigned_SS_long_SS_long((uint64_t) (*$1))); +} + /* Input argument typemap for UUID bytes */ %typemap(in) BTUUID { $1 = (unsigned char *) PyBytes_AsString($input); @@ -102,20 +175,26 @@ typedef const unsigned char *BTUUID; } /* Per-module interface files */ +%include "native_btccpriomap.i" %include "native_btclockclass.i" +%include "native_btcomponent.i" +%include "native_btcomponentclass.i" +%include "native_btconnection.i" +%include "native_btctfwriter.i" %include "native_btevent.i" %include "native_bteventclass.i" %include "native_btfields.i" %include "native_btft.i" +%include "native_btgraph.i" +%include "native_btlogging.i" +%include "native_btnotification.i" +%include "native_btnotifiter.i" %include "native_btpacket.i" +%include "native_btplugin.i" +%include "native_btport.i" %include "native_btref.i" %include "native_btstream.i" %include "native_btstreamclass.i" %include "native_bttrace.i" %include "native_btvalues.i" -%include "native_btctfwriter.i" -%include "native_btcomponentclass.i" -%include "native_btcomponent.i" -%include "native_btnotifiter.i" -%include "native_btnotification.i" -%include "native_btplugin.i" +%include "native_btversion.i" diff --git a/bindings/python/bt2/native_btccpriomap.i b/bindings/python/bt2/native_btccpriomap.i new file mode 100644 index 00000000..e128cd95 --- /dev/null +++ b/bindings/python/bt2/native_btccpriomap.i @@ -0,0 +1,54 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +%{ +#include +%} + +/* Type */ +struct bt_clock_class_priority_map; + +/* Functions */ +struct bt_clock_class_priority_map *bt_clock_class_priority_map_create(); +int64_t bt_clock_class_priority_map_get_clock_class_count( + struct bt_clock_class_priority_map *clock_class_priority_map); +struct bt_ctf_clock_class * +bt_clock_class_priority_map_get_clock_class_by_index( + struct bt_clock_class_priority_map *clock_class_priority_map, + uint64_t index); +struct bt_ctf_clock_class * +bt_clock_class_priority_map_get_clock_class_by_name( + struct bt_clock_class_priority_map *clock_class_priority_map, + const char *name); +struct bt_ctf_clock_class * +bt_clock_class_priority_map_get_highest_priority_clock_class( + struct bt_clock_class_priority_map *clock_class_priority_map); +int bt_clock_class_priority_map_get_clock_class_priority( + struct bt_clock_class_priority_map *clock_class_priority_map, + struct bt_ctf_clock_class *clock_class, uint64_t *OUTPUTINIT); +int bt_clock_class_priority_map_add_clock_class( + struct bt_clock_class_priority_map *clock_class_priority_map, + struct bt_ctf_clock_class *clock_class, uint64_t priority); +struct bt_clock_class_priority_map *bt_clock_class_priority_map_copy( + struct bt_clock_class_priority_map *clock_class_priority_map); diff --git a/bindings/python/bt2/native_btclockclass.i b/bindings/python/bt2/native_btclockclass.i index e79a7b4d..5a09e894 100644 --- a/bindings/python/bt2/native_btclockclass.i +++ b/bindings/python/bt2/native_btclockclass.i @@ -22,16 +22,13 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Types */ struct bt_ctf_clock_class; struct bt_ctf_clock_value; /* Clock class functions */ -struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name); +struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name, + uint64_t freq); const char *bt_ctf_clock_class_get_name(struct bt_ctf_clock_class *clock_class); int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class, const char *name); const char *bt_ctf_clock_class_get_description(struct bt_ctf_clock_class *clock_class); diff --git a/bindings/python/bt2/native_btcomponent.i b/bindings/python/bt2/native_btcomponent.i index 6394efd2..ea3e0f8a 100644 --- a/bindings/python/bt2/native_btcomponent.i +++ b/bindings/python/bt2/native_btcomponent.i @@ -22,99 +22,114 @@ * THE SOFTWARE. */ -%{ -#include -#include -#include -#include -%} - /* Types */ struct bt_component; /* Status */ enum bt_component_status { - BT_COMPONENT_STATUS_OK = 0, - BT_COMPONENT_STATUS_END = 1, - BT_COMPONENT_STATUS_AGAIN = 2, - BT_COMPONENT_STATUS_ERROR = -1, - BT_COMPONENT_STATUS_UNSUPPORTED = -2, - BT_COMPONENT_STATUS_INVALID = -3, - BT_COMPONENT_STATUS_NOMEM = -4, + BT_COMPONENT_STATUS_OK = 0, + BT_COMPONENT_STATUS_END = 1, + BT_COMPONENT_STATUS_AGAIN = 11, + BT_COMPONENT_STATUS_REFUSE_PORT_CONNECTION = 111, + BT_COMPONENT_STATUS_ERROR = -1, + BT_COMPONENT_STATUS_UNSUPPORTED = -2, + BT_COMPONENT_STATUS_INVALID = -22, + BT_COMPONENT_STATUS_NOMEM = -12, + BT_COMPONENT_STATUS_NOT_FOUND = -19, + BT_COMPONENT_STATUS_GRAPH_IS_CANCELED = 125, }; /* General functions */ -struct bt_component *bt_component_create_with_init_method_data( - struct bt_component_class *component_class, const char *name, - struct bt_value *params, void *init_method_data); const char *bt_component_get_name(struct bt_component *component); struct bt_component_class *bt_component_get_class( struct bt_component *component); enum bt_component_class_type bt_component_get_class_type( struct bt_component *component); +struct bt_graph *bt_component_get_graph(struct bt_component *component); +struct bt_component *bt_component_from_private_component( + struct bt_private_component *private_component); /* Source component functions */ -struct bt_notification_iterator *bt_component_source_create_notification_iterator_with_init_method_data( - struct bt_component *component, void *init_method_data); - -/* Filter component functions */ -%{ -static struct bt_notification_iterator *bt_component_filter_get_input_iterator_private( - struct bt_component *filter, unsigned int index) -{ - struct bt_notification_iterator *iterator = NULL; +int64_t bt_component_source_get_output_port_count( + struct bt_component *component); +struct bt_port *bt_component_source_get_output_port_by_name( + struct bt_component *component, const char *name); +struct bt_port *bt_component_source_get_output_port_by_index( + struct bt_component *component, uint64_t index); - if (bt_component_filter_get_input_iterator(filter, index, &iterator)) { - iterator = NULL; - goto end; - } +/* Private source component functions */ +struct bt_private_port * +bt_private_component_source_get_output_private_port_by_name( + struct bt_private_component *private_component, + const char *name); +struct bt_private_port * +bt_private_component_source_get_output_private_port_by_index( + struct bt_private_component *private_component, + uint64_t index); +enum bt_component_status +bt_private_component_source_add_output_private_port( + struct bt_private_component *private_component, + const char *name, void *user_data, + struct bt_private_port **BTOUTPRIVPORT); -end: - return iterator; -} -%} +/* Filter component functions */ +int64_t bt_component_filter_get_input_port_count( + struct bt_component *component); +struct bt_port *bt_component_filter_get_input_port_by_name( + struct bt_component *component, const char *name); +struct bt_port *bt_component_filter_get_input_port_by_index( + struct bt_component *component, uint64_t index); +int64_t bt_component_filter_get_output_port_count( + struct bt_component *component); +struct bt_port *bt_component_filter_get_output_port_by_name( + struct bt_component *component, const char *name); +struct bt_port *bt_component_filter_get_output_port_by_index( + struct bt_component *component, uint64_t index); -struct bt_notification_iterator *bt_component_filter_create_notification_iterator_with_init_method_data( - struct bt_component *component, void *init_method_data); -enum bt_component_status bt_component_filter_add_iterator( - struct bt_component *component, - struct bt_notification_iterator *iterator); -enum bt_component_status bt_component_filter_set_minimum_input_count( - struct bt_component *filter, unsigned int minimum); -enum bt_component_status bt_component_filter_set_maximum_input_count( - struct bt_component *filter, unsigned int maximum); -enum bt_component_status bt_component_filter_get_input_count( - struct bt_component *filter, unsigned int *OUTPUT); -struct bt_notification_iterator *bt_component_filter_get_input_iterator_private( - struct bt_component *filter, unsigned int index); +/* Private filter component functions */ +struct bt_private_port * +bt_private_component_filter_get_output_private_port_by_name( + struct bt_private_component *private_component, + const char *name); +struct bt_private_port * +bt_private_component_filter_get_output_private_port_by_index( + struct bt_private_component *private_component, uint64_t index); +enum bt_component_status +bt_private_component_filter_add_output_private_port( + struct bt_private_component *private_component, + const char *name, void *user_data, + struct bt_private_port **BTOUTPRIVPORT); +struct bt_private_port * +bt_private_component_filter_get_input_private_port_by_name( + struct bt_private_component *private_component, + const char *name); +struct bt_private_port * +bt_private_component_filter_get_input_private_port_by_index( + struct bt_private_component *private_component, uint64_t index); +enum bt_component_status +bt_private_component_filter_add_input_private_port( + struct bt_private_component *private_component, + const char *name, void *user_data, + struct bt_private_port **BTOUTPRIVPORT); /* Sink component functions */ -%{ -static struct bt_notification_iterator *bt_component_sink_get_input_iterator_private( - struct bt_component *sink, unsigned int index) -{ - struct bt_notification_iterator *iterator = NULL; - - if (bt_component_sink_get_input_iterator(sink, index, &iterator)) { - iterator = NULL; - goto end; - } - -end: - return iterator; -} -%} - -enum bt_component_status bt_component_sink_add_iterator( - struct bt_component *component, - struct bt_notification_iterator *iterator); -enum bt_component_status bt_component_sink_consume( +int64_t bt_component_sink_get_input_port_count( struct bt_component *component); -enum bt_component_status bt_component_sink_set_minimum_input_count( - struct bt_component *sink, unsigned int minimum); -enum bt_component_status bt_component_sink_set_maximum_input_count( - struct bt_component *sink, unsigned int maximum); -enum bt_component_status bt_component_sink_get_input_count( - struct bt_component *sink, unsigned int *OUTPUT); -struct bt_notification_iterator *bt_component_sink_get_input_iterator_private( - struct bt_component *sink, unsigned int index); +struct bt_port *bt_component_sink_get_input_port_by_name( + struct bt_component *component, const char *name); +struct bt_port *bt_component_sink_get_input_port_by_index( + struct bt_component *component, uint64_t index); + +/* Private sink component functions */ +struct bt_private_port * +bt_private_component_sink_get_input_private_port_by_name( + struct bt_private_component *private_component, + const char *name); +struct bt_private_port * +bt_private_component_sink_get_input_private_port_by_index( + struct bt_private_component *private_component, uint64_t index); +enum bt_component_status +bt_private_component_sink_add_input_private_port( + struct bt_private_component *private_component, + const char *name, void *user_data, + struct bt_private_port **BTOUTPRIVPORT); diff --git a/bindings/python/bt2/native_btcomponentclass.i b/bindings/python/bt2/native_btcomponentclass.i index 4fe24886..195ca381 100644 --- a/bindings/python/bt2/native_btcomponentclass.i +++ b/bindings/python/bt2/native_btcomponentclass.i @@ -22,26 +22,15 @@ * THE SOFTWARE. */ -%{ -#include -#include -#include -#include -#include -#include -#include -#include -%} - /* Types */ struct bt_component_class; /* Status */ enum bt_component_class_type { - BT_COMPONENT_CLASS_TYPE_UNKNOWN = -1, - BT_COMPONENT_CLASS_TYPE_SOURCE = 0, - BT_COMPONENT_CLASS_TYPE_SINK = 1, - BT_COMPONENT_CLASS_TYPE_FILTER = 2, + BT_COMPONENT_CLASS_TYPE_UNKNOWN = -1, + BT_COMPONENT_CLASS_TYPE_SOURCE = 0, + BT_COMPONENT_CLASS_TYPE_SINK = 1, + BT_COMPONENT_CLASS_TYPE_FILTER = 2, }; /* General functions */ @@ -53,7 +42,7 @@ const char *bt_component_class_get_help( struct bt_component_class *component_class); struct bt_value *bt_component_class_query( struct bt_component_class *component_class, - const char *action, struct bt_value *params); + const char *object, struct bt_value *params); enum bt_component_class_type bt_component_class_get_type( struct bt_component_class *component_class); @@ -81,12 +70,29 @@ static GHashTable *bt_cc_ptr_to_py_cls; static void register_cc_ptr_to_py_cls(struct bt_component_class *bt_cc, PyObject *py_cls) { + if (!bt_cc_ptr_to_py_cls) { + /* + * Lazy-initializing this GHashTable because GLib + * might not be initialized yet and it needs to be + * before we call g_hash_table_new() + */ + BT_LOGD_STR("Creating native component class to Python component class hash table."); + bt_cc_ptr_to_py_cls = g_hash_table_new(g_direct_hash, g_direct_equal); + assert(bt_cc_ptr_to_py_cls); + } + g_hash_table_insert(bt_cc_ptr_to_py_cls, (gpointer) bt_cc, (gpointer) py_cls); } static PyObject *lookup_cc_ptr_to_py_cls(struct bt_component_class *bt_cc) { + if (!bt_cc_ptr_to_py_cls) { + BT_LOGW("Cannot look up Python component class because hash table is NULL: " + "comp-cls-addr=%p", bt_cc); + return NULL; + } + return (PyObject *) g_hash_table_lookup(bt_cc_ptr_to_py_cls, (gconstpointer) bt_cc); } @@ -101,8 +107,10 @@ static PyObject *py_mod_bt2_exc_error_type = NULL; static PyObject *py_mod_bt2_exc_unsupported_feature_type = 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_values = NULL; -static PyObject *py_mod_bt2_values_create_from_ptr_func = NULL; +static PyObject *py_mod_bt2_exc_port_connection_refused_type = NULL; +static PyObject *py_mod_bt2_exc_graph_canceled_type = NULL; +static PyObject *py_mod_bt2_exc_notif_iter_canceled_type = NULL; +static PyObject *py_mod_bt2_exc_connection_ended_type = NULL; static void bt_py3_cc_init_from_bt2(void) { @@ -116,12 +124,6 @@ static void bt_py3_cc_init_from_bt2(void) */ py_mod_bt2 = PyImport_ImportModule("bt2"); assert(py_mod_bt2); - py_mod_bt2_values = PyImport_ImportModule("bt2.values"); - assert(py_mod_bt2_values); - py_mod_bt2_values_create_from_ptr_func = - PyObject_GetAttrString(py_mod_bt2_values, - "_create_from_ptr"); - assert(py_mod_bt2_values_create_from_ptr_func); py_mod_bt2_exc_error_type = PyObject_GetAttrString(py_mod_bt2, "Error"); assert(py_mod_bt2_exc_error_type); @@ -131,6 +133,12 @@ static void bt_py3_cc_init_from_bt2(void) PyObject_GetAttrString(py_mod_bt2, "TryAgain"); py_mod_bt2_exc_stop_type = PyObject_GetAttrString(py_mod_bt2, "Stop"); + py_mod_bt2_exc_port_connection_refused_type = + PyObject_GetAttrString(py_mod_bt2, "PortConnectionRefused"); + py_mod_bt2_exc_graph_canceled_type = + PyObject_GetAttrString(py_mod_bt2, "GraphCanceled"); + py_mod_bt2_exc_connection_ended_type = + PyObject_GetAttrString(py_mod_bt2, "ConnectionEnded"); assert(py_mod_bt2_exc_stop_type); } @@ -143,476 +151,399 @@ static void bt_py3_cc_exit_handler(void) * bt_py3_cc_init_from_bt2() here. The global variables continue * to exist for the code of this file, but they are now borrowed * references. If this code is executed, it means that somehow - * to modules are still loaded, so it should be safe to use them - * even without a strong reference. + * the modules are still loaded, so it should be safe to use + * them even without a strong reference. * * We cannot do this in the library's destructor because it * gets executed once Python is already finalized. */ - Py_XDECREF(py_mod_bt2_values_create_from_ptr_func); - Py_XDECREF(py_mod_bt2_values); Py_XDECREF(py_mod_bt2); Py_XDECREF(py_mod_bt2_exc_error_type); Py_XDECREF(py_mod_bt2_exc_unsupported_feature_type); Py_XDECREF(py_mod_bt2_exc_try_again_type); Py_XDECREF(py_mod_bt2_exc_stop_type); + Py_XDECREF(py_mod_bt2_exc_port_connection_refused_type); + Py_XDECREF(py_mod_bt2_exc_graph_canceled_type); + Py_XDECREF(py_mod_bt2_exc_notif_iter_canceled_type); + Py_XDECREF(py_mod_bt2_exc_connection_ended_type); } -/* Library constructor and destructor */ - -__attribute__((constructor)) -static void bt_py3_native_comp_class_ctor(void) { - /* Initialize component class association hash table */ - bt_cc_ptr_to_py_cls = g_hash_table_new(g_direct_hash, g_direct_equal); - assert(bt_cc_ptr_to_py_cls); -} +/* Library destructor */ __attribute__((destructor)) static void bt_py3_native_comp_class_dtor(void) { /* Destroy component class association hash table */ if (bt_cc_ptr_to_py_cls) { + BT_LOGD_STR("Destroying native component class to Python component class hash table."); g_hash_table_destroy(bt_cc_ptr_to_py_cls); } } -/* Converts a BT value object to a bt2.values object */ +/* Component class proxy methods (delegate to the attached Python object) */ -static PyObject *bt_py3_bt_value_from_ptr(struct bt_value *value) +static enum bt_notification_iterator_status bt_py3_exc_to_notif_iter_status(void) { - PyObject *py_create_from_ptr_func = NULL; - PyObject *py_value = NULL; - PyObject *py_ptr = NULL; + enum bt_notification_iterator_status status = + BT_NOTIFICATION_ITERATOR_STATUS_OK; + PyObject *exc = PyErr_Occurred(); - py_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(value), - SWIGTYPE_p_bt_value, 0); - py_value = PyObject_CallFunctionObjArgs( - py_mod_bt2_values_create_from_ptr_func, py_ptr, NULL); - if (!py_value) { + if (!exc) { goto end; } - /* - * _create_from_ptr() only wraps an existing reference. `value` - * is a borrowed reference here, so increment its reference - * count for this new owner. - */ - bt_get(value); + if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_unsupported_feature_type)) { + status = BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_stop_type)) { + status = BT_NOTIFICATION_ITERATOR_STATUS_END; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_try_again_type)) { + status = BT_NOTIFICATION_ITERATOR_STATUS_AGAIN; + } else { + status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; + } end: - Py_XDECREF(py_ptr); PyErr_Clear(); - return py_value; + return status; } - -/* self.__init__(*args, **kwargs) */ - -static int bt_py3_call_self_init(PyObject *py_self, PyObject *py_args, - PyObject *py_kwargs) +static enum bt_component_status bt_py3_exc_to_component_status(void) { - int ret = 0; - size_t i; - PyObject *py_init_method_result = NULL; - PyObject *py_init_method = NULL; - PyObject *py_init_method_args = NULL; - PyObject *py_class = NULL; + enum bt_component_status status = BT_COMPONENT_STATUS_OK; + PyObject *exc = PyErr_Occurred(); - py_class = PyObject_GetAttrString(py_self, "__class__"); - if (!py_class) { - goto error; + if (!exc) { + goto end; } - py_init_method = PyObject_GetAttrString(py_class, "__init__"); - if (!py_init_method) { - goto error; + if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_unsupported_feature_type)) { + status = BT_COMPONENT_STATUS_UNSUPPORTED; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_try_again_type)) { + status = BT_COMPONENT_STATUS_AGAIN; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_stop_type)) { + status = BT_COMPONENT_STATUS_END; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_port_connection_refused_type)) { + status = BT_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; + } else { + status = BT_COMPONENT_STATUS_ERROR; } - /* Prepend py_self to the arguments (copy them to new tuple) */ - py_init_method_args = PyTuple_New(PyTuple_Size(py_args) + 1); - if (!py_init_method_args) { +end: + PyErr_Clear(); + return status; +} + +static enum bt_component_status bt_py3_cc_init( + struct bt_private_component *priv_comp, + struct bt_value *params, void *init_method_data) +{ + struct bt_component *comp = + bt_component_from_private_component(priv_comp); + struct bt_component_class *comp_cls = bt_component_get_class(comp); + enum bt_component_status status = BT_COMPONENT_STATUS_OK; + PyObject *py_cls = NULL; + PyObject *py_comp = NULL; + PyObject *py_params_ptr = NULL; + PyObject *py_comp_ptr = NULL; + + (void) init_method_data; + assert(comp); + assert(comp_cls); + + /* + * Get the user-defined Python class which created this + * component's class in the first place (borrowed + * reference). + */ + py_cls = lookup_cc_ptr_to_py_cls(comp_cls); + if (!py_cls) { + BT_LOGE("Cannot find Python class associated to native component class: " + "comp-cls-addr=%p", comp_cls); goto error; } - Py_INCREF(py_self); - ret = PyTuple_SetItem(py_init_method_args, 0, py_self); - if (ret < 0) { - Py_DECREF(py_self); + /* Parameters pointer -> SWIG pointer Python object */ + py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), + SWIGTYPE_p_bt_value, 0); + if (!py_params_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); goto error; } - for (i = 0; i < PyTuple_Size(py_args); i++) { - PyObject *obj = PyTuple_GetItem(py_args, i); - if (!obj) { - goto error; - } - - Py_INCREF(obj); - ret = PyTuple_SetItem(py_init_method_args, i + 1, obj); - if (ret < 0) { - Py_DECREF(obj); - goto error; - } + /* Private component pointer -> SWIG pointer Python object */ + py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(priv_comp), + SWIGTYPE_p_bt_private_component, 0); + if (!py_comp_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; } - py_init_method_result = PyObject_Call(py_init_method, - py_init_method_args, py_kwargs); - if (!py_init_method_result) { + /* + * Do the equivalent of this: + * + * py_comp = py_cls._init_from_native(py_comp_ptr, py_params_ptr) + * + * _UserComponentType._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); + if (!py_comp) { + BT_LOGE("Failed to call Python class's _init_from_native() method: " + "py-cls-addr=%p", py_cls); goto error; } + /* + * Our user Python component object is now fully created and + * initialized by the user. Since we just created it, this + * native component is its only (persistent) owner. + */ + bt_private_component_set_user_data(priv_comp, py_comp); + py_comp = NULL; goto end; error: - ret = -1; - -end: - Py_XDECREF(py_init_method_args); - Py_XDECREF(py_init_method_result); - Py_XDECREF(py_init_method); - Py_XDECREF(py_class); - return ret; -} - - -/* Component class proxy methods (delegate to the attached Python object) */ - -static enum bt_component_status bt_py3_cc_init( - struct bt_component *component, struct bt_value *params, - void *init_method_data) -{ - enum bt_component_status status = BT_COMPONENT_STATUS_OK; - int ret; + status = BT_COMPONENT_STATUS_ERROR; /* - * This function is either called from - * _UserComponentType.__call__() (when init_method_data contains - * the PyObject * currently creating this component) or from - * other code (init_method_data is NULL). + * Clear any exception: we're returning a bad status anyway. If + * this call originated from Python (creation from a plugin's + * component class, for example), then the user gets an + * appropriate creation error. */ - if (init_method_data) { - /* - * Called from _UserComponentType.__call__(). - * - * The `params` argument is completely ignored here, - * because if the user creating the component object - * from the component class object wants parameters, - * they'll be in the keyword arguments anyway. - */ - PyObject *py_comp = init_method_data; - PyObject *py_comp_ptr = NULL; - PyObject *py_init_args = NULL; - PyObject *py_init_kwargs = NULL; + PyErr_Clear(); - /* - * No need to take a Python reference on py_comp here: - * we store it as a _borrowed_ reference. When all the - * Python references are dropped, the object's __del__() - * method is called. This method calls this side - * (bt_py3_component_on_del()) to swap the ownership: - * this BT component becomes the only owner of the - * Python object. - */ - bt_component_set_private_data(component, py_comp); +end: + bt_put(comp_cls); + bt_put(comp); + Py_XDECREF(py_comp); + Py_XDECREF(py_params_ptr); + Py_XDECREF(py_comp_ptr); + return status; +} - /* - * Set this BT component pointer as py_comp._ptr, and - * take a reference on it (on success, at the end). - * - * This reference is put in the Python component - * object's __del__() method. - */ - py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(component), - SWIGTYPE_p_bt_component, 0); - if (!py_comp_ptr) { - PyErr_Clear(); - goto with_init_method_data_error; - } +static void bt_py3_cc_finalize(struct bt_private_component *priv_comp) +{ + PyObject *py_comp = bt_private_component_get_user_data(priv_comp); + PyObject *py_method_result = NULL; - ret = PyObject_SetAttrString(py_comp, "_ptr", py_comp_ptr); - if (ret < 0) { - PyErr_Clear(); - goto with_init_method_data_error; - } + assert(py_comp); - /* - * _UserComponentType.__call__() puts the arguments and - * keyword arguments to use for the py_comp.__init__() - * call in py_comp._init_args and py_comp._init_kwargs. - * - * We need to get them and remove them from py_comp before - * calling py_comp.__init__(). - */ - py_init_args = PyObject_GetAttrString(py_comp, "_init_args"); - if (!py_init_args) { - PyErr_Clear(); - goto with_init_method_data_error; - } + /* Call user's _finalize() method */ + py_method_result = PyObject_CallMethod(py_comp, + "_finalize", NULL); - py_init_kwargs = PyObject_GetAttrString(py_comp, "_init_kwargs"); - if (!py_init_kwargs) { - PyErr_Clear(); - goto with_init_method_data_error; - } + if (PyErr_Occurred()) { + BT_LOGW("User's _finalize() method raised an exception: ignoring."); + } - ret = PyObject_DelAttrString(py_comp, "_init_args"); - if (ret < 0) { - PyErr_Clear(); - goto with_init_method_data_error; - } + /* + * Ignore any exception raised by the _finalize() method because + * it won't change anything at this point: the component is + * being destroyed anyway. + */ + PyErr_Clear(); + Py_XDECREF(py_method_result); + Py_DECREF(py_comp); +} - ret = PyObject_DelAttrString(py_comp, "_init_kwargs"); - if (ret < 0) { - PyErr_Clear(); - goto with_init_method_data_error; - } +static enum bt_component_status bt_py3_cc_accept_port_connection( + struct bt_private_component *priv_comp, + struct bt_private_port *self_priv_port, + struct bt_port *other_port) +{ + enum bt_component_status status; + PyObject *py_comp = NULL; + PyObject *py_self_port_ptr = NULL; + PyObject *py_other_port_ptr = NULL; + PyObject *py_method_result = NULL; - /* Ready to call py_comp.__init__() */ - ret = bt_py3_call_self_init(py_comp, py_init_args, - py_init_kwargs); - if (ret) { - /* - * We do not clear any raised exception here - * so that the Python caller (the one who is - * instantiating the class) gets the exception - * from the __init__() method. - */ - goto with_init_method_data_error; - } + py_comp = bt_private_component_get_user_data(priv_comp); + assert(py_comp); + py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_priv_port), + SWIGTYPE_p_bt_private_port, 0); + if (!py_self_port_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } - /* - * py_comp.__init__() went well: get the native BT - * component object reference for the Python object. - */ - bt_get(component); - goto with_init_method_data_end; + py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), + SWIGTYPE_p_bt_port, 0); + if (!py_other_port_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } -with_init_method_data_error: - status = BT_COMPONENT_STATUS_ERROR; + py_method_result = PyObject_CallMethod(py_comp, + "_accept_port_connection_from_native", "(OO)", py_self_port_ptr, + py_other_port_ptr); + status = bt_py3_exc_to_component_status(); + if (!py_method_result && status == BT_COMPONENT_STATUS_OK) { + /* Pretty sure this should never happen, but just in case */ + BT_LOGE("User's _accept_port_connection() method failed without raising an exception: " + "status=%d", status); + goto error; + } + if (status == BT_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { /* - * Reset py_comp._ptr to None to signal the error to - * _UserComponentType.__call__(). For exceptions raised - * from py_comp.__init__(), this is not important, - * because __call__() will also raise before even - * checking py_comp._ptr. + * Looks like the user method raised + * PortConnectionRefused: accept this like if it + * returned False. */ - if (PyObject_HasAttrString(py_comp, "_ptr")) { - PyObject *py_err_type, *py_err_value, *py_err_traceback; - PyErr_Fetch(&py_err_type, &py_err_value, - &py_err_traceback); - Py_INCREF(Py_None); - PyObject_SetAttrString(py_comp, "_ptr", Py_None); - PyErr_Restore(py_err_type, py_err_value, - py_err_traceback); - } + goto end; + } else if (status != BT_COMPONENT_STATUS_OK) { + BT_LOGE("User's _accept_port_connection() raised an unexpected exception: " + "status=%d", status); + goto error; + } - /* - * Important: remove py_comp from our the BT component's - * private data. Since we're failing, - * bt_py3_cc_destroy() is about to be called: it must - * not consider py_comp. py_comp's __del__() method - * will be called (because the instance exists), but - * since py_comp._ptr is None, it won't do anything. - */ - bt_component_set_private_data(component, NULL); + assert(PyBool_Check(py_method_result)); -with_init_method_data_end: - Py_XDECREF(py_comp_ptr); - Py_XDECREF(py_init_args); - Py_XDECREF(py_init_kwargs); + if (py_method_result == Py_True) { + status = BT_COMPONENT_STATUS_OK; } else { - /* - * Not called from _UserComponentType.__call__(). - * - * In this case we need to instantiate the user-defined - * Python class. To do this we call the user-defined - * class manually with this already-created BT component - * object pointer as the __comp_ptr keyword argument. - * - * We keep a reference, the only existing one, to the - * created Python object. bt_py3_component_on_del() will - * never be called by the object's __del__() method - * because _UserComponentType.__call__() marks the - * object as owned by the native BT component object. - */ - PyObject *py_cls = NULL; - PyObject *py_comp = NULL; - PyObject *py_params = NULL; - PyObject *py_comp_ptr = NULL; - PyObject *py_cls_ctor_args = NULL; - PyObject *py_cls_ctor_kwargs = NULL; - - /* - * Get the user-defined Python class which created this - * component's class in the first place (borrowed - * reference). - */ - py_cls = lookup_cc_ptr_to_py_cls(bt_component_get_class(component)); - if (!py_cls) { - goto without_init_method_data_error; - } - - /* BT value object -> Python bt2.values object */ - py_params = bt_py3_bt_value_from_ptr(params); - if (!py_params) { - goto without_init_method_data_error; - } - - /* Component pointer -> SWIG pointer Python object */ - py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(component), - SWIGTYPE_p_bt_component, 0); - if (!py_comp_ptr) { - goto without_init_method_data_error; - } - - /* - * Do the equivalent of this: - * - * py_comp = py_cls(__comp_ptr=py_comp_ptr, params=py_params) - * - * _UserComponentType.__call__() calls the Python - * component object's __init__() function itself in this - * case. - */ - py_cls_ctor_args = PyTuple_New(0); - if (!py_cls_ctor_args) { - goto without_init_method_data_error; - } - - py_cls_ctor_kwargs = PyDict_New(); - if (!py_cls_ctor_kwargs) { - goto without_init_method_data_error; - } - - ret = PyDict_SetItemString(py_cls_ctor_kwargs, "__comp_ptr", - py_comp_ptr); - if (ret < 0) { - goto without_init_method_data_error; - } - - ret = PyDict_SetItemString(py_cls_ctor_kwargs, "params", - py_params); - if (ret < 0) { - goto without_init_method_data_error; - } - - py_comp = PyObject_Call(py_cls, py_cls_ctor_args, - py_cls_ctor_kwargs); - if (!py_comp) { - goto without_init_method_data_error; - } + status = BT_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; + } - /* - * Our user Python component object is now fully created - * and initialized by the user. Since we just created - * it, this BT component is its only (persistent) owner. - */ - bt_component_set_private_data(component, py_comp); - py_comp = NULL; - goto without_init_method_data_end; + goto end; -without_init_method_data_error: - status = BT_COMPONENT_STATUS_ERROR; +error: + status = BT_COMPONENT_STATUS_ERROR; - /* - * Clear any exception: we're returning a bad status - * anyway. If this call originated from Python (creation - * from a plugin's component class, for example), then - * the user gets an appropriate creation error. - */ - PyErr_Clear(); - -without_init_method_data_end: - Py_XDECREF(py_comp); - Py_XDECREF(py_params); - Py_XDECREF(py_comp_ptr); - Py_XDECREF(py_cls_ctor_args); - Py_XDECREF(py_cls_ctor_kwargs); - } + /* + * Clear any exception: we're returning a bad status anyway. If + * this call originated from Python, then the user gets an + * appropriate error. + */ + PyErr_Clear(); +end: + Py_XDECREF(py_self_port_ptr); + Py_XDECREF(py_other_port_ptr); + Py_XDECREF(py_method_result); return status; } -static void bt_py3_cc_destroy(struct bt_component *component) +static void bt_py3_cc_port_connected( + struct bt_private_component *priv_comp, + struct bt_private_port *self_priv_port, + struct bt_port *other_port) { - PyObject *py_comp = bt_component_get_private_data(component); - PyObject *py_destroy_method_result = NULL; + PyObject *py_comp = NULL; + PyObject *py_self_port_ptr = NULL; + PyObject *py_other_port_ptr = NULL; + PyObject *py_method_result = NULL; - if (py_comp) { - /* - * If we're here, this BT component is the only owner of - * its private user Python object. It is safe to - * decrement its reference count, and thus destroy it. - * - * We call its _destroy() method before doing so to notify - * the Python user. - */ - py_destroy_method_result = PyObject_CallMethod(py_comp, - "_destroy", NULL); + py_comp = bt_private_component_get_user_data(priv_comp); + assert(py_comp); + py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_priv_port), + SWIGTYPE_p_bt_private_port, 0); + if (!py_self_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), + SWIGTYPE_p_bt_port, 0); + if (!py_other_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_method_result = PyObject_CallMethod(py_comp, + "_port_connected_from_native", "(OO)", py_self_port_ptr, + py_other_port_ptr); + assert(py_method_result == Py_None); + Py_XDECREF(py_self_port_ptr); + Py_XDECREF(py_other_port_ptr); + Py_XDECREF(py_method_result); +} - /* - * Ignore any exception raised by the _destroy() method - * because it won't change anything at this point: the - * component is being destroyed anyway. - */ - PyErr_Clear(); - Py_XDECREF(py_destroy_method_result); - Py_DECREF(py_comp); - } +static void bt_py3_cc_port_disconnected( + struct bt_private_component *priv_comp, + struct bt_private_port *priv_port) +{ + PyObject *py_comp = NULL; + PyObject *py_port_ptr = NULL; + PyObject *py_method_result = NULL; + + py_comp = bt_private_component_get_user_data(priv_comp); + assert(py_comp); + py_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(priv_port), + SWIGTYPE_p_bt_private_port, 0); + if (!py_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_method_result = PyObject_CallMethod(py_comp, + "_port_disconnected_from_native", "(O)", py_port_ptr); + assert(py_method_result == Py_None); + Py_XDECREF(py_port_ptr); + Py_XDECREF(py_method_result); } static struct bt_value *bt_py3_cc_query( - struct bt_component_class *component_class, + struct bt_component_class *comp_cls, const char *object, struct bt_value *params) { PyObject *py_cls = NULL; - PyObject *py_params = NULL; + PyObject *py_params_ptr = NULL; PyObject *py_query_func = NULL; PyObject *py_object = NULL; PyObject *py_results_addr = NULL; struct bt_value *results = NULL; - py_cls = lookup_cc_ptr_to_py_cls(component_class); + py_cls = lookup_cc_ptr_to_py_cls(comp_cls); if (!py_cls) { + BT_LOGE("Cannot find Python class associated to native component class: " + "comp-cls-addr=%p", comp_cls); goto error; } - py_params = bt_py3_bt_value_from_ptr(params); - if (!py_params) { + py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), + SWIGTYPE_p_bt_value, 0); + if (!py_params_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); goto error; } py_object = SWIG_FromCharPtr(object); if (!py_object) { + BT_LOGE_STR("Failed to create a Python string."); goto error; } py_results_addr = PyObject_CallMethod(py_cls, - "_query_from_bt", "(OO)", py_object, py_params); - if (!py_results_addr) { + "_query_from_native", "(OO)", py_object, py_params_ptr); + if (!py_results_addr || py_results_addr == Py_None) { + BT_LOGE_STR("User's _query() method failed."); goto error; } - /* - * The returned object, on success, is an integer object - * (PyLong) containing the address of a BT value object - * (which is now ours). - */ - results = (struct bt_value *) PyLong_AsUnsignedLongLong( - py_results_addr); - - /* Clear potential overflow error; should never happen */ - if (PyErr_Occurred()) { - results = NULL; - goto error; - } - - if (!results) { + if (py_results_addr == Py_NotImplemented) { + BT_LOGE_STR("User's _query() method is not implemented."); goto error; } + /* + * The returned object, on success, is an integer object + * (PyLong) containing the address of a BT value object (new + * reference). + */ + results = (void *) PyLong_AsUnsignedLongLong(py_results_addr); + assert(!PyErr_Occurred()); + assert(results); goto end; error: @@ -620,41 +551,16 @@ error: PyErr_Clear(); end: - Py_XDECREF(py_params); + Py_XDECREF(py_params_ptr); Py_XDECREF(py_query_func); Py_XDECREF(py_object); Py_XDECREF(py_results_addr); return results; } -static enum bt_notification_iterator_status bt_py3_exc_to_notif_iter_status(void) -{ - enum bt_notification_iterator_status status = - BT_NOTIFICATION_ITERATOR_STATUS_OK; - PyObject *exc = PyErr_Occurred(); - - if (!exc) { - goto end; - } - - if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_unsupported_feature_type)) { - status = BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_stop_type)) { - status = BT_NOTIFICATION_ITERATOR_STATUS_END; - } else { - status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; - } - -end: - PyErr_Clear(); - return status; -} - static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_init( - struct bt_component *component, - struct bt_notification_iterator *iter, void *init_method_data) + struct bt_private_notification_iterator *priv_notif_iter, + struct bt_private_port *priv_port) { enum bt_notification_iterator_status status = BT_NOTIFICATION_ITERATOR_STATUS_OK; @@ -663,34 +569,45 @@ static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_init PyObject *py_iter_ptr = NULL; PyObject *py_init_method_result = NULL; PyObject *py_iter = NULL; - PyObject *py_comp = bt_component_get_private_data(component); + struct bt_private_component *priv_comp = + bt_private_notification_iterator_get_private_component( + priv_notif_iter); + PyObject *py_comp; + + assert(priv_comp); + py_comp = bt_private_component_get_user_data(priv_comp); /* Find user's Python notification iterator class */ py_comp_cls = PyObject_GetAttrString(py_comp, "__class__"); if (!py_comp_cls) { + BT_LOGE_STR("Cannot get Python object's `__class__` attribute."); goto 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; + } + + py_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(priv_notif_iter), + SWIGTYPE_p_bt_private_notification_iterator, 0); + if (!py_iter_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); goto error; } /* - * Create object with borrowed BT notification iterator + * Create object with borrowed native notification iterator * reference: * * py_iter = py_iter_cls.__new__(py_iter_cls, py_iter_ptr) */ - py_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(iter), - SWIGTYPE_p_bt_notification_iterator, 0); - if (!py_iter_ptr) { - goto error; - } - py_iter = PyObject_CallMethod(py_iter_cls, "__new__", "(OO)", py_iter_cls, py_iter_ptr); if (!py_iter) { + BT_LOGE("Failed to call Python class's __new__() method: " + "py-cls-addr=%p", py_iter_cls); goto error; } @@ -700,33 +617,36 @@ static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_init * py_iter.__init__() * * At this point, py_iter._ptr is set, so this initialization - * function has access to the py_iter.component property (which - * gives it the user Python component object from which the - * iterator was created). + * function has access to self._component (which gives it the + * user Python component object from which the iterator was + * created). */ py_init_method_result = PyObject_CallMethod(py_iter, "__init__", NULL); if (!py_init_method_result) { + BT_LOGE_STR("User's __init__() method failed."); goto error; } /* * Since the Python code can never instantiate a user-defined - * notification iterator class, the BT notification iterator + * notification iterator class, the native notification iterator * object does NOT belong to a user Python notification iterator * object (borrowed reference). However this Python object is - * owned by this BT notification iterator object. + * owned by this native notification iterator object. * - * In the Python world, the lifetime of the BT notification + * In the Python world, the lifetime of the native notification * iterator is managed by a _GenericNotificationIterator * instance: * * _GenericNotificationIterator instance: * owns a native bt_notification_iterator object (iter) - * owns a UserNotificationIterator instance (py_iter) + * owns a _UserNotificationIterator instance (py_iter) * self._ptr is a borrowed reference to the - * native bt_notification_iterator object (iter) + * native bt_private_notification_iterator + * object (iter) */ - bt_notification_iterator_set_private_data(iter, py_iter); + bt_private_notification_iterator_set_user_data(priv_notif_iter, + py_iter); py_iter = NULL; goto end; @@ -748,6 +668,7 @@ error: PyErr_Clear(); end: + bt_put(priv_comp); Py_XDECREF(py_comp_cls); Py_XDECREF(py_iter_cls); Py_XDECREF(py_iter_ptr); @@ -756,197 +677,92 @@ end: return status; } -static void bt_py3_cc_notification_iterator_destroy( - struct bt_notification_iterator *iter) +static void bt_py3_cc_notification_iterator_finalize( + struct bt_private_notification_iterator *priv_notif_iter) { PyObject *py_notif_iter = - bt_notification_iterator_get_private_data(iter); - PyObject *py_destroy_method_result = NULL; + bt_private_notification_iterator_get_user_data(priv_notif_iter); + PyObject *py_method_result = NULL; assert(py_notif_iter); - py_destroy_method_result = PyObject_CallMethod(py_notif_iter, - "_destroy", NULL); + + /* Call user's _finalize() method */ + py_method_result = PyObject_CallMethod(py_notif_iter, + "_finalize", NULL); + + if (PyErr_Occurred()) { + BT_LOGW("User's _finalize() method raised an exception: ignoring."); + } /* - * Ignore any exception raised by the _destroy() method because - * it won't change anything at this point. The notification - * iterator is being destroyed anyway. + * Ignore any exception raised by the _finalize() method because + * it won't change anything at this point: the component is + * being destroyed anyway. */ PyErr_Clear(); - Py_XDECREF(py_destroy_method_result); + Py_XDECREF(py_method_result); Py_DECREF(py_notif_iter); } -static struct bt_notification *bt_py3_cc_notification_iterator_get( - struct bt_notification_iterator *iter) +static struct bt_notification_iterator_next_return +bt_py3_cc_notification_iterator_next( + struct bt_private_notification_iterator *priv_notif_iter) { + struct bt_notification_iterator_next_return next_ret = { + .status = BT_NOTIFICATION_ITERATOR_STATUS_OK, + .notification = NULL, + }; PyObject *py_notif_iter = - bt_notification_iterator_get_private_data(iter); - PyObject *py_get_method_result = NULL; - struct bt_notification *notif = NULL; + bt_private_notification_iterator_get_user_data(priv_notif_iter); + PyObject *py_method_result = NULL; assert(py_notif_iter); - py_get_method_result = PyObject_CallMethod(py_notif_iter, - "_get_from_bt", NULL); - if (!py_get_method_result) { - goto error; + py_method_result = PyObject_CallMethod(py_notif_iter, + "_next_from_native", NULL); + if (!py_method_result) { + next_ret.status = bt_py3_exc_to_notif_iter_status(); + assert(next_ret.status != BT_NOTIFICATION_ITERATOR_STATUS_OK); + goto end; } /* * The returned object, on success, is an integer object - * (PyLong) containing the address of a BT notification + * (PyLong) containing the address of a native notification * object (which is now ours). */ - notif = (struct bt_notification *) PyLong_AsUnsignedLongLong( - py_get_method_result); + next_ret.notification = + (struct bt_notification *) PyLong_AsUnsignedLongLong( + py_method_result); /* Clear potential overflow error; should never happen */ - if (PyErr_Occurred()) { - notif = NULL; - goto error; - } - - if (!notif) { - goto error; - } - + assert(!PyErr_Occurred()); + assert(next_ret.notification); goto end; -error: - BT_PUT(notif); - - /* - * Clear any exception: we're returning NULL anyway. If this - * call originated from Python, then the user gets an - * appropriate error. - */ - PyErr_Clear(); - end: - Py_XDECREF(py_get_method_result); - return notif; -} - -static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_next( - struct bt_notification_iterator *iter) -{ - enum bt_notification_iterator_status status; - PyObject *py_notif_iter = - bt_notification_iterator_get_private_data(iter); - PyObject *py_next_method_result = NULL; - - assert(py_notif_iter); - py_next_method_result = PyObject_CallMethod(py_notif_iter, - "_next", NULL); - status = bt_py3_exc_to_notif_iter_status(); - if (!py_next_method_result - && status == BT_NOTIFICATION_ITERATOR_STATUS_OK) { - /* Pretty sure this should never happen, but just in case */ - status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; - } - - Py_XDECREF(py_next_method_result); - return status; -} - -static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_seek_time( - struct bt_notification_iterator *iter, int64_t time) -{ - enum bt_notification_iterator_status status; - PyObject *py_notif_iter = - bt_notification_iterator_get_private_data(iter); - PyObject *py_seek_to_time_method_result = NULL; - - assert(py_notif_iter); - py_seek_to_time_method_result = PyObject_CallMethod(py_notif_iter, - "_seek_to_time", "(L)", time); - status = bt_py3_exc_to_notif_iter_status(); - if (!py_seek_to_time_method_result - && status == BT_NOTIFICATION_ITERATOR_STATUS_OK) { - /* Pretty sure this should never happen, but just in case */ - status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; - } - - Py_XDECREF(py_seek_to_time_method_result); - return status; -} - -static enum bt_component_status bt_py3_exc_to_component_status(void) -{ - enum bt_component_status status = BT_COMPONENT_STATUS_OK; - PyObject *exc = PyErr_Occurred(); - - if (!exc) { - goto end; - } - - if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_unsupported_feature_type)) { - status = BT_COMPONENT_STATUS_UNSUPPORTED; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_try_again_type)) { - status = BT_COMPONENT_STATUS_AGAIN; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_stop_type)) { - status = BT_COMPONENT_STATUS_END; - } else { - status = BT_COMPONENT_STATUS_ERROR; - } - -end: - PyErr_Clear(); - return status; + Py_XDECREF(py_method_result); + return next_ret; } static enum bt_component_status bt_py3_cc_sink_consume( - struct bt_component *component) + struct bt_private_component *priv_comp) { - PyObject *py_comp = bt_component_get_private_data(component); - PyObject *py_consume_method_result = NULL; + PyObject *py_comp = bt_private_component_get_user_data(priv_comp); + PyObject *py_method_result = NULL; enum bt_component_status status; - py_consume_method_result = PyObject_CallMethod(py_comp, + assert(py_comp); + py_method_result = PyObject_CallMethod(py_comp, "_consume", NULL); status = bt_py3_exc_to_component_status(); - if (!py_consume_method_result && status == BT_COMPONENT_STATUS_OK) { - /* Pretty sure this should never happen, but just in case */ - status = BT_COMPONENT_STATUS_ERROR; - } - - Py_XDECREF(py_consume_method_result); - return status; -} - -static enum bt_component_status bt_py3_cc_sink_filter_add_iterator( - struct bt_component *component, - struct bt_notification_iterator *iter) -{ - PyObject *py_comp = bt_component_get_private_data(component); - PyObject *py_add_iterator_method_result = NULL; - PyObject *py_notif_iter_ptr = NULL; - enum bt_component_status status; - - py_notif_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(iter), - SWIGTYPE_p_bt_notification_iterator, 0); - if (!py_notif_iter_ptr) { - PyErr_Clear(); - status = BT_COMPONENT_STATUS_ERROR; - goto end; - } - - bt_get(iter); - py_add_iterator_method_result = PyObject_CallMethod(py_comp, - "_add_iterator_from_bt", "(O)", py_notif_iter_ptr); - status = bt_py3_exc_to_component_status(); - if (!py_add_iterator_method_result - && status == BT_COMPONENT_STATUS_OK) { + if (!py_method_result && status == BT_COMPONENT_STATUS_OK) { /* Pretty sure this should never happen, but just in case */ + BT_LOGE("User's _consume() method failed without raising an exception: " + "status=%d", status); status = BT_COMPONENT_STATUS_ERROR; } -end: - Py_XDECREF(py_notif_iter_ptr); - Py_XDECREF(py_add_iterator_method_result); + Py_XDECREF(py_method_result); return status; } @@ -961,6 +777,8 @@ static int bt_py3_cc_set_optional_attrs_methods(struct bt_component_class *cc, if (description) { ret = bt_component_class_set_description(cc, description); if (ret) { + BT_LOGE("Cannot set component class's description: " + "comp-cls-addr=%p", cc); goto end; } } @@ -968,91 +786,71 @@ static int bt_py3_cc_set_optional_attrs_methods(struct bt_component_class *cc, if (help) { ret = bt_component_class_set_help(cc, help); if (ret) { + BT_LOGE("Cannot set component class's help text: " + "comp-cls-addr=%p", cc); goto end; } } ret = bt_component_class_set_init_method(cc, bt_py3_cc_init); - if (ret) { - goto end; - } - - ret = bt_component_class_set_destroy_method(cc, bt_py3_cc_destroy); - if (ret) { - goto end; - } - + assert(ret == 0); + ret = bt_component_class_set_finalize_method(cc, bt_py3_cc_finalize); + assert(ret == 0); + ret = bt_component_class_set_accept_port_connection_method(cc, + bt_py3_cc_accept_port_connection); + assert(ret == 0); + ret = bt_component_class_set_port_connected_method(cc, + bt_py3_cc_port_connected); + assert(ret == 0); + ret = bt_component_class_set_port_disconnected_method(cc, + bt_py3_cc_port_disconnected); + assert(ret == 0); ret = bt_component_class_set_query_method(cc, bt_py3_cc_query); - if (ret) { - goto end; - } + assert(ret == 0); end: return ret; } -static int bt_py3_cc_set_optional_iter_methods(struct bt_component_class *cc, - bool has_seek_time, +static void bt_py3_cc_set_optional_iter_methods(struct bt_component_class *cc, int (*set_notif_iter_init_method)(struct bt_component_class *, bt_component_class_notification_iterator_init_method), - int (*set_notif_iter_destroy_method)(struct bt_component_class *, bt_component_class_notification_iterator_destroy_method), - int (*set_notif_iter_seek_time_method)(struct bt_component_class *, bt_component_class_notification_iterator_seek_time_method)) + int (*set_notif_iter_finalize_method)(struct bt_component_class *, bt_component_class_notification_iterator_finalize_method)) { int ret; ret = set_notif_iter_init_method( cc, bt_py3_cc_notification_iterator_init); - if (ret) { - goto end; - } - - ret = set_notif_iter_destroy_method( - cc, bt_py3_cc_notification_iterator_destroy); - if (ret) { - goto end; - } - - if (has_seek_time) { - ret = set_notif_iter_seek_time_method( - cc, bt_py3_cc_notification_iterator_seek_time); - if (ret) { - goto end; - } - } - -end: - return ret; + assert(ret == 0); + ret = set_notif_iter_finalize_method( + cc, bt_py3_cc_notification_iterator_finalize); + assert(ret == 0); } static struct bt_component_class *bt_py3_component_class_source_create( PyObject *py_cls, const char *name, const char *description, - const char *help, bool has_seek_time) + const char *help) { struct bt_component_class *cc; int ret; assert(py_cls); cc = bt_component_class_source_create(name, - bt_py3_cc_notification_iterator_get, bt_py3_cc_notification_iterator_next); if (!cc) { + BT_LOGE_STR("Cannot create source component class."); goto end; } ret = bt_py3_cc_set_optional_attrs_methods(cc, description, help); if (ret) { + BT_LOGE_STR("Cannot set source component class's optional attributes and methods."); BT_PUT(cc); goto end; } - ret = bt_py3_cc_set_optional_iter_methods(cc, has_seek_time, + bt_py3_cc_set_optional_iter_methods(cc, bt_component_class_source_set_notification_iterator_init_method, - bt_component_class_source_set_notification_iterator_destroy_method, - bt_component_class_source_set_notification_iterator_seek_time_method); - if (ret) { - BT_PUT(cc); - goto end; - } - + bt_component_class_source_set_notification_iterator_finalize_method); register_cc_ptr_to_py_cls(cc, py_cls); bt_component_class_freeze(cc); @@ -1062,41 +860,29 @@ end: static struct bt_component_class *bt_py3_component_class_filter_create( PyObject *py_cls, const char *name, const char *description, - const char *help, bool has_seek_time) + const char *help) { struct bt_component_class *cc; int ret; assert(py_cls); cc = bt_component_class_filter_create(name, - bt_py3_cc_notification_iterator_get, bt_py3_cc_notification_iterator_next); if (!cc) { + BT_LOGE_STR("Cannot create filter component class."); goto end; } ret = bt_py3_cc_set_optional_attrs_methods(cc, description, help); if (ret) { + BT_LOGE_STR("Cannot set filter component class's optional attributes and methods."); BT_PUT(cc); goto end; } - ret = bt_py3_cc_set_optional_iter_methods(cc, has_seek_time, + bt_py3_cc_set_optional_iter_methods(cc, bt_component_class_filter_set_notification_iterator_init_method, - bt_component_class_filter_set_notification_iterator_destroy_method, - bt_component_class_filter_set_notification_iterator_seek_time_method); - if (ret) { - BT_PUT(cc); - goto end; - } - - ret = bt_component_class_filter_set_add_iterator_method(cc, - bt_py3_cc_sink_filter_add_iterator); - if (ret) { - BT_PUT(cc); - goto end; - } - + bt_component_class_filter_set_notification_iterator_finalize_method); register_cc_ptr_to_py_cls(cc, py_cls); bt_component_class_freeze(cc); @@ -1114,18 +900,13 @@ static struct bt_component_class *bt_py3_component_class_sink_create( assert(py_cls); cc = bt_component_class_sink_create(name, bt_py3_cc_sink_consume); if (!cc) { + BT_LOGE_STR("Cannot create sink component class."); goto end; } ret = bt_py3_cc_set_optional_attrs_methods(cc, description, help); if (ret) { - BT_PUT(cc); - goto end; - } - - ret = bt_component_class_sink_set_add_iterator_method(cc, - bt_py3_cc_sink_filter_add_iterator); - if (ret) { + BT_LOGE_STR("Cannot set sink component class's optional attributes and methods."); BT_PUT(cc); goto end; } @@ -1136,70 +917,16 @@ static struct bt_component_class *bt_py3_component_class_sink_create( end: return cc; } - - -/* Component creation function (called from Python module) */ - -static void bt_py3_component_create( - struct bt_component_class *comp_class, PyObject *py_self, - const char *name) -{ - struct bt_component *component = - bt_component_create_with_init_method_data(comp_class, - name, NULL, py_self); - - /* - * Our component initialization function, bt_py3_cc_init(), sets - * a new reference to the created component in the user's Python - * object (py_self._ptr). This is where the reference is kept. - * We don't need the returned component in this case (if any, - * because it might be NULL if the creation failed, most - * probably because py_self.__init__() raised something). - */ - bt_put(component); -} - - -/* Component delete notification */ - -static void bt_py3_component_on_del(PyObject *py_comp) -{ - /* - * The Python component's __del__() function calls this to - * indicate that there's no more reference on it from the - * Python world. - * - * Since the BT component can continue to live once the Python - * component object is deleted, we increment the Python - * component's reference count so that it now only belong to the - * BT component. We will decrement this reference count once - * the BT component is destroyed in bt_py3_cc_destroy(). - */ - assert(py_comp); - Py_INCREF(py_comp); -} %} -%exception bt_py3_component_create { - $action - if (PyErr_Occurred()) { - /* Exception is already set by bt_py3_component_create() */ - SWIG_fail; - } -} - struct bt_component_class *bt_py3_component_class_source_create( PyObject *py_cls, const char *name, const char *description, - const char *help, bool has_seek_time); + const char *help); struct bt_component_class *bt_py3_component_class_filter_create( PyObject *py_cls, const char *name, const char *description, - const char *help, bool has_seek_time); + const char *help); struct bt_component_class *bt_py3_component_class_sink_create( PyObject *py_cls, const char *name, const char *description, const char *help); -void bt_py3_component_create( - struct bt_component_class *comp_class, PyObject *py_self, - const char *name); -void bt_py3_component_on_del(PyObject *py_comp); void bt_py3_cc_init_from_bt2(void); void bt_py3_cc_exit_handler(void); diff --git a/bindings/python/bt2/native_btconnection.i b/bindings/python/bt2/native_btconnection.i new file mode 100644 index 00000000..e9c1ab90 --- /dev/null +++ b/bindings/python/bt2/native_btconnection.i @@ -0,0 +1,113 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +/* Type */ +struct bt_connection; +struct bt_private_connection; + +/* Status */ +enum bt_connection_status { + BT_CONNECTION_STATUS_GRAPH_IS_CANCELED = 125, + BT_CONNECTION_STATUS_OK = 0, + BT_CONNECTION_STATUS_INVALID = -22, + BT_CONNECTION_STATUS_ERROR = -1, + BT_CONNECTION_STATUS_NOMEM = -12, + BT_CONNECTION_STATUS_IS_ENDED = 104, +}; + +/* Functions (public) */ +struct bt_port *bt_connection_get_downstream_port( + struct bt_connection *connection); +struct bt_port *bt_connection_get_upstream_port( + struct bt_connection *connection); +int bt_connection_is_ended(struct bt_connection *connection); + +/* Functions (private) */ +struct bt_connection *bt_connection_from_private_connection( + struct bt_private_connection *private_connection); + +/* Helper functions for Python */ +%typemap(out) struct bt_py3_create_notif_iter_ret { + $result = PyTuple_New(2); + PyObject *py_notif_iter_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr($1.notif_iter), + SWIGTYPE_p_bt_notification_iterator, 0); + PyObject *py_status = SWIG_From_long_SS_long($1.status); + PyTuple_SET_ITEM($result, 0, py_status); + PyTuple_SET_ITEM($result, 1, py_notif_iter_ptr); +} + +%{ +struct bt_py3_create_notif_iter_ret { + enum bt_connection_status status; + struct bt_notification_iterator *notif_iter; +}; + +static struct bt_py3_create_notif_iter_ret bt_py3_create_notif_iter( + unsigned long long priv_conn_addr, PyObject *py_notif_types) +{ + struct bt_private_connection *priv_conn; + enum bt_notification_type *notification_types = NULL; + struct bt_py3_create_notif_iter_ret ret; + + priv_conn = (void *) priv_conn_addr; + assert(!PyErr_Occurred()); + assert(priv_conn); + + if (py_notif_types != Py_None) { + size_t i; + + assert(PyList_Check(py_notif_types)); + notification_types = g_new0(enum bt_notification_type, + PyList_Size(py_notif_types) + 1); + assert(notification_types); + notification_types[PyList_Size(py_notif_types)] = + BT_NOTIFICATION_TYPE_SENTINEL; + + for (i = 0; i < PyList_Size(py_notif_types); i++) { + PyObject *item = PyList_GetItem(py_notif_types, i); + long value; + int overflow; + + assert(item); + assert(PyLong_Check(item)); + value = PyLong_AsLongAndOverflow(item, &overflow); + assert(overflow == 0); + notification_types[i] = value; + } + } + + ret.status = bt_private_connection_create_notification_iterator( + priv_conn, notification_types, &ret.notif_iter); + + if (notification_types) { + g_free(notification_types); + } + + return ret; +} +%} + +struct bt_py3_create_notif_iter_ret bt_py3_create_notif_iter( + unsigned long long priv_conn_addr, PyObject *notif_types); diff --git a/bindings/python/bt2/native_btctfwriter.i b/bindings/python/bt2/native_btctfwriter.i index 479e1e33..293ed254 100644 --- a/bindings/python/bt2/native_btctfwriter.i +++ b/bindings/python/bt2/native_btctfwriter.i @@ -22,13 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -#include -#include -#include -%} - /* Types */ struct bt_ctf_clock; struct bt_ctf_writer; diff --git a/bindings/python/bt2/native_btevent.i b/bindings/python/bt2/native_btevent.i index 3d73b2f3..f6e64945 100644 --- a/bindings/python/bt2/native_btevent.i +++ b/bindings/python/bt2/native_btevent.i @@ -22,10 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_event; @@ -52,9 +48,9 @@ struct bt_ctf_field *bt_ctf_event_get_event_context( struct bt_ctf_event *event); int bt_ctf_event_set_event_context(struct bt_ctf_event *event, struct bt_ctf_field *context); -struct bt_ctf_field *bt_ctf_event_get_payload_field( +struct bt_ctf_field *bt_ctf_event_get_event_payload( struct bt_ctf_event *event); -int bt_ctf_event_set_payload_field(struct bt_ctf_event *event, +int bt_ctf_event_set_event_payload(struct bt_ctf_event *event, struct bt_ctf_field *payload); struct bt_ctf_clock_value *bt_ctf_event_get_clock_value( struct bt_ctf_event *event, diff --git a/bindings/python/bt2/native_bteventclass.i b/bindings/python/bt2/native_bteventclass.i index 66a59830..c4b8c8d7 100644 --- a/bindings/python/bt2/native_bteventclass.i +++ b/bindings/python/bt2/native_bteventclass.i @@ -22,13 +22,30 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_event_class; +/* Log levels */ +enum bt_ctf_event_class_log_level { + BT_CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN = -1, + BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED = 255, + BT_CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY = 0, + BT_CTF_EVENT_CLASS_LOG_LEVEL_ALERT = 1, + BT_CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL = 2, + BT_CTF_EVENT_CLASS_LOG_LEVEL_ERROR = 3, + BT_CTF_EVENT_CLASS_LOG_LEVEL_WARNING = 4, + BT_CTF_EVENT_CLASS_LOG_LEVEL_NOTICE = 5, + BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO = 6, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM = 7, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM = 8, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS = 9, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE = 10, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT = 11, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION = 12, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE = 13, + BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG = 14, +}; + /* Functions */ struct bt_ctf_event_class *bt_ctf_event_class_create(const char *name); struct bt_ctf_stream_class *bt_ctf_event_class_get_stream_class( @@ -38,17 +55,17 @@ const char *bt_ctf_event_class_get_name( int64_t bt_ctf_event_class_get_id( struct bt_ctf_event_class *event_class); int bt_ctf_event_class_set_id( - struct bt_ctf_event_class *event_class, uint32_t id); -int bt_ctf_event_class_get_attribute_count( + struct bt_ctf_event_class *event_class, uint64_t id); +enum bt_ctf_event_class_log_level bt_ctf_event_class_get_log_level( struct bt_ctf_event_class *event_class); -const char *bt_ctf_event_class_get_attribute_name( - struct bt_ctf_event_class *event_class, int index); -struct bt_value * -bt_ctf_event_class_get_attribute_value_by_name( - struct bt_ctf_event_class *event_class, const char *name); -int bt_ctf_event_class_set_attribute( - struct bt_ctf_event_class *event_class, const char *name, - struct bt_value *value); +int bt_ctf_event_class_set_log_level( + struct bt_ctf_event_class *event_class, + enum bt_ctf_event_class_log_level log_level); +const char *bt_ctf_event_class_get_emf_uri( + struct bt_ctf_event_class *event_class); +int bt_ctf_event_class_set_emf_uri( + struct bt_ctf_event_class *event_class, + const char *emf_uri); struct bt_ctf_field_type *bt_ctf_event_class_get_context_type( struct bt_ctf_event_class *event_class); int bt_ctf_event_class_set_context_type( diff --git a/bindings/python/bt2/native_btfields.i b/bindings/python/bt2/native_btfields.i index e3753fdd..883bec3b 100644 --- a/bindings/python/bt2/native_btfields.i +++ b/bindings/python/bt2/native_btfields.i @@ -22,10 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_field; @@ -73,8 +69,10 @@ int bt_ctf_field_string_append_len( /* Structure field functions */ struct bt_ctf_field *bt_ctf_field_structure_get_field_by_index( struct bt_ctf_field *structure, int index); -struct bt_ctf_field *bt_ctf_field_structure_get_field( - struct bt_ctf_field *structure, const char *name); +struct bt_ctf_field *bt_ctf_field_structure_get_field_by_name( + struct bt_ctf_field *struct_field, const char *name); +int bt_ctf_field_structure_set_field_by_name(struct bt_ctf_field *struct_field, + const char *name, struct bt_ctf_field *field); /* Array field functions */ struct bt_ctf_field *bt_ctf_field_array_get_field( diff --git a/bindings/python/bt2/native_btft.i b/bindings/python/bt2/native_btft.i index 1636e83d..c24a8630 100644 --- a/bindings/python/bt2/native_btft.i +++ b/bindings/python/bt2/native_btft.i @@ -22,10 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_field_type; @@ -42,23 +38,23 @@ enum bt_ctf_scope { BT_CTF_SCOPE_EVENT_FIELDS = 6, }; -enum bt_ctf_type_id { - BT_CTF_TYPE_ID_UNKNOWN = CTF_TYPE_UNKNOWN, - BT_CTF_TYPE_ID_INTEGER = CTF_TYPE_INTEGER, - BT_CTF_TYPE_ID_FLOAT = CTF_TYPE_FLOAT, - BT_CTF_TYPE_ID_ENUM = CTF_TYPE_ENUM, - BT_CTF_TYPE_ID_STRING = CTF_TYPE_STRING, - BT_CTF_TYPE_ID_STRUCT = CTF_TYPE_STRUCT, - BT_CTF_TYPE_ID_UNTAGGED_VARIANT = CTF_TYPE_UNTAGGED_VARIANT, - BT_CTF_TYPE_ID_ARRAY = CTF_TYPE_ARRAY, - BT_CTF_TYPE_ID_SEQUENCE = CTF_TYPE_SEQUENCE, - BT_CTF_TYPE_ID_VARIANT = CTF_TYPE_VARIANT, +enum bt_ctf_field_type_id { + BT_CTF_FIELD_TYPE_ID_UNKNOWN = CTF_TYPE_UNKNOWN, + BT_CTF_FIELD_TYPE_ID_INTEGER = CTF_TYPE_INTEGER, + BT_CTF_FIELD_TYPE_ID_FLOAT = CTF_TYPE_FLOAT, + BT_CTF_FIELD_TYPE_ID_ENUM = CTF_TYPE_ENUM, + BT_CTF_FIELD_TYPE_ID_STRING = CTF_TYPE_STRING, + BT_CTF_FIELD_TYPE_ID_STRUCT = CTF_TYPE_STRUCT, + BT_CTF_FIELD_TYPE_ID_ARRAY = CTF_TYPE_ARRAY, + BT_CTF_FIELD_TYPE_ID_SEQUENCE = CTF_TYPE_SEQUENCE, + BT_CTF_FIELD_TYPE_ID_VARIANT = CTF_TYPE_VARIANT, BT_CTF_NR_TYPE_IDS = NR_CTF_TYPES, }; enum bt_ctf_byte_order { BT_CTF_BYTE_ORDER_UNKNOWN = -1, BT_CTF_BYTE_ORDER_NATIVE = 0, + BT_CTF_BYTE_ORDER_UNSPECIFIED, BT_CTF_BYTE_ORDER_LITTLE_ENDIAN, BT_CTF_BYTE_ORDER_BIG_ENDIAN, BT_CTF_BYTE_ORDER_NETWORK, @@ -72,7 +68,7 @@ enum bt_ctf_string_encoding { }; /* Common functions */ -enum bt_ctf_type_id bt_ctf_field_type_get_type_id( +enum bt_ctf_field_type_id bt_ctf_field_type_get_type_id( struct bt_ctf_field_type *field_type); int bt_ctf_field_type_get_alignment( struct bt_ctf_field_type *field_type); @@ -102,9 +98,11 @@ struct bt_ctf_field_type *bt_ctf_field_type_integer_create( unsigned int size); int bt_ctf_field_type_integer_get_size( struct bt_ctf_field_type *int_field_type); -int bt_ctf_field_type_integer_get_signed( +int bt_ctf_field_type_integer_set_size( + struct bt_ctf_field_type *int_field_type, unsigned int size); +int bt_ctf_field_type_integer_is_signed( struct bt_ctf_field_type *int_field_type); -int bt_ctf_field_type_integer_set_signed( +int bt_ctf_field_type_integer_set_is_signed( struct bt_ctf_field_type *int_field_type, int is_signed); enum bt_ctf_integer_base bt_ctf_field_type_integer_get_base( struct bt_ctf_field_type *int_field_type); @@ -140,7 +138,7 @@ struct bt_ctf_field_type *bt_ctf_field_type_enumeration_create( struct bt_ctf_field_type *int_field_type); struct bt_ctf_field_type *bt_ctf_field_type_enumeration_get_container_type( struct bt_ctf_field_type *enum_field_type); -int bt_ctf_field_type_enumeration_get_mapping_count( +int64_t bt_ctf_field_type_enumeration_get_mapping_count( struct bt_ctf_field_type *enum_field_type); int bt_ctf_field_type_enumeration_get_mapping_signed( struct bt_ctf_field_type *enum_field_type, int index, @@ -149,7 +147,7 @@ int bt_ctf_field_type_enumeration_get_mapping_unsigned( struct bt_ctf_field_type *enum_field_type, int index, const char **BTOUTSTR, uint64_t *OUTPUT, uint64_t *OUTPUT); -int bt_ctf_field_type_enumeration_add_mapping( +int bt_ctf_field_type_enumeration_add_mapping_signed( struct bt_ctf_field_type *enum_field_type, const char *name, int64_t range_begin, int64_t range_end); int bt_ctf_field_type_enumeration_add_mapping_unsigned( @@ -174,7 +172,7 @@ int bt_ctf_field_type_enumeration_mapping_iterator_get_signed( const char **BTOUTSTR, int64_t *OUTPUT, int64_t *OUTPUT); int bt_ctf_field_type_enumeration_mapping_iterator_get_unsigned( struct bt_ctf_field_type_enumeration_mapping_iterator *iter, - const char **BTOUTSTR, int64_t *OUTPUT, int64_t *OUTPUT); + const char **BTOUTSTR, uint64_t *OUTPUT, uint64_t *OUTPUT); int bt_ctf_field_type_enumeration_mapping_iterator_next( struct bt_ctf_field_type_enumeration_mapping_iterator *iter); @@ -188,12 +186,12 @@ int bt_ctf_field_type_string_set_encoding( /* Structure field type functions */ struct bt_ctf_field_type *bt_ctf_field_type_structure_create(void); -int bt_ctf_field_type_structure_get_field_count( +int64_t bt_ctf_field_type_structure_get_field_count( struct bt_ctf_field_type *struct_field_type); -int bt_ctf_field_type_structure_get_field( +int bt_ctf_field_type_structure_get_field_by_index( struct bt_ctf_field_type *struct_field_type, const char **BTOUTSTR, struct bt_ctf_field_type **BTOUTFT, - int index); + uint64_t index); struct bt_ctf_field_type *bt_ctf_field_type_structure_get_field_type_by_name( struct bt_ctf_field_type *struct_field_type, const char *field_name); @@ -212,14 +210,14 @@ int64_t bt_ctf_field_type_array_get_length( struct bt_ctf_field_type *array_field_type); /* Sequence field type functions */ -extern struct bt_ctf_field_type *bt_ctf_field_type_sequence_create( +struct bt_ctf_field_type *bt_ctf_field_type_sequence_create( struct bt_ctf_field_type *element_field_type, const char *length_name); -extern struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_type( +struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_type( struct bt_ctf_field_type *sequence_field_type); -extern const char *bt_ctf_field_type_sequence_get_length_field_name( +const char *bt_ctf_field_type_sequence_get_length_field_name( struct bt_ctf_field_type *sequence_field_type); -extern struct bt_ctf_field_path *bt_ctf_field_type_sequence_get_length_field_path( +struct bt_ctf_field_path *bt_ctf_field_type_sequence_get_length_field_path( struct bt_ctf_field_type *sequence_field_type); /* Variant field type functions */ @@ -235,12 +233,12 @@ int bt_ctf_field_type_variant_set_tag_name( const char *tag_name); struct bt_ctf_field_path *bt_ctf_field_type_variant_get_tag_field_path( struct bt_ctf_field_type *variant_field_type); -int bt_ctf_field_type_variant_get_field_count( +int64_t bt_ctf_field_type_variant_get_field_count( struct bt_ctf_field_type *variant_field_type); -int bt_ctf_field_type_variant_get_field( +int bt_ctf_field_type_variant_get_field_by_index( struct bt_ctf_field_type *variant_field_type, const char **BTOUTSTR, - struct bt_ctf_field_type **BTOUTFT, int index); + struct bt_ctf_field_type **BTOUTFT, uint64_t index); struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_by_name( struct bt_ctf_field_type *variant_field_type, const char *field_name); diff --git a/bindings/python/bt2/native_btgraph.i b/bindings/python/bt2/native_btgraph.i new file mode 100644 index 00000000..f8ee71da --- /dev/null +++ b/bindings/python/bt2/native_btgraph.i @@ -0,0 +1,262 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +/* Types */ +struct bt_graph; + +/* Status */ +enum bt_graph_status { + BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION = 111, + BT_GRAPH_STATUS_CANCELED = 125, + BT_GRAPH_STATUS_AGAIN = 11, + BT_GRAPH_STATUS_END = 1, + BT_GRAPH_STATUS_OK = 0, + BT_GRAPH_STATUS_INVALID = -22, + BT_GRAPH_STATUS_NO_SINK = -6, + BT_GRAPH_STATUS_ERROR = -1, + BT_GRAPH_STATUS_NOMEM = -12, +}; + +/* Functions */ +struct bt_graph *bt_graph_create(void); +enum bt_graph_status bt_graph_add_component( + struct bt_graph *graph, + struct bt_component_class *component_class, + const char *name, struct bt_value *params, + struct bt_component **BTOUTCOMP); +enum bt_graph_status bt_graph_add_component_with_init_method_data( + struct bt_graph *graph, + struct bt_component_class *component_class, + const char *name, struct bt_value *params, + void *init_method_data, + struct bt_component **BTOUTCOMP); +enum bt_graph_status bt_graph_connect_ports(struct bt_graph *graph, + struct bt_port *upstream, struct bt_port *downstream, + struct bt_connection **BTOUTCONN); +enum bt_graph_status bt_graph_run(struct bt_graph *graph); +enum bt_graph_status bt_graph_consume(struct bt_graph *graph); +enum bt_graph_status bt_graph_cancel(struct bt_graph *graph); +int bt_graph_is_canceled(struct bt_graph *graph); + +/* Helper functions for Python */ +%{ +static void port_added_listener(struct bt_port *port, void *py_callable) +{ + PyObject *py_port_ptr = NULL; + PyObject *py_res = NULL; + + py_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(port), + SWIGTYPE_p_bt_port, 0); + if (!py_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_res = PyObject_CallFunction(py_callable, "(O)", py_port_ptr); + assert(py_res == Py_None); + Py_DECREF(py_port_ptr); + Py_DECREF(py_res); +} + +static void port_removed_listener(struct bt_component *component, + struct bt_port *port, void *py_callable) +{ + PyObject *py_port_ptr = NULL; + PyObject *py_res = NULL; + + py_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(port), + SWIGTYPE_p_bt_port, 0); + if (!py_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_res = PyObject_CallFunction(py_callable, "(O)", py_port_ptr); + assert(py_res == Py_None); + Py_DECREF(py_port_ptr); + Py_DECREF(py_res); +} + +static void ports_connected_listener(struct bt_port *upstream_port, + struct bt_port *downstream_port, void *py_callable) +{ + PyObject *py_upstream_port_ptr = NULL; + PyObject *py_downstream_port_ptr = NULL; + PyObject *py_res = NULL; + + py_upstream_port_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(upstream_port), SWIGTYPE_p_bt_port, 0); + if (!py_upstream_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_downstream_port_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(downstream_port), SWIGTYPE_p_bt_port, 0); + if (!py_downstream_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_res = PyObject_CallFunction(py_callable, "(OO)", + py_upstream_port_ptr, py_downstream_port_ptr); + assert(py_res == Py_None); + Py_DECREF(py_upstream_port_ptr); + Py_DECREF(py_downstream_port_ptr); + Py_DECREF(py_res); +} + +static void ports_disconnected_listener( + struct bt_component *upstream_component, + struct bt_component *downstream_component, + struct bt_port *upstream_port, struct bt_port *downstream_port, + void *py_callable) +{ + PyObject *py_upstream_comp_ptr = NULL; + PyObject *py_downstream_comp_ptr = NULL; + PyObject *py_upstream_port_ptr = NULL; + PyObject *py_downstream_port_ptr = NULL; + PyObject *py_res = NULL; + + py_upstream_comp_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(upstream_component), + SWIGTYPE_p_bt_component, 0); + if (!py_upstream_comp_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_downstream_comp_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(downstream_component), + SWIGTYPE_p_bt_component, 0); + if (!py_downstream_comp_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_upstream_port_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(upstream_port), SWIGTYPE_p_bt_port, 0); + if (!py_upstream_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_downstream_port_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(downstream_port), SWIGTYPE_p_bt_port, 0); + if (!py_downstream_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_res = PyObject_CallFunction(py_callable, "(OOOO)", + py_upstream_comp_ptr, py_downstream_comp_ptr, + py_upstream_port_ptr, py_downstream_port_ptr); + assert(py_res == Py_None); + Py_DECREF(py_upstream_comp_ptr); + Py_DECREF(py_downstream_comp_ptr); + Py_DECREF(py_upstream_port_ptr); + Py_DECREF(py_downstream_port_ptr); + Py_DECREF(py_res); +} + +static void graph_listener_removed(void *py_callable) +{ + assert(py_callable); + Py_DECREF(py_callable); +} + +static int bt_py3_graph_add_port_added_listener(struct bt_graph *graph, + PyObject *py_callable) +{ + int ret = 0; + + assert(graph); + assert(py_callable); + ret = bt_graph_add_port_added_listener(graph, port_added_listener, + graph_listener_removed, py_callable); + if (ret >= 0) { + Py_INCREF(py_callable); + } + + return ret; +} + +static int bt_py3_graph_add_port_removed_listener(struct bt_graph *graph, + PyObject *py_callable) +{ + int ret = 0; + + assert(graph); + assert(py_callable); + ret = bt_graph_add_port_removed_listener(graph, port_removed_listener, + graph_listener_removed, py_callable); + if (ret >= 0) { + Py_INCREF(py_callable); + } + + return ret; +} + +static int bt_py3_graph_add_ports_connected_listener(struct bt_graph *graph, + PyObject *py_callable) +{ + int ret = 0; + + assert(graph); + assert(py_callable); + ret = bt_graph_add_ports_connected_listener(graph, + ports_connected_listener, graph_listener_removed, py_callable); + if (ret >= 0) { + Py_INCREF(py_callable); + } + + return ret; +} + +static int bt_py3_graph_add_ports_disconnected_listener(struct bt_graph *graph, + PyObject *py_callable) +{ + int ret = 0; + + assert(graph); + assert(py_callable); + ret = bt_graph_add_ports_disconnected_listener(graph, + ports_disconnected_listener, graph_listener_removed, + py_callable); + if (ret >= 0) { + Py_INCREF(py_callable); + } + + return ret; +} +%} + +int bt_py3_graph_add_port_added_listener(struct bt_graph *graph, + PyObject *py_callable); +int bt_py3_graph_add_port_removed_listener(struct bt_graph *graph, + PyObject *py_callable); +int bt_py3_graph_add_ports_connected_listener(struct bt_graph *graph, + PyObject *py_callable); +int bt_py3_graph_add_ports_disconnected_listener(struct bt_graph *graph, + PyObject *py_callable); diff --git a/bindings/python/bt2/native_btlogging.i b/bindings/python/bt2/native_btlogging.i new file mode 100644 index 00000000..0406a2aa --- /dev/null +++ b/bindings/python/bt2/native_btlogging.i @@ -0,0 +1,43 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +%{ +#include +%} + +/* Log levels */ +enum bt_logging_level { + BT_LOGGING_LEVEL_VERBOSE = 1, + BT_LOGGING_LEVEL_DEBUG = 2, + BT_LOGGING_LEVEL_INFO = 3, + BT_LOGGING_LEVEL_WARN = 4, + BT_LOGGING_LEVEL_ERROR = 5, + BT_LOGGING_LEVEL_FATAL = 6, + BT_LOGGING_LEVEL_NONE = 0xff, +}; + +/* Logging functions */ +enum bt_logging_level bt_logging_get_minimal_level(void); +enum bt_logging_level bt_logging_get_global_level(void); +void bt_logging_set_global_level(enum bt_logging_level log_level); diff --git a/bindings/python/bt2/native_btnotification.i b/bindings/python/bt2/native_btnotification.i index 294110e0..41922785 100644 --- a/bindings/python/bt2/native_btnotification.i +++ b/bindings/python/bt2/native_btnotification.i @@ -22,29 +22,22 @@ * THE SOFTWARE. */ -%{ -#include -#include -#include -#include -%} - /* Type */ -struct bt_notification;; +struct bt_notification; /* Notification type */ enum bt_notification_type { + BT_NOTIFICATION_TYPE_SENTINEL = -1000, BT_NOTIFICATION_TYPE_UNKNOWN = -1, - BT_NOTIFICATION_TYPE_ALL = 0, - BT_NOTIFICATION_TYPE_EVENT = 1, - BT_NOTIFICATION_TYPE_PACKET_BEGIN = 2, - BT_NOTIFICATION_TYPE_PACKET_END = 3, - BT_NOTIFICATION_TYPE_STREAM_END = 4, - BT_NOTIFICATION_TYPE_NEW_TRACE = 5, - BT_NOTIFICATION_TYPE_NEW_STREAM_CLASS = 6, - BT_NOTIFICATION_TYPE_NEW_EVENT_CLASS = 7, - BT_NOTIFICATION_TYPE_END_OF_TRACE = 8, - BT_NOTIFICATION_TYPE_NR, + BT_NOTIFICATION_TYPE_ALL = -2, + BT_NOTIFICATION_TYPE_EVENT = 0, + BT_NOTIFICATION_TYPE_INACTIVITY = 1, + BT_NOTIFICATION_TYPE_STREAM_BEGIN = 2, + BT_NOTIFICATION_TYPE_STREAM_END = 3, + BT_NOTIFICATION_TYPE_PACKET_BEGIN = 4, + BT_NOTIFICATION_TYPE_PACKET_END = 5, + BT_NOTIFICATION_TYPE_DISCARDED_EVENTS = 6, + BT_NOTIFICATION_TYPE_DISCARDED_PACKETS = 7, }; /* General functions */ @@ -53,34 +46,67 @@ enum bt_notification_type bt_notification_get_type( /* Event notification functions */ struct bt_notification *bt_notification_event_create( - struct bt_ctf_event *event); + struct bt_ctf_event *event, + struct bt_clock_class_priority_map *clock_class_priority_map); struct bt_ctf_event *bt_notification_event_get_event( struct bt_notification *notification); +struct bt_clock_class_priority_map * +bt_notification_event_get_clock_class_priority_map( + struct bt_notification *notification); + +/* Inactivity notification functions */ +struct bt_notification *bt_notification_inactivity_create( + struct bt_clock_class_priority_map *clock_class_priority_map); +struct bt_clock_class_priority_map * +bt_notification_inactivity_get_clock_class_priority_map( + struct bt_notification *notification); +struct bt_ctf_clock_value *bt_notification_inactivity_get_clock_value( + struct bt_notification *notification, + struct bt_ctf_clock_class *clock_class); +int bt_notification_inactivity_set_clock_value( + struct bt_notification *notification, + struct bt_ctf_clock_value *clock_value); /* Packet notification functions */ struct bt_notification *bt_notification_packet_begin_create( struct bt_ctf_packet *packet); -struct bt_ctf_packet *bt_notification_packet_begin_get_packet( - struct bt_notification *notification); struct bt_notification *bt_notification_packet_end_create( struct bt_ctf_packet *packet); -struct bt_ctf_packet *bt_notification_packet_end_get_packet( - struct bt_notification *notification); - -/* Schema notification functions */ -/* -struct bt_notification *bt_notification_new_trace_create( - struct bt_ctf_trace *trace); -struct bt_ctf_trace *bt_notification_new_trace_get_trace( +struct bt_ctf_packet *bt_notification_packet_begin_get_packet( struct bt_notification *notification); -struct bt_notification *bt_notification_new_stream_class_create( - struct bt_ctf_stream_class *stream_class); -struct bt_ctf_trace *bt_notification_new_stream_class_get_stream_class( +struct bt_ctf_packet *bt_notification_packet_end_get_packet( struct bt_notification *notification); -*/ /* Stream notification functions */ +struct bt_notification *bt_notification_stream_begin_create( + struct bt_ctf_stream *stream); struct bt_notification *bt_notification_stream_end_create( struct bt_ctf_stream *stream); +struct bt_ctf_stream *bt_notification_stream_begin_get_stream( + struct bt_notification *notification); struct bt_ctf_stream *bt_notification_stream_end_get_stream( struct bt_notification *notification); + +/* Discarded packets notification functions */ +struct bt_ctf_clock_value * +bt_notification_discarded_packets_get_begin_clock_value( + struct bt_notification *notification); +struct bt_ctf_clock_value * +bt_notification_discarded_packets_get_end_clock_value( + struct bt_notification *notification); +int64_t bt_notification_discarded_packets_get_count( + struct bt_notification *notification); +struct bt_ctf_stream *bt_notification_discarded_packets_get_stream( + struct bt_notification *notification); + +/* Discarded events notification functions */ +struct bt_ctf_clock_value * +bt_notification_discarded_events_get_begin_clock_value( + struct bt_notification *notification); +struct bt_ctf_clock_value * +bt_notification_discarded_events_get_end_clock_value( + struct bt_notification *notification); +int64_t bt_notification_discarded_events_get_count( + struct bt_notification *notification); +struct bt_ctf_stream *bt_notification_discarded_events_get_stream( + struct bt_notification *notification); diff --git a/bindings/python/bt2/native_btnotifiter.i b/bindings/python/bt2/native_btnotifiter.i index d1deb487..bdc7e73d 100644 --- a/bindings/python/bt2/native_btnotifiter.i +++ b/bindings/python/bt2/native_btnotifiter.i @@ -22,29 +22,19 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_notification_iterator; /* Status */ enum bt_notification_iterator_status { + BT_NOTIFICATION_ITERATOR_STATUS_CANCELED = 125, + BT_NOTIFICATION_ITERATOR_STATUS_AGAIN = 11, BT_NOTIFICATION_ITERATOR_STATUS_END = 1, BT_NOTIFICATION_ITERATOR_STATUS_OK = 0, - BT_NOTIFICATION_ITERATOR_STATUS_INVAL = -1, - BT_NOTIFICATION_ITERATOR_STATUS_ERROR = -2, - BT_NOTIFICATION_ITERATOR_STATUS_NOMEM = -3, - BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED = -4, -}; - -/* Seek reference */ -enum bt_notification_iterator_seek_origin { - BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_BEGIN = 0, - BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_CURRENT = 1, - BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_END = 2, - BT_NOTIFICATION_ITERATOR_SEEK_ORIGIN_EPOCH = 3, + BT_NOTIFICATION_ITERATOR_STATUS_INVALID = -22, + BT_NOTIFICATION_ITERATOR_STATUS_ERROR = -1, + BT_NOTIFICATION_ITERATOR_STATUS_NOMEM = -12, + BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED = -2, }; /* Functions */ @@ -52,28 +42,22 @@ struct bt_notification *bt_notification_iterator_get_notification( struct bt_notification_iterator *iterator); enum bt_notification_iterator_status bt_notification_iterator_next( struct bt_notification_iterator *iterator); -enum bt_notification_iterator_status bt_notification_iterator_seek_time( - struct bt_notification_iterator *iterator, - enum bt_notification_iterator_seek_origin seek_origin, - int64_t time); struct bt_component *bt_notification_iterator_get_component( struct bt_notification_iterator *iterator); -enum bt_notification_iterator_status bt_notification_iterator_set_private_data( - struct bt_notification_iterator *iterator, void *data); -void *bt_notification_iterator_get_private_data( - struct bt_notification_iterator *iterator); /* Helper functions for Python */ %{ -static PyObject *bt_py3_get_component_from_notif_iter( - struct bt_notification_iterator *iter) +static PyObject *bt_py3_get_user_component_from_user_notif_iter( + struct bt_private_notification_iterator *priv_notif_iter) { - struct bt_component *component = bt_notification_iterator_get_component(iter); + struct bt_private_component *priv_comp = + bt_private_notification_iterator_get_private_component( + priv_notif_iter); PyObject *py_comp; - assert(component); - py_comp = bt_component_get_private_data(component); - BT_PUT(component); + assert(priv_comp); + py_comp = bt_private_component_get_user_data(priv_comp); + bt_put(priv_comp); assert(py_comp); /* Return new reference */ @@ -82,5 +66,5 @@ static PyObject *bt_py3_get_component_from_notif_iter( } %} -PyObject *bt_py3_get_component_from_notif_iter( - struct bt_notification_iterator *iter); +PyObject *bt_py3_get_user_component_from_user_notif_iter( + struct bt_private_notification_iterator *priv_notif_iter); diff --git a/bindings/python/bt2/native_btpacket.i b/bindings/python/bt2/native_btpacket.i index e9181e41..68877110 100644 --- a/bindings/python/bt2/native_btpacket.i +++ b/bindings/python/bt2/native_btpacket.i @@ -22,10 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_packet; diff --git a/bindings/python/bt2/native_btplugin.i b/bindings/python/bt2/native_btplugin.i index 95164f1b..5df3fcf9 100644 --- a/bindings/python/bt2/native_btplugin.i +++ b/bindings/python/bt2/native_btplugin.i @@ -22,115 +22,42 @@ * THE SOFTWARE. */ -%{ -#include -#include -%} - /* Types */ struct bt_plugin; +struct bt_plugin_set; /* Status */ enum bt_plugin_status { - BT_PLUGIN_STATUS_OK = 0, - BT_PLUGIN_STATUS_ERROR = -1, - BT_PLUGIN_STATUS_NOMEM = -4, + BT_PLUGIN_STATUS_OK = 0, + BT_PLUGIN_STATUS_ERROR = -1, + BT_PLUGIN_STATUS_NOMEM = -4, }; -/* Functions */ +/* Plugin functions */ +struct bt_plugin *bt_plugin_find(const char *plugin_name); +struct bt_component_class *bt_plugin_find_component_class( + const char *plugin_name, const char *component_class_name, + enum bt_component_class_type component_class_type); +struct bt_plugin_set *bt_plugin_create_all_from_file(const char *path); +struct bt_plugin_set *bt_plugin_create_all_from_dir(const char *path, + int recurse); +struct bt_plugin_set *bt_plugin_create_all_from_static(void); const char *bt_plugin_get_name(struct bt_plugin *plugin); const char *bt_plugin_get_author(struct bt_plugin *plugin); const char *bt_plugin_get_license(struct bt_plugin *plugin); const char *bt_plugin_get_description(struct bt_plugin *plugin); const char *bt_plugin_get_path(struct bt_plugin *plugin); enum bt_plugin_status bt_plugin_get_version(struct bt_plugin *plugin, - unsigned int *OUTPUT, unsigned int *OUTPUT, unsigned int *OUTPUT, - const char **BTOUTSTR); -int bt_plugin_get_component_class_count(struct bt_plugin *plugin); -struct bt_component_class *bt_plugin_get_component_class( - struct bt_plugin *plugin, size_t index); + unsigned int *OUTPUTINIT, unsigned int *OUTPUTINIT, + unsigned int *OUTPUTINIT, const char **BTOUTSTR); +int64_t bt_plugin_get_component_class_count(struct bt_plugin *plugin); +struct bt_component_class *bt_plugin_get_component_class_by_index( + struct bt_plugin *plugin, uint64_t index); struct bt_component_class *bt_plugin_get_component_class_by_name_and_type( struct bt_plugin *plugin, const char *name, enum bt_component_class_type type); -struct bt_plugin *bt_plugin_create_from_name(const char *plugin_name); - -%{ -static PyObject *bt_py3_plugin_ptrs_list_from_bt_plugins(struct bt_plugin **plugins) -{ - PyObject *py_plugin_ptrs = NULL; - struct bt_plugin **plugin_at; - - if (!plugins) { - goto error; - } - - py_plugin_ptrs = PyList_New(0); - if (!py_plugin_ptrs) { - goto error; - } - - plugin_at = plugins; - - while (*plugin_at) { - struct bt_plugin *plugin = *plugin_at; - PyObject *py_plugin_ptr; - int ret; - - py_plugin_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(plugin), - SWIGTYPE_p_bt_plugin, 0); - if (!py_plugin_ptr) { - goto error; - } - - ret = PyList_Append(py_plugin_ptrs, py_plugin_ptr); - Py_DECREF(py_plugin_ptr); - if (ret < 0) { - goto error; - } - - plugin_at++; - } - - goto end; - -error: - Py_XDECREF(py_plugin_ptrs); - py_plugin_ptrs = Py_None; - Py_INCREF(py_plugin_ptrs); - - if (plugins) { - /* - * Put existing plugin references since they are not - * moved to the caller. - */ - plugin_at = plugins; - - while (*plugin_at) { - bt_put(*plugin_at); - plugin_at++; - } - } - -end: - PyErr_Clear(); - free(plugins); - return py_plugin_ptrs; -} - -static PyObject *bt_py3_plugin_create_all_from_file(const char *path) -{ - return bt_py3_plugin_ptrs_list_from_bt_plugins( - bt_plugin_create_all_from_file(path)); -} - -static PyObject *bt_py3_plugin_create_all_from_dir(const char *path, - bool recurse) -{ - return bt_py3_plugin_ptrs_list_from_bt_plugins( - bt_plugin_create_all_from_dir(path, recurse)); -} -%} -PyObject *bt_py3_plugin_create_all_from_file(const char *path); -PyObject *bt_py3_plugin_create_all_from_dir(const char *path, - bool recurse); +/* Plugin set functions */ +int64_t bt_plugin_set_get_plugin_count(struct bt_plugin_set *plugin_set); +struct bt_plugin *bt_plugin_set_get_plugin(struct bt_plugin_set *plugin_set, + uint64_t index); diff --git a/bindings/python/bt2/native_btport.i b/bindings/python/bt2/native_btport.i new file mode 100644 index 00000000..e88de583 --- /dev/null +++ b/bindings/python/bt2/native_btport.i @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +/* Type */ +struct bt_port; +struct bt_private_port; + +/* Status */ +enum bt_port_status { + BT_PORT_STATUS_OK = 0, + BT_PORT_STATUS_ERROR = -1, + BT_PORT_STATUS_INVALID = -2, +}; + +/* Port type */ +enum bt_port_type { + BT_PORT_TYPE_INPUT = 0, + BT_PORT_TYPE_OUTPUT = 1, + BT_PORT_TYPE_UNKOWN = -1, +}; + +/* Functions (public) */ +const char *bt_port_get_name(struct bt_port *port); +enum bt_port_type bt_port_get_type(struct bt_port *port); +struct bt_connection *bt_port_get_connection(struct bt_port *port); +struct bt_component *bt_port_get_component(struct bt_port *port); +enum bt_port_status bt_port_disconnect(struct bt_port *port); +int bt_port_is_connected(struct bt_port *port); + +/* Functions (private) */ +struct bt_port *bt_port_from_private_port(struct bt_private_port *private_port); +struct bt_private_connection *bt_private_port_get_private_connection( + struct bt_private_port *private_port); +struct bt_private_component *bt_private_port_get_private_component( + struct bt_private_port *private_port); +enum bt_port_status bt_private_port_remove_from_component( + struct bt_private_port *private_port); +void *bt_private_port_get_user_data( + struct bt_private_port *private_port); diff --git a/bindings/python/bt2/native_btref.i b/bindings/python/bt2/native_btref.i index 1d7a2d31..dacf916a 100644 --- a/bindings/python/bt2/native_btref.i +++ b/bindings/python/bt2/native_btref.i @@ -22,10 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Functions */ void *bt_get(void *obj); void bt_put(void *obj); diff --git a/bindings/python/bt2/native_btstream.i b/bindings/python/bt2/native_btstream.i index 91d4dd82..39747bc7 100644 --- a/bindings/python/bt2/native_btstream.i +++ b/bindings/python/bt2/native_btstream.i @@ -22,10 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_stream; @@ -33,6 +29,10 @@ struct bt_ctf_stream; struct bt_ctf_stream *bt_ctf_stream_create( struct bt_ctf_stream_class *stream_class, const char *name); +struct bt_ctf_stream *bt_ctf_stream_create_with_id( + struct bt_ctf_stream_class *stream_class, + const char *name, uint64_t id); const char *bt_ctf_stream_get_name(struct bt_ctf_stream *stream); +int64_t bt_ctf_stream_get_id(struct bt_ctf_stream *stream); struct bt_ctf_stream_class *bt_ctf_stream_get_class( struct bt_ctf_stream *stream); diff --git a/bindings/python/bt2/native_btstreamclass.i b/bindings/python/bt2/native_btstreamclass.i index 2ba6024c..82440571 100644 --- a/bindings/python/bt2/native_btstreamclass.i +++ b/bindings/python/bt2/native_btstreamclass.i @@ -22,14 +22,12 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_stream_class; /* Functions */ +struct bt_ctf_stream_class *bt_ctf_stream_class_create_empty( + const char *name); struct bt_ctf_stream_class *bt_ctf_stream_class_create(const char *name); struct bt_ctf_trace *bt_ctf_stream_class_get_trace( struct bt_ctf_stream_class *stream_class); @@ -40,7 +38,7 @@ int bt_ctf_stream_class_set_name( int64_t bt_ctf_stream_class_get_id( struct bt_ctf_stream_class *stream_class); int bt_ctf_stream_class_set_id( - struct bt_ctf_stream_class *stream_class, uint32_t id); + struct bt_ctf_stream_class *stream_class, uint64_t id); struct bt_ctf_field_type *bt_ctf_stream_class_get_packet_context_type( struct bt_ctf_stream_class *stream_class); int bt_ctf_stream_class_set_packet_context_type( @@ -58,14 +56,12 @@ bt_ctf_stream_class_get_event_context_type( int bt_ctf_stream_class_set_event_context_type( struct bt_ctf_stream_class *stream_class, struct bt_ctf_field_type *event_context_type); -int bt_ctf_stream_class_get_event_class_count( +int64_t bt_ctf_stream_class_get_event_class_count( struct bt_ctf_stream_class *stream_class); -struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class( - struct bt_ctf_stream_class *stream_class, int index); -struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_name( - struct bt_ctf_stream_class *stream_class, const char *name); +struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_index( + struct bt_ctf_stream_class *stream_class, uint64_t index); struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_id( - struct bt_ctf_stream_class *stream_class, uint32_t id); + struct bt_ctf_stream_class *stream_class, uint64_t id); int bt_ctf_stream_class_add_event_class( struct bt_ctf_stream_class *stream_class, struct bt_ctf_event_class *event_class); diff --git a/bindings/python/bt2/native_bttrace.i b/bindings/python/bt2/native_bttrace.i index 523acf7a..8947ae07 100644 --- a/bindings/python/bt2/native_bttrace.i +++ b/bindings/python/bt2/native_bttrace.i @@ -22,51 +22,105 @@ * THE SOFTWARE. */ -%{ -#include -%} - /* Type */ struct bt_ctf_trace; /* Functions */ struct bt_ctf_trace *bt_ctf_trace_create(void); -const char *bt_ctf_trace_get_name(struct bt_ctf_trace *trace); -int bt_ctf_trace_set_name(struct bt_ctf_trace *trace, +const char *bt_ctf_trace_get_name(struct bt_ctf_trace *trace_class); +int bt_ctf_trace_set_name(struct bt_ctf_trace *trace_class, const char *name); -enum bt_ctf_byte_order bt_ctf_trace_get_byte_order( - struct bt_ctf_trace *trace); -int bt_ctf_trace_set_byte_order(struct bt_ctf_trace *trace, - enum bt_ctf_byte_order byte_order); -int bt_ctf_trace_get_environment_field_count( - struct bt_ctf_trace *trace); +enum bt_ctf_byte_order bt_ctf_trace_get_native_byte_order( + struct bt_ctf_trace *trace_class); +int bt_ctf_trace_set_native_byte_order(struct bt_ctf_trace *trace_class, + enum bt_ctf_byte_order native_byte_order); +BTUUID bt_ctf_trace_get_uuid( + struct bt_ctf_trace *trace_class); +int bt_ctf_trace_set_uuid(struct bt_ctf_trace *trace_class, + BTUUID uuid); +int64_t bt_ctf_trace_get_environment_field_count( + struct bt_ctf_trace *trace_class); const char * -bt_ctf_trace_get_environment_field_name(struct bt_ctf_trace *trace, - int index); +bt_ctf_trace_get_environment_field_name_by_index( + struct bt_ctf_trace *trace_class, uint64_t index); struct bt_value * -bt_ctf_trace_get_environment_field_value(struct bt_ctf_trace *trace, - int index); +bt_ctf_trace_get_environment_field_value_by_index(struct bt_ctf_trace *trace_class, + uint64_t index); struct bt_value * bt_ctf_trace_get_environment_field_value_by_name( - struct bt_ctf_trace *trace, const char *name); + struct bt_ctf_trace *trace_class, const char *name); int bt_ctf_trace_set_environment_field( - struct bt_ctf_trace *trace, const char *name, + struct bt_ctf_trace *trace_class, const char *name, struct bt_value *value); struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_type( - struct bt_ctf_trace *trace); -int bt_ctf_trace_set_packet_header_type(struct bt_ctf_trace *trace, + struct bt_ctf_trace *trace_class); +int bt_ctf_trace_set_packet_header_type(struct bt_ctf_trace *trace_class, struct bt_ctf_field_type *packet_header_type); -int bt_ctf_trace_get_clock_class_count(struct bt_ctf_trace *trace); -struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class( - struct bt_ctf_trace *trace, int index); +int64_t bt_ctf_trace_get_clock_class_count( + struct bt_ctf_trace *trace_class); +struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index( + struct bt_ctf_trace *trace_class, uint64_t index); struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name( - struct bt_ctf_trace *trace, const char *name); -int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace, + struct bt_ctf_trace *trace_class, const char *name); +int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace_class, struct bt_ctf_clock_class *clock_class); -int bt_ctf_trace_get_stream_class_count(struct bt_ctf_trace *trace); -struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class( - struct bt_ctf_trace *trace, int index); +int64_t bt_ctf_trace_get_stream_class_count( + struct bt_ctf_trace *trace_class); +struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_index( + struct bt_ctf_trace *trace_class, uint64_t index); struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_id( - struct bt_ctf_trace *trace, uint32_t id); -int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace, + struct bt_ctf_trace *trace_class, uint64_t id); +int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace_class, struct bt_ctf_stream_class *stream_class); +int64_t bt_ctf_trace_get_stream_count(struct bt_ctf_trace *trace_class); +struct bt_ctf_stream *bt_ctf_trace_get_stream_by_index( + struct bt_ctf_trace *trace_class, uint64_t index); +int bt_ctf_trace_is_static(struct bt_ctf_trace *trace_class); +int bt_ctf_trace_set_is_static(struct bt_ctf_trace *trace_class); + +/* Helper functions for Python */ +%{ +void trace_is_static_listener(struct bt_ctf_trace *trace, void *py_callable) +{ + PyObject *py_trace_ptr = NULL; + PyObject *py_res = NULL; + + py_trace_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(trace), + SWIGTYPE_p_bt_ctf_trace, 0); + if (!py_trace_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_res = PyObject_CallFunction(py_callable, "(O)", py_trace_ptr); + assert(py_res == Py_None); + Py_DECREF(py_trace_ptr); + Py_DECREF(py_res); +} + +void trace_listener_removed(struct bt_ctf_trace *trace, void *py_callable) +{ + assert(py_callable); + Py_DECREF(py_callable); +} + +static int bt_py3_trace_add_is_staitc_listener(unsigned long long trace_addr, + PyObject *py_callable) +{ + struct bt_ctf_trace *trace = (void *) trace_addr; + int ret = 0; + + assert(trace); + assert(py_callable); + ret = bt_ctf_trace_add_is_static_listener(trace, + trace_is_static_listener, trace_listener_removed, py_callable); + if (ret >= 0) { + Py_INCREF(py_callable); + } + + return ret; +} +%} + +int bt_py3_trace_add_is_staitc_listener(unsigned long long trace_addr, + PyObject *py_callable); diff --git a/bindings/python/bt2/native_btvalues.i b/bindings/python/bt2/native_btvalues.i index 57fdae7d..6563bced 100644 --- a/bindings/python/bt2/native_btvalues.i +++ b/bindings/python/bt2/native_btvalues.i @@ -22,13 +22,6 @@ * THE SOFTWARE. */ -%{ -#include -%} - -/* For uint*_t/int*_t */ -%include "stdint.i" - /* Remove prefix from `bt_value_null` */ %rename(value_null) bt_value_null; @@ -61,18 +54,18 @@ struct bt_value * const bt_value_null; /* Common functions */ enum bt_value_status bt_value_freeze(struct bt_value *object); -bool bt_value_is_frozen(const struct bt_value *object); +int bt_value_is_frozen(const struct bt_value *object); struct bt_value *bt_value_copy(const struct bt_value *object); -bool bt_value_compare(const struct bt_value *object_a, +int bt_value_compare(const struct bt_value *object_a, const struct bt_value *object_b); /* Boolean value object functions */ struct bt_value *bt_value_bool_create(void); -struct bt_value *bt_value_bool_create_init(bool val); +struct bt_value *bt_value_bool_create_init(int val); enum bt_value_status bt_value_bool_get( - const struct bt_value *bool_obj, bool *OUTPUT); + const struct bt_value *bool_obj, int *OUTPUT); enum bt_value_status bt_value_bool_set(struct bt_value *bool_obj, - bool val); + int val); /* Integer value object functions */ struct bt_value *bt_value_integer_create(void); @@ -113,18 +106,20 @@ struct bt_value *bt_value_map_create(void); int bt_value_map_size(const struct bt_value *map_obj); struct bt_value *bt_value_map_get(const struct bt_value *map_obj, const char *key); -bool bt_value_map_has_key(const struct bt_value *map_obj, +int bt_value_map_has_key(const struct bt_value *map_obj, const char *key); enum bt_value_status bt_value_map_insert( struct bt_value *map_obj, const char *key, struct bt_value *element_obj); +struct bt_value *bt_value_map_extend(struct bt_value *base_map_obj, + struct bt_value *extension_map_obj); %{ struct bt_value_map_get_keys_private_data { struct bt_value *keys; }; -static bool bt_value_map_get_keys_private_cb(const char *key, +static int bt_value_map_get_keys_private_cb(const char *key, struct bt_value *object, void *data) { enum bt_value_status status; @@ -132,10 +127,10 @@ static bool bt_value_map_get_keys_private_cb(const char *key, status = bt_value_array_append_string(priv_data->keys, key); if (status != BT_VALUE_STATUS_OK) { - return false; + return BT_FALSE; } - return true; + return BT_TRUE; } static struct bt_value *bt_value_map_get_keys_private( diff --git a/bindings/python/bt2/native_btversion.i b/bindings/python/bt2/native_btversion.i new file mode 100644 index 00000000..e368b9b3 --- /dev/null +++ b/bindings/python/bt2/native_btversion.i @@ -0,0 +1,33 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * 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. + */ + +%{ +#include +%} + +/* Version functions */ +int bt_version_get_major(void); +int bt_version_get_minor(void); +int bt_version_get_patch(void); +const char *bt_version_get_extra(void); diff --git a/bindings/python/bt2/notification.py b/bindings/python/bt2/notification.py index 66ed4cd0..2ad84878 100644 --- a/bindings/python/bt2/notification.py +++ b/bindings/python/bt2/notification.py @@ -21,9 +21,11 @@ # THE SOFTWARE. from bt2 import native_bt, object, utils +import bt2.clock_class_priority_map import bt2.packet import bt2.stream import bt2.event +import copy import bt2 @@ -41,113 +43,423 @@ class _Notification(object._Object): pass -class TraceEventNotification(_Notification): - def __init__(self, event): +class _CopyableNotification(_Notification): + def __copy__(self): + return self._copy(lambda obj: obj) + + def __deepcopy__(self, memo): + cpy = self._copy(copy.deepcopy) + memo[id(self)] = cpy + return cpy + + +class EventNotification(_CopyableNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_EVENT + + def __init__(self, event, cc_prio_map=None): utils._check_type(event, bt2.event._Event) - ptr = native_bt.notification_event_create(event._ptr) + + if cc_prio_map is not None: + utils._check_type(cc_prio_map, bt2.clock_class_priority_map.ClockClassPriorityMap) + cc_prio_map_ptr = cc_prio_map._ptr + else: + cc_prio_map_ptr = None + + ptr = native_bt.notification_event_create(event._ptr, cc_prio_map_ptr) if ptr is None: - raise bt2.CreationError('cannot create trace event notification object') + raise bt2.CreationError('cannot create event notification object') super().__init__(ptr) @property def event(self): event_ptr = native_bt.notification_event_get_event(self._ptr) - utils._handle_ptr(event_ptr, "cannot get trace event notification object's event object") + assert(event_ptr) return bt2.event._create_from_ptr(event_ptr) + @property + def clock_class_priority_map(self): + cc_prio_map_ptr = native_bt.notification_event_get_clock_class_priority_map(self._ptr) + assert(cc_prio_map_ptr) + return bt2.clock_class_priority_map.ClockClassPriorityMap._create_from_ptr(cc_prio_map_ptr) + + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + self_props = ( + self.event, + self.clock_class_priority_map, + ) + other_props = ( + other.event, + other.clock_class_priority_map, + ) + return self_props == other_props + + def _copy(self, copy_func): + # We can always use references here because those properties are + # frozen anyway if they are part of a notification. Since the + # user cannot modify them after copying the notification, it's + # useless to copy/deep-copy them. + return EventNotification(self.event, self.clock_class_priority_map) + + +class PacketBeginningNotification(_CopyableNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_PACKET_BEGIN -class BeginningOfPacketNotification(_Notification): def __init__(self, packet): utils._check_type(packet, bt2.packet._Packet) ptr = native_bt.notification_packet_begin_create(packet._ptr) if ptr is None: - raise bt2.CreationError('cannot create beginning of packet notification object') + raise bt2.CreationError('cannot create packet beginning notification object') super().__init__(ptr) @property def packet(self): packet_ptr = native_bt.notification_packet_begin_get_packet(self._ptr) - utils._handle_ptr(packet_ptr, "cannot get beginning of packet notification object's packet object") + assert(packet_ptr) return bt2.packet._Packet._create_from_ptr(packet_ptr) + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + return self.packet == other.packet + + def _copy(self, copy_func): + # We can always use references here because those properties are + # frozen anyway if they are part of a notification. Since the + # user cannot modify them after copying the notification, it's + # useless to copy/deep-copy them. + return PacketBeginningNotification(self.packet) + + +class PacketEndNotification(_CopyableNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_PACKET_END -class EndOfPacketNotification(_Notification): def __init__(self, packet): utils._check_type(packet, bt2.packet._Packet) ptr = native_bt.notification_packet_end_create(packet._ptr) if ptr is None: - raise bt2.CreationError('cannot create end of packet notification object') + raise bt2.CreationError('cannot create packet end notification object') super().__init__(ptr) @property def packet(self): packet_ptr = native_bt.notification_packet_end_get_packet(self._ptr) - utils._handle_ptr(packet_ptr, "cannot get end of packet notification object's packet object") + assert(packet_ptr) return bt2.packet._Packet._create_from_ptr(packet_ptr) + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + return self.packet == other.packet + + def _copy(self, copy_func): + # We can always use references here because those properties are + # frozen anyway if they are part of a notification. Since the + # user cannot modify them after copying the notification, it's + # useless to copy/deep-copy them. + return PacketEndNotification(self.packet) + + +class StreamBeginningNotification(_CopyableNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_STREAM_BEGIN -class EndOfStreamNotification(_Notification): def __init__(self, stream): utils._check_type(stream, bt2.stream._Stream) - ptr = native_bt.notification_stream_end_create(stream._ptr) + ptr = native_bt.notification_stream_begin_create(stream._ptr) if ptr is None: - raise bt2.CreationError('cannot create end of stream notification object') + raise bt2.CreationError('cannot create stream beginning notification object') super().__init__(ptr) @property def stream(self): - stream_ptr = native_bt.notification_stream_end_get_stream(self._ptr) - utils._handle_ptr(stream_ptr, "cannot get end of stream notification object's stream object") + stream_ptr = native_bt.notification_stream_begin_get_stream(self._ptr) + assert(stream_ptr) return bt2.stream._create_from_ptr(stream_ptr) + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + return self.stream == other.stream + + def _copy(self, copy_func): + # We can always use references here because those properties are + # frozen anyway if they are part of a notification. Since the + # user cannot modify them after copying the notification, it's + # useless to copy/deep-copy them. + return StreamBeginningNotification(self.stream) + + +class StreamEndNotification(_CopyableNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_STREAM_END -class NewTraceNotification(_Notification): - def __init__(self, trace): - utils._check_type(trace, bt2.Trace) - ptr = native_bt.notification_new_trace_create(trace._ptr) + def __init__(self, stream): + utils._check_type(stream, bt2.stream._Stream) + ptr = native_bt.notification_stream_end_create(stream._ptr) if ptr is None: - raise bt2.CreationError('cannot create new trace notification object') + raise bt2.CreationError('cannot create stream end notification object') super().__init__(ptr) @property - def trace(self): - trace_ptr = native_bt.notification_new_trace_get_trace(self._ptr) - utils._handle_ptr(trace_ptr, "cannot get new trace notification object's trace object") - return bt2.trace._create_from_ptr(trace_ptr) + def stream(self): + stream_ptr = native_bt.notification_stream_end_get_stream(self._ptr) + assert(stream_ptr) + return bt2.stream._create_from_ptr(stream_ptr) + + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + return self.stream == other.stream + + def _copy(self, copy_func): + # We can always use references here because those properties are + # frozen anyway if they are part of a notification. Since the + # user cannot modify them after copying the notification, it's + # useless to copy/deep-copy them. + return StreamEndNotification(self.stream) + + +class InactivityNotification(_CopyableNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_INACTIVITY + def __init__(self, cc_prio_map=None): + if cc_prio_map is not None: + utils._check_type(cc_prio_map, bt2.clock_class_priority_map.ClockClassPriorityMap) + cc_prio_map_ptr = cc_prio_map._ptr + else: + cc_prio_map_ptr = None -class NewStreamClassNotification(_Notification): - def __init__(self, stream_class): - utils._check_type(stream_class, bt2.StreamClass) - ptr = native_bt.notification_new_stream_class_create(stream_class._ptr) + ptr = native_bt.notification_inactivity_create(cc_prio_map_ptr) if ptr is None: - raise bt2.CreationError('cannot create new stream class notification object') + raise bt2.CreationError('cannot create inactivity notification object') super().__init__(ptr) @property - def stream_class(self): - stream_class_ptr = native_bt.notification_new_stream_class_get_stream_class(self._ptr) - utils._handle_ptr(stream_class_ptr, "cannot get new stream class notification object's stream class object") - return bt2.stream_class.StreamClass._create_from_ptr(stream_class_ptr) + def clock_class_priority_map(self): + cc_prio_map_ptr = native_bt.notification_inactivity_get_clock_class_priority_map(self._ptr) + assert(cc_prio_map_ptr) + return bt2.clock_class_priority_map.ClockClassPriorityMap._create_from_ptr(cc_prio_map_ptr) + + def clock_value(self, clock_class): + utils._check_type(clock_class, bt2.ClockClass) + clock_value_ptr = native_bt.notification_inactivity_get_clock_value(self._ptr, + clock_class._ptr) + + if clock_value_ptr is None: + return + + clock_value = bt2.clock_class._create_clock_value_from_ptr(clock_value_ptr) + return clock_value + + def add_clock_value(self, clock_value): + utils._check_type(clock_value, bt2.clock_class._ClockValue) + ret = native_bt.notification_inactivity_set_clock_value(self._ptr, + clock_value._ptr) + utils._handle_ret(ret, "cannot set inactivity notification object's clock value") + + def _get_clock_values(self): + clock_values = {} + + for clock_class in self.clock_class_priority_map: + clock_value = self.clock_value(clock_class) + + if clock_value is None: + continue + + clock_values[clock_class] = clock_value + + return clock_values + + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + self_props = ( + self.clock_class_priority_map, + self._get_clock_values(), + ) + other_props = ( + other.clock_class_priority_map, + other._get_clock_values(), + ) + return self_props == other_props + + def __copy__(self): + cpy = InactivityNotification(self.clock_class_priority_map) + + for clock_class in self.clock_class_priority_map: + clock_value = self.clock_value(clock_class) + + if clock_value is None: + continue + + cpy.add_clock_value(clock_value) + + return cpy + + def __deepcopy__(self, memo): + cc_prio_map_cpy = copy.deepcopy(self.clock_class_priority_map) + cpy = InactivityNotification(cc_prio_map_cpy) + + # copy clock values + for orig_clock_class in self.clock_class_priority_map: + orig_clock_value = self.clock_value(orig_clock_class) + + if orig_clock_value is None: + continue + + # find equivalent, copied clock class in CC priority map copy + for cpy_clock_class in cc_prio_map_cpy: + if cpy_clock_class == orig_clock_class: + break + + # create copy of clock value from copied clock class + clock_value_cpy = cpy_clock_class(orig_clock_value.cycles) + + # set copied clock value in notification copy + cpy.add_clock_value(clock_value_cpy) + + memo[id(self)] = cpy + return cpy + + +class _DiscardedElementsNotification(_Notification): + def __eq__(self, other): + if type(other) is not type(self): + return False + + if self.addr == other.addr: + return True + + self_props = ( + self.count, + self.stream, + self.beginning_clock_value, + self.end_clock_value, + ) + other_props = ( + other.count, + other.stream, + other.beginning_clock_value, + other.end_clock_value, + ) + return self_props == other_props + + +class _DiscardedPacketsNotification(_DiscardedElementsNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_DISCARDED_PACKETS + + @property + def count(self): + count = native_bt.notification_discarded_packets_get_count(self._ptr) + assert(count >= 0) + return count + + @property + def stream(self): + stream_ptr = native_bt.notification_discarded_packets_get_stream(self._ptr) + assert(stream_ptr) + return bt2.stream._create_from_ptr(stream_ptr) + + @property + def beginning_clock_value(self): + clock_value_ptr = native_bt.notification_discarded_packets_get_begin_clock_value(self._ptr) + + if clock_value_ptr is None: + return + + clock_value = bt2.clock_class._create_clock_value_from_ptr(clock_value_ptr) + return clock_value + + @property + def end_clock_value(self): + clock_value_ptr = native_bt.notification_discarded_packets_get_end_clock_value(self._ptr) + + if clock_value_ptr is None: + return + + clock_value = bt2.clock_class._create_clock_value_from_ptr(clock_value_ptr) + return clock_value + + +class _DiscardedEventsNotification(_DiscardedElementsNotification): + _TYPE = native_bt.NOTIFICATION_TYPE_DISCARDED_EVENTS + + @property + def count(self): + count = native_bt.notification_discarded_events_get_count(self._ptr) + assert(count >= 0) + return count + + @property + def stream(self): + stream_ptr = native_bt.notification_discarded_events_get_stream(self._ptr) + assert(stream_ptr) + return bt2.stream._create_from_ptr(stream_ptr) + + @property + def beginning_clock_value(self): + clock_value_ptr = native_bt.notification_discarded_events_get_begin_clock_value(self._ptr) + + if clock_value_ptr is None: + return + + clock_value = bt2.clock_class._create_clock_value_from_ptr(clock_value_ptr) + return clock_value + + @property + def end_clock_value(self): + clock_value_ptr = native_bt.notification_discarded_events_get_end_clock_value(self._ptr) + + if clock_value_ptr is None: + return + + clock_value = bt2.clock_class._create_clock_value_from_ptr(clock_value_ptr) + return clock_value _NOTIF_TYPE_TO_CLS = { - native_bt.NOTIFICATION_TYPE_EVENT: TraceEventNotification, - native_bt.NOTIFICATION_TYPE_PACKET_BEGIN: BeginningOfPacketNotification, - native_bt.NOTIFICATION_TYPE_PACKET_END: EndOfPacketNotification, - native_bt.NOTIFICATION_TYPE_STREAM_END: EndOfStreamNotification, - native_bt.NOTIFICATION_TYPE_NEW_TRACE: NewTraceNotification, - native_bt.NOTIFICATION_TYPE_NEW_STREAM_CLASS: NewStreamClassNotification, + native_bt.NOTIFICATION_TYPE_EVENT: EventNotification, + native_bt.NOTIFICATION_TYPE_PACKET_BEGIN: PacketBeginningNotification, + native_bt.NOTIFICATION_TYPE_PACKET_END: PacketEndNotification, + native_bt.NOTIFICATION_TYPE_STREAM_BEGIN: StreamBeginningNotification, + native_bt.NOTIFICATION_TYPE_STREAM_END: StreamEndNotification, + native_bt.NOTIFICATION_TYPE_INACTIVITY: InactivityNotification, + native_bt.NOTIFICATION_TYPE_DISCARDED_PACKETS: _DiscardedPacketsNotification, + native_bt.NOTIFICATION_TYPE_DISCARDED_EVENTS: _DiscardedEventsNotification, } diff --git a/bindings/python/bt2/notification_iterator.py b/bindings/python/bt2/notification_iterator.py index e0d0b0c7..39b6bf0e 100644 --- a/bindings/python/bt2/notification_iterator.py +++ b/bindings/python/bt2/notification_iterator.py @@ -27,62 +27,54 @@ import bt2.component import bt2 -class NotificationIteratorSeekOrigin: - BEGIN = native_bt.NOTIFICATION_ITERATOR_SEEK_ORIGIN_BEGIN - CURRENT = native_bt.NOTIFICATION_ITERATOR_SEEK_ORIGIN_CURRENT - END = native_bt.NOTIFICATION_ITERATOR_SEEK_ORIGIN_END - - -class _GenericNotificationIteratorMethods(collections.abc.Iterator): - @property - def notification(self): - notif_ptr = native_bt.notification_iterator_get_notification(self._ptr) - utils._handle_ptr(notif_ptr, "cannot get notification iterator object's current notification object") - return bt2.notification._create_from_ptr(notif_ptr) - +class _NotificationIterator(collections.abc.Iterator): def _handle_status(self, status, gen_error_msg): - if status == native_bt.NOTIFICATION_ITERATOR_STATUS_END: + if status == native_bt.NOTIFICATION_ITERATOR_STATUS_CANCELED: + raise bt2.NotificationIteratorCanceled + elif status == native_bt.NOTIFICATION_ITERATOR_STATUS_AGAIN: + raise bt2.TryAgain + elif status == native_bt.NOTIFICATION_ITERATOR_STATUS_END: raise bt2.Stop elif status == native_bt.NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED: raise bt2.UnsupportedFeature elif status < 0: raise bt2.Error(gen_error_msg) - def next(self): - status = native_bt.notification_iterator_next(self._ptr) - self._handle_status(status, - 'unexpected error: cannot go to the next notification') - def __next__(self): - self.next() - return self.notification - - def seek_to_time(self, origin, time): - utils._check_int64(origin) - utils._check_int64(time) - status = native_bt.notification_iterator_seek_time(self._ptr, origin, - time) - self._handle_status(status, - 'unexpected error: cannot seek notification iterator to time') + raise NotImplementedError -class _GenericNotificationIterator(object._Object, _GenericNotificationIteratorMethods): +class _GenericNotificationIterator(object._Object, _NotificationIterator): @property def component(self): comp_ptr = native_bt.notification_iterator_get_component(self._ptr) - utils._handle_ptr(comp_ptr, "cannot get notification iterator object's component object") + assert(comp_ptr) return bt2.component._create_generic_component_from_ptr(comp_ptr) + def _get_notif(self): + notif_ptr = native_bt.notification_iterator_get_notification(self._ptr) + utils._handle_ptr(notif_ptr, "cannot get notification iterator object's current notification object") + return bt2.notification._create_from_ptr(notif_ptr) + + def _next(self): + status = native_bt.notification_iterator_next(self._ptr) + self._handle_status(status, + 'unexpected error: cannot advance the notification iterator') + + def __next__(self): + self._next() + return self._get_notif() -class UserNotificationIterator(_GenericNotificationIteratorMethods): + +class _UserNotificationIterator(_NotificationIterator): def __new__(cls, ptr): - # User iterator objects are always created by the BT system, + # User iterator objects are always created by the native side, # that is, never instantiated directly by Python code. # - # The system calls this, then manually calls self.__init__() - # without the `ptr` argument. The user has access to - # self.component during this call, thanks to this self._ptr - # argument being set. + # The native code calls this, then manually calls + # self.__init__() without the `ptr` argument. The user has + # access to self.component during this call, thanks to this + # self._ptr argument being set. # # self._ptr is NOT owned by this object here, so there's nothing # to do in __del__(). @@ -94,22 +86,30 @@ class UserNotificationIterator(_GenericNotificationIteratorMethods): pass @property - def component(self): - return native_bt.py3_get_component_from_notif_iter(self._ptr) + def _component(self): + return native_bt.py3_get_user_component_from_user_notif_iter(self._ptr) @property def addr(self): return int(self._ptr) - def _destroy(self): + def _finalize(self): pass - def _get_from_bt(self): - # this can raise anything: it's catched by the system - notif = self._get() + def __next__(self): + raise bt2.Stop + + def _next_from_native(self): + # this can raise anything: it's catched by the native part + try: + notif = next(self) + except StopIteration: + raise bt2.Stop + except: + raise + utils._check_type(notif, bt2.notification._Notification) - # steal the underlying native notification object for the caller - notif_ptr = notif._ptr - notif._ptr = None - return int(notif_ptr) + # take a new reference for the native part + notif._get() + return int(notif._ptr) diff --git a/bindings/python/bt2/object.py b/bindings/python/bt2/object.py index ef574e12..774027cb 100644 --- a/bindings/python/bt2/object.py +++ b/bindings/python/bt2/object.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -44,6 +44,7 @@ class _Object: def __del__(self): ptr = getattr(self, '_ptr', None) native_bt.put(ptr) + self._ptr = None def __repr__(self): return '<{}.{} object @ {}>'.format(self.__class__.__module__, @@ -51,6 +52,14 @@ class _Object: hex(self.addr)) +class _PrivateObject: + def __del__(self): + pub_ptr = getattr(self, '_pub_ptr', None) + native_bt.put(pub_ptr) + self._pub_ptr = None + super().__del__() + + class _Freezable(metaclass=abc.ABCMeta): @property def is_frozen(self): diff --git a/bindings/python/bt2/packet.py b/bindings/python/bt2/packet.py index 9a9d79b7..1fba03ca 100644 --- a/bindings/python/bt2/packet.py +++ b/bindings/python/bt2/packet.py @@ -32,7 +32,7 @@ class _Packet(object._Object): @property def stream(self): stream_ptr = native_bt.ctf_packet_get_stream(self._ptr) - utils._handle_ptr(stream_ptr, "cannot get packet object's stream object") + assert(stream_ptr) return bt2.stream._Stream._create_from_ptr(stream_ptr) @property diff --git a/bindings/python/bt2/plugin.py b/bindings/python/bt2/plugin.py index 920eb636..a37e7631 100644 --- a/bindings/python/bt2/plugin.py +++ b/bindings/python/bt2/plugin.py @@ -23,47 +23,51 @@ from bt2 import native_bt, object, utils import collections.abc import bt2.component +import os.path import bt2 -def _plugin_ptrs_to_plugins(plugin_ptrs): - plugins = [] - - for plugin_ptr in plugin_ptrs: - plugin = _Plugin._create_from_ptr(plugin_ptr) - plugins.append(plugin) +def find_plugins(path, recurse=True): + utils._check_str(path) + utils._check_bool(recurse) + plugin_set_ptr = None - return plugins + if os.path.isfile(path): + plugin_set_ptr = native_bt.plugin_create_all_from_file(path) + elif os.path.isdir(path): + plugin_set_ptr = native_bt.plugin_create_all_from_dir(path, int(recurse)) + if plugin_set_ptr is None: + return -def create_plugins_from_file(path): - utils._check_str(path) - plugin_ptrs = native_bt.py3_plugin_create_all_from_file(path) + return _PluginSet._create_from_ptr(plugin_set_ptr) - if plugin_ptrs is None: - raise bt2.Error('cannot get plugin objects from file') - return _plugin_ptrs_to_plugins(plugin_ptrs) +def find_plugin(name): + utils._check_str(name) + ptr = native_bt.plugin_find(name) + if ptr is None: + return -def create_plugins_from_dir(path, recurse=True): - utils._check_str(path) - plugin_ptrs = native_bt.py3_plugin_create_all_from_dir(path, recurse) + return _Plugin._create_from_ptr(ptr) - if plugin_ptrs is None: - raise bt2.Error('cannot get plugin objects from directory') - - return _plugin_ptrs_to_plugins(plugin_ptrs) +class _PluginSet(object._Object, collections.abc.Sequence): + def __len__(self): + count = native_bt.plugin_set_get_plugin_count(self._ptr) + assert(count >= 0) + return count -def create_plugin_from_name(name): - utils._check_str(name) - plugin_ptr = native_bt.plugin_create_from_name(name) + def __getitem__(self, index): + utils._check_uint64(index) - if plugin_ptr is None: - raise bt2.NoSuchPluginError(name) + if index >= len(self): + raise IndexError - return _Plugin._create_from_ptr(plugin_ptr) + plugin_ptr = native_bt.plugin_set_get_plugin(self._ptr, index) + assert(plugin_ptr) + return _Plugin._create_from_ptr(plugin_ptr) class _PluginVersion: @@ -98,11 +102,79 @@ class _PluginVersion: return '{}.{}.{}{}'.format(self._major, self._minor, self._patch, extra) -class _Plugin(object._Object, collections.abc.Sequence): +class _PluginComponentClassesIterator(collections.abc.Iterator): + def __init__(self, plugin_comp_cls): + self._plugin_comp_cls = plugin_comp_cls + self._at = 0 + + def __next__(self): + plugin_ptr = self._plugin_comp_cls._plugin._ptr + comp_cls_type = self._plugin_comp_cls._comp_cls_type + total = native_bt.plugin_get_component_class_count(plugin_ptr) + + while True: + if self._at == total: + raise StopIteration + + comp_cls_ptr = native_bt.plugin_get_component_class_by_index(plugin_ptr, + self._at) + assert(comp_cls_ptr) + cc_type = native_bt.component_class_get_type(comp_cls_ptr) + self._at += 1 + + if cc_type == comp_cls_type: + break + + native_bt.put(comp_cls_ptr) + + name = native_bt.component_class_get_name(comp_cls_ptr) + native_bt.put(comp_cls_ptr) + assert(name is not None) + return name + + +class _PluginComponentClasses(collections.abc.Mapping): + def __init__(self, plugin, comp_cls_type): + self._plugin = plugin + self._comp_cls_type = comp_cls_type + + def __getitem__(self, key): + utils._check_str(key) + cc_ptr = native_bt.plugin_get_component_class_by_name_and_type(self._plugin._ptr, + key, + self._comp_cls_type) + + if cc_ptr is None: + raise KeyError(key) + + return bt2.component._create_generic_component_class_from_ptr(cc_ptr) + + def __len__(self): + count = 0 + total = native_bt.plugin_get_component_class_count(self._plugin._ptr) + + for at in range(total): + comp_cls_ptr = native_bt.plugin_get_component_class_by_index(self._plugin._ptr, + at) + assert(comp_cls_ptr) + cc_type = native_bt.component_class_get_type(comp_cls_ptr) + + if cc_type == self._comp_cls_type: + count += 1 + + native_bt.put(comp_cls_ptr) + + return count + + def __iter__(self): + return _PluginComponentClassesIterator(self) + + +class _Plugin(object._Object): @property def name(self): name = native_bt.plugin_get_name(self._ptr) - utils._handle_ptr(name, "cannot get plugin object's name") + assert(name is not None) return name @property @@ -130,50 +202,14 @@ class _Plugin(object._Object, collections.abc.Sequence): return _PluginVersion(major, minor, patch, extra) - def source_component_class(self, name): - utils._check_str(name) - cc_ptr = native_bt.plugin_get_component_class_by_name_and_type(self._ptr, - name, - native_bt.COMPONENT_CLASS_TYPE_SOURCE) - - if cc_ptr is None: - return - - return bt2.component._create_generic_component_class_from_ptr(cc_ptr) - - def filter_component_class(self, name): - utils._check_str(name) - cc_ptr = native_bt.plugin_get_component_class_by_name_and_type(self._ptr, - name, - native_bt.COMPONENT_CLASS_TYPE_FILTER) - - if cc_ptr is None: - return - - return bt2.component._create_generic_component_class_from_ptr(cc_ptr) - - def sink_component_class(self, name): - utils._check_str(name) - cc_ptr = native_bt.plugin_get_component_class_by_name_and_type(self._ptr, - name, - native_bt.COMPONENT_CLASS_TYPE_SINK) - - if cc_ptr is None: - return - - return bt2.component._create_generic_component_class_from_ptr(cc_ptr) - - def __len__(self): - count = native_bt.plugin_get_component_class_count(self._ptr) - utils._handle_ret(count, "cannot get plugin object's component class count") - return count - - def __getitem__(self, index): - utils._check_uint64(index) + @property + def source_component_classes(self): + return _PluginComponentClasses(self, native_bt.COMPONENT_CLASS_TYPE_SOURCE) - if index >= len(self): - raise IndexError + @property + def filter_component_classes(self): + return _PluginComponentClasses(self, native_bt.COMPONENT_CLASS_TYPE_FILTER) - cc_ptr = native_bt.plugin_get_component_class(self._ptr, index) - utils._handle_ptr(cc_ptr, "cannot get plugin object's component class object") - return bt2.component._create_generic_component_class_from_ptr(cc_ptr) + @property + def sink_component_classes(self): + return _PluginComponentClasses(self, native_bt.COMPONENT_CLASS_TYPE_SINK) diff --git a/bindings/python/bt2/port.py b/bindings/python/bt2/port.py new file mode 100644 index 00000000..ed168c4a --- /dev/null +++ b/bindings/python/bt2/port.py @@ -0,0 +1,160 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# 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. + +from bt2 import native_bt, object, utils +import collections.abc +import bt2.component +import bt2.connection +import copy +import bt2 + + +def _create_from_ptr(ptr): + port_type = native_bt.port_get_type(ptr) + + if port_type == native_bt.PORT_TYPE_INPUT: + cls = _InputPort + elif port_type == native_bt.PORT_TYPE_OUTPUT: + cls = _OutputPort + else: + raise bt2.Error('unknown port type: {}'.format(port_type)) + + return cls._create_from_ptr(ptr) + + +def _create_private_from_ptr(ptr): + pub_ptr = native_bt.port_from_private_port(ptr) + utils._handle_ptr(pub_ptr, 'cannot get port object from private port object') + port_type = native_bt.port_get_type(pub_ptr) + assert(port_type == native_bt.PORT_TYPE_INPUT or port_type == native_bt.PORT_TYPE_OUTPUT) + + if port_type == native_bt.PORT_TYPE_INPUT: + cls = _PrivateInputPort + elif port_type == native_bt.PORT_TYPE_OUTPUT: + cls = _PrivateOutputPort + + obj = cls._create_from_ptr(ptr) + obj._pub_ptr = pub_ptr + return obj + + +class _Port(object._Object): + @staticmethod + def _name(ptr): + name = native_bt.port_get_name(ptr) + assert(name is not None) + return name + + @staticmethod + def _disconnect(ptr): + status = native_bt.port_disconnect(ptr) + + if status < 0: + raise bt2.Error('cannot disconnect port') + + @property + def name(self): + return self._name(self._ptr) + + @property + def component(self): + comp_ptr = native_bt.port_get_component(self._ptr) + + if comp_ptr is None: + return + + return bt2.component._create_generic_component_from_ptr(comp_ptr) + + @property + def connection(self): + conn_ptr = native_bt.port_get_connection(self._ptr) + + if conn_ptr is None: + return + + return bt2.connection._Connection._create_from_ptr(conn_ptr) + + @property + def is_connected(self): + return self.connection is not None + + def disconnect(self): + self._disconnect(self._ptr) + + def __eq__(self, other): + if type(other) is not type(self): + return False + + return self.addr == other.addr + + +class _InputPort(_Port): + pass + + +class _OutputPort(_Port): + pass + + +class _PrivatePort(object._PrivateObject, _Port): + @property + def name(self): + return self._name(self._pub_ptr) + + @property + def component(self): + comp_ptr = native_bt.private_port_get_private_component(self._ptr) + + if comp_ptr is None: + return + + pub_comp_ptr = native_bt.component_from_private_component(comp_ptr) + assert(pub_comp_ptr) + comp = bt2.component._create_generic_component_from_ptr(pub_comp_ptr) + native_bt.put(comp_ptr) + return comp + + @property + def connection(self): + conn_ptr = native_bt.private_port_get_private_connection(self._ptr) + + if conn_ptr is None: + return + + return bt2.connection._create_private_from_ptr(conn_ptr) + + def remove_from_component(self): + status = native_bt.private_port_remove_from_component(self._ptr) + + if status < 0: + raise bt2.Error("cannot remove port from component") + + def disconnect(self): + self._disconnect(self._pub_ptr) + + +class _PrivateInputPort(_PrivatePort, _InputPort): + pass + + +class _PrivateOutputPort(_PrivatePort, _OutputPort): + pass diff --git a/bindings/python/bt2/stream.py b/bindings/python/bt2/stream.py index 56bd5100..32fb9a5f 100644 --- a/bindings/python/bt2/stream.py +++ b/bindings/python/bt2/stream.py @@ -42,18 +42,23 @@ class _StreamBase(object._Object): @property def stream_class(self): stream_class_ptr = native_bt.ctf_stream_get_class(self._ptr) - utils._handle_ptr(stream_class_ptr, "cannot get stream object's stream class object") + assert(stream_class_ptr) return bt2.StreamClass._create_from_ptr(stream_class_ptr) @property def name(self): return native_bt.ctf_stream_get_name(self._ptr) + @property + def id(self): + id = native_bt.ctf_stream_get_id(self._ptr) + return id if id >= 0 else None + def __eq__(self, other): if self.addr == other.addr: return True - return self.name == other.name + return (self.name, self.id) == (other.name, other.id) class _Stream(_StreamBase): @@ -72,7 +77,7 @@ class _Stream(_StreamBase): return _StreamBase.__eq__(self, other) def _copy(self): - return self.stream_class(self.name) + return self.stream_class(self.name, self.id) def __copy__(self): return self._copy() diff --git a/bindings/python/bt2/stream_class.py b/bindings/python/bt2/stream_class.py index ce03edfe..17c6de0f 100644 --- a/bindings/python/bt2/stream_class.py +++ b/bindings/python/bt2/stream_class.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -38,21 +38,21 @@ class _EventClassIterator(collections.abc.Iterator): if self._at == len(self._stream_class): raise StopIteration - ec_ptr = native_bt.ctf_stream_class_get_event_class(self._stream_class._ptr, - self._at) - utils._handle_ptr(ec_ptr, "cannot get stream class object's event class object") - name = native_bt.ctf_event_class_get_name(ec_ptr) + ec_ptr = native_bt.ctf_stream_class_get_event_class_by_index(self._stream_class._ptr, + self._at) + assert(ec_ptr) + ev_id = native_bt.ctf_event_class_get_id(ec_ptr) native_bt.put(ec_ptr) - utils._handle_ptr(name, "cannot get event class object's name") + utils._handle_ret(ev_id, "cannot get event class object's ID") self._at += 1 - return name + return ev_id class StreamClass(object._Object, collections.abc.Mapping): def __init__(self, name=None, id=None, packet_context_field_type=None, event_header_field_type=None, event_context_field_type=None, event_classes=None): - ptr = native_bt.ctf_stream_class_create(None) + ptr = native_bt.ctf_stream_class_create_empty(None) if ptr is None: raise bt2.CreationError('cannot create stream class object') @@ -79,9 +79,9 @@ class StreamClass(object._Object, collections.abc.Mapping): self.add_event_class(event_class) def __getitem__(self, key): - utils._check_str(key) - ec_ptr = native_bt.ctf_stream_class_get_event_class_by_name(self._ptr, - key) + utils._check_int64(key) + ec_ptr = native_bt.ctf_stream_class_get_event_class_by_id(self._ptr, + key) if ec_ptr is None: raise KeyError(key) @@ -90,18 +90,12 @@ class StreamClass(object._Object, collections.abc.Mapping): def __len__(self): count = native_bt.ctf_stream_class_get_event_class_count(self._ptr) - utils._handle_ret(count, "cannot get stream class object's event class count") + assert(count >= 0) return count def __iter__(self): return _EventClassIterator(self) - def event_class_with_id(self, id): - utils._check_int64(id) - ec_ptr = native_bt.ctf_stream_class_get_event_class_by_id(self._ptr, id) - utils._handle_ptr(ec_ptr, "cannot get stream class object's event class object") - return bt2.EventClass._create_from_ptr(ec_ptr) - def add_event_class(self, event_class): utils._check_type(event_class, bt2.EventClass) ret = native_bt.ctf_stream_class_add_event_class(self._ptr, event_class._ptr) @@ -128,8 +122,8 @@ class StreamClass(object._Object, collections.abc.Mapping): def id(self): id = native_bt.ctf_stream_class_get_id(self._ptr) - if utils._is_m1ull(id): - raise bt2.Error("cannot get stream class object's ID") + if id < 0: + return return id @@ -217,11 +211,14 @@ class StreamClass(object._Object, collections.abc.Mapping): event_context_field_type_ptr) utils._handle_ret(ret, "cannot set stream class object's event context field type") - def __call__(self, name=None): + def __call__(self, name=None, id=None): if name is not None: utils._check_str(name) - stream_ptr = native_bt.ctf_stream_create(self._ptr, name) + if id is None: + stream_ptr = native_bt.ctf_stream_create(self._ptr, name) + else: + stream_ptr = native_bt.ctf_stream_create_with_id(self._ptr, name, id) if stream_ptr is None: raise bt2.CreationError('cannot create stream object') @@ -241,26 +238,35 @@ class StreamClass(object._Object, collections.abc.Mapping): self_event_classes, self.name, self.id, - self.clock, self.packet_context_field_type, self.event_header_field_type, self.event_context_field_type, + self.clock, ) other_props = ( other_event_classes, other.name, other.id, - other.clock, other.packet_context_field_type, other.event_header_field_type, other.event_context_field_type, + other.clock, ) + return self_props == other_props def _copy(self, ft_copy_func, ev_copy_func): cpy = StreamClass() - cpy.id = self.id - cpy.name = self.name + + if self.id is not None: + cpy.id = self.id + + if self.name is not None: + cpy.name = self.name + + if self.clock is not None: + cpy.clock = self.clock + cpy.packet_context_field_type = ft_copy_func(self.packet_context_field_type) cpy.event_header_field_type = ft_copy_func(self.event_header_field_type) cpy.event_context_field_type = ft_copy_func(self.event_context_field_type) diff --git a/bindings/python/bt2/trace.py b/bindings/python/bt2/trace.py index 8ecbb386..3d5c4e3f 100644 --- a/bindings/python/bt2/trace.py +++ b/bindings/python/bt2/trace.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,7 @@ from bt2 import native_bt, object, utils import bt2.field_types import collections.abc import bt2.values +import bt2.stream import copy import bt2 @@ -37,19 +38,37 @@ class _StreamClassIterator(collections.abc.Iterator): if self._at == len(self._trace): raise StopIteration - sc_ptr = native_bt.ctf_trace_get_stream_class(self._trace._ptr, - self._at) - utils._handle_ptr(sc_ptr, "cannot get trace class object's stream class object") + sc_ptr = native_bt.ctf_trace_get_stream_class_by_index(self._trace._ptr, + self._at) + assert(sc_ptr) id = native_bt.ctf_stream_class_get_id(sc_ptr) native_bt.put(sc_ptr) - - if utils._is_m1ull(id): - raise bt2.Error("cannot get stream class object's ID") - + assert(id >= 0) self._at += 1 return id +class _TraceStreams(collections.abc.Sequence): + def __init__(self, trace): + self._trace = trace + + def __len__(self): + count = native_bt.ctf_trace_get_stream_count(self._trace._ptr) + assert(count >= 0) + return count + + def __getitem__(self, index): + utils._check_uint64(index) + + if index >= len(self): + raise IndexError + + stream_ptr = native_bt.ctf_trace_get_stream_by_index(self._trace._ptr, + index) + assert(stream_ptr) + return bt2.stream._create_from_ptr(stream_ptr) + + class _TraceClockClassesIterator(collections.abc.Iterator): def __init__(self, trace_clock_classes): self._trace_clock_classes = trace_clock_classes @@ -60,11 +79,11 @@ class _TraceClockClassesIterator(collections.abc.Iterator): raise StopIteration trace_ptr = self._trace_clock_classes._trace._ptr - cc_ptr = native_bt.ctf_trace_get_clock_class(trace_ptr, self._at) - utils._handle_ptr(cc_ptr, "cannot get trace class object's clock class") + cc_ptr = native_bt.ctf_trace_get_clock_class_by_index(trace_ptr, self._at) + assert(cc_ptr) name = native_bt.ctf_clock_class_get_name(cc_ptr) native_bt.put(cc_ptr) - utils._handle_ptr(name, "cannot get clock class object's name") + assert(name is not None) self._at += 1 return name @@ -85,7 +104,7 @@ class _TraceClockClasses(collections.abc.Mapping): def __len__(self): count = native_bt.ctf_trace_get_clock_class_count(self._trace._ptr) - utils._handle_ret(count, "cannot get trace class object's clock class count") + assert(count >= 0) return count def __iter__(self): @@ -102,9 +121,9 @@ class _TraceEnvIterator(collections.abc.Iterator): raise StopIteration trace_ptr = self._trace_env._trace._ptr - entry_name = native_bt.ctf_trace_get_environment_field_name(trace_ptr, - self._at) - utils._handle_ptr(entry_name, "cannot get trace class object's environment entry name") + entry_name = native_bt.ctf_trace_get_environment_field_name_by_index(trace_ptr, + self._at) + assert(entry_name is not None) self._at += 1 return entry_name @@ -135,7 +154,7 @@ class _TraceEnv(collections.abc.MutableMapping): def __len__(self): count = native_bt.ctf_trace_get_environment_field_count(self._trace._ptr) - utils._handle_ret(count, "cannot get trace class object's environment entry count") + assert(count >= 0) return count def __iter__(self): @@ -185,7 +204,7 @@ class Trace(object._Object, collections.abc.Mapping): def __len__(self): count = native_bt.ctf_trace_get_stream_class_count(self._ptr) - utils._handle_ret(count, "cannot get trace class object's stream class count") + assert(count >= 0) return count def __iter__(self): @@ -208,16 +227,25 @@ class Trace(object._Object, collections.abc.Mapping): @property def native_byte_order(self): - bo = native_bt.ctf_trace_get_byte_order(self._ptr) - utils._handle_ret(bo, "cannot get trace class object's native byte order") + bo = native_bt.ctf_trace_get_native_byte_order(self._ptr) + assert(bo >= 0) return bo @native_byte_order.setter def native_byte_order(self, native_byte_order): utils._check_int(native_byte_order) - ret = native_bt.ctf_trace_set_byte_order(self._ptr, native_byte_order) + ret = native_bt.ctf_trace_set_native_byte_order(self._ptr, native_byte_order) utils._handle_ret(ret, "cannot set trace class object's native byte order") + @property + def is_static(self): + is_static = native_bt.ctf_trace_is_static(self._ptr) + return is_static > 0 + + def set_is_static(self): + ret = native_bt.ctf_trace_set_is_static(self._ptr) + utils._handle_ret(ret, "cannot set trace object as static") + @property def env(self): return _TraceEnv(self) @@ -231,6 +259,10 @@ class Trace(object._Object, collections.abc.Mapping): ret = native_bt.ctf_trace_add_clock_class(self._ptr, clock_class._ptr) utils._handle_ret(ret, "cannot add clock class object to trace class object") + @property + def streams(self): + return _TraceStreams(self) + @property def packet_header_field_type(self): ft_ptr = native_bt.ctf_trace_get_packet_header_type(self._ptr) diff --git a/bindings/python/bt2/utils.py b/bindings/python/bt2/utils.py index 52133a58..294fadb6 100644 --- a/bindings/python/bt2/utils.py +++ b/bindings/python/bt2/utils.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -88,7 +88,6 @@ def _is_pow2(v): def _check_alignment(a): _check_uint64(a) - if not _is_pow2(a): raise ValueError('{} is not a power of two'.format(a)) diff --git a/bindings/python/bt2/values.py b/bindings/python/bt2/values.py index 6af3d7a0..26444e1a 100644 --- a/bindings/python/bt2/values.py +++ b/bindings/python/bt2/values.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2016 Philippe Proulx +# Copyright (c) 2017 Philippe Proulx # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,7 @@ def _handle_status(status, obj_name): return if status == native_bt.VALUE_STATUS_FROZEN: - raise bt2.FrozenError('{} value object is frozen'.format(obj_name)) + raise bt2.Frozen('{} value object is frozen'.format(obj_name)) elif status == native_bt.VALUE_STATUS_INVAL: # In practice, this should never happen, because arguments # should always be validated in this Python module before @@ -89,9 +89,6 @@ def create_value(value): class _Value(object._Object, object._Freezable, metaclass=abc.ABCMeta): - def __init__(self, ptr): - super().__init__(ptr) - def __eq__(self, other): if other is None: # self is never the null value object @@ -187,7 +184,7 @@ class _NumericValue(_Value, _BasicCopy): return self.value <= float(other) def _spec_eq(self, other): - return + pass def __eq__(self, other): if not isinstance(other, numbers.Number): @@ -372,13 +369,13 @@ class BoolValue(_Value, _BasicCopy): if not isinstance(value, bool): raise TypeError("'{}' object is not a 'bool' or 'BoolValue' object".format(value.__class__)) - return value + return int(value) @property def value(self): status, value = native_bt.value_bool_get(self._ptr) - self._handle_status(status) - return value + assert(status == native_bt.VALUE_STATUS_OK) + return value > 0 @value.setter def value(self, value): @@ -409,7 +406,7 @@ class IntegerValue(_IntegralValue): @property def value(self): status, value = native_bt.value_integer_get(self._ptr) - self._handle_status(status) + assert(status == native_bt.VALUE_STATUS_OK) return value @value.setter @@ -440,7 +437,7 @@ class FloatValue(_RealValue): @property def value(self): status, value = native_bt.value_float_get(self._ptr) - self._handle_status(status) + assert(status == native_bt.VALUE_STATUS_OK) return value @value.setter @@ -473,7 +470,7 @@ class StringValue(_BasicCopy, collections.abc.Sequence, _Value): @property def value(self): status, value = native_bt.value_string_get(self._ptr) - self._handle_status(status) + assert(status == native_bt.VALUE_STATUS_OK) return value @value.setter @@ -523,8 +520,7 @@ class _Container: ptr = native_bt.value_copy(self._ptr) if ptr is None: - fmt = 'unexpected error: cannot deep-copy {} value object' - raise RuntimeError(fmt.format(self._NAME)) + raise RuntimeError('unexpected error: cannot deep-copy {} value object'.format(self._NAME)) copy = self.__class__._create_from_ptr(ptr) memo[id(self)] = copy @@ -564,7 +560,7 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): def __len__(self): size = native_bt.value_array_size(self._ptr) - self._handle_status(size) + assert(size >= 0) return size def _check_index(self, index): @@ -580,10 +576,7 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): def __getitem__(self, index): self._check_index(index) ptr = native_bt.value_array_get(self._ptr, index) - - if ptr is None: - raise RuntimeError('unexpected error: cannot get array value object element') - + assert(ptr) return _create_from_ptr(ptr) def __setitem__(self, index, value): @@ -694,7 +687,7 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): def __len__(self): size = native_bt.value_map_size(self._ptr) - self._handle_status(size) + assert(size >= 0) return size def __contains__(self, key): @@ -711,10 +704,7 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): def __getitem__(self, key): self._check_key(key) ptr = native_bt.value_map_get(self._ptr, key) - - if ptr is None: - raise RuntimeError('unexpected error: cannot get map value object element') - + assert(ptr) return _create_from_ptr(ptr) def __iter__(self): diff --git a/configure.ac b/configure.ac index cd24ae42..ad89358d 100644 --- a/configure.ac +++ b/configure.ac @@ -497,7 +497,7 @@ AS_IF([test "x$enable_built_in_python_plugin_support" = xyes], ) -# Check for conflicting optionnal features user choices +# Check for conflicting optional features user choices AS_IF([test "x$enable_python_bindings" = xno], [ @@ -655,14 +655,11 @@ CFLAGS=${save_CFLAGS} program_transform_name="s&babeltrace\.bin&babeltrace&;s&babeltrace-log\.bin&babeltrace-log&;$program_transform_name" AC_SUBST(program_transform_name) - - #TODO: removed, work in progress - #bindings/python/Makefile - #bindings/python/babeltrace/Makefile - #bindings/python/bt2/Makefile - #bindings/python/bt2/__init__.py AC_CONFIG_FILES([ Makefile + bindings/python/Makefile + bindings/python/bt2/Makefile + bindings/python/bt2/__init__.py common/Makefile compat/Makefile cli/Makefile @@ -737,7 +734,7 @@ AC_CONFIG_FILES([tests/plugins/test-utils-muxer-complete], [chmod +x tests/plugi AC_CONFIG_FILES([tests/plugins/test_dwarf_complete], [chmod +x tests/plugins/test_dwarf_complete]) AC_CONFIG_FILES([tests/plugins/test_bin_info_complete], [chmod +x tests/plugins/test_bin_info_complete]) -AS_IF([test "x$enable_python" = xyes], +AS_IF([test "x$enable_python_bindings_tests" = xyes], [AC_CONFIG_FILES([tests/bindings/python/bt2/testall.sh], [chmod +x tests/bindings/python/bt2/testall.sh])] ) diff --git a/tests/bindings/python/bt2/.coveragerc b/tests/bindings/python/bt2/.coveragerc index 869bc005..1a31f26e 100644 --- a/tests/bindings/python/bt2/.coveragerc +++ b/tests/bindings/python/bt2/.coveragerc @@ -5,10 +5,17 @@ omit = *native_*.py */object.py */utils.py + */logging.py + */py_plugin.py [report] exclude_lines = def __repr__ raise bt2\.CreationError raise NotImplementedError + return NotImplemented pass + raise \w+Error\(['"][Uu]nexpected + if (self|other)\.addr == (self|other)\.addr + if not _NO_PRINT_TRACEBACK + class _PluginVersion: diff --git a/tests/bindings/python/bt2/Makefile.am b/tests/bindings/python/bt2/Makefile.am index 31dfbe4d..a31d9be8 100644 --- a/tests/bindings/python/bt2/Makefile.am +++ b/tests/bindings/python/bt2/Makefile.am @@ -1,17 +1,26 @@ check_SCRIPTS = testall.sh -EXTRA_DIST = \ - $(check_SCRIPTS) \ - test_clock_class.py \ - test_ctf_writer_clock.py \ - test_event_class.py \ - test_event.py \ - test_fields.py \ - test_field_types.py \ - test_packet.py \ - test_stream_class.py \ - test_stream.py \ - test_trace.py \ - test_values.py \ +EXTRA_DIST = \ + $(check_SCRIPTS) \ + test_clock_class.py \ + test_clock_class_priority_map.py \ + test_component.py \ + test_component_class.py \ + test_connection.py \ + test_ctf_writer_clock.py \ + test_event.py \ + test_event_class.py \ + test_field_types.py \ + test_fields.py \ + test_graph.py \ + test_notification.py \ + test_notification_iterator.py \ + test_packet.py \ + test_plugin.py \ + test_port.py \ + test_stream.py \ + test_stream_class.py \ + test_trace.py \ + test_values.py \ .coveragerc LOG_DRIVER_FLAGS='--merge' diff --git a/tests/bindings/python/bt2/test_clock_class.py b/tests/bindings/python/bt2/test_clock_class.py index 436b2b9e..c667e4ff 100644 --- a/tests/bindings/python/bt2/test_clock_class.py +++ b/tests/bindings/python/bt2/test_clock_class.py @@ -49,7 +49,10 @@ class ClockClassOffsetTestCase(unittest.TestCase): class ClockClassTestCase(unittest.TestCase): def setUp(self): - self._cc = bt2.ClockClass('salut') + self._cc = bt2.ClockClass('salut', 1000000) + + def tearDown(self): + del self._cc def test_create_default(self): self.assertEqual(self._cc.name, 'salut') @@ -130,7 +133,7 @@ class ClockClassTestCase(unittest.TestCase): self._cc.uuid = object() def test_create_clock_value(self): - cv = self._cc.create_clock_value(756) + cv = self._cc(756) self.assertEqual(cv.clock_class.addr, self._cc.addr) def _test_copy(self, cpy): @@ -245,10 +248,15 @@ class ClockClassTestCase(unittest.TestCase): self.assertFalse(self._cc == 23) -class ClockClassValueTestCase(unittest.TestCase): +class ClockValueTestCase(unittest.TestCase): def setUp(self): - self._cc = bt2.ClockClass('salut') - self._cv = self._cc.create_clock_value(123) + self._cc = bt2.ClockClass('salut', 1000, + offset=bt2.ClockClassOffset(45, 354)) + self._cv = self._cc(123) + + def tearDown(self): + del self._cc + del self._cv def test_create_default(self): self.assertEqual(self._cv.clock_class.addr, self._cc.addr) @@ -256,30 +264,32 @@ class ClockClassValueTestCase(unittest.TestCase): def test_create_invalid_cycles_type(self): with self.assertRaises(TypeError): - self._cc.create_clock_value('yes') + self._cc('yes') def test_ns_from_epoch(self): - self._cv.clock_class.frequency = 1000 - self._cv.clock_class.offset = bt2.ClockClassOffset(45, 354) s_from_epoch = 45 + ((354 + 123) / 1000) ns_from_epoch = int(s_from_epoch * 1e9) self.assertEqual(self._cv.ns_from_epoch, ns_from_epoch) def test_eq(self): - cv1 = self._cc.create_clock_value(123) - cv2 = self._cc.create_clock_value(123) + cv1 = self._cc(123) + cv2 = self._cc(123) self.assertEqual(cv1, cv2) + def test_eq_int(self): + cv1 = self._cc(123) + self.assertEqual(cv1, 123) + def test_ne_clock_class(self): - cc1 = bt2.ClockClass('yes') - cc2 = bt2.ClockClass('yes') - cv1 = cc1.create_clock_value(123) - cv2 = cc2.create_clock_value(123) + cc1 = bt2.ClockClass('yes', 1500) + cc2 = bt2.ClockClass('yes', 1501) + cv1 = cc1(123) + cv2 = cc2(123) self.assertNotEqual(cv1, cv2) def test_ne_cycles(self): - cv1 = self._cc.create_clock_value(123) - cv2 = self._cc.create_clock_value(125) + cv1 = self._cc(123) + cv2 = self._cc(125) self.assertNotEqual(cv1, cv2) def test_eq_invalid(self): diff --git a/tests/bindings/python/bt2/test_clock_class_priority_map.py b/tests/bindings/python/bt2/test_clock_class_priority_map.py new file mode 100644 index 00000000..989927e8 --- /dev/null +++ b/tests/bindings/python/bt2/test_clock_class_priority_map.py @@ -0,0 +1,139 @@ +import unittest +import uuid +import copy +import bt2 + + +class ClockClassPriorityMapTestCase(unittest.TestCase): + def test_create_empty(self): + cc_prio_map = bt2.ClockClassPriorityMap() + self.assertEqual(len(cc_prio_map), 0) + self.assertIsNone(cc_prio_map.highest_priority_clock_class) + + def test_create_full(self): + cc1 = bt2.ClockClass('meow', 1234) + cc2 = bt2.ClockClass('mix', 5678) + cc_prio_map = bt2.ClockClassPriorityMap({cc1: 17, cc2: 2}) + self.assertEqual(len(cc_prio_map), 2) + self.assertEqual(cc_prio_map[cc1], 17) + self.assertEqual(cc_prio_map[cc2], 2) + self.assertEqual(cc_prio_map.highest_priority_clock_class, cc2) + + def test_setitem(self): + cc_prio_map = bt2.ClockClassPriorityMap() + cc1 = bt2.ClockClass('mix', 5678) + cc_prio_map[cc1] = 184 + self.assertEqual(len(cc_prio_map), 1) + self.assertEqual(cc_prio_map[cc1], 184) + self.assertEqual(cc_prio_map.highest_priority_clock_class, cc1) + + def test_setitem_wrong_key_type(self): + cc_prio_map = bt2.ClockClassPriorityMap() + + with self.assertRaises(TypeError): + cc_prio_map['the clock'] = 184 + + def test_setitem_wrong_value_type(self): + cc_prio_map = bt2.ClockClassPriorityMap() + cc1 = bt2.ClockClass('mix', 5678) + + with self.assertRaises(TypeError): + cc_prio_map[cc1] = 'priority' + + def test_getitem_wrong_key(self): + cc_prio_map = bt2.ClockClassPriorityMap() + cc1 = bt2.ClockClass('mix', 5678) + cc2 = bt2.ClockClass('mix', 5678) + cc_prio_map[cc1] = 2 + + with self.assertRaises(KeyError): + cc_prio_map[cc2] + + def test_getitem_wrong_key_type(self): + cc_prio_map = bt2.ClockClassPriorityMap() + + with self.assertRaises(TypeError): + cc_prio_map[23] + + def test_iter(self): + cc1 = bt2.ClockClass('meow', 1234) + cc2 = bt2.ClockClass('mix', 5678) + cc3 = bt2.ClockClass('lel', 1548) + cc_prio_map = bt2.ClockClassPriorityMap({cc1: 17, cc2: 2, cc3: 25}) + cc_prios = {} + + for cc, prio in cc_prio_map.items(): + cc_prios[cc] = prio + + self.assertEqual(len(cc_prios), 3) + self.assertEqual(cc_prios[cc1], 17) + self.assertEqual(cc_prios[cc2], 2) + self.assertEqual(cc_prios[cc3], 25) + + def test_eq(self): + cc1 = bt2.ClockClass('meow', 1234) + cc2 = bt2.ClockClass('mix', 5678) + cc3 = bt2.ClockClass('lel', 1548) + cc_prio_map = bt2.ClockClassPriorityMap({cc1: 17, cc2: 2, cc3: 25}) + other_cc1 = bt2.ClockClass('meow', 1234) + other_cc2 = bt2.ClockClass('mix', 5678) + other_cc3 = bt2.ClockClass('lel', 1548) + other_cc_prio_map = bt2.ClockClassPriorityMap({other_cc1: 17, other_cc2: 2, other_cc3: 25}) + self.assertEqual(cc_prio_map, other_cc_prio_map) + + def test_ne_clock_class(self): + cc1 = bt2.ClockClass('meow', 1234) + cc2 = bt2.ClockClass('mix', 5678) + cc3 = bt2.ClockClass('lel', 1548) + cc_prio_map = bt2.ClockClassPriorityMap({cc1: 17, cc2: 2, cc3: 25}) + other_cc1 = bt2.ClockClass('meow', 1234) + other_cc2 = bt2.ClockClass('coucou', 5678) + other_cc3 = bt2.ClockClass('lel', 1548) + other_cc_prio_map = bt2.ClockClassPriorityMap({other_cc1: 17, other_cc2: 2, other_cc3: 25}) + self.assertNotEqual(cc_prio_map, other_cc_prio_map) + + def test_ne_prio(self): + cc1 = bt2.ClockClass('meow', 1234) + cc2 = bt2.ClockClass('mix', 5678) + cc3 = bt2.ClockClass('lel', 1548) + cc_prio_map = bt2.ClockClassPriorityMap({cc1: 17, cc2: 2, cc3: 25}) + other_cc1 = bt2.ClockClass('meow', 1234) + other_cc2 = bt2.ClockClass('mix', 5678) + other_cc3 = bt2.ClockClass('lel', 1548) + other_cc_prio_map = bt2.ClockClassPriorityMap({other_cc1: 17, other_cc2: 3, other_cc3: 25}) + self.assertNotEqual(cc_prio_map, other_cc_prio_map) + + def test_eq_invalid(self): + self.assertFalse(bt2.ClockClassPriorityMap() == 23) + + def test_copy(self): + cc1 = bt2.ClockClass('meow', 1234) + cc2 = bt2.ClockClass('mix', 5678) + cc3 = bt2.ClockClass('lel', 1548) + cc_prio_map = bt2.ClockClassPriorityMap({cc1: 17, cc2: 2, cc3: 25}) + cc_prio_map_cpy = copy.copy(cc_prio_map) + self.assertEqual(cc_prio_map, cc_prio_map_cpy) + self.assertEqual(list(cc_prio_map.keys())[0].addr, list(cc_prio_map_cpy.keys())[0].addr) + self.assertEqual(list(cc_prio_map.keys())[1].addr, list(cc_prio_map_cpy.keys())[1].addr) + self.assertEqual(list(cc_prio_map.keys())[2].addr, list(cc_prio_map_cpy.keys())[2].addr) + self.assertEqual(list(cc_prio_map.values())[0], list(cc_prio_map_cpy.values())[0]) + self.assertEqual(list(cc_prio_map.values())[1], list(cc_prio_map_cpy.values())[1]) + self.assertEqual(list(cc_prio_map.values())[2], list(cc_prio_map_cpy.values())[2]) + self.assertEqual(cc_prio_map.highest_priority_clock_class.addr, + cc_prio_map_cpy.highest_priority_clock_class.addr) + + def test_deep_copy(self): + cc1 = bt2.ClockClass('meow', 1234) + cc2 = bt2.ClockClass('mix', 5678) + cc3 = bt2.ClockClass('lel', 1548) + cc_prio_map = bt2.ClockClassPriorityMap({cc1: 17, cc2: 2, cc3: 25}) + cc_prio_map_cpy = copy.deepcopy(cc_prio_map) + self.assertEqual(cc_prio_map, cc_prio_map_cpy) + self.assertNotEqual(list(cc_prio_map.keys())[0].addr, list(cc_prio_map_cpy.keys())[0].addr) + self.assertNotEqual(list(cc_prio_map.keys())[1].addr, list(cc_prio_map_cpy.keys())[1].addr) + self.assertNotEqual(list(cc_prio_map.keys())[2].addr, list(cc_prio_map_cpy.keys())[2].addr) + self.assertEqual(list(cc_prio_map.values())[0], list(cc_prio_map_cpy.values())[0]) + self.assertEqual(list(cc_prio_map.values())[1], list(cc_prio_map_cpy.values())[1]) + self.assertEqual(list(cc_prio_map.values())[2], list(cc_prio_map_cpy.values())[2]) + self.assertNotEqual(cc_prio_map.highest_priority_clock_class.addr, + cc_prio_map_cpy.highest_priority_clock_class.addr) diff --git a/tests/bindings/python/bt2/test_comp_notif_iter.py b/tests/bindings/python/bt2/test_comp_notif_iter.py deleted file mode 100644 index 70afebe9..00000000 --- a/tests/bindings/python/bt2/test_comp_notif_iter.py +++ /dev/null @@ -1,861 +0,0 @@ -from collections import OrderedDict -import unittest -import copy -import bt2.notification_iterator -import bt2 - - -def _create_ec(): - # clock class - cc = bt2.ClockClass('salut_clock') - - # event header - eh = bt2.StructureFieldType() - eh += OrderedDict(( - ('id', bt2.IntegerFieldType(8)), - ('ts', bt2.IntegerFieldType(32, mapped_clock_class=cc)), - )) - - # packet context - pc = bt2.StructureFieldType() - pc += OrderedDict(( - ('something', bt2.IntegerFieldType(8)), - )) - - # stream class - sc = bt2.StreamClass() - sc.packet_context_field_type = pc - sc.event_header_field_type = eh - sc.event_context_field_type = None - - # event payload - ep = bt2.StructureFieldType() - ep += OrderedDict(( - ('mosquito', bt2.StringFieldType()), - )) - - # event class - event_class = bt2.EventClass('ec', id=0) - event_class.context_field_type = None - event_class.payload_field_type = ep - sc.add_event_class(event_class) - - # packet header - ph = bt2.StructureFieldType() - ph += OrderedDict(( - ('magic', bt2.IntegerFieldType(32)), - ('stream_id', bt2.IntegerFieldType(16)), - )) - - # trace - trace = bt2.Trace() - trace.packet_header_field_type = ph - trace.add_clock_class(cc) - trace.add_stream_class(sc) - return event_class - - -def _create_event(event_class, msg): - ev = event_class() - ev.header_field['id'] = 0 - ev.header_field['ts'] = 19487 - ev.payload_field['mosquito'] = msg - return ev - - -def _create_packet(stream): - packet = stream.create_packet() - packet.header_field['magic'] = 0xc1fc1fc1 - packet.header_field['stream_id'] = 0 - packet.context_field['something'] = 194 - return packet - - -def _create_source(): - class MyIter(bt2.UserNotificationIterator): - def __init__(self): - self._event_class = self.component._event_class - self._stream = self.component._stream - self._packet = _create_packet(self._stream) - self._at = 0 - self._cur_notif = None - - def _get(self): - if self._cur_notif is None: - raise bt2.Error('nothing here!') - - return self._cur_notif - - def _next(self): - if self._at == 0: - notif = bt2.BeginningOfPacketNotification(self._packet) - elif self._at < 5: - ev = _create_event(self._event_class, 'at {}'.format(self._at)) - ev.packet = self._packet - notif = bt2.TraceEventNotification(ev) - elif self._at == 5: - notif = bt2.EndOfPacketNotification(self._packet) - elif self._at == 6: - notif = bt2.EndOfStreamNotification(self._stream) - else: - raise bt2.Stop - - self._at += 1 - self._cur_notif = notif - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - def __init__(self): - self._event_class = _create_ec() - self._stream = self._event_class.stream_class(name='abcdef') - - return MySource() - - -class GenCompClassTestCase(unittest.TestCase): - def test_attr_name(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - sink = MySink() - self.assertEqual(sink.component_class.name, 'MySink') - - def test_attr_description(self): - class MySink(bt2.UserSinkComponent): - '''This is the description. - - This is the help. - - This too: - - * And this. - * And also that. - - Voilà. - ''' - - def _consume(self): - pass - - sink = MySink() - self.assertEqual(sink.component_class.description, - 'This is the description.') - - def test_attr_help(self): - class MySink(bt2.UserSinkComponent): - '''This is the description. - - This is the help. - - This too: - - * And this. - * And also that. - - Voilà. - ''' - - def _consume(self): - pass - - sink = MySink() - expected_help = '''This is the help. - -This too: - - * And this. - * And also that. - -Voilà.''' - self.assertEqual(sink.component_class.help, expected_help) - - def test_instantiate(self): - class MySink(bt2.UserSinkComponent): - 'hello' - - def __init__(self, params=None, name=None): - if params is None or name is None: - return - - nonlocal nl_params_a, nl_name - nl_params_a = params['a'] - nl_name = name - - def _consume(self): - pass - - nl_params_a = None - nl_name = None - sink = MySink() - self.assertIsNone(nl_params_a) - self.assertIsNone(nl_name) - gen_comp_class = sink.component_class - sink2 = gen_comp_class(params={'a': 23}, name='salut') - self.assertEqual(nl_params_a, 23) - self.assertEqual(nl_name, 'salut') - - -class UserCompClassTestCase(unittest.TestCase): - def test_attr_name(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - self.assertEqual(MySink.name, 'MySink') - - def test_attr_name_param(self): - class MySink(bt2.UserSinkComponent, name='my custom name'): - def _consume(self): - pass - - self.assertEqual(MySink.name, 'my custom name') - - def test_attr_description(self): - class MySink(bt2.UserSinkComponent): - '''This is the description. - - This is the help. - - This too: - - * And this. - * And also that. - - Voilà. - ''' - - def _consume(self): - pass - - self.assertEqual(MySink.description, 'This is the description.') - - def test_attr_help(self): - class MySink(bt2.UserSinkComponent): - '''This is the description. - - This is the help. - - This too: - - * And this. - * And also that. - - Voilà. - ''' - - def _consume(self): - pass - - expected_help = '''This is the help. - -This too: - - * And this. - * And also that. - -Voilà.''' - self.assertEqual(MySink.help, expected_help) - - def test_query_missing(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - with self.assertRaises(bt2.Error): - MySink.query('salut') - - def test_query_raises(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - @staticmethod - def _query(obj, params): - raise ValueError - - with self.assertRaises(bt2.Error): - MySink.query('salut') - - def test_query_gets_none_params(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - @staticmethod - def _query(obj, params): - nonlocal recv_params - recv_params = params - - recv_params = NotImplemented - MySink.query('allo', None) - self.assertIsNone(recv_params) - - def test_query_gets_same_params(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - @staticmethod - def _query(obj, params): - nonlocal recv_params - recv_params = params - - recv_params = NotImplemented - params = bt2.create_value(23) - MySink.query('allo', params) - self.assertEqual(recv_params.addr, params.addr) - - def test_query_obj(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - @staticmethod - def _query(obj, params): - nonlocal recv_obj - recv_obj = obj - - recv_obj = None - MySink.query('salut') - self.assertEqual(recv_obj, 'salut') - - def test_query_returns_none(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - @staticmethod - def _query(obj, params): - pass - - self.assertIsNone(MySink.query('allo', 177)) - - def test_query_returns_params(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - @staticmethod - def _query(obj, params): - return {'obj': obj, 'params': params} - - results = MySink.query('hello', (45, 'lol')) - self.assertEqual(results['obj'], 'hello') - self.assertEqual(results['params'], (45, 'lol')) - - def test_init(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - nonlocal inited - inited = True - - def _consume(self): - pass - - inited = False - sink = MySink() - self.assertTrue(inited) - - def test_init_raises(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - raise RuntimeError('oops') - - def _consume(self): - pass - - with self.assertRaises(RuntimeError): - sink = MySink() - - def test_destroy(self): - class MySink(bt2.UserSinkComponent): - def _destroy(self): - nonlocal destroyed - destroyed = True - - def _consume(self): - pass - - destroyed = False - sink = MySink() - del sink - self.assertTrue(destroyed) - - def test_destroy_raises(self): - class MySink(bt2.UserSinkComponent): - def _destroy(self): - raise RuntimeError('oh oh') - nonlocal destroyed - destroyed = True - - def _consume(self): - pass - - destroyed = False - sink = MySink() - del sink - self.assertFalse(destroyed) - - def test_comp_attr_name(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - sink = MySink(name='salut') - self.assertEqual(sink.name, 'salut') - - def test_comp_attr_no_name(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - sink = MySink() - self.assertIsNone(sink.name) - - def test_comp_attr_class(self): - class MySink(bt2.UserSinkComponent): - def _consume(self): - pass - - sink = MySink() - self.assertEqual(sink.component_class.name, 'MySink') - - -class UserSinkCompClassTestCase(unittest.TestCase): - def test_missing_consume(self): - with self.assertRaises(bt2.IncompleteUserClassError): - class MySink(bt2.UserSinkComponent): - pass - - def test_set_min_input(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - self._maximum_input_notification_iterator_count = 10 - self._minimum_input_notification_iterator_count = 5 - - def _consume(self): - pass - - sink = MySink() - - def test_set_max_input(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - self._maximum_input_notification_iterator_count = 5 - - def _consume(self): - pass - - sink = MySink() - - def test_consume(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - pass - - def _consume(self): - nonlocal consumed - consumed = True - - sink = MySink() - consumed = False - source = _create_source() - notif_iter = source.create_notification_iterator() - sink.add_notification_iterator(notif_iter) - sink.consume() - self.assertTrue(consumed) - - def test_consume_raises(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - pass - - def _consume(self): - raise RuntimeError('hello') - - sink = MySink() - source = _create_source() - notif_iter = source.create_notification_iterator() - sink.add_notification_iterator(notif_iter) - - with self.assertRaises(bt2.Error): - sink.consume() - - def test_add_notification_iterator(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - pass - - def _consume(self): - pass - - def _add_notification_iterator(self, notif_iter): - nonlocal added - added = True - - sink = MySink() - added = False - source = _create_source() - notif_iter = source.create_notification_iterator() - sink.add_notification_iterator(notif_iter) - self.assertTrue(added) - - def test_input_notif_iterators(self): - class MySink(bt2.UserSinkComponent): - def __init__(self): - self._maximum_input_notification_iterator_count = 5 - - def _consume(self): - nonlocal count - count = 0 - - for notif_iter in self._input_notification_iterators: - count += 1 - - sink = MySink() - count = 0 - source = _create_source() - notif_iter1 = source.create_notification_iterator() - notif_iter2 = source.create_notification_iterator() - sink.add_notification_iterator(notif_iter1) - sink.add_notification_iterator(notif_iter2) - sink.consume() - self.assertEqual(count, 2) - - -class UserSourceCompClassTestCase(unittest.TestCase): - def test_missing_notif_iterator_class(self): - with self.assertRaises(bt2.IncompleteUserClassError): - class MySource(bt2.UserSourceComponent): - pass - - def test_invalid_notif_iterator_class(self): - class Lel: - pass - - with self.assertRaises(bt2.IncompleteUserClassError): - class MySource(bt2.UserSourceComponent, notification_iterator_class=Lel): - pass - - def test_notif_iterator_class_missing_get(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - pass - - with self.assertRaises(bt2.IncompleteUserClassError): - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - def test_notif_iterator_class_missing_next(self): - class MyIter(bt2.UserNotificationIterator): - def _get(self): - pass - - with self.assertRaises(bt2.IncompleteUserClassError): - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - def test_create_notification_iterator(self): - class MyIter(bt2.UserNotificationIterator): - def __init__(self): - nonlocal created - created = True - - def _next(self): - pass - - def _get(self): - pass - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - created = False - source = MySource() - notif_iter = source.create_notification_iterator() - self.assertTrue(created) - - -class UserSourceCompClassTestCase(unittest.TestCase): - def test_missing_notif_iterator_class(self): - with self.assertRaises(bt2.IncompleteUserClassError): - class MyFilter(bt2.UserFilterComponent): - pass - - def test_invalid_notif_iterator_class(self): - class Lel: - pass - - with self.assertRaises(bt2.IncompleteUserClassError): - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=Lel): - pass - - def test_notif_iterator_class_missing_get(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - pass - - with self.assertRaises(bt2.IncompleteUserClassError): - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter): - pass - - def test_notif_iterator_class_missing_next(self): - class MyIter(bt2.UserNotificationIterator): - def _get(self): - pass - - with self.assertRaises(bt2.IncompleteUserClassError): - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter): - pass - - def test_create_notification_iterator(self): - class MyIter(bt2.UserNotificationIterator): - def __init__(self): - nonlocal created - created = True - - def _next(self): - pass - - def _get(self): - pass - - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter): - pass - - created = False - filter = MyFilter() - notif_iter = filter.create_notification_iterator() - self.assertTrue(created) - - def test_set_min_input(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - pass - - def _get(self): - pass - - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter): - def __init__(self): - self._maximum_input_notification_iterator_count = 10 - self._minimum_input_notification_iterator_count = 5 - - filter = MyFilter() - - def test_set_max_input(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - pass - - def _get(self): - pass - - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter): - def __init__(self): - self._maximum_input_notification_iterator_count = 5 - - filter = MyFilter() - - def test_add_notification_iterator(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - pass - - def _get(self): - pass - - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter): - def __init__(self): - pass - - def _add_notification_iterator(self, notif_iter): - nonlocal added - added = True - - filter = MyFilter() - added = False - source = _create_source() - notif_iter = source.create_notification_iterator() - filter.add_notification_iterator(notif_iter) - self.assertTrue(added) - - def test_input_notif_iterators(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - pass - - def _get(self): - pass - - class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter): - def __init__(self): - self._maximum_input_notification_iterator_count = 5 - - def _test(self): - nonlocal count - count = 0 - - for notif_iter in self._input_notification_iterators: - count += 1 - - filter = MyFilter() - count = 0 - source = _create_source() - notif_iter1 = source.create_notification_iterator() - notif_iter2 = source.create_notification_iterator() - filter.add_notification_iterator(notif_iter1) - filter.add_notification_iterator(notif_iter2) - filter._test() - self.assertEqual(count, 2) - - -class UserNotificationIteratorTestCase(unittest.TestCase): - def test_init(self): - class MyIter(bt2.UserNotificationIterator): - def __init__(self): - nonlocal inited - inited = True - - def _next(self): - pass - - def _get(self): - pass - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - inited = False - source = MySource() - notif_iter = source.create_notification_iterator() - self.assertTrue(inited) - - def test_destroy(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - pass - - def _get(self): - pass - - def _destroy(self): - nonlocal destroyed - destroyed = True - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - source = MySource() - notif_iter = source.create_notification_iterator() - destroyed = False - del notif_iter - self.assertTrue(destroyed) - - def test_attr_component(self): - class MyIter(bt2.UserNotificationIterator): - def __init__(self): - nonlocal source, is_same - is_same = self.component is source - - def _next(self): - pass - - def _get(self): - pass - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - source = MySource() - is_same = False - notif_iter = source.create_notification_iterator() - self.assertTrue(is_same) - - def test_next_raises_stop(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - raise bt2.Stop - - def _get(self): - pass - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - source = MySource() - is_same = False - notif_iter = source.create_notification_iterator() - - with self.assertRaises(bt2.Stop): - notif_iter.next() - - def test_next_raises_unsupported_feature(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - raise bt2.UnsupportedFeature - - def _get(self): - pass - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - source = MySource() - is_same = False - notif_iter = source.create_notification_iterator() - - with self.assertRaises(bt2.UnsupportedFeature): - notif_iter.next() - - def test_next_raises_unknown(self): - class MyIter(bt2.UserNotificationIterator): - def _next(self): - raise TypeError('lel') - - def _get(self): - pass - - class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter): - pass - - source = MySource() - is_same = False - notif_iter = source.create_notification_iterator() - - with self.assertRaises(bt2.Error): - notif_iter.next() - - def test_iteration(self): - source = _create_source() - notif_iter = source.create_notification_iterator() - pkt_addr = None - - for index, notif in enumerate(notif_iter): - if index == 0: - self.assertIsInstance(notif, bt2.BeginningOfPacketNotification) - self.assertEqual(notif.packet.header_field['magic'], 0xc1fc1fc1) - self.assertEqual(notif.packet.context_field['something'], 194) - pkt_addr = notif.packet.addr - elif index < 5: - self.assertIsInstance(notif, bt2.TraceEventNotification) - self.assertEqual(notif.event.header_field['ts'], 19487) - self.assertEqual(notif.event.payload_field['mosquito'], 'at {}'.format(index)) - elif index == 5: - self.assertIsInstance(notif, bt2.EndOfPacketNotification) - self.assertEqual(notif.packet.header_field['magic'], 0xc1fc1fc1) - self.assertEqual(notif.packet.context_field['something'], 194) - self.assertEqual(notif.packet.addr, pkt_addr) - elif index == 6: - self.assertIsInstance(notif, bt2.EndOfStreamNotification) - self.assertEqual(notif.stream.name, 'abcdef') - else: - raise RuntimeError('FAIL') - - -class GenNotificationIteratorTestCase(unittest.TestCase): - def test_attr_component(self): - source = _create_source() - notif_iter = source.create_notification_iterator() - self.assertIsInstance(notif_iter, bt2.notification_iterator._GenericNotificationIterator) - self.assertEqual(notif_iter.component.addr, source.addr) diff --git a/tests/bindings/python/bt2/test_component.py b/tests/bindings/python/bt2/test_component.py new file mode 100644 index 00000000..99438dc1 --- /dev/null +++ b/tests/bindings/python/bt2/test_component.py @@ -0,0 +1,121 @@ +from bt2 import values +import unittest +import copy +import bt2 + + +class UserComponentTestCase(unittest.TestCase): + @staticmethod + def _create_comp(comp_cls, name=None): + graph = bt2.Graph() + + if name is None: + name = 'comp' + + return graph.add_component(comp_cls, name) + + def test_name(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + self.assertEqual(comp_self.name, 'yaes') + + def _consume(self): + pass + + comp = self._create_comp(MySink, 'yaes') + + def test_graph(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + nonlocal graph + self.assertEqual(comp_self.graph, graph) + + def _consume(self): + pass + + graph = bt2.Graph() + comp = graph.add_component(MySink, 'lel') + del graph + + def test_class(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + self.assertEqual(comp_self.component_class, MySink) + + def _consume(self): + pass + + self._create_comp(MySink) + + def test_addr(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + self.assertIsInstance(comp_self.addr, int) + self.assertNotEqual(comp_self.addr, 0) + + def _consume(self): + pass + + self._create_comp(MySink) + + def test_finalize(self): + finalized = False + + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + def _finalize(comp_self): + nonlocal finalized + finalized = True + + graph = bt2.Graph() + comp = graph.add_component(MySink, 'lel') + del graph + del comp + self.assertTrue(finalized) + + +class GenericComponentTestCase(unittest.TestCase): + @staticmethod + def _create_comp(comp_cls, name=None): + graph = bt2.Graph() + + if name is None: + name = 'comp' + + return graph.add_component(comp_cls, name) + + def test_name(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + comp = self._create_comp(MySink, 'yaes') + self.assertEqual(comp.name, 'yaes') + + def test_graph(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + graph = bt2.Graph() + comp = graph.add_component(MySink, 'lel') + self.assertEqual(comp.graph, graph) + + def test_class(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertEqual(comp.component_class, MySink) + + def test_addr(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertIsInstance(comp.addr, int) + self.assertNotEqual(comp.addr, 0) diff --git a/tests/bindings/python/bt2/test_component_class.py b/tests/bindings/python/bt2/test_component_class.py new file mode 100644 index 00000000..a8160c33 --- /dev/null +++ b/tests/bindings/python/bt2/test_component_class.py @@ -0,0 +1,323 @@ +from bt2 import values +import unittest +import copy +import bt2 + + +class UserComponentClassTestCase(unittest.TestCase): + def _test_no_init(self, cls): + with self.assertRaises(bt2.Error): + cls() + + def test_no_init_source(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + pass + + self._test_no_init(MySource) + + def test_no_init_filter(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + pass + + self._test_no_init(MyFilter) + + def test_no_init_sink(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + self._test_no_init(MySink) + + def test_incomplete_source_no_notif_iter_cls(self): + class MyIter(bt2._UserNotificationIterator): + pass + + with self.assertRaises(bt2.IncompleteUserClass): + class MySource(bt2._UserSourceComponent): + pass + + def test_incomplete_source_wrong_notif_iter_cls_type(self): + class MyIter(bt2._UserNotificationIterator): + pass + + with self.assertRaises(bt2.IncompleteUserClass): + class MySource(bt2._UserSourceComponent, + notification_iterator_class=int): + pass + + def test_incomplete_filter_no_notif_iter_cls(self): + class MyIter(bt2._UserNotificationIterator): + pass + + with self.assertRaises(bt2.IncompleteUserClass): + class MyFilter(bt2._UserFilterComponent): + pass + + def test_incomplete_sink_no_consume_method(self): + class MyIter(bt2._UserNotificationIterator): + pass + + with self.assertRaises(bt2.IncompleteUserClass): + class MySink(bt2._UserSinkComponent): + pass + + def test_minimal_source(self): + class MyIter(bt2._UserNotificationIterator): + pass + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + pass + + def test_minimal_filter(self): + class MyIter(bt2._UserNotificationIterator): + pass + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + pass + + def test_minimal_sink(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + def test_default_name(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + self.assertEqual(MySink.name, 'MySink') + + def test_custom_name(self): + class MySink(bt2._UserSinkComponent, name='salut'): + def _consume(self): + pass + + self.assertEqual(MySink.name, 'salut') + + def test_invalid_custom_name(self): + with self.assertRaises(TypeError): + class MySink(bt2._UserSinkComponent, name=23): + def _consume(self): + pass + + def test_description(self): + class MySink(bt2._UserSinkComponent): + """ + The description. + + Bacon ipsum dolor amet ribeye t-bone corned beef, beef jerky + porchetta burgdoggen prosciutto chicken frankfurter boudin + hamburger doner bacon turducken. Sirloin shank sausage, + boudin meatloaf alcatra meatball t-bone tongue pastrami + cupim flank tenderloin. + """ + + def _consume(self): + pass + + self.assertEqual(MySink.description, 'The description.') + + def test_empty_description(self): + class MySink(bt2._UserSinkComponent): + """ + """ + + def _consume(self): + pass + + self.assertIsNone(MySink.description) + + def test_help(self): + class MySink(bt2._UserSinkComponent): + """ + The description. + + The help + text is + here. + """ + + def _consume(self): + pass + + self.assertEqual(MySink.help, 'The help\ntext is\nhere.') + + def test_addr(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + self.assertIsInstance(MySink.addr, int) + self.assertNotEqual(MySink.addr, 0) + + def test_query_not_implemented(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + with self.assertRaises(bt2.Error): + MySink.query('obj', 23) + + def test_query_raises(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + @classmethod + def _query(cls, obj, params): + raise ValueError + + with self.assertRaises(bt2.Error): + MySink.query('obj', 23) + + def test_query_wrong_return_type(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + @classmethod + def _query(cls, obj, params): + return ... + + with self.assertRaises(bt2.Error): + MySink.query('obj', 23) + + def test_query_params_none(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + @classmethod + def _query(cls, obj, params): + nonlocal query_params + query_params = params + return None + + query_params = None + params = None + res = MySink.query('obj', params) + self.assertEqual(query_params, params) + self.assertIsNone(res) + del query_params + + def test_query_simple(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + @classmethod + def _query(cls, obj, params): + nonlocal query_params + query_params = params + return 17.5 + + query_params = None + params = ['coucou', 23, None] + res = MySink.query('obj', params) + self.assertEqual(query_params, params) + self.assertEqual(res, 17.5) + del query_params + + def test_query_complex(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + @classmethod + def _query(cls, obj, params): + nonlocal query_params + query_params = params + return { + 'null': None, + 'bt2': 'BT2', + } + + query_params = None + params = { + 'array': ['coucou', 23, None], + 'other_map': { + 'yes': 'yeah', + '19': 19, + 'minus 1.5': -1.5, + }, + 'null': None, + } + + res = MySink.query('obj', params) + self.assertEqual(query_params, params) + self.assertEqual(res, { + 'null': None, + 'bt2': 'BT2', + }) + del query_params + + def test_eq(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + self.assertEqual(MySink, MySink) + + +class GenericComponentClassTestCase(unittest.TestCase): + def setUp(self): + class MySink(bt2._UserSinkComponent): + ''' + The description. + + The help. + ''' + def _consume(self): + pass + + @classmethod + def _query(cls, obj, params): + return [obj, params, 23] + + self._py_comp_cls = MySink + graph = bt2.Graph() + comp = graph.add_component(MySink, 'salut') + self._comp_cls = comp.component_class + self.assertTrue(issubclass(type(self._comp_cls), + bt2.component._GenericComponentClass)) + + def tearDown(self): + del self._py_comp_cls + del self._comp_cls + + def test_description(self): + self.assertEqual(self._comp_cls.description, 'The description.') + + def test_help(self): + self.assertEqual(self._comp_cls.help, 'The help.') + + def test_name(self): + self.assertEqual(self._comp_cls.name, 'MySink') + + def test_addr(self): + self.assertIsInstance(self._comp_cls.addr, int) + self.assertNotEqual(self._comp_cls.addr, 0) + + def test_eq_invalid(self): + self.assertFalse(self._comp_cls == 23) + + def test_eq(self): + self.assertEqual(self._comp_cls, self._comp_cls) + self.assertEqual(self._py_comp_cls, self._comp_cls) + + def test_query(self): + res = self._comp_cls.query('an object', {'yes': 'no', 'book': -17}) + expected = ['an object', {'yes': 'no', 'book': -17}, 23] + self.assertEqual(res, expected) diff --git a/tests/bindings/python/bt2/test_connection.py b/tests/bindings/python/bt2/test_connection.py new file mode 100644 index 00000000..04814132 --- /dev/null +++ b/tests/bindings/python/bt2/test_connection.py @@ -0,0 +1,392 @@ +from bt2 import values +import unittest +import copy +import bt2 + + +class ConnectionTestCase(unittest.TestCase): + def test_create(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertIsInstance(conn, bt2._Connection) + self.assertNotIsInstance(conn, bt2._PrivateConnection) + + def test_is_ended_false(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertFalse(conn.is_ended) + + def test_is_ended_true(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + src.output_ports['out'].disconnect() + self.assertTrue(conn.is_ended) + + def test_downstream_port(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertEqual(conn.downstream_port.addr, sink.input_ports['in'].addr) + + def test_upstream_port(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertEqual(conn.upstream_port.addr, src.output_ports['out'].addr) + + def test_eq(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertEqual(conn, conn) + + def test_eq_invalid(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertNotEqual(conn, 23) + + +class PrivateConnectionTestCase(unittest.TestCase): + def test_create(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + nonlocal priv_conn + priv_conn = port.connection + + priv_conn = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertIsInstance(priv_conn, bt2._PrivateConnection) + self.assertEqual(conn, priv_conn) + del priv_conn + + def test_is_ended_false(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + nonlocal priv_conn + priv_conn = port.connection + + priv_conn = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertFalse(priv_conn.is_ended) + del priv_conn + + def test_is_ended_true(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + nonlocal priv_conn + priv_conn = port.connection + + priv_conn = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + sink.input_ports['in'].disconnect() + self.assertTrue(priv_conn.is_ended) + del priv_conn + + def test_downstream_port(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + nonlocal priv_port + priv_conn = port.connection + priv_port = priv_conn.downstream_port + + priv_port = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertEqual(priv_port.addr, sink.input_ports['in'].addr) + del priv_port + + def test_upstream_port(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + nonlocal priv_port + priv_conn = port.connection + priv_port = priv_conn.upstream_port + + priv_port = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertEqual(priv_port.addr, src.output_ports['out'].addr) + del priv_port + + def test_eq(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + nonlocal priv_conn + priv_conn = port.connection + + priv_conn = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertEqual(priv_conn, conn) + del priv_conn + + def test_eq_invalid(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + nonlocal priv_conn + priv_conn = port.connection + + priv_conn = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertNotEqual(priv_conn, 23) + del priv_conn diff --git a/tests/bindings/python/bt2/test_ctf_writer_clock.py b/tests/bindings/python/bt2/test_ctf_writer_clock.py index 14791467..02e9609b 100644 --- a/tests/bindings/python/bt2/test_ctf_writer_clock.py +++ b/tests/bindings/python/bt2/test_ctf_writer_clock.py @@ -8,6 +8,9 @@ class CtfWriterClockTestCase(unittest.TestCase): def setUp(self): self._clock = bt2.CtfWriterClock('salut') + def tearDown(self): + del self._clock + def test_create_default(self): self.assertEqual(self._clock.name, 'salut') diff --git a/tests/bindings/python/bt2/test_event.py b/tests/bindings/python/bt2/test_event.py index 6e7bdc67..ed6a107e 100644 --- a/tests/bindings/python/bt2/test_event.py +++ b/tests/bindings/python/bt2/test_event.py @@ -9,6 +9,9 @@ class EventTestCase(unittest.TestCase): def setUp(self): self._ec = self._create_ec() + def tearDown(self): + del self._ec + def _create_ec(self, with_eh=True, with_sec=True, with_ec=True, with_ep=True): # event header if with_eh: @@ -164,19 +167,19 @@ class EventTestCase(unittest.TestCase): def test_clock_value(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev = self._ec() - ev.set_clock_value(cc.create_clock_value(177)) - self.assertEqual(ev.get_clock_value(cc).cycles, 177) + ev.add_clock_value(cc(177)) + self.assertEqual(ev.clock_value(cc).cycles, 177) def test_no_clock_value(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev = self._ec() - self.assertIsNone(ev.get_clock_value(cc)) + self.assertIsNone(ev.clock_value(cc)) def test_no_packet(self): ev = self._ec() @@ -234,7 +237,7 @@ class EventTestCase(unittest.TestCase): def _get_full_ev(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev = self._ec() self._fill_ev(ev) @@ -280,70 +283,70 @@ class EventTestCase(unittest.TestCase): def test_eq(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev1 = self._ec() self._fill_ev(ev1) - ev1.set_clock_value(cc.create_clock_value(234)) + ev1.add_clock_value(cc(234)) ev2 = self._ec() self._fill_ev(ev2) - ev2.set_clock_value(cc.create_clock_value(234)) + ev2.add_clock_value(cc(234)) self.assertEqual(ev1, ev2) def test_ne_header_field(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev1 = self._ec() self._fill_ev(ev1) ev1.header_field['id'] = 19 - ev1.set_clock_value(cc.create_clock_value(234)) + ev1.add_clock_value(cc(234)) ev2 = self._ec() self._fill_ev(ev2) - ev2.set_clock_value(cc.create_clock_value(234)) + ev2.add_clock_value(cc(234)) self.assertNotEqual(ev1, ev2) def test_ne_stream_event_context_field(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev1 = self._ec() self._fill_ev(ev1) ev1.stream_event_context_field['cpu_id'] = 3 - ev1.set_clock_value(cc.create_clock_value(234)) + ev1.add_clock_value(cc(234)) ev2 = self._ec() self._fill_ev(ev2) - ev2.set_clock_value(cc.create_clock_value(234)) + ev2.add_clock_value(cc(234)) self.assertNotEqual(ev1, ev2) def test_ne_context_field(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev1 = self._ec() self._fill_ev(ev1) ev1.context_field['ant'] = -3 - ev1.set_clock_value(cc.create_clock_value(234)) + ev1.add_clock_value(cc(234)) ev2 = self._ec() self._fill_ev(ev2) - ev2.set_clock_value(cc.create_clock_value(234)) + ev2.add_clock_value(cc(234)) self.assertNotEqual(ev1, ev2) def test_ne_payload_field(self): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev1 = self._ec() self._fill_ev(ev1) ev1.payload_field['mosquito'] = 98 - ev1.set_clock_value(cc.create_clock_value(234)) + ev1.add_clock_value(cc(234)) ev2 = self._ec() self._fill_ev(ev2) - ev2.set_clock_value(cc.create_clock_value(234)) + ev2.add_clock_value(cc(234)) self.assertNotEqual(ev1, ev2) def test_eq_invalid(self): @@ -353,11 +356,11 @@ class EventTestCase(unittest.TestCase): def _test_copy(self, func): tc = bt2.Trace() tc.add_stream_class(self._ec.stream_class) - cc = bt2.ClockClass('hi') + cc = bt2.ClockClass('hi', 1000) tc.add_clock_class(cc) ev = self._ec() self._fill_ev(ev) - ev.set_clock_value(cc.create_clock_value(234)) + ev.add_clock_value(cc(234)) cpy = func(ev) self.assertIsNot(ev, cpy) self.assertNotEqual(ev.addr, cpy.addr) diff --git a/tests/bindings/python/bt2/test_event_class.py b/tests/bindings/python/bt2/test_event_class.py index 8d21b844..d1f6d8e7 100644 --- a/tests/bindings/python/bt2/test_event_class.py +++ b/tests/bindings/python/bt2/test_event_class.py @@ -13,14 +13,23 @@ class EventClassTestCase(unittest.TestCase): self._payload_ft.append_field('zoom', bt2.StringFieldType()) self._ec = bt2.EventClass('my_event') self._ec.id = 18 + self._ec.emf_uri = 'yes' + self._ec.log_level = bt2.EventClassLogLevel.INFO self._ec.context_field_type = self._context_ft self._ec.payload_field_type = self._payload_ft + def tearDown(self): + del self._context_ft + del self._payload_ft + del self._ec + def test_create(self): self.assertEqual(self._ec.name, 'my_event') self.assertEqual(self._ec.id, 18) self.assertEqual(self._ec.context_field_type, self._context_ft) self.assertEqual(self._ec.payload_field_type, self._payload_ft) + self.assertEqual(self._ec.emf_uri, 'yes') + self.assertEqual(self._ec.log_level, bt2.EventClassLogLevel.INFO) def test_create_invalid_no_name(self): with self.assertRaises(TypeError): @@ -30,12 +39,14 @@ class EventClassTestCase(unittest.TestCase): ec = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) self.assertEqual(ec.name, 'name') self.assertEqual(ec.id, 23) self.assertEqual(ec.context_field_type, self._context_ft) self.assertEqual(ec.payload_field_type, self._payload_ft) - self.assertEqual(ec.attributes['model.emf.uri'], 'my URI') + self.assertEqual(ec.emf_uri, 'my URI') + self.assertEqual(ec.log_level, bt2.EventClassLogLevel.WARNING) def test_assign_id(self): self._ec.id = 1717 @@ -94,90 +105,111 @@ class EventClassTestCase(unittest.TestCase): self.assertNotEqual(self._ec.context_field_type.addr, cpy.context_field_type.addr) self.assertNotEqual(self._ec.payload_field_type.addr, cpy.payload_field_type.addr) - def test_attr_getitem(self): - self.assertEqual(self._ec.attributes['id'], 18) - self.assertEqual(self._ec.attributes['name'], 'my_event') - - def test_attr_setitem(self): - self._ec.attributes['model.emf.uri'] = 'my url' - self.assertEqual(self._ec.attributes['model.emf.uri'], 'my url') + def test_assign_emf_uri(self): + self._ec.emf_uri = 'salut' + self.assertEqual(self._ec.emf_uri, 'salut') - def test_attr_len(self): - self.assertTrue(len(self._ec.attributes) != 0) + def test_assign_invalid_emf_uri(self): + with self.assertRaises(TypeError): + self._ec.emf_uri = 23 - def test_attr_iter(self): - for name, value in self._ec.attributes.items(): - self.assertIsInstance(value, values._Value) + def test_assign_log_level(self): + self._ec.log_level = bt2.EventClassLogLevel.EMERGENCY + self.assertEqual(self._ec.log_level, bt2.EventClassLogLevel.EMERGENCY) - if name == 'name': - self.assertEqual(value, 'my_event') - elif name == 'id': - self.assertEqual(value, 18) + def test_assign_invalid_log_level(self): + with self.assertRaises(ValueError): + self._ec.log_level = 'zoom' def test_eq(self): ec1 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) ec2 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) self.assertEqual(ec1, ec2) def test_ne_name(self): ec1 = bt2.EventClass(name='name1', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) ec2 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) self.assertNotEqual(ec1, ec2) def test_ne_id(self): ec1 = bt2.EventClass(name='name', id=24, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) ec2 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) self.assertNotEqual(ec1, ec2) def test_ne_context_field_type(self): ec1 = bt2.EventClass(name='name', id=23, context_field_type=self._payload_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) ec2 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) self.assertNotEqual(ec1, ec2) def test_ne_payload_field_type(self): ec1 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._context_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) + ec2 = bt2.EventClass(name='name', id=23, + context_field_type=self._context_ft, + payload_field_type=self._payload_ft, + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) + self.assertNotEqual(ec1, ec2) + + def test_ne_emf_uri(self): + ec1 = bt2.EventClass(name='name', id=23, + context_field_type=self._context_ft, + payload_field_type=self._payload_ft, + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) ec2 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my UR', + log_level=bt2.EventClassLogLevel.WARNING) self.assertNotEqual(ec1, ec2) - def test_ne_attribute(self): + def test_ne_log_level(self): ec1 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my URI'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.WARNING) ec2 = bt2.EventClass(name='name', id=23, context_field_type=self._context_ft, payload_field_type=self._payload_ft, - attributes={'model.emf.uri': 'my UR'}) + emf_uri='my URI', + log_level=bt2.EventClassLogLevel.ERROR) self.assertNotEqual(ec1, ec2) def test_eq_invalid(self): diff --git a/tests/bindings/python/bt2/test_field_types.py b/tests/bindings/python/bt2/test_field_types.py index b265402d..763e30e6 100644 --- a/tests/bindings/python/bt2/test_field_types.py +++ b/tests/bindings/python/bt2/test_field_types.py @@ -73,7 +73,7 @@ class _TestIntegerFieldTypeProps: self._ft.encoding = 'hey' def test_assign_mapped_clock_class(self): - cc = bt2.ClockClass('name') + cc = bt2.ClockClass('name', 1000) self._ft.mapped_clock_class = cc self.assertEqual(self._ft.mapped_clock_class, cc) @@ -88,8 +88,12 @@ class IntegerFieldTypeTestCase(_TestIntegerFieldTypeProps, _TestCopySimple, def setUp(self): self._ft = bt2.IntegerFieldType(35) + def tearDown(self): + del self._ft + def test_create_default(self): self.assertEqual(self._ft.size, 35) + self.assertIsNone(self._ft.mapped_clock_class) def test_create_invalid_size(self): with self.assertRaises(TypeError): @@ -104,7 +108,7 @@ class IntegerFieldTypeTestCase(_TestIntegerFieldTypeProps, _TestCopySimple, ft = bt2.IntegerFieldType(0) def test_create_full(self): - cc = bt2.ClockClass('name') + cc = bt2.ClockClass('name', 1000) ft = bt2.IntegerFieldType(24, alignment=16, byte_order=bt2.ByteOrder.BIG_ENDIAN, is_signed=True, base=bt2.Base.OCTAL, @@ -133,6 +137,9 @@ class FloatingPointNumberFieldTypeTestCase(_TestCopySimple, _TestAlignmentProp, def setUp(self): self._ft = bt2.FloatingPointNumberFieldType() + def tearDown(self): + del self._ft + def test_create_default(self): pass @@ -177,6 +184,9 @@ class EnumerationFieldTypeTestCase(_TestIntegerFieldTypeProps, _TestInvalidEq, def setUp(self): self._ft = bt2.EnumerationFieldType(size=35) + def tearDown(self): + del self._ft + def test_create_from_int_ft(self): int_ft = bt2.IntegerFieldType(23) self._ft = bt2.EnumerationFieldType(int_ft) @@ -421,6 +431,9 @@ class StringFieldTypeTestCase(_TestCopySimple, _TestInvalidEq, def setUp(self): self._ft = bt2.StringFieldType() + def tearDown(self): + del self._ft + def test_create_default(self): pass @@ -551,11 +564,20 @@ class _TestFieldContainer(_TestInvalidEq, _TestCopySimple): with self.assertRaises(TypeError): self._ft.at_index('yes') + def test_at_index_out_of_bounds_after(self): + self._ft.append_field('c', bt2.IntegerFieldType(32)) + + with self.assertRaises(IndexError): + self._ft.at_index(len(self._ft)) + class StructureFieldTypeTestCase(_TestFieldContainer, unittest.TestCase): def setUp(self): self._ft = bt2.StructureFieldType() + def tearDown(self): + del self._ft + def test_create_default(self): self.assertEqual(self._ft.alignment, 1) @@ -592,6 +614,9 @@ class VariantFieldTypeTestCase(_TestFieldContainer, unittest.TestCase): def setUp(self): self._ft = bt2.VariantFieldType('path.to.tag') + def tearDown(self): + del self._ft + def test_create_default(self): self.assertEqual(self._ft.tag_name, 'path.to.tag') @@ -614,6 +639,10 @@ class ArrayFieldTypeTestCase(_TestInvalidEq, _TestCopySimple, self._elem_ft = bt2.IntegerFieldType(23) self._ft = bt2.ArrayFieldType(self._elem_ft, 45) + def tearDown(self): + del self._ft + del self._elem_ft + def test_create_default(self): self.assertEqual(self._ft.element_field_type, self._elem_ft) self.assertEqual(self._ft.length, 45) @@ -645,6 +674,10 @@ class SequenceFieldTypeTestCase(_TestInvalidEq, _TestCopySimple, self._elem_ft = bt2.IntegerFieldType(23) self._ft = bt2.SequenceFieldType(self._elem_ft, 'the.length') + def tearDown(self): + del self._ft + del self._elem_ft + def test_create_default(self): self.assertEqual(self._ft.element_field_type, self._elem_ft) self.assertEqual(self._ft.length_name, 'the.length') diff --git a/tests/bindings/python/bt2/test_fields.py b/tests/bindings/python/bt2/test_fields.py index c14e0dea..f8fbd4a5 100644 --- a/tests/bindings/python/bt2/test_fields.py +++ b/tests/bindings/python/bt2/test_fields.py @@ -595,7 +595,6 @@ def _inject_numeric_testing_methods(cls): # inject testing methods for each binary operation for name, binop in _BINOPS: - setattr(cls, test_binop_name('invalid_unknown'), partialmethod(_TestNumericField._test_binop_invalid_unknown, op=binop)) setattr(cls, test_binop_name('invalid_none'), partialmethod(_TestNumericField._test_binop_invalid_none, op=binop)) setattr(cls, test_binop_name('type_true'), partialmethod(_TestNumericField._test_binop_type_true, op=binop)) @@ -767,16 +766,48 @@ class IntegerFieldTestCase(_TestIntegerFieldCommon, unittest.TestCase): self._def_value = 17 self._def_new_value = -101 + def tearDown(self): + del self._ft + del self._field + del self._def + class EnumerationFieldTestCase(_TestIntegerFieldCommon, unittest.TestCase): def setUp(self): self._ft = bt2.EnumerationFieldType(size=32, is_signed=True) self._ft.append_mapping('whole range', -(2 ** 31), (2 ** 31) - 1) + self._ft.append_mapping('something', 17) + self._ft.append_mapping('speaker', 12, 16) + self._ft.append_mapping('can', 18, 2540) + self._ft.append_mapping('zip', -45, 1001) self._def = self._ft() self._def.value = 17 self._def_value = 17 self._def_new_value = -101 + def tearDown(self): + del self._ft + del self._def + + def test_mappings(self): + mappings = ( + ('whole range', -(2 ** 31), (2 ** 31) - 1), + ('something', 17, 17), + ('zip', -45, 1001), + ) + + total = 0 + index_set = set() + + for fm in self._def.mappings: + total += 1 + for index, mapping in enumerate(mappings): + if fm.name == mapping[0] and fm.lower == mapping[1] and fm.upper == mapping[2]: + index_set.add(index) + + self.assertEqual(total, 3) + self.assertTrue(0 in index_set and 1 in index_set and 2 in index_set) + class FloatingPointNumberFieldTestCase(_TestNumericField, unittest.TestCase): def setUp(self): @@ -787,6 +818,11 @@ class FloatingPointNumberFieldTestCase(_TestNumericField, unittest.TestCase): self._def_value = 52.7 self._def_new_value = -17.164857 + def tearDown(self): + del self._ft + del self._field + del self._def + def _test_invalid_op(self, cb): with self.assertRaises(TypeError): cb() @@ -871,6 +907,10 @@ class StringFieldTestCase(_TestCopySimple, unittest.TestCase): self._def.value = self._def_value self._def_new_value = 'Yes!' + def tearDown(self): + del self._ft + del self._def + def test_assign_int(self): with self.assertRaises(TypeError): self._def.value = 283 @@ -1059,6 +1099,11 @@ class ArrayFieldTestCase(_TestArraySequenceFieldCommon, unittest.TestCase): self._def[1] = 1847 self._def[2] = 1948754 + def tearDown(self): + del self._elem_ft + del self._ft + del self._def + class SequenceFieldTestCase(_TestArraySequenceFieldCommon, unittest.TestCase): def setUp(self): @@ -1071,6 +1116,12 @@ class SequenceFieldTestCase(_TestArraySequenceFieldCommon, unittest.TestCase): self._def[1] = 1847 self._def[2] = 1948754 + def tearDown(self): + del self._elem_ft + del self._ft + del self._def + del self._length_field + class StructureFieldTestCase(_TestCopySimple, unittest.TestCase): def setUp(self): @@ -1089,6 +1140,14 @@ class StructureFieldTestCase(_TestCopySimple, unittest.TestCase): self._def['C'] = 17.5 self._def['D'] = 16497 + def tearDown(self): + del self._ft0 + del self._ft1 + del self._ft2 + del self._ft3 + del self._ft + del self._def + def _modify_def(self): self._def['B'] = 'hola' @@ -1108,6 +1167,10 @@ class StructureFieldTestCase(_TestCopySimple, unittest.TestCase): self.assertIs(type(field), bt2.fields._IntegerField) self.assertEqual(field, -1872) + def test_at_index_out_of_bounds_after(self): + with self.assertRaises(IndexError): + self._def.at_index(len(self._ft)) + def test_eq(self): ft = bt2.StructureFieldType() ft.append_field('A', self._ft0) @@ -1208,3 +1271,88 @@ class StructureFieldTestCase(_TestCopySimple, unittest.TestCase): for vkey, vval in self._def.items(): val = orig_values[vkey] self.assertEqual(vval, val) + + +class VariantFieldTestCase(_TestCopySimple, unittest.TestCase): + def setUp(self): + self._tag_ft = bt2.EnumerationFieldType(size=32) + self._tag_ft.append_mapping('corner', 23) + self._tag_ft.append_mapping('zoom', 17, 20) + self._tag_ft.append_mapping('mellotron', 1001) + self._tag_ft.append_mapping('giorgio', 2000, 3000) + self._ft0 = bt2.IntegerFieldType(32, is_signed=True) + self._ft1 = bt2.StringFieldType() + self._ft2 = bt2.FloatingPointNumberFieldType() + self._ft3 = bt2.IntegerFieldType(17) + self._ft = bt2.VariantFieldType('salut', self._tag_ft) + self._ft.append_field('corner', self._ft0) + self._ft.append_field('zoom', self._ft1) + self._ft.append_field('mellotron', self._ft2) + self._ft.append_field('giorgio', self._ft3) + self._def = self._ft() + + def tearDown(self): + del self._tag_ft + del self._ft0 + del self._ft1 + del self._ft2 + del self._ft3 + del self._ft + del self._def + + def test_bool_op_true(self): + tag_field = self._tag_ft(1001) + self._def.field(tag_field).value = -17.34 + self.assertTrue(self._def) + + def test_bool_op_false(self): + self.assertFalse(self._def) + + def test_tag_field_none(self): + self.assertIsNone(self._def.tag_field) + + def test_tag_field(self): + tag_field = self._tag_ft(2800) + self._def.field(tag_field).value = 1847 + self.assertEqual(self._def.tag_field, tag_field) + self.assertEqual(self._def.tag_field.addr, tag_field.addr) + + def test_selected_field_none(self): + self.assertIsNone(self._def.selected_field) + + def test_selected_field(self): + var_field1 = self._ft() + tag_field1 = self._tag_ft(1001) + var_field1.field(tag_field1).value = -17.34 + self.assertEqual(var_field1.field(), -17.34) + self.assertEqual(var_field1.selected_field, -17.34) + var_field2 = self._ft() + tag_field2 = self._tag_ft(2500) + var_field2.field(tag_field2).value = 1921 + self.assertEqual(var_field2.field(), 1921) + self.assertEqual(var_field2.selected_field, 1921) + + def test_eq(self): + tag_ft = bt2.EnumerationFieldType(size=32) + tag_ft.append_mapping('corner', 23) + tag_ft.append_mapping('zoom', 17, 20) + tag_ft.append_mapping('mellotron', 1001) + tag_ft.append_mapping('giorgio', 2000, 3000) + ft0 = bt2.IntegerFieldType(32, is_signed=True) + ft1 = bt2.StringFieldType() + ft2 = bt2.FloatingPointNumberFieldType() + ft3 = bt2.IntegerFieldType(17) + ft = bt2.VariantFieldType('salut', tag_ft) + ft.append_field('corner', ft0) + ft.append_field('zoom', ft1) + ft.append_field('mellotron', ft2) + ft.append_field('giorgio', ft3) + field = ft() + field_tag = tag_ft(23) + def_tag = self._tag_ft(23) + field.field(field_tag).value = 1774 + self._def.field(def_tag).value = 1774 + self.assertEqual(self._def, field) + + def test_eq_invalid_type(self): + self.assertNotEqual(self._def, 23) diff --git a/tests/bindings/python/bt2/test_graph.py b/tests/bindings/python/bt2/test_graph.py new file mode 100644 index 00000000..7ce3ed8c --- /dev/null +++ b/tests/bindings/python/bt2/test_graph.py @@ -0,0 +1,471 @@ +from bt2 import values +import collections +import unittest +import copy +import bt2 + + +class GraphTestCase(unittest.TestCase): + def setUp(self): + self._graph = bt2.Graph() + + def tearDown(self): + del self._graph + + def test_create_empty(self): + graph = bt2.Graph() + + def test_add_component_user_cls(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + comp = self._graph.add_component(MySink, 'salut') + self.assertEqual(comp.name, 'salut') + + def test_add_component_gen_cls(self): + class MySink(bt2._UserSinkComponent): + def _consume(self): + pass + + comp = self._graph.add_component(MySink, 'salut') + assert(comp) + comp2 = self._graph.add_component(comp.component_class, 'salut2') + self.assertEqual(comp2.name, 'salut2') + + def test_add_component_params(self): + comp_params = None + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + nonlocal comp_params + comp_params = params + + def _consume(self): + pass + + params = {'hello': 23, 'path': '/path/to/stuff'} + comp = self._graph.add_component(MySink, 'salut', params) + self.assertEqual(params, comp_params) + del comp_params + + def test_add_component_invalid_cls_type(self): + with self.assertRaises(TypeError): + self._graph.add_component(int, 'salut') + + def test_connect_ports(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + conn = self._graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self.assertTrue(src.output_ports['out'].is_connected) + self.assertTrue(sink.input_ports['in'].is_connected) + self.assertEqual(src.output_ports['out'].connection, conn) + self.assertEqual(sink.input_ports['in'].connection, conn) + + def test_connect_ports_invalid_direction(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + + with self.assertRaises(TypeError): + conn = self._graph.connect_ports(sink.input_ports['in'], + src.output_ports['out']) + + def test_connect_ports_refused(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _accept_port_connection(self, port, other_port): + return False + + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + + with self.assertRaises(bt2.PortConnectionRefused): + conn = self._graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + + def test_connect_ports_canceled(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + self._graph.cancel() + + with self.assertRaises(bt2.GraphCanceled): + conn = self._graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + + def test_cancel(self): + self.assertFalse(self._graph.is_canceled) + self._graph.cancel() + self.assertTrue(self._graph.is_canceled) + + def test_run(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(self): + self._build_meta() + self._at = 0 + + def _build_meta(self): + self._trace = bt2.Trace() + self._sc = bt2.StreamClass() + self._ec = bt2.EventClass('salut') + self._my_int_ft = bt2.IntegerFieldType(32) + self._ec.payload_field_type = bt2.StructureFieldType() + self._ec.payload_field_type += collections.OrderedDict([ + ('my_int', self._my_int_ft), + ]) + self._sc.add_event_class(self._ec) + self._trace.add_stream_class(self._sc) + self._stream = self._sc() + self._packet = self._stream.create_packet() + + def _create_event(self, value): + ev = self._ec() + ev.payload_field['my_int'] = value + ev.packet = self._packet + return ev + + def __next__(self): + if self._at == 5: + raise bt2.Stop + + notif = bt2.EventNotification(self._create_event(self._at * 3)) + self._at += 1 + return notif + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + self._at = 0 + + def _consume(comp_self): + notif = next(comp_self._notif_iter) + + if comp_self._at == 0: + self.assertIsInstance(notif, bt2.StreamBeginningNotification) + elif comp_self._at == 1: + self.assertIsInstance(notif, bt2.PacketBeginningNotification) + elif comp_self._at >= 2 and comp_self._at <= 6: + self.assertIsInstance(notif, bt2.EventNotification) + self.assertEqual(notif.event.event_class.name, 'salut') + field = notif.event.payload_field['my_int'] + self.assertEqual(field, (comp_self._at - 2) * 3) + elif comp_self._at == 7: + self.assertIsInstance(notif, bt2.PacketEndNotification) + elif comp_self._at == 8: + self.assertIsInstance(notif, bt2.StreamEndNotification) + + comp_self._at += 1 + + def _port_connected(self, port, other_port): + self._notif_iter = port.connection.create_notification_iterator() + + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + conn = self._graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + self._graph.run() + + def test_run_again(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(self): + self._build_meta() + self._at = 0 + + def _build_meta(self): + self._trace = bt2.Trace() + self._sc = bt2.StreamClass() + self._ec = bt2.EventClass('salut') + self._my_int_ft = bt2.IntegerFieldType(32) + self._ec.payload_field_type = bt2.StructureFieldType() + self._ec.payload_field_type += collections.OrderedDict([ + ('my_int', self._my_int_ft), + ]) + self._sc.add_event_class(self._ec) + self._trace.add_stream_class(self._sc) + self._stream = self._sc() + self._packet = self._stream.create_packet() + + def _create_event(self, value): + ev = self._ec() + ev.payload_field['my_int'] = value + ev.packet = self._packet + return ev + + def __next__(self): + if self._at == 1: + raise bt2.TryAgain + + notif = bt2.EventNotification(self._create_event(self._at * 3)) + self._at += 1 + return notif + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + self._at = 0 + + def _consume(comp_self): + if comp_self._at == 0: + notif = next(comp_self._notif_iter) + self.assertIsInstance(notif, bt2.EventNotification) + elif comp_self._at == 1: + with self.assertRaises(bt2.TryAgain): + notif = next(comp_self._notif_iter) + + raise bt2.TryAgain + + comp_self._at += 1 + + def _port_connected(self, port, other_port): + types = [bt2.EventNotification] + self._notif_iter = port.connection.create_notification_iterator(types) + + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + conn = self._graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + + with self.assertRaises(bt2.TryAgain): + self._graph.run() + + def test_run_no_sink(self): + class MyIter(bt2._UserNotificationIterator): + pass + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + self._add_input_port('in') + + src = self._graph.add_component(MySource, 'src') + flt = self._graph.add_component(MyFilter, 'flt') + conn = self._graph.connect_ports(src.output_ports['out'], + flt.input_ports['in']) + + with self.assertRaises(bt2.NoSinkComponent): + self._graph.run() + + def test_run_error(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(self): + self._build_meta() + self._at = 0 + + def _build_meta(self): + self._trace = bt2.Trace() + self._sc = bt2.StreamClass() + self._ec = bt2.EventClass('salut') + self._my_int_ft = bt2.IntegerFieldType(32) + self._ec.payload_field_type = bt2.StructureFieldType() + self._ec.payload_field_type += collections.OrderedDict([ + ('my_int', self._my_int_ft), + ]) + self._sc.add_event_class(self._ec) + self._trace.add_stream_class(self._sc) + self._stream = self._sc() + self._packet = self._stream.create_packet() + + def _create_event(self, value): + ev = self._ec() + ev.payload_field['my_int'] = value + ev.packet = self._packet + return ev + + def __next__(self): + if self._at == 1: + raise bt2.TryAgain + + notif = bt2.EventNotification(self._create_event(self._at * 3)) + self._at += 1 + return notif + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + self._at = 0 + + def _consume(comp_self): + if comp_self._at == 0: + notif = next(comp_self._notif_iter) + self.assertIsInstance(notif, bt2.EventNotification) + elif comp_self._at == 1: + raise RuntimeError('error!') + + comp_self._at += 1 + + def _port_connected(self, port, other_port): + types = [bt2.EventNotification] + self._notif_iter = port.connection.create_notification_iterator(types) + + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + conn = self._graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + + with self.assertRaises(bt2.Error): + self._graph.run() + + def test_listeners(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + self._add_output_port('zero') + + def _port_connected(self, port, other_port): + self._output_ports['zero'].remove_from_component() + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + raise bt2.Stop + + def _port_connected(self, port, other_port): + self._add_input_port('taste') + + def _port_disconnected(self, port): + port.remove_from_component() + + def port_added_listener(port): + nonlocal calls + calls.append((port_added_listener, port)) + + def port_removed_listener(port): + nonlocal calls + calls.append((port_removed_listener, port)) + + def ports_connected_listener(upstream_port, downstream_port): + nonlocal calls + calls.append((ports_connected_listener, upstream_port, + downstream_port)) + + def ports_disconnected_listener(upstream_comp, downstream_comp, + upstream_port, downstream_port): + nonlocal calls + calls.append((ports_disconnected_listener, upstream_comp, + downstream_comp, upstream_port, downstream_port)) + + calls = [] + self._graph.add_listener(bt2.GraphListenerType.PORT_ADDED, + port_added_listener) + self._graph.add_listener(bt2.GraphListenerType.PORT_REMOVED, + port_removed_listener) + self._graph.add_listener(bt2.GraphListenerType.PORTS_CONNECTED, + ports_connected_listener) + self._graph.add_listener(bt2.GraphListenerType.PORTS_DISCONNECTED, + ports_disconnected_listener) + src = self._graph.add_component(MySource, 'src') + sink = self._graph.add_component(MySink, 'sink') + self._graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + sink.input_ports['in'].disconnect() + self.assertIs(calls[0][0], port_added_listener) + self.assertEqual(calls[0][1].name, 'out') + self.assertIs(calls[1][0], port_added_listener) + self.assertEqual(calls[1][1].name, 'zero') + self.assertIs(calls[2][0], port_added_listener) + self.assertEqual(calls[2][1].name, 'in') + self.assertIs(calls[3][0], port_removed_listener) + self.assertEqual(calls[3][1].name, 'zero') + self.assertIs(calls[4][0], port_added_listener) + self.assertEqual(calls[4][1].name, 'taste') + self.assertIs(calls[5][0], ports_connected_listener) + self.assertEqual(calls[5][1].name, 'out') + self.assertEqual(calls[5][2].name, 'in') + self.assertIs(calls[6][0], port_removed_listener) + self.assertEqual(calls[6][1].name, 'in') + self.assertIs(calls[7][0], ports_disconnected_listener) + self.assertEqual(calls[7][1].name, 'src') + self.assertEqual(calls[7][2].name, 'sink') + self.assertEqual(calls[7][3].name, 'out') + self.assertEqual(calls[7][4].name, 'in') + del calls diff --git a/tests/bindings/python/bt2/test_notification.py b/tests/bindings/python/bt2/test_notification.py new file mode 100644 index 00000000..62d48bd4 --- /dev/null +++ b/tests/bindings/python/bt2/test_notification.py @@ -0,0 +1,566 @@ +from bt2 import values +import collections +import unittest +import copy +import bt2 + + +class _NotificationTestCase(unittest.TestCase): + def setUp(self): + self._trace = bt2.Trace() + self._sc = bt2.StreamClass() + self._ec = bt2.EventClass('salut') + self._my_int_ft = bt2.IntegerFieldType(32) + self._ec.payload_field_type = bt2.StructureFieldType() + self._ec.payload_field_type += collections.OrderedDict([ + ('my_int', self._my_int_ft), + ]) + self._sc.add_event_class(self._ec) + self._clock_class = bt2.ClockClass('allo', 1000) + self._trace.add_clock_class(self._clock_class) + self._trace.packet_header_field_type = bt2.StructureFieldType() + self._trace.packet_header_field_type += collections.OrderedDict([ + ('hello', self._my_int_ft), + ]) + self._trace.add_stream_class(self._sc) + self._cc_prio_map = bt2.ClockClassPriorityMap() + self._cc_prio_map[self._clock_class] = 231 + self._stream = self._sc() + self._packet = self._stream.create_packet() + self._packet.header_field['hello'] = 19487 + self._event = self._ec() + self._event.add_clock_value(self._clock_class(1772)) + self._event.payload_field['my_int'] = 23 + self._event.packet = self._packet + + def tearDown(self): + del self._trace + del self._sc + del self._ec + del self._my_int_ft + del self._clock_class + del self._cc_prio_map + del self._stream + del self._packet + del self._event + + +class EventNotificationTestCase(_NotificationTestCase): + def test_create_no_cc_prio_map(self): + notif = bt2.EventNotification(self._event) + self.assertEqual(notif.event.addr, self._event.addr) + self.assertEqual(len(notif.clock_class_priority_map), 0) + + def test_create_with_cc_prio_map(self): + notif = bt2.EventNotification(self._event, self._cc_prio_map) + self.assertEqual(notif.event.addr, self._event.addr) + self.assertEqual(len(notif.clock_class_priority_map), 1) + self.assertEqual(notif.clock_class_priority_map.highest_priority_clock_class.addr, + self._clock_class.addr) + self.assertEqual(notif.clock_class_priority_map[self._clock_class], 231) + + def test_eq(self): + notif = bt2.EventNotification(self._event, self._cc_prio_map) + event_copy = copy.copy(self._event) + event_copy.packet = self._packet + cc_prio_map_copy = copy.copy(self._cc_prio_map) + notif2 = bt2.EventNotification(event_copy, cc_prio_map_copy) + self.assertEqual(notif, notif2) + + def test_ne_event(self): + notif = bt2.EventNotification(self._event, self._cc_prio_map) + event_copy = copy.copy(self._event) + event_copy.payload_field['my_int'] = 17 + event_copy.packet = self._packet + cc_prio_map_copy = copy.copy(self._cc_prio_map) + notif2 = bt2.EventNotification(event_copy, cc_prio_map_copy) + self.assertNotEqual(notif, notif2) + + def test_ne_cc_prio_map(self): + notif = bt2.EventNotification(self._event) + event_copy = copy.copy(self._event) + event_copy.packet = self._packet + cc_prio_map_copy = copy.copy(self._cc_prio_map) + notif2 = bt2.EventNotification(event_copy, cc_prio_map_copy) + self.assertNotEqual(notif, notif2) + + def test_eq_invalid(self): + notif = bt2.EventNotification(self._event) + self.assertNotEqual(notif, 23) + + def test_copy(self): + notif = bt2.EventNotification(self._event, self._cc_prio_map) + notif2 = copy.copy(notif) + self.assertEqual(notif, notif2) + + def test_deepcopy(self): + notif = bt2.EventNotification(self._event, self._cc_prio_map) + notif2 = copy.deepcopy(notif) + self.assertEqual(notif, notif2) + + +class PacketBeginningNotificationTestCase(_NotificationTestCase): + def test_create(self): + notif = bt2.PacketBeginningNotification(self._packet) + self.assertEqual(notif.packet.addr, self._packet.addr) + + def test_eq(self): + notif = bt2.PacketBeginningNotification(self._packet) + packet_copy = copy.copy(self._packet) + notif2 = bt2.PacketBeginningNotification(packet_copy) + self.assertEqual(notif, notif2) + + def test_ne_packet(self): + notif = bt2.PacketBeginningNotification(self._packet) + packet_copy = copy.copy(self._packet) + packet_copy.header_field['hello'] = 1847 + notif2 = bt2.PacketBeginningNotification(packet_copy) + self.assertNotEqual(notif, notif2) + + def test_eq_invalid(self): + notif = bt2.PacketBeginningNotification(self._packet) + self.assertNotEqual(notif, 23) + + def test_copy(self): + notif = bt2.PacketBeginningNotification(self._packet) + notif2 = copy.copy(notif) + self.assertEqual(notif, notif2) + + def test_deepcopy(self): + notif = bt2.PacketBeginningNotification(self._packet) + notif2 = copy.deepcopy(notif) + self.assertEqual(notif, notif2) + + +class PacketEndNotificationTestCase(_NotificationTestCase): + def test_create(self): + notif = bt2.PacketEndNotification(self._packet) + self.assertEqual(notif.packet.addr, self._packet.addr) + + def test_eq(self): + notif = bt2.PacketEndNotification(self._packet) + packet_copy = copy.copy(self._packet) + notif2 = bt2.PacketEndNotification(packet_copy) + self.assertEqual(notif, notif2) + + def test_ne_packet(self): + notif = bt2.PacketEndNotification(self._packet) + packet_copy = copy.copy(self._packet) + packet_copy.header_field['hello'] = 1847 + notif2 = bt2.PacketEndNotification(packet_copy) + self.assertNotEqual(notif, notif2) + + def test_eq_invalid(self): + notif = bt2.PacketEndNotification(self._packet) + self.assertNotEqual(notif, 23) + + def test_copy(self): + notif = bt2.PacketEndNotification(self._packet) + notif2 = copy.copy(notif) + self.assertEqual(notif, notif2) + + def test_deepcopy(self): + notif = bt2.PacketEndNotification(self._packet) + notif2 = copy.deepcopy(notif) + self.assertEqual(notif, notif2) + + +class StreamBeginningNotificationTestCase(_NotificationTestCase): + def test_create(self): + notif = bt2.StreamBeginningNotification(self._stream) + self.assertEqual(notif.stream.addr, self._stream.addr) + + def test_eq(self): + notif = bt2.StreamBeginningNotification(self._stream) + stream_copy = copy.copy(self._stream) + notif2 = bt2.StreamBeginningNotification(stream_copy) + self.assertEqual(notif, notif2) + + def test_ne_stream(self): + notif = bt2.StreamBeginningNotification(self._stream) + stream_copy = self._sc(name='salut') + notif2 = bt2.StreamBeginningNotification(stream_copy) + self.assertNotEqual(notif, notif2) + + def test_eq_invalid(self): + notif = bt2.StreamBeginningNotification(self._stream) + self.assertNotEqual(notif, 23) + + def test_copy(self): + notif = bt2.StreamBeginningNotification(self._stream) + notif2 = copy.copy(notif) + self.assertEqual(notif, notif2) + + def test_deepcopy(self): + notif = bt2.StreamBeginningNotification(self._stream) + notif2 = copy.deepcopy(notif) + self.assertEqual(notif, notif2) + + +class StreamEndNotificationTestCase(_NotificationTestCase): + def test_create(self): + notif = bt2.StreamEndNotification(self._stream) + self.assertEqual(notif.stream.addr, self._stream.addr) + + def test_eq(self): + notif = bt2.StreamEndNotification(self._stream) + stream_copy = copy.copy(self._stream) + notif2 = bt2.StreamEndNotification(stream_copy) + self.assertEqual(notif, notif2) + + def test_ne_stream(self): + notif = bt2.StreamEndNotification(self._stream) + stream_copy = self._sc(name='salut') + notif2 = bt2.StreamEndNotification(stream_copy) + self.assertNotEqual(notif, notif2) + + def test_eq_invalid(self): + notif = bt2.StreamEndNotification(self._stream) + self.assertNotEqual(notif, 23) + + def test_copy(self): + notif = bt2.StreamEndNotification(self._stream) + notif2 = copy.copy(notif) + self.assertEqual(notif, notif2) + + def test_deepcopy(self): + notif = bt2.StreamEndNotification(self._stream) + notif2 = copy.deepcopy(notif) + self.assertEqual(notif, notif2) + + +class InactivityNotificationTestCase(unittest.TestCase): + def setUp(self): + self._cc1 = bt2.ClockClass('cc1', 1000) + self._cc2 = bt2.ClockClass('cc2', 2000) + self._cc_prio_map = bt2.ClockClassPriorityMap() + self._cc_prio_map[self._cc1] = 25 + self._cc_prio_map[self._cc2] = 50 + + def tearDown(self): + del self._cc1 + del self._cc2 + del self._cc_prio_map + + def test_create_no_cc_prio_map(self): + notif = bt2.InactivityNotification() + self.assertEqual(len(notif.clock_class_priority_map), 0) + + def test_create_with_cc_prio_map(self): + notif = bt2.InactivityNotification(self._cc_prio_map) + notif.add_clock_value(self._cc1(123)) + notif.add_clock_value(self._cc2(19487)) + self.assertEqual(len(notif.clock_class_priority_map), 2) + self.assertEqual(notif.clock_class_priority_map, self._cc_prio_map) + self.assertEqual(notif.clock_value(self._cc1), 123) + self.assertEqual(notif.clock_value(self._cc2), 19487) + + def test_eq(self): + notif = bt2.InactivityNotification(self._cc_prio_map) + notif.add_clock_value(self._cc1(123)) + notif.add_clock_value(self._cc2(19487)) + cc_prio_map_copy = copy.copy(self._cc_prio_map) + notif2 = bt2.InactivityNotification(cc_prio_map_copy) + notif2.add_clock_value(self._cc1(123)) + notif2.add_clock_value(self._cc2(19487)) + self.assertEqual(notif, notif2) + + def test_ne_cc_prio_map(self): + notif = bt2.InactivityNotification(self._cc_prio_map) + notif.add_clock_value(self._cc1(123)) + notif.add_clock_value(self._cc2(19487)) + cc_prio_map_copy = copy.copy(self._cc_prio_map) + cc_prio_map_copy[self._cc2] = 23 + notif2 = bt2.InactivityNotification(cc_prio_map_copy) + self.assertNotEqual(notif, notif2) + + def test_ne_clock_value(self): + notif = bt2.InactivityNotification(self._cc_prio_map) + notif.add_clock_value(self._cc1(123)) + notif.add_clock_value(self._cc2(19487)) + notif2 = bt2.InactivityNotification(self._cc_prio_map) + notif.add_clock_value(self._cc1(123)) + notif.add_clock_value(self._cc2(1847)) + self.assertNotEqual(notif, notif2) + + def test_eq_invalid(self): + notif = bt2.InactivityNotification(self._cc_prio_map) + self.assertNotEqual(notif, 23) + + def test_copy(self): + notif = bt2.InactivityNotification(self._cc_prio_map) + notif.add_clock_value(self._cc1(123)) + notif.add_clock_value(self._cc2(19487)) + notif_copy = copy.copy(notif) + self.assertEqual(notif, notif_copy) + self.assertNotEqual(notif.addr, notif_copy.addr) + self.assertEqual(notif.clock_class_priority_map.addr, + notif_copy.clock_class_priority_map.addr) + self.assertEqual(notif_copy.clock_value(self._cc1), 123) + self.assertEqual(notif_copy.clock_value(self._cc2), 19487) + + def test_deepcopy(self): + notif = bt2.InactivityNotification(self._cc_prio_map) + notif.add_clock_value(self._cc1(123)) + notif.add_clock_value(self._cc2(19487)) + notif_copy = copy.deepcopy(notif) + self.assertEqual(notif, notif_copy) + self.assertNotEqual(notif.addr, notif_copy.addr) + self.assertNotEqual(notif.clock_class_priority_map.addr, + notif_copy.clock_class_priority_map.addr) + self.assertEqual(notif.clock_class_priority_map, + notif_copy.clock_class_priority_map) + self.assertNotEqual(list(notif.clock_class_priority_map)[0].addr, + list(notif_copy.clock_class_priority_map)[0].addr) + self.assertIsNone(notif_copy.clock_value(self._cc1)) + self.assertIsNone(notif_copy.clock_value(self._cc2)) + self.assertEqual(notif_copy.clock_value(list(notif_copy.clock_class_priority_map)[0]), 123) + self.assertEqual(notif_copy.clock_value(list(notif_copy.clock_class_priority_map)[1]), 19487) + + +class DiscardedPacketsNotificationTestCase(unittest.TestCase): + def setUp(self): + self._trace = bt2.Trace() + self._sc = bt2.StreamClass() + self._ec = bt2.EventClass('salut') + self._clock_class = bt2.ClockClass('yo', 1000) + self._uint64_int_ft = bt2.IntegerFieldType(64, mapped_clock_class=self._clock_class) + self._my_int_ft = bt2.IntegerFieldType(32) + self._ec.payload_field_type = bt2.StructureFieldType() + self._ec.payload_field_type += collections.OrderedDict([ + ('my_int', self._my_int_ft), + ]) + self._sc.add_event_class(self._ec) + self._sc.packet_context_field_type = bt2.StructureFieldType() + self._sc.packet_context_field_type += collections.OrderedDict([ + ('packet_seq_num', self._my_int_ft), + ('timestamp_begin', self._uint64_int_ft), + ('timestamp_end', self._uint64_int_ft), + ]) + self._trace.add_clock_class(self._clock_class) + self._trace.add_stream_class(self._sc) + self._stream = self._sc() + + def tearDown(self): + del self._trace + del self._sc + del self._ec + del self._clock_class + del self._uint64_int_ft + del self._my_int_ft + del self._stream + + def _create_event(self, packet): + event = self._ec() + event.payload_field['my_int'] = 23 + event.packet = packet + return event + + def _get_notif(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(iter_self): + packet1 = self._stream.create_packet() + packet1.context_field['packet_seq_num'] = 0 + packet1.context_field['timestamp_begin'] = 3 + packet1.context_field['timestamp_end'] = 6 + packet2 = self._stream.create_packet() + packet2.context_field['packet_seq_num'] = 5 + packet2.context_field['timestamp_begin'] = 7 + packet2.context_field['timestamp_end'] = 10 + iter_self._ev1 = self._create_event(packet1) + iter_self._ev2 = self._create_event(packet2) + iter_self._at = 0 + + def __next__(self): + if self._at == 0: + notif = bt2.EventNotification(self._ev1) + elif self._at == 1: + notif = bt2.EventNotification(self._ev2) + else: + raise bt2.Stop + + self._at += 1 + return notif + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(comp_self): + nonlocal the_notif + notif = next(comp_self._notif_iter) + + if type(notif) is bt2._DiscardedPacketsNotification: + the_notif = notif + raise bt2.Stop + + def _port_connected(self, port, other_port): + self._notif_iter = port.connection.create_notification_iterator() + + the_notif = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + graph.run() + return the_notif + + def test_create(self): + self.assertIsInstance(self._get_notif(), bt2._DiscardedPacketsNotification) + + def test_count(self): + self.assertEqual(self._get_notif().count, 4) + + def test_stream(self): + self.assertEqual(self._get_notif().stream.addr, self._stream.addr) + + def test_beginning_clock_value(self): + notif = self._get_notif() + beginning_clock_value = notif.beginning_clock_value + self.assertEqual(beginning_clock_value.clock_class, self._clock_class) + self.assertEqual(beginning_clock_value, 6) + + def test_end_clock_value(self): + notif = self._get_notif() + end_clock_value = notif.end_clock_value + self.assertEqual(end_clock_value.clock_class, self._clock_class) + self.assertEqual(end_clock_value, 7) + + def test_eq(self): + notif1 = self._get_notif() + notif2 = self._get_notif() + self.assertEqual(notif1, notif2) + + def test_eq_invalid(self): + notif1 = self._get_notif() + self.assertNotEqual(notif1, 23) + + +class DiscardedEventsNotificationTestCase(unittest.TestCase): + def setUp(self): + self._trace = bt2.Trace() + self._sc = bt2.StreamClass() + self._ec = bt2.EventClass('salut') + self._clock_class = bt2.ClockClass('yo', 1000) + self._uint64_int_ft = bt2.IntegerFieldType(64, mapped_clock_class=self._clock_class) + self._my_int_ft = bt2.IntegerFieldType(32) + self._ec.payload_field_type = bt2.StructureFieldType() + self._ec.payload_field_type += collections.OrderedDict([ + ('my_int', self._my_int_ft), + ]) + self._sc.add_event_class(self._ec) + self._sc.packet_context_field_type = bt2.StructureFieldType() + self._sc.packet_context_field_type += collections.OrderedDict([ + ('events_discarded', self._my_int_ft), + ('timestamp_begin', self._uint64_int_ft), + ('timestamp_end', self._uint64_int_ft), + ]) + self._trace.add_clock_class(self._clock_class) + self._trace.add_stream_class(self._sc) + self._stream = self._sc() + + def tearDown(self): + del self._trace + del self._sc + del self._ec + del self._clock_class + del self._uint64_int_ft + del self._my_int_ft + del self._stream + + def _create_event(self, packet): + event = self._ec() + event.payload_field['my_int'] = 23 + event.packet = packet + return event + + def _get_notif(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(iter_self): + packet1 = self._stream.create_packet() + packet1.context_field['events_discarded'] = 0 + packet1.context_field['timestamp_begin'] = 3 + packet1.context_field['timestamp_end'] = 6 + packet2 = self._stream.create_packet() + packet2.context_field['events_discarded'] = 10 + packet2.context_field['timestamp_begin'] = 7 + packet2.context_field['timestamp_end'] = 10 + iter_self._ev1 = self._create_event(packet1) + iter_self._ev2 = self._create_event(packet2) + iter_self._at = 0 + + def __next__(self): + if self._at == 0: + notif = bt2.EventNotification(self._ev1) + elif self._at == 1: + notif = bt2.EventNotification(self._ev2) + else: + raise bt2.Stop + + self._at += 1 + return notif + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(comp_self): + nonlocal the_notif + notif = next(comp_self._notif_iter) + + if type(notif) is bt2._DiscardedEventsNotification: + the_notif = notif + raise bt2.Stop + + def _port_connected(self, port, other_port): + self._notif_iter = port.connection.create_notification_iterator() + + the_notif = None + graph = bt2.Graph() + src = graph.add_component(MySource, 'src') + sink = graph.add_component(MySink, 'sink') + conn = graph.connect_ports(src.output_ports['out'], + sink.input_ports['in']) + graph.run() + return the_notif + + def test_create(self): + self.assertIsInstance(self._get_notif(), bt2._DiscardedEventsNotification) + + def test_count(self): + self.assertEqual(self._get_notif().count, 10) + + def test_stream(self): + self.assertEqual(self._get_notif().stream.addr, self._stream.addr) + + def test_beginning_clock_value(self): + notif = self._get_notif() + beginning_clock_value = notif.beginning_clock_value + self.assertEqual(beginning_clock_value.clock_class, self._clock_class) + self.assertEqual(beginning_clock_value, 6) + + def test_end_clock_value(self): + notif = self._get_notif() + end_clock_value = notif.end_clock_value + self.assertEqual(end_clock_value.clock_class, self._clock_class) + self.assertEqual(end_clock_value, 10) + + def test_eq(self): + notif1 = self._get_notif() + notif2 = self._get_notif() + self.assertEqual(notif1, notif2) + + def test_eq_invalid(self): + notif1 = self._get_notif() + self.assertNotEqual(notif1, 23) diff --git a/tests/bindings/python/bt2/test_notification_iterator.py b/tests/bindings/python/bt2/test_notification_iterator.py new file mode 100644 index 00000000..04ed79d8 --- /dev/null +++ b/tests/bindings/python/bt2/test_notification_iterator.py @@ -0,0 +1,120 @@ +from bt2 import values +import unittest +import copy +import bt2 + + +class UserNotificationIteratorTestCase(unittest.TestCase): + @staticmethod + def _create_graph(src_comp_cls): + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + next(self._notif_iter) + + def _port_connected(self, port, other_port): + self._notif_iter = port.connection.create_notification_iterator() + + graph = bt2.Graph() + src_comp = graph.add_component(src_comp_cls, 'src') + sink_comp = graph.add_component(MySink, 'sink') + graph.connect_ports(src_comp.output_ports['out'], + sink_comp.input_ports['in']) + return graph + + def test_init(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(self): + nonlocal initialized + initialized = True + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + initialized = False + graph = self._create_graph(MySource) + self.assertTrue(initialized) + + def test_finalize(self): + class MyIter(bt2._UserNotificationIterator): + def _finalize(self): + nonlocal finalized + finalized = True + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + finalized = False + graph = self._create_graph(MySource) + del graph + self.assertTrue(finalized) + + def test_component(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(self): + nonlocal salut + salut = self._component._salut + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + self._salut = 23 + + salut = None + graph = self._create_graph(MySource) + self.assertEqual(salut, 23) + + def test_addr(self): + class MyIter(bt2._UserNotificationIterator): + def __init__(self): + nonlocal addr + addr = self.addr + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + addr = None + graph = self._create_graph(MySource) + self.assertIsNotNone(addr) + self.assertNotEqual(addr, 0) + + +class GenericNotificationIteratorTestCase(unittest.TestCase): + def test_component(self): + class MyIter(bt2._UserNotificationIterator): + pass + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(self, params): + self._add_output_port('out') + + class MySink(bt2._UserSinkComponent): + def __init__(self, params): + self._add_input_port('in') + + def _consume(self): + next(self._notif_iter) + + def _port_connected(self, port, other_port): + nonlocal upstream_comp + self._notif_iter = port.connection.create_notification_iterator() + upstream_comp = self._notif_iter.component + + upstream_comp = None + graph = bt2.Graph() + src_comp = graph.add_component(MySource, 'src') + sink_comp = graph.add_component(MySink, 'sink') + graph.connect_ports(src_comp.output_ports['out'], + sink_comp.input_ports['in']) + self.assertEqual(src_comp, upstream_comp) + del upstream_comp diff --git a/tests/bindings/python/bt2/test_packet.py b/tests/bindings/python/bt2/test_packet.py index 3ee58a15..51391cfb 100644 --- a/tests/bindings/python/bt2/test_packet.py +++ b/tests/bindings/python/bt2/test_packet.py @@ -9,6 +9,9 @@ class PacketTestCase(unittest.TestCase): def setUp(self): self._packet = self._create_packet() + def tearDown(self): + del self._packet + def _create_packet(self, with_ph=True, with_pc=True): # event header eh = bt2.StructureFieldType() diff --git a/tests/bindings/python/bt2/test_plugin.py b/tests/bindings/python/bt2/test_plugin.py new file mode 100644 index 00000000..6e461f3e --- /dev/null +++ b/tests/bindings/python/bt2/test_plugin.py @@ -0,0 +1,104 @@ +from bt2 import values +import unittest +import copy +import bt2 +import bt2.plugin +import os + + +_TEST_PLUGIN_PLUGINS_PATH = os.environ['TEST_PLUGIN_PLUGINS_PATH'] + + +class PluginSetTestCase(unittest.TestCase): + def test_create(self): + pset = bt2.find_plugins(_TEST_PLUGIN_PLUGINS_PATH) + self.assertTrue(len(pset) >= 3) + + def test_getitem(self): + pset = bt2.find_plugins(_TEST_PLUGIN_PLUGINS_PATH) + self.assertTrue(pset[0].path.startswith(_TEST_PLUGIN_PLUGINS_PATH)) + + def test_iter(self): + pset = bt2.find_plugins(_TEST_PLUGIN_PLUGINS_PATH) + names = set() + + for plugin in pset: + names.add(plugin.name) + + self.assertTrue('ctf' in names) + self.assertTrue('utils' in names) + self.assertTrue('text' in names) + + +class FindPluginsTestCase(unittest.TestCase): + def test_find_none(self): + pset = bt2.find_plugins('/this/does/not/exist/246703df-cb85-46d5-8406-5e8dc4a88b41') + self.assertIsNone(pset) + + def test_find_dir(self): + pset = bt2.find_plugins(_TEST_PLUGIN_PLUGINS_PATH) + self.assertTrue(len(pset) >= 3) + + +class FindPluginTestCase(unittest.TestCase): + def test_find_none(self): + plugin = bt2.find_plugin('this-does-not-exist-246703df-cb85-46d5-8406-5e8dc4a88b41') + self.assertIsNone(plugin) + + def test_find_existing(self): + plugin = bt2.find_plugin('ctf') + self.assertIsInstance(plugin, bt2.plugin._Plugin) + + +class PluginTestCase(unittest.TestCase): + def setUp(self): + self._plugin = bt2.find_plugin('ctf') + + def tearDown(self): + del self._plugin + + def test_name(self): + self.assertEqual(self._plugin.name, 'ctf') + + def test_path(self): + self.assertTrue(self._plugin.path.startswith(_TEST_PLUGIN_PLUGINS_PATH)) + + def test_author(self): + self.assertTrue('Philippe Proulx' in self._plugin.author) + + def test_license(self): + self.assertTrue('MIT' in self._plugin.license) + + def test_description(self): + self.assertTrue('CTF source and sink support' in self._plugin.description) + + def test_version(self): + self.assertIsNone(self._plugin.version) + + def test_source_comp_classes_len(self): + self.assertEqual(len(self._plugin.source_component_classes), 2) + + def test_source_comp_classes_getitem(self): + self.assertEqual(self._plugin.source_component_classes['fs'].name, 'fs') + + def test_source_comp_classes_getitem_invalid(self): + with self.assertRaises(KeyError): + self._plugin.source_component_classes['lol'] + + def test_source_comp_classes_iter(self): + plugins = {} + + for cc_name, cc in self._plugin.source_component_classes.items(): + plugins[cc_name] = cc + + self.assertTrue('fs' in plugins) + self.assertTrue('lttng-live' in plugins) + self.assertEqual(plugins['fs'].name, 'fs') + self.assertEqual(plugins['lttng-live'].name, 'lttng-live') + + def test_filter_comp_classes_len(self): + plugin = bt2.find_plugin('utils') + self.assertEqual(len(plugin.filter_component_classes), 2) + + def test_sink_comp_classes_len(self): + self.assertEqual(len(self._plugin.sink_component_classes), 1) diff --git a/tests/bindings/python/bt2/test_port.py b/tests/bindings/python/bt2/test_port.py new file mode 100644 index 00000000..592a5b6f --- /dev/null +++ b/tests/bindings/python/bt2/test_port.py @@ -0,0 +1,888 @@ +from bt2 import values +import unittest +import copy +import bt2 + + +class PortTestCase(unittest.TestCase): + @staticmethod + def _create_comp(comp_cls, name=None): + graph = bt2.Graph() + + if name is None: + name = 'comp' + + return graph.add_component(comp_cls, name) + + def test_src_add_output_port(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port = comp_self._add_output_port('out') + self.assertEqual(port.name, 'out') + + comp = self._create_comp(MySource) + self.assertEqual(len(comp.output_ports), 1) + + + def test_flt_add_output_port(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port = comp_self._add_output_port('out') + self.assertEqual(port.name, 'out') + + comp = self._create_comp(MyFilter) + self.assertEqual(len(comp.output_ports), 1) + + def test_flt_add_input_port(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port = comp_self._add_input_port('in') + self.assertEqual(port.name, 'in') + + comp = self._create_comp(MyFilter) + self.assertEqual(len(comp.input_ports), 1) + + def test_sink_add_input_port(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('in') + self.assertEqual(port.name, 'in') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertEqual(len(comp.input_ports), 1) + + def test_user_src_output_ports_getitem(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + self.assertEqual(port3.addr, comp_self._output_ports['insert'].addr) + self.assertEqual(port2.addr, comp_self._output_ports['print'].addr) + self.assertEqual(port1.addr, comp_self._output_ports['clear'].addr) + + comp = self._create_comp(MySource) + + def test_user_flt_output_ports_getitem(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + self.assertEqual(port3.addr, comp_self._output_ports['insert'].addr) + self.assertEqual(port2.addr, comp_self._output_ports['print'].addr) + self.assertEqual(port1.addr, comp_self._output_ports['clear'].addr) + + comp = self._create_comp(MyFilter) + + def test_user_flt_input_ports_getitem(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + self.assertEqual(port3.addr, comp_self._input_ports['insert'].addr) + self.assertEqual(port2.addr, comp_self._input_ports['print'].addr) + self.assertEqual(port1.addr, comp_self._input_ports['clear'].addr) + + comp = self._create_comp(MyFilter) + + def test_user_sink_input_ports_getitem(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + self.assertEqual(port3.addr, comp_self._input_ports['insert'].addr) + self.assertEqual(port2.addr, comp_self._input_ports['print'].addr) + self.assertEqual(port1.addr, comp_self._input_ports['clear'].addr) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_user_src_output_ports_getitem_invalid_key(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + + with self.assertRaises(KeyError): + comp_self._output_ports['hello'] + + comp = self._create_comp(MySource) + + def test_user_flt_output_ports_getitem_invalid_key(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + + with self.assertRaises(KeyError): + comp_self._output_ports['hello'] + + comp = self._create_comp(MyFilter) + + def test_user_flt_input_ports_getitem_invalid_key(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + + with self.assertRaises(KeyError): + comp_self._input_ports['hello'] + + comp = self._create_comp(MyFilter) + + def test_user_sink_input_ports_getitem_invalid_key(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + + with self.assertRaises(KeyError): + comp_self._input_ports['hello'] + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_user_src_output_ports_len(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + self.assertEqual(len(comp_self._output_ports), 3) + + comp = self._create_comp(MySource) + + def test_user_flt_output_ports_len(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + self.assertEqual(len(comp_self._output_ports), 3) + + comp = self._create_comp(MyFilter) + + def test_user_flt_input_ports_len(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + self.assertEqual(len(comp_self._input_ports), 3) + + comp = self._create_comp(MyFilter) + + def test_user_sink_input_ports_len(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + self.assertEqual(len(comp_self._input_ports), 3) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_user_src_output_ports_iter(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + ports = [] + + for port_name, port in comp_self._output_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + + comp = self._create_comp(MySource) + + def test_user_flt_output_ports_iter(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + ports = [] + + for port_name, port in comp_self._output_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + + comp = self._create_comp(MyFilter) + + def test_user_flt_input_ports_iter(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + ports = [] + + for port_name, port in comp_self._input_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + + comp = self._create_comp(MyFilter) + + def test_user_sink_input_ports_iter(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + ports = [] + + for port_name, port in comp_self._input_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_gen_src_output_ports_getitem(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + port1 = None + port2 = None + port3 = None + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + + comp = self._create_comp(MySource) + self.assertEqual(port3.addr, comp.output_ports['insert'].addr) + self.assertEqual(port2.addr, comp.output_ports['print'].addr) + self.assertEqual(port1.addr, comp.output_ports['clear'].addr) + del port1 + del port2 + del port3 + + def test_gen_flt_output_ports_getitem(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + port1 = None + port2 = None + port3 = None + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + + comp = self._create_comp(MyFilter) + self.assertEqual(port3.addr, comp.output_ports['insert'].addr) + self.assertEqual(port2.addr, comp.output_ports['print'].addr) + self.assertEqual(port1.addr, comp.output_ports['clear'].addr) + del port1 + del port2 + del port3 + + def test_gen_flt_input_ports_getitem(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + port1 = None + port2 = None + port3 = None + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + + comp = self._create_comp(MyFilter) + self.assertEqual(port3.addr, comp.input_ports['insert'].addr) + self.assertEqual(port2.addr, comp.input_ports['print'].addr) + self.assertEqual(port1.addr, comp.input_ports['clear'].addr) + del port1 + del port2 + del port3 + + def test_gen_sink_input_ports_getitem(self): + port1 = None + port2 = None + port3 = None + + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertEqual(port3.addr, comp.input_ports['insert'].addr) + self.assertEqual(port2.addr, comp.input_ports['print'].addr) + self.assertEqual(port1.addr, comp.input_ports['clear'].addr) + del port1 + del port2 + del port3 + + def test_gen_src_output_ports_getitem_invalid_key(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + + comp = self._create_comp(MySource) + + with self.assertRaises(KeyError): + comp.output_ports['hello'] + + def test_gen_flt_output_ports_getitem_invalid_key(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + + comp = self._create_comp(MyFilter) + + with self.assertRaises(KeyError): + comp.output_ports['hello'] + + def test_gen_flt_input_ports_getitem_invalid_key(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + + comp = self._create_comp(MyFilter) + + with self.assertRaises(KeyError): + comp.input_ports['hello'] + + def test_gen_sink_input_ports_getitem_invalid_key(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + + with self.assertRaises(KeyError): + comp_self._input_ports['hello'] + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + with self.assertRaises(KeyError): + comp.input_ports['hello'] + + def test_gen_src_output_ports_len(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + + comp = self._create_comp(MySource) + self.assertEqual(len(comp.output_ports), 3) + + def test_gen_flt_output_ports_len(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_output_port('clear') + comp_self._add_output_port('print') + comp_self._add_output_port('insert') + + comp = self._create_comp(MyFilter) + self.assertEqual(len(comp.output_ports), 3) + + def test_gen_flt_input_ports_len(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + + comp = self._create_comp(MyFilter) + self.assertEqual(len(comp.input_ports), 3) + + def test_gen_sink_input_ports_len(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + comp_self._add_input_port('print') + comp_self._add_input_port('insert') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertEqual(len(comp.input_ports), 3) + + def test_gen_src_output_ports_iter(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + port1 = None + port2 = None + port3 = None + + class MySource(bt2._UserSourceComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + + comp = self._create_comp(MySource) + ports = [] + + for port_name, port in comp.output_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + del port1 + del port2 + del port3 + + def test_gen_flt_output_ports_iter(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + port1 = None + port2 = None + port3 = None + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_output_port('clear') + port2 = comp_self._add_output_port('print') + port3 = comp_self._add_output_port('insert') + + comp = self._create_comp(MyFilter) + ports = [] + + for port_name, port in comp.output_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + del port1 + del port2 + del port3 + + def test_gen_flt_input_ports_iter(self): + class MyIter(bt2._UserNotificationIterator): + def __next__(self): + raise bt2.Stop + + port1 = None + port2 = None + port3 = None + + class MyFilter(bt2._UserFilterComponent, + notification_iterator_class=MyIter): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + + comp = self._create_comp(MyFilter) + ports = [] + + for port_name, port in comp.input_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + del port1 + del port2 + del port3 + + def test_gen_sink_input_ports_iter(self): + port1 = None + port2 = None + port3 = None + + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + nonlocal port1, port2, port3 + port1 = comp_self._add_input_port('clear') + port2 = comp_self._add_input_port('print') + port3 = comp_self._add_input_port('insert') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + ports = [] + + for port_name, port in comp.input_ports.items(): + ports.append((port_name, port)) + + self.assertEqual(ports[0][0], 'clear') + self.assertEqual(ports[0][1].addr, port1.addr) + self.assertEqual(ports[1][0], 'print') + self.assertEqual(ports[1][1].addr, port2.addr) + self.assertEqual(ports[2][0], 'insert') + self.assertEqual(ports[2][1].addr, port3.addr) + del port1 + del port2 + del port3 + + def test_name(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertEqual(comp.input_ports['clear'].name, 'clear') + + def test_component(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertEqual(comp.input_ports['clear'].component.addr, comp.addr) + + def test_connection_none(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertIsNone(comp.input_ports['clear'].connection) + + def test_is_connected_false(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertFalse(comp.input_ports['clear'].is_connected) + + def test_eq(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertEqual(comp.input_ports['clear'], + comp.input_ports['clear']) + + def test_eq_invalid(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + comp_self._add_input_port('clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + self.assertNotEqual(comp.input_ports['clear'], 23) + + def test_disconnect_no_connection(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + comp.input_ports['clear'].disconnect() + + def test_priv_name(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + self.assertEqual(port.name, 'clear') + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_priv_component(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + self.assertEqual(port.component, comp_self) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_priv_connection_none(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + self.assertIsNone(port.connection) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_priv_is_connected_false(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + self.assertFalse(port.is_connected) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_priv_eq(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + self.assertEqual(port, port) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_priv_eq_invalid(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + self.assertNotEqual(port, 23) + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_priv_disconnect_no_connection(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + port.disconnect() + + def _consume(self): + pass + + comp = self._create_comp(MySink) + + def test_priv_remove_from_component(self): + class MySink(bt2._UserSinkComponent): + def __init__(comp_self, params): + port = comp_self._add_input_port('clear') + self.assertEqual(len(comp_self._input_ports), 1) + + try: + port.remove_from_component() + except: + import traceback + traceback.print_exc() + + self.assertEqual(len(comp_self._input_ports), 0) + self.assertIsNone(port.component) + + def _consume(self): + pass + + comp = self._create_comp(MySink) diff --git a/tests/bindings/python/bt2/test_stream.py b/tests/bindings/python/bt2/test_stream.py index afeb80fb..7accbcdd 100644 --- a/tests/bindings/python/bt2/test_stream.py +++ b/tests/bindings/python/bt2/test_stream.py @@ -7,9 +7,12 @@ import bt2 class StreamTestCase(unittest.TestCase): def setUp(self): - self._stream = self._create_stream() + self._stream = self._create_stream(stream_id=23) - def _create_stream(self, name='my_stream'): + def tearDown(self): + del self._stream + + def _create_stream(self, name='my_stream', stream_id=None): # event header eh = bt2.StructureFieldType() eh += OrderedDict(( @@ -71,7 +74,7 @@ class StreamTestCase(unittest.TestCase): tc.add_stream_class(sc) # stream - return sc(name=name) + return sc(name=name, id=stream_id) def test_attr_stream_class(self): self.assertIsNotNone(self._stream.stream_class) @@ -80,13 +83,18 @@ class StreamTestCase(unittest.TestCase): self.assertEqual(self._stream.name, 'my_stream') def test_eq(self): - stream1 = self._create_stream() - stream2 = self._create_stream() + stream1 = self._create_stream(stream_id=17) + stream2 = self._create_stream(stream_id=17) self.assertEqual(stream1, stream2) def test_ne_name(self): - stream1 = self._create_stream() - stream2 = self._create_stream('lel') + stream1 = self._create_stream(stream_id=17) + stream2 = self._create_stream('lel', 17) + self.assertNotEqual(stream1, stream2) + + def test_ne_id(self): + stream1 = self._create_stream(stream_id=17) + stream2 = self._create_stream(stream_id=23) self.assertNotEqual(stream1, stream2) def test_eq_invalid(self): diff --git a/tests/bindings/python/bt2/test_stream_class.py b/tests/bindings/python/bt2/test_stream_class.py index 84bf286a..ae005778 100644 --- a/tests/bindings/python/bt2/test_stream_class.py +++ b/tests/bindings/python/bt2/test_stream_class.py @@ -20,6 +20,13 @@ class StreamClassTestCase(unittest.TestCase): event_context_field_type=self._event_context_ft, event_classes=(self._ec1, self._ec2)) + def tearDown(self): + del self._packet_context_ft + del self._event_header_ft + del self._event_context_ft + del self._ec1 + del self._sc + def _create_event_classes(self): context_ft = bt2.StructureFieldType() context_ft.append_field('allo', bt2.StringFieldType()) @@ -38,8 +45,9 @@ class StreamClassTestCase(unittest.TestCase): self.assertEqual(self._sc.packet_context_field_type, self._packet_context_ft) self.assertEqual(self._sc.event_header_field_type, self._event_header_ft) self.assertEqual(self._sc.event_context_field_type, self._event_context_ft) - self.assertEqual(self._sc['event23'], self._ec1) - self.assertEqual(self._sc['event17'], self._ec2) + self.assertEqual(self._sc[23], self._ec1) + self.assertEqual(self._sc[17], self._ec2) + self.assertEqual(len(self._sc), 2) def test_assign_name(self): self._sc.name = 'lel' @@ -57,6 +65,10 @@ class StreamClassTestCase(unittest.TestCase): with self.assertRaises(TypeError): self._sc.id = 'lel' + def test_no_id(self): + sc = bt2.StreamClass() + self.assertIsNone(sc.id) + def test_assign_packet_context_field_type(self): self._sc.packet_context_field_type = self._event_context_ft self.assertEqual(self._sc.packet_context_field_type, self._event_context_ft) @@ -121,36 +133,29 @@ class StreamClassTestCase(unittest.TestCase): self.assertNotEqual(self._sc.event_context_field_type.addr, cpy.event_context_field_type.addr) def test_getitem(self): - self.assertEqual(self._sc['event23'], self._ec1) - self.assertEqual(self._sc['event17'], self._ec2) + self.assertEqual(self._sc[23], self._ec1) + self.assertEqual(self._sc[17], self._ec2) def test_getitem_wrong_key_type(self): with self.assertRaises(TypeError): - self._sc[23] + self._sc['event23'] def test_getitem_wrong_key(self): with self.assertRaises(KeyError): - self._sc['hello'] + self._sc[19] def test_len(self): self.assertEqual(len(self._sc), 2) def test_iter(self): - for name, event_class in self._sc.items(): + for ec_id, event_class in self._sc.items(): self.assertIsInstance(event_class, bt2.EventClass) - if name == 'event23': + if ec_id == 23: self.assertEqual(event_class, self._ec1) - elif name == 'event17': + elif ec_id == 17: self.assertEqual(event_class, self._ec2) - def test_event_class_with_id(self): - self.assertEqual(self._sc.event_class_with_id(23), self._ec1) - - def test_event_class_with_id_wrong_type(self): - with self.assertRaises(TypeError): - self._sc.event_class_with_id('yes') - def test_eq(self): ec1, ec2 = self._create_event_classes() sc1 = bt2.StreamClass(name='my_stream_class', id=12, diff --git a/tests/bindings/python/bt2/test_trace.py b/tests/bindings/python/bt2/test_trace.py index b6979a43..b2b9cdad 100644 --- a/tests/bindings/python/bt2/test_trace.py +++ b/tests/bindings/python/bt2/test_trace.py @@ -10,6 +10,10 @@ class TraceTestCase(unittest.TestCase): self._sc = self._create_stream_class('sc1', 3) self._tc = bt2.Trace() + def tearDown(self): + del self._sc + del self._tc + def _create_stream_class(self, name, id): ec1, ec2 = self._create_event_classes() packet_context_ft = bt2.StructureFieldType() @@ -40,22 +44,26 @@ class TraceTestCase(unittest.TestCase): def test_create_default(self): self.assertEqual(len(self._tc), 0) - def test_create_full(self): + def _get_std_header(self): header_ft = bt2.StructureFieldType() header_ft.append_field('magic', bt2.IntegerFieldType(32)) - clock_classes = bt2.ClockClass('cc1'), bt2.ClockClass('cc2') + header_ft.append_field('stream_id', bt2.IntegerFieldType(32)) + return header_ft + + def test_create_full(self): + clock_classes = bt2.ClockClass('cc1', 1000), bt2.ClockClass('cc2', 30) sc = self._create_stream_class('sc1', 3) tc = bt2.Trace(name='my name', native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN, env={'the_string': 'value', 'the_int': 23}, - packet_header_field_type=header_ft, + packet_header_field_type=self._get_std_header(), clock_classes=clock_classes, stream_classes=(sc,)) self.assertEqual(tc.name, 'my name') self.assertEqual(tc.native_byte_order, bt2.ByteOrder.LITTLE_ENDIAN) self.assertEqual(tc.env['the_string'], 'value') self.assertEqual(tc.env['the_int'], 23) - self.assertEqual(tc.packet_header_field_type, header_ft) + self.assertEqual(tc.packet_header_field_type, self._get_std_header()) self.assertEqual(tc.clock_classes['cc1'], clock_classes[0]) self.assertEqual(tc.clock_classes['cc2'], clock_classes[1]) self.assertEqual(tc[3], sc) @@ -68,6 +76,10 @@ class TraceTestCase(unittest.TestCase): with self.assertRaises(TypeError): self._tc.name = 17 + def test_assign_static(self): + self._tc.set_is_static() + self.assertTrue(self._tc.is_static) + def test_assign_native_byte_order(self): self._tc.native_byte_order = bt2.ByteOrder.BIG_ENDIAN self.assertEqual(self._tc.native_byte_order, bt2.ByteOrder.BIG_ENDIAN) @@ -93,12 +105,13 @@ class TraceTestCase(unittest.TestCase): self.assertEqual(len(self._tc), len(cpy)) def _pre_copy(self): + self._tc.packet_header_field_type = self._get_std_header() self._tc.name = 'the trace class' sc1 = self._create_stream_class('sc1', 3) sc2 = self._create_stream_class('sc2', 9) sc3 = self._create_stream_class('sc3', 17) - self._tc.add_clock_class(bt2.ClockClass('cc1')) - self._tc.add_clock_class(bt2.ClockClass('cc2')) + self._tc.add_clock_class(bt2.ClockClass('cc1', 1000)) + self._tc.add_clock_class(bt2.ClockClass('cc2', 30)) self._tc.env['allo'] = 'bateau' self._tc.env['bateau'] = 'cart' self._tc.add_stream_class(sc1) @@ -145,6 +158,7 @@ class TraceTestCase(unittest.TestCase): self.assertEqual(len(self._tc), 1) def test_iter(self): + self._tc.packet_header_field_type = self._get_std_header() sc1 = self._create_stream_class('sc1', 3) sc2 = self._create_stream_class('sc2', 9) sc3 = self._create_stream_class('sc3', 17) @@ -170,16 +184,37 @@ class TraceTestCase(unittest.TestCase): with self.assertRaises(KeyError): self._tc.clock_classes['lel'] + def test_streams_none(self): + self.assertEqual(len(self._tc.streams), 0) + + def test_streams_len(self): + self._tc.add_stream_class(self._create_stream_class('sc1', 3)) + stream0 = self._tc[3]() + stream1 = self._tc[3]() + stream2 = self._tc[3]() + self.assertEqual(len(self._tc.streams), 3) + + def test_streams_iter(self): + self._tc.add_stream_class(self._create_stream_class('sc1', 3)) + stream0 = self._tc[3](id=12) + stream1 = self._tc[3](id=15) + stream2 = self._tc[3](id=17) + sids = set() + + for stream in self._tc.streams: + sids.add(stream.id) + + self.assertEqual(len(sids), 3) + self.assertTrue(12 in sids and 15 in sids and 17 in sids) + def _test_eq_create_objects(self): cc1_uuid = uuid.UUID('bc7f2f2d-2ee4-4e03-ab1f-2e0e1304e94f') - cc1 = bt2.ClockClass('cc1', uuid=cc1_uuid) + cc1 = bt2.ClockClass('cc1', 1000, uuid=cc1_uuid) cc2_uuid = uuid.UUID('da7d6b6f-3108-4706-89bd-ab554732611b') - cc2 = bt2.ClockClass('cc2', uuid=cc2_uuid) + cc2 = bt2.ClockClass('cc2', 30, uuid=cc2_uuid) sc1 = self._create_stream_class('sc1', 3) sc2 = self._create_stream_class('sc2', 9) - header_ft = bt2.StructureFieldType() - header_ft.append_field('magic', bt2.IntegerFieldType(32)) - return cc1, cc2, sc1, sc2, header_ft + return cc1, cc2, sc1, sc2, self._get_std_header() def test_eq(self): cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects() diff --git a/tests/bindings/python/bt2/test_values.py b/tests/bindings/python/bt2/test_values.py index f97af21a..57fd682b 100644 --- a/tests/bindings/python/bt2/test_values.py +++ b/tests/bindings/python/bt2/test_values.py @@ -19,7 +19,7 @@ class _TestFrozen: def test_frozen_exc(self): self._def.freeze() - with self.assertRaisesRegex(bt2.FrozenError, r'.* value object is frozen$') as cm: + with self.assertRaisesRegex(bt2.Frozen, r'.* value object is frozen$') as cm: self._modify_def() self.assertEqual(self._def, self._def_value) @@ -833,6 +833,11 @@ class BoolValueTestCase(_TestFrozenSimple, _TestCopySimple, unittest.TestCase): self._def_value = False self._def_new_value = True + def tearDown(self): + del self._f + del self._t + del self._def + def _assert_expecting_bool(self): return self.assertRaisesRegex(TypeError, r"expecting a 'bool' object") @@ -926,6 +931,12 @@ class IntegerValueTestCase(_TestNumericValue, unittest.TestCase): self._def_value = self._pv self._def_new_value = -101 + def tearDown(self): + del self._ip + del self._in + del self._def + del self._def_value + def _assert_expecting_int(self): return self.assertRaisesRegex(TypeError, r'expecting a number object') @@ -1039,6 +1050,12 @@ class FloatValueTestCase(_TestNumericValue, unittest.TestCase): self._def_value = self._pv self._def_new_value = -101.88 + def tearDown(self): + del self._fp + del self._fn + del self._def + del self._def_value + def _assert_expecting_float(self): return self.assertRaisesRegex(TypeError, r"expecting a real number object") @@ -1166,6 +1183,9 @@ class StringValueTestCase(_TestCopySimple, _TestFrozenSimple, unittest.TestCase) self._def = bt2.StringValue(self._def_value) self._def_new_value = 'Yes!' + def tearDown(self): + del self._def + def _assert_expecting_str(self): return self.assertRaises(TypeError) @@ -1280,6 +1300,9 @@ class ArrayValueTestCase(_TestFrozen, unittest.TestCase): self._def_value = [None, False, True, -23, 0, 42, -42.4, 23.17, 'yes'] self._def = bt2.ArrayValue(copy.deepcopy(self._def_value)) + def tearDown(self): + del self._def + def _modify_def(self): self._def[2] = 'xyz' @@ -1442,6 +1465,9 @@ class MapValueTestCase(_TestFrozen, unittest.TestCase): } self._def = bt2.MapValue(copy.deepcopy(self._def_value)) + def tearDown(self): + del self._def + def _modify_def(self): self._def['zero'] = 1 diff --git a/tests/bindings/python/bt2/testall.sh.in b/tests/bindings/python/bt2/testall.sh.in index 90657537..d682e4c8 100644 --- a/tests/bindings/python/bt2/testall.sh.in +++ b/tests/bindings/python/bt2/testall.sh.in @@ -4,6 +4,9 @@ check_coverage() { coverage run $@ } +export BABELTRACE_PYTHON_BT2_NO_TRACEBACK=1 +export TEST_PLUGIN_PLUGINS_PATH="@abs_top_builddir@/plugins" +export BABELTRACE_PLUGIN_PATH="@abs_top_builddir@/plugins/ctf:@abs_top_builddir@/plugins/utils:@abs_top_builddir@/plugins/text" PYTHON_BUILD_DIR="@abs_top_builddir@/bindings/python" BT2_NATIVE_LIBS_DIR="@abs_top_builddir@/bindings/python/bt2/.libs" TESTS_UTILS_PYTHON_DIR="@abs_top_srcdir@/tests/utils/python" diff --git a/tests/utils/Makefile.am b/tests/utils/Makefile.am index feae65a5..1e97a81c 100644 --- a/tests/utils/Makefile.am +++ b/tests/utils/Makefile.am @@ -1 +1,2 @@ SUBDIRS = tap +EXTRA_DIST = python/testrunner.py -- 2.34.1