bt2: Mass notification -> message rename
[babeltrace.git] / bindings / python / bt2 / bt2 / fields.py
CommitLineData
81447b5b
PP
1# The MIT License (MIT)
2#
811644b8 3# Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
81447b5b
PP
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in
13# all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21# THE SOFTWARE.
22
23from bt2 import native_bt, object, utils
24import bt2.field_types
25import collections.abc
26import functools
27import numbers
28import math
29import abc
30import bt2
31
32
e1c6bebd
JG
33def _get_leaf_field(obj):
34 if type(obj) is not _VariantField:
35 return obj
36
37 return _get_leaf_field(obj.selected_field)
38
39
81447b5b
PP
40def _create_from_ptr(ptr):
41 # recreate the field type wrapper of this field's type (the identity
42 # could be different, but the underlying address should be the
43 # same)
50842bdc 44 field_type_ptr = native_bt.field_get_type(ptr)
81447b5b
PP
45 utils._handle_ptr(field_type_ptr, "cannot get field object's type")
46 field_type = bt2.field_types._create_from_ptr(field_type_ptr)
50842bdc 47 typeid = native_bt.field_type_get_type_id(field_type._ptr)
81447b5b
PP
48 field = _TYPE_ID_TO_OBJ[typeid]._create_from_ptr(ptr)
49 field._field_type = field_type
50 return field
51
52
53class _Field(object._Object, metaclass=abc.ABCMeta):
54 def __copy__(self):
50842bdc 55 ptr = native_bt.field_copy(self._ptr)
81447b5b
PP
56 utils._handle_ptr(ptr, 'cannot copy {} field object'.format(self._NAME.lower()))
57 return _create_from_ptr(ptr)
58
59 def __deepcopy__(self, memo):
60 cpy = self.__copy__()
61 memo[id(self)] = cpy
62 return cpy
63
e1c6bebd
JG
64 def __eq__(self, other):
65 # special case: two unset fields with the same field type are equal
66 if isinstance(other, _Field):
67 if not self.is_set or not other.is_set:
68 if not self.is_set and not other.is_set and self.field_type == other.field_type:
69 return True
70 return False
71
72 other = _get_leaf_field(other)
73 return self._spec_eq(other)
74
81447b5b
PP
75 @property
76 def field_type(self):
77 return self._field_type
78
742e4747
JG
79 @property
80 def is_set(self):
50842bdc 81 is_set = native_bt.field_is_set(self._ptr)
742e4747
JG
82 return is_set > 0
83
84 def reset(self):
50842bdc 85 ret = native_bt.field_reset(self._ptr)
742e4747
JG
86 utils._handle_ret(ret, "cannot reset field object's value")
87
12bf0d88
JG
88 def _repr(self):
89 raise NotImplementedError
90
91 def __repr__(self):
92 return self._repr() if self.is_set else 'Unset'
93
81447b5b
PP
94
95@functools.total_ordering
96class _NumericField(_Field):
97 @staticmethod
98 def _extract_value(other):
99 if other is True or other is False:
100 return other
101
102 if isinstance(other, numbers.Integral):
103 return int(other)
104
105 if isinstance(other, numbers.Real):
106 return float(other)
107
108 if isinstance(other, numbers.Complex):
109 return complex(other)
110
111 raise TypeError("'{}' object is not a number object".format(other.__class__.__name__))
112
113 def __int__(self):
e1c6bebd 114 return int(self._value)
81447b5b
PP
115
116 def __float__(self):
e1c6bebd 117 return float(self._value)
81447b5b 118
12bf0d88 119 def _repr(self):
5abb9e33 120 return repr(self._value)
81447b5b
PP
121
122 def __lt__(self, other):
123 if not isinstance(other, numbers.Number):
124 raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__,
125 other.__class__.__name__))
126
e1c6bebd 127 return self._value < float(other)
81447b5b
PP
128
129 def __le__(self, other):
130 if not isinstance(other, numbers.Number):
131 raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__,
132 other.__class__.__name__))
133
e1c6bebd 134 return self._value <= float(other)
81447b5b 135
e1c6bebd 136 def _spec_eq(self, other):
81447b5b
PP
137 if not isinstance(other, numbers.Number):
138 return False
139
e1c6bebd 140 return self._value == complex(other)
81447b5b
PP
141
142 def __rmod__(self, other):
e1c6bebd 143 return self._extract_value(other) % self._value
81447b5b
PP
144
145 def __mod__(self, other):
e1c6bebd 146 return self._value % self._extract_value(other)
81447b5b
PP
147
148 def __rfloordiv__(self, other):
e1c6bebd 149 return self._extract_value(other) // self._value
81447b5b
PP
150
151 def __floordiv__(self, other):
e1c6bebd 152 return self._value // self._extract_value(other)
81447b5b
PP
153
154 def __round__(self, ndigits=None):
155 if ndigits is None:
e1c6bebd 156 return round(self._value)
81447b5b 157 else:
e1c6bebd 158 return round(self._value, ndigits)
81447b5b
PP
159
160 def __ceil__(self):
e1c6bebd 161 return math.ceil(self._value)
81447b5b
PP
162
163 def __floor__(self):
e1c6bebd 164 return math.floor(self._value)
81447b5b
PP
165
166 def __trunc__(self):
e1c6bebd 167 return int(self._value)
81447b5b
PP
168
169 def __abs__(self):
e1c6bebd 170 return abs(self._value)
81447b5b
PP
171
172 def __add__(self, other):
e1c6bebd 173 return self._value + self._extract_value(other)
81447b5b
PP
174
175 def __radd__(self, other):
176 return self.__add__(other)
177
178 def __neg__(self):
e1c6bebd 179 return -self._value
81447b5b
PP
180
181 def __pos__(self):
e1c6bebd 182 return +self._value
81447b5b
PP
183
184 def __mul__(self, other):
e1c6bebd 185 return self._value * self._extract_value(other)
81447b5b
PP
186
187 def __rmul__(self, other):
188 return self.__mul__(other)
189
190 def __truediv__(self, other):
e1c6bebd 191 return self._value / self._extract_value(other)
81447b5b
PP
192
193 def __rtruediv__(self, other):
e1c6bebd 194 return self._extract_value(other) / self._value
81447b5b
PP
195
196 def __pow__(self, exponent):
e1c6bebd 197 return self._value ** self._extract_value(exponent)
81447b5b
PP
198
199 def __rpow__(self, base):
e1c6bebd 200 return self._extract_value(base) ** self._value
81447b5b
PP
201
202 def __iadd__(self, other):
203 self.value = self + other
204 return self
205
206 def __isub__(self, other):
207 self.value = self - other
208 return self
209
210 def __imul__(self, other):
211 self.value = self * other
212 return self
213
214 def __itruediv__(self, other):
215 self.value = self / other
216 return self
217
218 def __ifloordiv__(self, other):
219 self.value = self // other
220 return self
221
222 def __imod__(self, other):
223 self.value = self % other
224 return self
225
226 def __ipow__(self, other):
227 self.value = self ** other
228 return self
229
230
231class _IntegralField(_NumericField, numbers.Integral):
232 def __lshift__(self, other):
e1c6bebd 233 return self._value << self._extract_value(other)
81447b5b
PP
234
235 def __rlshift__(self, other):
e1c6bebd 236 return self._extract_value(other) << self._value
81447b5b
PP
237
238 def __rshift__(self, other):
e1c6bebd 239 return self._value >> self._extract_value(other)
81447b5b
PP
240
241 def __rrshift__(self, other):
e1c6bebd 242 return self._extract_value(other) >> self._value
81447b5b
PP
243
244 def __and__(self, other):
e1c6bebd 245 return self._value & self._extract_value(other)
81447b5b
PP
246
247 def __rand__(self, other):
e1c6bebd 248 return self._extract_value(other) & self._value
81447b5b
PP
249
250 def __xor__(self, other):
e1c6bebd 251 return self._value ^ self._extract_value(other)
81447b5b
PP
252
253 def __rxor__(self, other):
e1c6bebd 254 return self._extract_value(other) ^ self._value
81447b5b
PP
255
256 def __or__(self, other):
e1c6bebd 257 return self._value | self._extract_value(other)
81447b5b
PP
258
259 def __ror__(self, other):
e1c6bebd 260 return self._extract_value(other) | self._value
81447b5b
PP
261
262 def __invert__(self):
e1c6bebd 263 return ~self._value
81447b5b
PP
264
265 def __ilshift__(self, other):
266 self.value = self << other
267 return self
268
269 def __irshift__(self, other):
270 self.value = self >> other
271 return self
272
273 def __iand__(self, other):
274 self.value = self & other
275 return self
276
277 def __ixor__(self, other):
278 self.value = self ^ other
279 return self
280
281 def __ior__(self, other):
282 self.value = self | other
283 return self
284
285
286class _RealField(_NumericField, numbers.Real):
287 pass
288
289
290class _IntegerField(_IntegralField):
291 _NAME = 'Integer'
292
293 def _value_to_int(self, value):
294 if not isinstance(value, numbers.Real):
295 raise TypeError('expecting a real number object')
296
297 value = int(value)
298
299 if self.field_type.is_signed:
300 utils._check_int64(value)
301 else:
302 utils._check_uint64(value)
303
304 return value
305
306 @property
e1c6bebd 307 def _value(self):
81447b5b 308 if self.field_type.is_signed:
50842bdc 309 ret, value = native_bt.field_signed_integer_get_value(self._ptr)
81447b5b 310 else:
50842bdc 311 ret, value = native_bt.field_unsigned_integer_get_value(self._ptr)
81447b5b 312
811644b8 313 if ret < 0:
e1c6bebd
JG
314 if not self.is_set:
315 return
316
317 utils._handle_ret(ret, "cannot get integer field's value")
811644b8 318
81447b5b
PP
319 return value
320
e1c6bebd 321 def _set_value(self, value):
81447b5b
PP
322 value = self._value_to_int(value)
323
324 if self.field_type.is_signed:
50842bdc 325 ret = native_bt.field_signed_integer_set_value(self._ptr, value)
81447b5b 326 else:
50842bdc 327 ret = native_bt.field_unsigned_integer_set_value(self._ptr, value)
81447b5b
PP
328
329 utils._handle_ret(ret, "cannot set integer field object's value")
330
e1c6bebd 331 value = property(fset=_set_value)
81447b5b 332
0b03f63e 333
81447b5b
PP
334class _FloatingPointNumberField(_RealField):
335 _NAME = 'Floating point number'
336
337 def _value_to_float(self, value):
338 if not isinstance(value, numbers.Real):
339 raise TypeError("expecting a real number object")
340
341 return float(value)
342
343 @property
e1c6bebd 344 def _value(self):
50842bdc 345 ret, value = native_bt.field_floating_point_get_value(self._ptr)
811644b8
PP
346
347 if ret < 0:
e1c6bebd
JG
348 if not self.is_set:
349 return
350
351 utils._handle_ret(ret, "cannot get floating point number field's value")
811644b8 352
81447b5b
PP
353 return value
354
e1c6bebd 355 def _set_value(self, value):
81447b5b 356 value = self._value_to_float(value)
50842bdc 357 ret = native_bt.field_floating_point_set_value(self._ptr, value)
81447b5b
PP
358 utils._handle_ret(ret, "cannot set floating point number field object's value")
359
e1c6bebd 360 value = property(fset=_set_value)
81447b5b 361
0b03f63e 362
81447b5b
PP
363class _EnumerationField(_IntegerField):
364 _NAME = 'Enumeration'
365
366 @property
367 def integer_field(self):
50842bdc 368 int_field_ptr = native_bt.field_enumeration_get_container(self._ptr)
811644b8 369 assert(int_field_ptr)
81447b5b
PP
370 return _create_from_ptr(int_field_ptr)
371
e1c6bebd 372 def _set_value(self, value):
81447b5b
PP
373 self.integer_field.value = value
374
12bf0d88 375 def _repr(self):
4addd228
JG
376 labels = [repr(v.name) for v in self.mappings]
377 return '{} ({})'.format(self._value, ', '.join(labels))
378
e1c6bebd
JG
379 value = property(fset=_set_value)
380
381 @property
382 def _value(self):
383 return self.integer_field._value
384
81447b5b
PP
385 @property
386 def mappings(self):
50842bdc 387 iter_ptr = native_bt.field_enumeration_get_mappings(self._ptr)
811644b8
PP
388 assert(iter_ptr)
389 return bt2.field_types._EnumerationFieldTypeMappingIterator(iter_ptr,
390 self.field_type.is_signed)
81447b5b
PP
391
392
393@functools.total_ordering
394class _StringField(_Field, collections.abc.Sequence):
395 _NAME = 'String'
396
397 def _value_to_str(self, value):
398 if isinstance(value, self.__class__):
e1c6bebd 399 value = value._value
81447b5b
PP
400
401 if not isinstance(value, str):
402 raise TypeError("expecting a 'str' object")
403
404 return value
405
406 @property
e1c6bebd 407 def _value(self):
50842bdc 408 value = native_bt.field_string_get_value(self._ptr)
81447b5b
PP
409 return value
410
e1c6bebd 411 def _set_value(self, value):
81447b5b 412 value = self._value_to_str(value)
50842bdc 413 ret = native_bt.field_string_set_value(self._ptr, value)
81447b5b
PP
414 utils._handle_ret(ret, "cannot set string field object's value")
415
e1c6bebd
JG
416 value = property(fset=_set_value)
417
418 def _spec_eq(self, other):
81447b5b
PP
419 try:
420 other = self._value_to_str(other)
421 except:
422 return False
423
e1c6bebd 424 return self._value == other
81447b5b
PP
425
426 def __le__(self, other):
e1c6bebd 427 return self._value <= self._value_to_str(other)
81447b5b
PP
428
429 def __lt__(self, other):
e1c6bebd 430 return self._value < self._value_to_str(other)
81447b5b
PP
431
432 def __bool__(self):
e1c6bebd 433 return bool(self._value)
81447b5b 434
12bf0d88 435 def _repr(self):
d623d2e9
JG
436 return repr(self._value)
437
81447b5b 438 def __str__(self):
12bf0d88 439 return self._value if self.is_set else repr(self)
81447b5b
PP
440
441 def __getitem__(self, index):
e1c6bebd 442 return self._value[index]
81447b5b
PP
443
444 def __len__(self):
e1c6bebd 445 return len(self._value)
81447b5b
PP
446
447 def __iadd__(self, value):
448 value = self._value_to_str(value)
50842bdc 449 ret = native_bt.field_string_append(self._ptr, value)
81447b5b
PP
450 utils._handle_ret(ret, "cannot append to string field object's value")
451 return self
452
453
454class _ContainerField(_Field):
455 def __bool__(self):
456 return len(self) != 0
457
458 def __len__(self):
459 count = self._count()
811644b8 460 assert(count >= 0)
81447b5b
PP
461 return count
462
463 def __delitem__(self, index):
464 raise NotImplementedError
465
466
467class _StructureField(_ContainerField, collections.abc.MutableMapping):
468 _NAME = 'Structure'
469
470 def _count(self):
471 return len(self.field_type)
472
473 def __getitem__(self, key):
474 utils._check_str(key)
50842bdc 475 ptr = native_bt.field_structure_get_field_by_name(self._ptr, key)
81447b5b
PP
476
477 if ptr is None:
478 raise KeyError(key)
479
480 return _create_from_ptr(ptr)
481
482 def __setitem__(self, key, value):
236355c2 483 # raises if key is somehow invalid
81447b5b
PP
484 field = self[key]
485
81447b5b
PP
486 # the field's property does the appropriate conversion or raises
487 # the appropriate exception
488 field.value = value
489
490 def at_index(self, index):
491 utils._check_uint64(index)
811644b8
PP
492
493 if index >= len(self):
494 raise IndexError
495
50842bdc 496 field_ptr = native_bt.field_structure_get_field_by_index(self._ptr, index)
811644b8 497 assert(field_ptr)
81447b5b
PP
498 return _create_from_ptr(field_ptr)
499
500 def __iter__(self):
501 # same name iterator
502 return iter(self.field_type)
503
e1c6bebd
JG
504 def _spec_eq(self, other):
505 try:
506 if len(self) != len(other):
81447b5b
PP
507 return False
508
e1c6bebd
JG
509 for self_key, self_value in self.items():
510 if self_key not in other:
511 return False
81447b5b 512
e1c6bebd 513 other_value = other[self_key]
81447b5b 514
e1c6bebd
JG
515 if self_value != other_value:
516 return False
517
518 return True
519 except:
520 return False
81447b5b 521
7c54e2e7 522 @property
e1c6bebd
JG
523 def _value(self):
524 return {key: value._value for key, value in self.items()}
7c54e2e7 525
e1c6bebd
JG
526 def _set_value(self, values):
527 original_values = self._value
7c54e2e7 528
e1c6bebd
JG
529 try:
530 for key, value in values.items():
531 self[key].value = value
532 except:
533 self.value = original_values
534 raise
7c54e2e7 535
e1c6bebd 536 value = property(fset=_set_value)
81447b5b 537
12bf0d88 538 def _repr(self):
ac7e2dc6
JG
539 items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()]
540 return '{{{}}}'.format(', '.join(items))
541
0b03f63e 542
81447b5b
PP
543class _VariantField(_Field):
544 _NAME = 'Variant'
545
546 @property
547 def tag_field(self):
50842bdc 548 field_ptr = native_bt.field_variant_get_tag(self._ptr)
811644b8
PP
549
550 if field_ptr is None:
551 return
552
81447b5b
PP
553 return _create_from_ptr(field_ptr)
554
555 @property
556 def selected_field(self):
557 return self.field()
558
559 def field(self, tag_field=None):
560 if tag_field is None:
50842bdc 561 field_ptr = native_bt.field_variant_get_current_field(self._ptr)
811644b8
PP
562
563 if field_ptr is None:
564 return
81447b5b
PP
565 else:
566 utils._check_type(tag_field, _EnumerationField)
50842bdc 567 field_ptr = native_bt.field_variant_get_field(self._ptr, tag_field._ptr)
81447b5b
PP
568 utils._handle_ptr(field_ptr, "cannot select variant field object's field")
569
570 return _create_from_ptr(field_ptr)
571
e1c6bebd
JG
572 def _spec_eq(self, other):
573 return _get_leaf_field(self) == other
811644b8
PP
574
575 def __bool__(self):
576 return bool(self.selected_field)
81447b5b 577
12bf0d88
JG
578 def __str__(self):
579 return str(self.selected_field) if self.is_set else repr(self)
580
581 def _repr(self):
582 return repr(self.selected_field)
872eeccb 583
e1c6bebd
JG
584 @property
585 def _value(self):
586 if self.selected_field is not None:
587 return self.selected_field._value
588
589 def _set_value(self, value):
590 self.selected_field.value = value
591
592 value = property(fset=_set_value)
81447b5b 593
0b03f63e 594
81447b5b
PP
595class _ArraySequenceField(_ContainerField, collections.abc.MutableSequence):
596 def __getitem__(self, index):
597 if not isinstance(index, numbers.Integral):
598 raise TypeError("'{}' is not an integral number object: invalid index".format(index.__class__.__name__))
599
600 index = int(index)
601
602 if index < 0 or index >= len(self):
603 raise IndexError('{} field object index is out of range'.format(self._NAME))
604
605 field_ptr = self._get_field_ptr_at_index(index)
811644b8 606 assert(field_ptr)
81447b5b
PP
607 return _create_from_ptr(field_ptr)
608
609 def __setitem__(self, index, value):
610 # we can only set numbers and strings
611 if not isinstance(value, (numbers.Number, _StringField, str)):
612 raise TypeError('expecting number or string object')
613
614 # raises if index is somehow invalid
615 field = self[index]
616
617 if not isinstance(field, (_NumericField, _StringField)):
618 raise TypeError('can only set the value of a number or string field')
619
620 # the field's property does the appropriate conversion or raises
621 # the appropriate exception
622 field.value = value
623
624 def insert(self, index, value):
625 raise NotImplementedError
626
e1c6bebd
JG
627 def _spec_eq(self, other):
628 try:
629 if len(self) != len(other):
81447b5b
PP
630 return False
631
e1c6bebd
JG
632 for self_field, other_field in zip(self, other):
633 if self_field != other_field:
634 return False
7c54e2e7 635
e1c6bebd
JG
636 return True
637 except:
638 return False
7c54e2e7 639
e1c6bebd
JG
640 @property
641 def _value(self):
642 return [field._value for field in self]
7c54e2e7 643
12bf0d88 644 def _repr(self):
2bc21382
JG
645 return '[{}]'.format(', '.join([repr(v) for v in self]))
646
81447b5b
PP
647
648class _ArrayField(_ArraySequenceField):
649 _NAME = 'Array'
650
651 def _count(self):
652 return self.field_type.length
653
654 def _get_field_ptr_at_index(self, index):
50842bdc 655 return native_bt.field_array_get_field(self._ptr, index)
81447b5b 656
e1c6bebd
JG
657 def _set_value(self, values):
658 if len(self) != len(values):
659 raise ValueError(
660 'expected length of value and array field to match')
661
662 original_values = self._value
663 try:
664 for index, value in enumerate(values):
665 if value is not None:
666 self[index].value = value
667 else:
668 self[index].reset()
669 except:
670 self.value = original_values
671 raise
672
673 value = property(fset=_set_value)
674
81447b5b
PP
675
676class _SequenceField(_ArraySequenceField):
677 _NAME = 'Sequence'
678
679 def _count(self):
a29094be 680 return int(self.length_field)
81447b5b
PP
681
682 @property
683 def length_field(self):
50842bdc 684 field_ptr = native_bt.field_sequence_get_length(self._ptr)
a29094be
JG
685 if field_ptr is None:
686 return
81447b5b
PP
687 return _create_from_ptr(field_ptr)
688
689 @length_field.setter
690 def length_field(self, length_field):
691 utils._check_type(length_field, _IntegerField)
50842bdc 692 ret = native_bt.field_sequence_set_length(self._ptr, length_field._ptr)
81447b5b
PP
693 utils._handle_ret(ret, "cannot set sequence field object's length field")
694
695 def _get_field_ptr_at_index(self, index):
50842bdc 696 return native_bt.field_sequence_get_field(self._ptr, index)
81447b5b 697
e1c6bebd
JG
698 def _set_value(self, values):
699 original_length_field = self.length_field
700 if original_length_field is not None:
701 original_values = self._value
702
703 if len(values) != self.length_field:
704 if self.length_field is not None:
705 length_ft = self.length_field.field_type
706 else:
707 length_ft = bt2.IntegerFieldType(size=64, is_signed=False)
708 self.length_field = length_ft(len(values))
709
710 try:
711 for index, value in enumerate(values):
712 if value is not None:
713 self[index].value = value
714 else:
715 self[index].reset()
716 except:
717 if original_length_field is not None:
718 self.length_field = original_length_field
719 self.value = original_values
720 else:
721 self.reset()
722 raise
723
724 value = property(fset=_set_value)
81447b5b 725
0b03f63e 726
81447b5b 727_TYPE_ID_TO_OBJ = {
81447b5b 728}
This page took 0.069466 seconds and 4 git commands to generate.