Python bt2: add reset and is_set to fields
[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
33def _create_from_ptr(ptr):
34 # recreate the field type wrapper of this field's type (the identity
35 # could be different, but the underlying address should be the
36 # same)
37 field_type_ptr = native_bt.ctf_field_get_type(ptr)
38 utils._handle_ptr(field_type_ptr, "cannot get field object's type")
39 field_type = bt2.field_types._create_from_ptr(field_type_ptr)
40 typeid = native_bt.ctf_field_type_get_type_id(field_type._ptr)
41 field = _TYPE_ID_TO_OBJ[typeid]._create_from_ptr(ptr)
42 field._field_type = field_type
43 return field
44
45
46class _Field(object._Object, metaclass=abc.ABCMeta):
47 def __copy__(self):
48 ptr = native_bt.ctf_field_copy(self._ptr)
49 utils._handle_ptr(ptr, 'cannot copy {} field object'.format(self._NAME.lower()))
50 return _create_from_ptr(ptr)
51
52 def __deepcopy__(self, memo):
53 cpy = self.__copy__()
54 memo[id(self)] = cpy
55 return cpy
56
57 @property
58 def field_type(self):
59 return self._field_type
60
742e4747
JG
61 @property
62 def is_set(self):
63 is_set = native_bt.ctf_field_value_is_set(self._ptr)
64 return is_set > 0
65
66 def reset(self):
67 ret = native_bt.ctf_field_reset_value(self._ptr)
68 utils._handle_ret(ret, "cannot reset field object's value")
69
81447b5b
PP
70
71@functools.total_ordering
72class _NumericField(_Field):
73 @staticmethod
74 def _extract_value(other):
75 if other is True or other is False:
76 return other
77
78 if isinstance(other, numbers.Integral):
79 return int(other)
80
81 if isinstance(other, numbers.Real):
82 return float(other)
83
84 if isinstance(other, numbers.Complex):
85 return complex(other)
86
87 raise TypeError("'{}' object is not a number object".format(other.__class__.__name__))
88
89 def __int__(self):
90 return int(self.value)
91
92 def __float__(self):
93 return float(self.value)
94
95 def __str__(self):
96 return str(self.value)
97
98 def __lt__(self, other):
99 if not isinstance(other, numbers.Number):
100 raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__,
101 other.__class__.__name__))
102
103 return self.value < float(other)
104
105 def __le__(self, other):
106 if not isinstance(other, numbers.Number):
107 raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__,
108 other.__class__.__name__))
109
110 return self.value <= float(other)
111
112 def __eq__(self, other):
113 if not isinstance(other, numbers.Number):
114 return False
115
116 return self.value == complex(other)
117
118 def __rmod__(self, other):
119 return self._extract_value(other) % self.value
120
121 def __mod__(self, other):
122 return self.value % self._extract_value(other)
123
124 def __rfloordiv__(self, other):
125 return self._extract_value(other) // self.value
126
127 def __floordiv__(self, other):
128 return self.value // self._extract_value(other)
129
130 def __round__(self, ndigits=None):
131 if ndigits is None:
132 return round(self.value)
133 else:
134 return round(self.value, ndigits)
135
136 def __ceil__(self):
137 return math.ceil(self.value)
138
139 def __floor__(self):
140 return math.floor(self.value)
141
142 def __trunc__(self):
143 return int(self.value)
144
145 def __abs__(self):
146 return abs(self.value)
147
148 def __add__(self, other):
149 return self.value + self._extract_value(other)
150
151 def __radd__(self, other):
152 return self.__add__(other)
153
154 def __neg__(self):
155 return -self.value
156
157 def __pos__(self):
158 return +self.value
159
160 def __mul__(self, other):
161 return self.value * self._extract_value(other)
162
163 def __rmul__(self, other):
164 return self.__mul__(other)
165
166 def __truediv__(self, other):
167 return self.value / self._extract_value(other)
168
169 def __rtruediv__(self, other):
170 return self._extract_value(other) / self.value
171
172 def __pow__(self, exponent):
173 return self.value ** self._extract_value(exponent)
174
175 def __rpow__(self, base):
176 return self._extract_value(base) ** self.value
177
178 def __iadd__(self, other):
179 self.value = self + other
180 return self
181
182 def __isub__(self, other):
183 self.value = self - other
184 return self
185
186 def __imul__(self, other):
187 self.value = self * other
188 return self
189
190 def __itruediv__(self, other):
191 self.value = self / other
192 return self
193
194 def __ifloordiv__(self, other):
195 self.value = self // other
196 return self
197
198 def __imod__(self, other):
199 self.value = self % other
200 return self
201
202 def __ipow__(self, other):
203 self.value = self ** other
204 return self
205
206
207class _IntegralField(_NumericField, numbers.Integral):
208 def __lshift__(self, other):
209 return self.value << self._extract_value(other)
210
211 def __rlshift__(self, other):
212 return self._extract_value(other) << self.value
213
214 def __rshift__(self, other):
215 return self.value >> self._extract_value(other)
216
217 def __rrshift__(self, other):
218 return self._extract_value(other) >> self.value
219
220 def __and__(self, other):
221 return self.value & self._extract_value(other)
222
223 def __rand__(self, other):
224 return self._extract_value(other) & self.value
225
226 def __xor__(self, other):
227 return self.value ^ self._extract_value(other)
228
229 def __rxor__(self, other):
230 return self._extract_value(other) ^ self.value
231
232 def __or__(self, other):
233 return self.value | self._extract_value(other)
234
235 def __ror__(self, other):
236 return self._extract_value(other) | self.value
237
238 def __invert__(self):
239 return ~self.value
240
241 def __ilshift__(self, other):
242 self.value = self << other
243 return self
244
245 def __irshift__(self, other):
246 self.value = self >> other
247 return self
248
249 def __iand__(self, other):
250 self.value = self & other
251 return self
252
253 def __ixor__(self, other):
254 self.value = self ^ other
255 return self
256
257 def __ior__(self, other):
258 self.value = self | other
259 return self
260
261
262class _RealField(_NumericField, numbers.Real):
263 pass
264
265
266class _IntegerField(_IntegralField):
267 _NAME = 'Integer'
268
269 def _value_to_int(self, value):
270 if not isinstance(value, numbers.Real):
271 raise TypeError('expecting a real number object')
272
273 value = int(value)
274
275 if self.field_type.is_signed:
276 utils._check_int64(value)
277 else:
278 utils._check_uint64(value)
279
280 return value
281
282 @property
283 def value(self):
284 if self.field_type.is_signed:
285 ret, value = native_bt.ctf_field_signed_integer_get_value(self._ptr)
286 else:
287 ret, value = native_bt.ctf_field_unsigned_integer_get_value(self._ptr)
288
811644b8
PP
289 if ret < 0:
290 # field is not set
291 return
292
81447b5b
PP
293 return value
294
295 @value.setter
296 def value(self, value):
297 value = self._value_to_int(value)
298
299 if self.field_type.is_signed:
300 ret = native_bt.ctf_field_signed_integer_set_value(self._ptr, value)
301 else:
302 ret = native_bt.ctf_field_unsigned_integer_set_value(self._ptr, value)
303
304 utils._handle_ret(ret, "cannot set integer field object's value")
305
306
307class _FloatingPointNumberField(_RealField):
308 _NAME = 'Floating point number'
309
310 def _value_to_float(self, value):
311 if not isinstance(value, numbers.Real):
312 raise TypeError("expecting a real number object")
313
314 return float(value)
315
316 @property
317 def value(self):
318 ret, value = native_bt.ctf_field_floating_point_get_value(self._ptr)
811644b8
PP
319
320 if ret < 0:
321 # field is not set
322 return
323
81447b5b
PP
324 return value
325
326 @value.setter
327 def value(self, value):
328 value = self._value_to_float(value)
329 ret = native_bt.ctf_field_floating_point_set_value(self._ptr, value)
330 utils._handle_ret(ret, "cannot set floating point number field object's value")
331
332
333class _EnumerationField(_IntegerField):
334 _NAME = 'Enumeration'
335
336 @property
337 def integer_field(self):
338 int_field_ptr = native_bt.ctf_field_enumeration_get_container(self._ptr)
811644b8 339 assert(int_field_ptr)
81447b5b
PP
340 return _create_from_ptr(int_field_ptr)
341
342 @property
343 def value(self):
344 return self.integer_field.value
345
346 @value.setter
347 def value(self, value):
348 self.integer_field.value = value
349
350 @property
351 def mappings(self):
811644b8
PP
352 iter_ptr = native_bt.ctf_field_enumeration_get_mappings(self._ptr)
353 assert(iter_ptr)
354 return bt2.field_types._EnumerationFieldTypeMappingIterator(iter_ptr,
355 self.field_type.is_signed)
81447b5b
PP
356
357
358@functools.total_ordering
359class _StringField(_Field, collections.abc.Sequence):
360 _NAME = 'String'
361
362 def _value_to_str(self, value):
363 if isinstance(value, self.__class__):
364 value = value.value
365
366 if not isinstance(value, str):
367 raise TypeError("expecting a 'str' object")
368
369 return value
370
371 @property
372 def value(self):
373 value = native_bt.ctf_field_string_get_value(self._ptr)
811644b8
PP
374
375 if value is None:
376 # field is not set
377 return
378
81447b5b
PP
379 return value
380
381 @value.setter
382 def value(self, value):
383 value = self._value_to_str(value)
384 ret = native_bt.ctf_field_string_set_value(self._ptr, value)
385 utils._handle_ret(ret, "cannot set string field object's value")
386
387 def __eq__(self, other):
388 try:
389 other = self._value_to_str(other)
390 except:
391 return False
392
393 return self.value == other
394
395 def __le__(self, other):
396 return self.value <= self._value_to_str(other)
397
398 def __lt__(self, other):
399 return self.value < self._value_to_str(other)
400
401 def __bool__(self):
402 return bool(self.value)
403
404 def __str__(self):
405 return self.value
406
407 def __getitem__(self, index):
408 return self.value[index]
409
410 def __len__(self):
411 return len(self.value)
412
413 def __iadd__(self, value):
414 value = self._value_to_str(value)
415 ret = native_bt.ctf_field_string_append(self._ptr, value)
416 utils._handle_ret(ret, "cannot append to string field object's value")
417 return self
418
419
420class _ContainerField(_Field):
421 def __bool__(self):
422 return len(self) != 0
423
424 def __len__(self):
425 count = self._count()
811644b8 426 assert(count >= 0)
81447b5b
PP
427 return count
428
429 def __delitem__(self, index):
430 raise NotImplementedError
431
432
433class _StructureField(_ContainerField, collections.abc.MutableMapping):
434 _NAME = 'Structure'
435
436 def _count(self):
437 return len(self.field_type)
438
439 def __getitem__(self, key):
440 utils._check_str(key)
811644b8 441 ptr = native_bt.ctf_field_structure_get_field_by_name(self._ptr, key)
81447b5b
PP
442
443 if ptr is None:
444 raise KeyError(key)
445
446 return _create_from_ptr(ptr)
447
448 def __setitem__(self, key, value):
449 # we can only set numbers and strings
450 if not isinstance(value, (numbers.Number, str)):
451 raise TypeError('expecting number object or string')
452
453 # raises if index is somehow invalid
454 field = self[key]
455
456 if not isinstance(field, (_NumericField, _StringField)):
457 raise TypeError('can only set the value of a number or string field')
458
459 # the field's property does the appropriate conversion or raises
460 # the appropriate exception
461 field.value = value
462
463 def at_index(self, index):
464 utils._check_uint64(index)
811644b8
PP
465
466 if index >= len(self):
467 raise IndexError
468
81447b5b 469 field_ptr = native_bt.ctf_field_structure_get_field_by_index(self._ptr, index)
811644b8 470 assert(field_ptr)
81447b5b
PP
471 return _create_from_ptr(field_ptr)
472
473 def __iter__(self):
474 # same name iterator
475 return iter(self.field_type)
476
477 def __eq__(self, other):
478 if not isinstance(other, collections.abc.Mapping):
479 return False
480
481 if len(self) != len(other):
482 return False
483
484 for self_key, self_value in self.items():
485 if self_key not in other:
486 return False
487
488 other_value = other[self_key]
489
490 if self_value != other_value:
491 return False
492
493 return True
494
7c54e2e7
JG
495 @property
496 def value(self):
497 return {key: field.value for key, field in self.items()}
498
499 @value.setter
500 def value(self, values):
501 if not hasattr(type(values), '__getitem__'):
502 raise TypeError('expecting a Mapping collection')
503
504 for key, value in values.items():
505 self[key].value = value
506
81447b5b
PP
507
508class _VariantField(_Field):
509 _NAME = 'Variant'
510
511 @property
512 def tag_field(self):
513 field_ptr = native_bt.ctf_field_variant_get_tag(self._ptr)
811644b8
PP
514
515 if field_ptr is None:
516 return
517
81447b5b
PP
518 return _create_from_ptr(field_ptr)
519
520 @property
521 def selected_field(self):
522 return self.field()
523
524 def field(self, tag_field=None):
525 if tag_field is None:
526 field_ptr = native_bt.ctf_field_variant_get_current_field(self._ptr)
811644b8
PP
527
528 if field_ptr is None:
529 return
81447b5b
PP
530 else:
531 utils._check_type(tag_field, _EnumerationField)
532 field_ptr = native_bt.ctf_field_variant_get_field(self._ptr, tag_field._ptr)
533 utils._handle_ptr(field_ptr, "cannot select variant field object's field")
534
535 return _create_from_ptr(field_ptr)
536
537 def __eq__(self, other):
811644b8
PP
538 if type(other) is not type(self):
539 return False
540
541 if self.addr == other.addr:
542 return True
543
544 return self.selected_field == other.selected_field
545
546 def __bool__(self):
547 return bool(self.selected_field)
81447b5b
PP
548
549
550class _ArraySequenceField(_ContainerField, collections.abc.MutableSequence):
551 def __getitem__(self, index):
552 if not isinstance(index, numbers.Integral):
553 raise TypeError("'{}' is not an integral number object: invalid index".format(index.__class__.__name__))
554
555 index = int(index)
556
557 if index < 0 or index >= len(self):
558 raise IndexError('{} field object index is out of range'.format(self._NAME))
559
560 field_ptr = self._get_field_ptr_at_index(index)
811644b8 561 assert(field_ptr)
81447b5b
PP
562 return _create_from_ptr(field_ptr)
563
564 def __setitem__(self, index, value):
565 # we can only set numbers and strings
566 if not isinstance(value, (numbers.Number, _StringField, str)):
567 raise TypeError('expecting number or string object')
568
569 # raises if index is somehow invalid
570 field = self[index]
571
572 if not isinstance(field, (_NumericField, _StringField)):
573 raise TypeError('can only set the value of a number or string field')
574
575 # the field's property does the appropriate conversion or raises
576 # the appropriate exception
577 field.value = value
578
579 def insert(self, index, value):
580 raise NotImplementedError
581
582 def __eq__(self, other):
583 if not isinstance(other, collections.abc.Sequence):
584 return False
585
586 if len(self) != len(other):
587 return False
588
589 for self_field, other_field in zip(self, other):
590 if self_field != other_field:
591 return False
592
593 return True
594
7c54e2e7
JG
595 @property
596 def value(self):
597 return [field.value for field in self]
598
599 @value.setter
600 def value(self, values):
601 if not hasattr(type(values), '__iter__'):
602 raise TypeError('expecting an iterable container (Sequence)')
603
604 if len(self) != len(values):
605 raise ValueError('expected length of value and field to match')
606
607 for index, value in enumerate(values):
608 self[index].value = value
609
81447b5b
PP
610
611class _ArrayField(_ArraySequenceField):
612 _NAME = 'Array'
613
614 def _count(self):
615 return self.field_type.length
616
617 def _get_field_ptr_at_index(self, index):
618 return native_bt.ctf_field_array_get_field(self._ptr, index)
619
620
621class _SequenceField(_ArraySequenceField):
622 _NAME = 'Sequence'
623
624 def _count(self):
625 return self.length_field.value
626
627 @property
628 def length_field(self):
629 field_ptr = native_bt.ctf_field_sequence_get_length(self._ptr)
630 utils._handle_ptr("cannot get sequence field object's length field")
631 return _create_from_ptr(field_ptr)
632
633 @length_field.setter
634 def length_field(self, length_field):
635 utils._check_type(length_field, _IntegerField)
636 ret = native_bt.ctf_field_sequence_set_length(self._ptr, length_field._ptr)
637 utils._handle_ret(ret, "cannot set sequence field object's length field")
638
639 def _get_field_ptr_at_index(self, index):
640 return native_bt.ctf_field_sequence_get_field(self._ptr, index)
641
642
643_TYPE_ID_TO_OBJ = {
811644b8
PP
644 native_bt.CTF_FIELD_TYPE_ID_INTEGER: _IntegerField,
645 native_bt.CTF_FIELD_TYPE_ID_FLOAT: _FloatingPointNumberField,
646 native_bt.CTF_FIELD_TYPE_ID_ENUM: _EnumerationField,
647 native_bt.CTF_FIELD_TYPE_ID_STRING: _StringField,
648 native_bt.CTF_FIELD_TYPE_ID_STRUCT: _StructureField,
649 native_bt.CTF_FIELD_TYPE_ID_ARRAY: _ArrayField,
650 native_bt.CTF_FIELD_TYPE_ID_SEQUENCE: _SequenceField,
651 native_bt.CTF_FIELD_TYPE_ID_VARIANT: _VariantField,
81447b5b 652}
This page took 0.053425 seconds and 4 git commands to generate.