From f03b6364aec2d77bbb5ef0625cbaea8de4179f63 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Mon, 6 Apr 2020 20:22:16 -0400 Subject: [PATCH] bt2: make `_EventConst` a mapping The `_EventConst` class already implemented the __getitem__() method to make it easy to access a payload, specific context, common context, or packet context field (in this order) by name, for example: print(event['next_prio']) If two root fields contain fields which share the same name, one of them has the priority and the other one is not accessible through this interface. This patch makes the `_EventConst` class a full mapping protocol, inheriting `collections.abc.Mapping`. This patch implements the __iter__() and __len__() methods so as to let `collections.abc.Mapping` implement __contains__(), keys(), items(), values(), get(), __eq__(), and __ne__(). Now you can do, for example: if 'next_prio' in event: print(event['next_prio']) __iter__() keeps a set of yielded field names so as to avoid repeating field names. __len__() simply reuses __iter__(). `test_event.py` is updated to test the new features. Signed-off-by: Philippe Proulx Change-Id: I2485e0df2373005fada0d0732c6269acbce5457c Reviewed-on: https://review.lttng.org/c/babeltrace/+/3343 Tested-by: jenkins Reviewed-by: Simon Marchi --- src/bindings/python/bt2/bt2/event.py | 36 +++++- tests/bindings/python/bt2/test_event.py | 151 ++++++++++++++++++++---- 2 files changed, 165 insertions(+), 22 deletions(-) diff --git a/src/bindings/python/bt2/bt2/event.py b/src/bindings/python/bt2/bt2/event.py index 6a6c1185..e7ac59ab 100644 --- a/src/bindings/python/bt2/bt2/event.py +++ b/src/bindings/python/bt2/bt2/event.py @@ -25,9 +25,10 @@ from bt2 import event_class as bt2_event_class from bt2 import packet as bt2_packet from bt2 import stream as bt2_stream from bt2 import field as bt2_field +import collections.abc -class _EventConst(object._UniqueObject): +class _EventConst(object._UniqueObject, collections.abc.Mapping): _borrow_class_ptr = staticmethod(native_bt.event_borrow_class_const) _borrow_packet_ptr = staticmethod(native_bt.event_borrow_packet_const) _borrow_stream_ptr = staticmethod(native_bt.event_borrow_stream_const) @@ -131,6 +132,39 @@ class _EventConst(object._UniqueObject): raise KeyError(key) + def __iter__(self): + # To only yield unique keys, keep a set of member names that are + # already yielded. Two root structure fields (for example, + # payload and common context) can contain immediate members + # which share the same name. + member_names = set() + + if self.payload_field is not None: + for field_name in self.payload_field: + yield field_name + member_names.add(field_name) + + if self.specific_context_field is not None: + for field_name in self.specific_context_field: + if field_name not in member_names: + yield field_name + member_names.add(field_name) + + if self.common_context_field is not None: + for field_name in self.common_context_field: + if field_name not in member_names: + yield field_name + member_names.add(field_name) + + if self.packet and self.packet.context_field is not None: + for field_name in self.packet.context_field: + if field_name not in member_names: + yield field_name + member_names.add(field_name) + + def __len__(self): + return sum(1 for _ in self) + class _Event(_EventConst): _borrow_class_ptr = staticmethod(native_bt.event_borrow_class) diff --git a/tests/bindings/python/bt2/test_event.py b/tests/bindings/python/bt2/test_event.py index e8effef3..15007453 100644 --- a/tests/bindings/python/bt2/test_event.py +++ b/tests/bindings/python/bt2/test_event.py @@ -96,6 +96,7 @@ class EventTestCase(unittest.TestCase): cc += [ ('cpu_id', tc.create_signed_integer_field_class(8)), ('stuff', tc.create_double_precision_real_field_class()), + ('gnu', tc.create_string_field_class()), ] # packet context (stream-class-defined) @@ -192,6 +193,7 @@ class EventTestCase(unittest.TestCase): def event_fields_config(event): event.common_context_field['cpu_id'] = 1 event.common_context_field['stuff'] = 13.194 + event.common_context_field['gnu'] = 'salut' msg = self._create_test_const_event_message( event_fields_config=event_fields_config, with_cc=True @@ -199,6 +201,7 @@ class EventTestCase(unittest.TestCase): self.assertEqual(msg.event.common_context_field['cpu_id'], 1) self.assertEqual(msg.event.common_context_field['stuff'], 13.194) + self.assertEqual(msg.event.common_context_field['gnu'], 'salut') self.assertIs( type(msg.event.common_context_field), bt2_field._StructureFieldConst ) @@ -287,23 +290,30 @@ class EventTestCase(unittest.TestCase): msg = utils.get_event_message() self.assertIs(type(msg.event.stream), bt2_stream._Stream) - def test_const_getitem(self): - def event_fields_config(event): - event.payload_field['giraffe'] = 1 - event.payload_field['gnu'] = 23 - event.payload_field['mosquito'] = 42 - event.specific_context_field['ant'] = -1 - event.specific_context_field['msg'] = 'hellooo' - event.common_context_field['cpu_id'] = 1 - event.common_context_field['stuff'] = 13.194 - - def packet_fields_config(packet): - packet.context_field['something'] = 154 - packet.context_field['something_else'] = 17.2 + @staticmethod + def _event_payload_fields_config(event): + event.payload_field['giraffe'] = 1 + event.payload_field['gnu'] = 23 + event.payload_field['mosquito'] = 42 + + @staticmethod + def _event_fields_config(event): + EventTestCase._event_payload_fields_config(event) + event.specific_context_field['ant'] = -1 + event.specific_context_field['msg'] = 'hellooo' + event.common_context_field['cpu_id'] = 1 + event.common_context_field['stuff'] = 13.194 + event.common_context_field['gnu'] = 'salut' + + @staticmethod + def _packet_fields_config(packet): + packet.context_field['something'] = 154 + packet.context_field['something_else'] = 17.2 + def test_const_getitem(self): msg = self._create_test_const_event_message( - packet_fields_config=packet_fields_config, - event_fields_config=event_fields_config, + packet_fields_config=self._packet_fields_config, + event_fields_config=self._event_fields_config, with_cc=True, with_sc=True, with_ep=True, @@ -332,13 +342,8 @@ class EventTestCase(unittest.TestCase): ev['yes'] def test_const_getitem_no_packet(self): - def event_fields_config(event): - event.payload_field['giraffe'] = 1 - event.payload_field['gnu'] = 23 - event.payload_field['mosquito'] = 42 - msg = self._create_test_const_event_message( - event_fields_config=event_fields_config, with_ep=True, + event_fields_config=self._event_payload_fields_config, with_ep=True, ) ev = msg.event @@ -357,6 +362,110 @@ class EventTestCase(unittest.TestCase): self.assertEqual(ev['something'], 154) self.assertIs(type(ev['something']), bt2_field._UnsignedIntegerField) + def test_iter_full(self): + msg = self._create_test_const_event_message( + packet_fields_config=self._packet_fields_config, + event_fields_config=self._event_fields_config, + with_cc=True, + with_sc=True, + with_ep=True, + with_packet=True, + ) + expected_field_names = [ + # payload + 'giraffe', + 'gnu', + 'mosquito', + # specific context + 'ant', + 'msg', + # common context + 'cpu_id', + 'stuff', + # packet context + 'something', + 'something_else', + ] + self.assertEqual(list(msg.event), expected_field_names) + + def test_iter_payload_only(self): + msg = self._create_test_const_event_message( + event_fields_config=self._event_payload_fields_config, with_ep=True, + ) + expected_field_names = [ + # payload + 'giraffe', + 'gnu', + 'mosquito', + ] + self.assertEqual(list(msg.event), expected_field_names) + + def test_len_full(self): + msg = self._create_test_const_event_message( + packet_fields_config=self._packet_fields_config, + event_fields_config=self._event_fields_config, + with_cc=True, + with_sc=True, + with_ep=True, + with_packet=True, + ) + self.assertEqual(len(msg.event), 9) + + def test_len_payload_only(self): + msg = self._create_test_const_event_message( + packet_fields_config=None, + event_fields_config=self._event_payload_fields_config, + with_ep=True, + ) + self.assertEqual(len(msg.event), 3) + + def test_in_full(self): + msg = self._create_test_const_event_message( + packet_fields_config=self._packet_fields_config, + event_fields_config=self._event_fields_config, + with_cc=True, + with_sc=True, + with_ep=True, + with_packet=True, + ) + field_names = [ + # payload + 'giraffe', + 'gnu', + 'mosquito', + # specific context + 'ant', + 'msg', + # common context + 'cpu_id', + 'stuff', + # packet context + 'something', + 'something_else', + ] + + for field_name in field_names: + self.assertTrue(field_name in msg.event) + + self.assertFalse('lol' in msg.event) + + def test_in_payload_only(self): + msg = self._create_test_const_event_message( + packet_fields_config=None, + event_fields_config=self._event_payload_fields_config, + with_ep=True, + ) + field_names = [ + 'giraffe', + 'gnu', + 'mosquito', + ] + + for field_name in field_names: + self.assertTrue(field_name in msg.event) + + self.assertFalse('lol' in msg.event) + if __name__ == "__main__": unittest.main() -- 2.34.1