Fix: variable declaration shadows previously declared variable
[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)
44 field_type_ptr = native_bt.ctf_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.ctf_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
53class _Field(object._Object, metaclass=abc.ABCMeta):
54 def __copy__(self):
55 ptr = native_bt.ctf_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
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):
d4bf905a 81 is_set = native_bt.ctf_field_is_set(self._ptr)
742e4747
JG
82 return is_set > 0
83
84 def reset(self):
d4bf905a 85 ret = native_bt.ctf_field_reset(self._ptr)
742e4747
JG
86 utils._handle_ret(ret, "cannot reset field object's value")
87
81447b5b
PP
88
89@functools.total_ordering
90class _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):
e1c6bebd 108 return int(self._value)
81447b5b
PP
109
110 def __float__(self):
e1c6bebd 111 return float(self._value)
81447b5b
PP
112
113 def __str__(self):
e1c6bebd 114 return str(self._value)
81447b5b
PP
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
e1c6bebd 121 return self._value < float(other)
81447b5b
PP
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
e1c6bebd 128 return self._value <= float(other)
81447b5b 129
e1c6bebd 130 def _spec_eq(self, other):
81447b5b
PP
131 if not isinstance(other, numbers.Number):
132 return False
133
e1c6bebd 134 return self._value == complex(other)
81447b5b
PP
135
136 def __rmod__(self, other):
e1c6bebd 137 return self._extract_value(other) % self._value
81447b5b
PP
138
139 def __mod__(self, other):
e1c6bebd 140 return self._value % self._extract_value(other)
81447b5b
PP
141
142 def __rfloordiv__(self, other):
e1c6bebd 143 return self._extract_value(other) // self._value
81447b5b
PP
144
145 def __floordiv__(self, other):
e1c6bebd 146 return self._value // self._extract_value(other)
81447b5b
PP
147
148 def __round__(self, ndigits=None):
149 if ndigits is None:
e1c6bebd 150 return round(self._value)
81447b5b 151 else:
e1c6bebd 152 return round(self._value, ndigits)
81447b5b
PP
153
154 def __ceil__(self):
e1c6bebd 155 return math.ceil(self._value)
81447b5b
PP
156
157 def __floor__(self):
e1c6bebd 158 return math.floor(self._value)
81447b5b
PP
159
160 def __trunc__(self):
e1c6bebd 161 return int(self._value)
81447b5b
PP
162
163 def __abs__(self):
e1c6bebd 164 return abs(self._value)
81447b5b
PP
165
166 def __add__(self, other):
e1c6bebd 167 return self._value + self._extract_value(other)
81447b5b
PP
168
169 def __radd__(self, other):
170 return self.__add__(other)
171
172 def __neg__(self):
e1c6bebd 173 return -self._value
81447b5b
PP
174
175 def __pos__(self):
e1c6bebd 176 return +self._value
81447b5b
PP
177
178 def __mul__(self, other):
e1c6bebd 179 return self._value * self._extract_value(other)
81447b5b
PP
180
181 def __rmul__(self, other):
182 return self.__mul__(other)
183
184 def __truediv__(self, other):
e1c6bebd 185 return self._value / self._extract_value(other)
81447b5b
PP
186
187 def __rtruediv__(self, other):
e1c6bebd 188 return self._extract_value(other) / self._value
81447b5b
PP
189
190 def __pow__(self, exponent):
e1c6bebd 191 return self._value ** self._extract_value(exponent)
81447b5b
PP
192
193 def __rpow__(self, base):
e1c6bebd 194 return self._extract_value(base) ** self._value
81447b5b
PP
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
225class _IntegralField(_NumericField, numbers.Integral):
226 def __lshift__(self, other):
e1c6bebd 227 return self._value << self._extract_value(other)
81447b5b
PP
228
229 def __rlshift__(self, other):
e1c6bebd 230 return self._extract_value(other) << self._value
81447b5b
PP
231
232 def __rshift__(self, other):
e1c6bebd 233 return self._value >> self._extract_value(other)
81447b5b
PP
234
235 def __rrshift__(self, other):
e1c6bebd 236 return self._extract_value(other) >> self._value
81447b5b
PP
237
238 def __and__(self, other):
e1c6bebd 239 return self._value & self._extract_value(other)
81447b5b
PP
240
241 def __rand__(self, other):
e1c6bebd 242 return self._extract_value(other) & self._value
81447b5b
PP
243
244 def __xor__(self, other):
e1c6bebd 245 return self._value ^ self._extract_value(other)
81447b5b
PP
246
247 def __rxor__(self, other):
e1c6bebd 248 return self._extract_value(other) ^ self._value
81447b5b
PP
249
250 def __or__(self, other):
e1c6bebd 251 return self._value | self._extract_value(other)
81447b5b
PP
252
253 def __ror__(self, other):
e1c6bebd 254 return self._extract_value(other) | self._value
81447b5b
PP
255
256 def __invert__(self):
e1c6bebd 257 return ~self._value
81447b5b
PP
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
280class _RealField(_NumericField, numbers.Real):
281 pass
282
283
284class _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
e1c6bebd 301 def _value(self):
81447b5b
PP
302 if self.field_type.is_signed:
303 ret, value = native_bt.ctf_field_signed_integer_get_value(self._ptr)
304 else:
305 ret, value = native_bt.ctf_field_unsigned_integer_get_value(self._ptr)
306
811644b8 307 if ret < 0:
e1c6bebd
JG
308 if not self.is_set:
309 return
310
311 utils._handle_ret(ret, "cannot get integer field's value")
811644b8 312
81447b5b
PP
313 return value
314
e1c6bebd 315 def _set_value(self, value):
81447b5b
PP
316 value = self._value_to_int(value)
317
318 if self.field_type.is_signed:
319 ret = native_bt.ctf_field_signed_integer_set_value(self._ptr, value)
320 else:
321 ret = native_bt.ctf_field_unsigned_integer_set_value(self._ptr, value)
322
323 utils._handle_ret(ret, "cannot set integer field object's value")
324
e1c6bebd 325 value = property(fset=_set_value)
81447b5b
PP
326
327class _FloatingPointNumberField(_RealField):
328 _NAME = 'Floating point number'
329
330 def _value_to_float(self, value):
331 if not isinstance(value, numbers.Real):
332 raise TypeError("expecting a real number object")
333
334 return float(value)
335
336 @property
e1c6bebd 337 def _value(self):
81447b5b 338 ret, value = native_bt.ctf_field_floating_point_get_value(self._ptr)
811644b8
PP
339
340 if ret < 0:
e1c6bebd
JG
341 if not self.is_set:
342 return
343
344 utils._handle_ret(ret, "cannot get floating point number field's value")
811644b8 345
81447b5b
PP
346 return value
347
e1c6bebd 348 def _set_value(self, value):
81447b5b
PP
349 value = self._value_to_float(value)
350 ret = native_bt.ctf_field_floating_point_set_value(self._ptr, value)
351 utils._handle_ret(ret, "cannot set floating point number field object's value")
352
e1c6bebd 353 value = property(fset=_set_value)
81447b5b
PP
354
355class _EnumerationField(_IntegerField):
356 _NAME = 'Enumeration'
357
358 @property
359 def integer_field(self):
360 int_field_ptr = native_bt.ctf_field_enumeration_get_container(self._ptr)
811644b8 361 assert(int_field_ptr)
81447b5b
PP
362 return _create_from_ptr(int_field_ptr)
363
e1c6bebd 364 def _set_value(self, value):
81447b5b
PP
365 self.integer_field.value = value
366
e1c6bebd
JG
367 value = property(fset=_set_value)
368
369 @property
370 def _value(self):
371 return self.integer_field._value
372
81447b5b
PP
373 @property
374 def mappings(self):
811644b8
PP
375 iter_ptr = native_bt.ctf_field_enumeration_get_mappings(self._ptr)
376 assert(iter_ptr)
377 return bt2.field_types._EnumerationFieldTypeMappingIterator(iter_ptr,
378 self.field_type.is_signed)
81447b5b
PP
379
380
381@functools.total_ordering
382class _StringField(_Field, collections.abc.Sequence):
383 _NAME = 'String'
384
385 def _value_to_str(self, value):
386 if isinstance(value, self.__class__):
e1c6bebd 387 value = value._value
81447b5b
PP
388
389 if not isinstance(value, str):
390 raise TypeError("expecting a 'str' object")
391
392 return value
393
394 @property
e1c6bebd 395 def _value(self):
81447b5b 396 value = native_bt.ctf_field_string_get_value(self._ptr)
81447b5b
PP
397 return value
398
e1c6bebd 399 def _set_value(self, value):
81447b5b
PP
400 value = self._value_to_str(value)
401 ret = native_bt.ctf_field_string_set_value(self._ptr, value)
402 utils._handle_ret(ret, "cannot set string field object's value")
403
e1c6bebd
JG
404 value = property(fset=_set_value)
405
406 def _spec_eq(self, other):
81447b5b
PP
407 try:
408 other = self._value_to_str(other)
409 except:
410 return False
411
e1c6bebd 412 return self._value == other
81447b5b
PP
413
414 def __le__(self, other):
e1c6bebd 415 return self._value <= self._value_to_str(other)
81447b5b
PP
416
417 def __lt__(self, other):
e1c6bebd 418 return self._value < self._value_to_str(other)
81447b5b
PP
419
420 def __bool__(self):
e1c6bebd 421 return bool(self._value)
81447b5b
PP
422
423 def __str__(self):
e1c6bebd 424 return self._value
81447b5b
PP
425
426 def __getitem__(self, index):
e1c6bebd 427 return self._value[index]
81447b5b
PP
428
429 def __len__(self):
e1c6bebd 430 return len(self._value)
81447b5b
PP
431
432 def __iadd__(self, value):
433 value = self._value_to_str(value)
434 ret = native_bt.ctf_field_string_append(self._ptr, value)
435 utils._handle_ret(ret, "cannot append to string field object's value")
436 return self
437
438
439class _ContainerField(_Field):
440 def __bool__(self):
441 return len(self) != 0
442
443 def __len__(self):
444 count = self._count()
811644b8 445 assert(count >= 0)
81447b5b
PP
446 return count
447
448 def __delitem__(self, index):
449 raise NotImplementedError
450
451
452class _StructureField(_ContainerField, collections.abc.MutableMapping):
453 _NAME = 'Structure'
454
455 def _count(self):
456 return len(self.field_type)
457
458 def __getitem__(self, key):
459 utils._check_str(key)
811644b8 460 ptr = native_bt.ctf_field_structure_get_field_by_name(self._ptr, key)
81447b5b
PP
461
462 if ptr is None:
463 raise KeyError(key)
464
465 return _create_from_ptr(ptr)
466
467 def __setitem__(self, key, value):
236355c2 468 # raises if key is somehow invalid
81447b5b
PP
469 field = self[key]
470
81447b5b
PP
471 # the field's property does the appropriate conversion or raises
472 # the appropriate exception
473 field.value = value
474
475 def at_index(self, index):
476 utils._check_uint64(index)
811644b8
PP
477
478 if index >= len(self):
479 raise IndexError
480
81447b5b 481 field_ptr = native_bt.ctf_field_structure_get_field_by_index(self._ptr, index)
811644b8 482 assert(field_ptr)
81447b5b
PP
483 return _create_from_ptr(field_ptr)
484
485 def __iter__(self):
486 # same name iterator
487 return iter(self.field_type)
488
e1c6bebd
JG
489 def _spec_eq(self, other):
490 try:
491 if len(self) != len(other):
81447b5b
PP
492 return False
493
e1c6bebd
JG
494 for self_key, self_value in self.items():
495 if self_key not in other:
496 return False
81447b5b 497
e1c6bebd 498 other_value = other[self_key]
81447b5b 499
e1c6bebd
JG
500 if self_value != other_value:
501 return False
502
503 return True
504 except:
505 return False
81447b5b 506
7c54e2e7 507 @property
e1c6bebd
JG
508 def _value(self):
509 return {key: value._value for key, value in self.items()}
7c54e2e7 510
e1c6bebd
JG
511 def _set_value(self, values):
512 original_values = self._value
7c54e2e7 513
e1c6bebd
JG
514 try:
515 for key, value in values.items():
516 self[key].value = value
517 except:
518 self.value = original_values
519 raise
7c54e2e7 520
e1c6bebd 521 value = property(fset=_set_value)
81447b5b
PP
522
523class _VariantField(_Field):
524 _NAME = 'Variant'
525
526 @property
527 def tag_field(self):
528 field_ptr = native_bt.ctf_field_variant_get_tag(self._ptr)
811644b8
PP
529
530 if field_ptr is None:
531 return
532
81447b5b
PP
533 return _create_from_ptr(field_ptr)
534
535 @property
536 def selected_field(self):
537 return self.field()
538
539 def field(self, tag_field=None):
540 if tag_field is None:
541 field_ptr = native_bt.ctf_field_variant_get_current_field(self._ptr)
811644b8
PP
542
543 if field_ptr is None:
544 return
81447b5b
PP
545 else:
546 utils._check_type(tag_field, _EnumerationField)
547 field_ptr = native_bt.ctf_field_variant_get_field(self._ptr, tag_field._ptr)
548 utils._handle_ptr(field_ptr, "cannot select variant field object's field")
549
550 return _create_from_ptr(field_ptr)
551
e1c6bebd
JG
552 def _spec_eq(self, other):
553 return _get_leaf_field(self) == other
811644b8
PP
554
555 def __bool__(self):
556 return bool(self.selected_field)
81447b5b 557
e1c6bebd
JG
558 @property
559 def _value(self):
560 if self.selected_field is not None:
561 return self.selected_field._value
562
563 def _set_value(self, value):
564 self.selected_field.value = value
565
566 value = property(fset=_set_value)
81447b5b
PP
567
568class _ArraySequenceField(_ContainerField, collections.abc.MutableSequence):
569 def __getitem__(self, index):
570 if not isinstance(index, numbers.Integral):
571 raise TypeError("'{}' is not an integral number object: invalid index".format(index.__class__.__name__))
572
573 index = int(index)
574
575 if index < 0 or index >= len(self):
576 raise IndexError('{} field object index is out of range'.format(self._NAME))
577
578 field_ptr = self._get_field_ptr_at_index(index)
811644b8 579 assert(field_ptr)
81447b5b
PP
580 return _create_from_ptr(field_ptr)
581
582 def __setitem__(self, index, value):
583 # we can only set numbers and strings
584 if not isinstance(value, (numbers.Number, _StringField, str)):
585 raise TypeError('expecting number or string object')
586
587 # raises if index is somehow invalid
588 field = self[index]
589
590 if not isinstance(field, (_NumericField, _StringField)):
591 raise TypeError('can only set the value of a number or string field')
592
593 # the field's property does the appropriate conversion or raises
594 # the appropriate exception
595 field.value = value
596
597 def insert(self, index, value):
598 raise NotImplementedError
599
e1c6bebd
JG
600 def _spec_eq(self, other):
601 try:
602 if len(self) != len(other):
81447b5b
PP
603 return False
604
e1c6bebd
JG
605 for self_field, other_field in zip(self, other):
606 if self_field != other_field:
607 return False
7c54e2e7 608
e1c6bebd
JG
609 return True
610 except:
611 return False
7c54e2e7 612
e1c6bebd
JG
613 @property
614 def _value(self):
615 return [field._value for field in self]
7c54e2e7 616
81447b5b
PP
617
618class _ArrayField(_ArraySequenceField):
619 _NAME = 'Array'
620
621 def _count(self):
622 return self.field_type.length
623
624 def _get_field_ptr_at_index(self, index):
625 return native_bt.ctf_field_array_get_field(self._ptr, index)
626
e1c6bebd
JG
627 def _set_value(self, values):
628 if len(self) != len(values):
629 raise ValueError(
630 'expected length of value and array field to match')
631
632 original_values = self._value
633 try:
634 for index, value in enumerate(values):
635 if value is not None:
636 self[index].value = value
637 else:
638 self[index].reset()
639 except:
640 self.value = original_values
641 raise
642
643 value = property(fset=_set_value)
644
81447b5b
PP
645
646class _SequenceField(_ArraySequenceField):
647 _NAME = 'Sequence'
648
649 def _count(self):
a29094be 650 return int(self.length_field)
81447b5b
PP
651
652 @property
653 def length_field(self):
654 field_ptr = native_bt.ctf_field_sequence_get_length(self._ptr)
a29094be
JG
655 if field_ptr is None:
656 return
81447b5b
PP
657 return _create_from_ptr(field_ptr)
658
659 @length_field.setter
660 def length_field(self, length_field):
661 utils._check_type(length_field, _IntegerField)
662 ret = native_bt.ctf_field_sequence_set_length(self._ptr, length_field._ptr)
663 utils._handle_ret(ret, "cannot set sequence field object's length field")
664
665 def _get_field_ptr_at_index(self, index):
666 return native_bt.ctf_field_sequence_get_field(self._ptr, index)
667
e1c6bebd
JG
668 def _set_value(self, values):
669 original_length_field = self.length_field
670 if original_length_field is not None:
671 original_values = self._value
672
673 if len(values) != self.length_field:
674 if self.length_field is not None:
675 length_ft = self.length_field.field_type
676 else:
677 length_ft = bt2.IntegerFieldType(size=64, is_signed=False)
678 self.length_field = length_ft(len(values))
679
680 try:
681 for index, value in enumerate(values):
682 if value is not None:
683 self[index].value = value
684 else:
685 self[index].reset()
686 except:
687 if original_length_field is not None:
688 self.length_field = original_length_field
689 self.value = original_values
690 else:
691 self.reset()
692 raise
693
694 value = property(fset=_set_value)
81447b5b
PP
695
696_TYPE_ID_TO_OBJ = {
811644b8
PP
697 native_bt.CTF_FIELD_TYPE_ID_INTEGER: _IntegerField,
698 native_bt.CTF_FIELD_TYPE_ID_FLOAT: _FloatingPointNumberField,
699 native_bt.CTF_FIELD_TYPE_ID_ENUM: _EnumerationField,
700 native_bt.CTF_FIELD_TYPE_ID_STRING: _StringField,
701 native_bt.CTF_FIELD_TYPE_ID_STRUCT: _StructureField,
702 native_bt.CTF_FIELD_TYPE_ID_ARRAY: _ArrayField,
703 native_bt.CTF_FIELD_TYPE_ID_SEQUENCE: _SequenceField,
704 native_bt.CTF_FIELD_TYPE_ID_VARIANT: _VariantField,
81447b5b 705}
This page took 0.0615 seconds and 4 git commands to generate.