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