Move to kernel style SPDX license identifiers
[babeltrace.git] / src / bindings / python / bt2 / bt2 / value.py
1 # SPDX-License-Identifier: MIT
2 #
3 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
4
5 from bt2 import native_bt, object, utils
6 import collections.abc
7 import functools
8 import numbers
9 import math
10 import abc
11 import bt2
12
13
14 def _create_from_ptr_template(ptr, object_map):
15 if ptr is None:
16 return
17
18 # bt_value_null is translated to None. However, we are given a reference
19 # to it that we are not going to manage anymore, since we don't create a
20 # Python wrapper for it. Therefore put that reference immediately.
21 if ptr == native_bt.value_null:
22 _Value._put_ref(ptr)
23 return
24
25 typeid = native_bt.value_get_type(ptr)
26 return object_map[typeid]._create_from_ptr(ptr)
27
28
29 def _create_from_ptr(ptr):
30 return _create_from_ptr_template(ptr, _TYPE_TO_OBJ)
31
32
33 def _create_from_const_ptr(ptr):
34 return _create_from_ptr_template(ptr, _TYPE_TO_CONST_OBJ)
35
36
37 def _create_from_ptr_and_get_ref_template(ptr, object_map):
38 if ptr is None or ptr == native_bt.value_null:
39 return
40
41 typeid = native_bt.value_get_type(ptr)
42 return object_map[typeid]._create_from_ptr_and_get_ref(ptr)
43
44
45 def _create_from_ptr_and_get_ref(ptr):
46 return _create_from_ptr_and_get_ref_template(ptr, _TYPE_TO_OBJ)
47
48
49 def _create_from_const_ptr_and_get_ref(ptr):
50 return _create_from_ptr_and_get_ref_template(ptr, _TYPE_TO_CONST_OBJ)
51
52
53 def create_value(value):
54 if value is None:
55 # null value object
56 return
57
58 if isinstance(value, _Value):
59 return value
60
61 if isinstance(value, bool):
62 return BoolValue(value)
63
64 if isinstance(value, numbers.Integral):
65 return SignedIntegerValue(value)
66
67 if isinstance(value, numbers.Real):
68 return RealValue(value)
69
70 if isinstance(value, str):
71 return StringValue(value)
72
73 if isinstance(value, collections.abc.Sequence):
74 return ArrayValue(value)
75
76 if isinstance(value, collections.abc.Mapping):
77 return MapValue(value)
78
79 raise TypeError(
80 "cannot create value object from '{}' object".format(value.__class__.__name__)
81 )
82
83
84 class _ValueConst(object._SharedObject, metaclass=abc.ABCMeta):
85 _get_ref = staticmethod(native_bt.value_get_ref)
86 _put_ref = staticmethod(native_bt.value_put_ref)
87 _create_value_from_ptr = staticmethod(_create_from_const_ptr)
88 _create_value_from_ptr_and_get_ref = staticmethod(
89 _create_from_const_ptr_and_get_ref
90 )
91
92 def __ne__(self, other):
93 return not (self == other)
94
95 def _check_create_status(self, ptr):
96 if ptr is None:
97 raise bt2._MemoryError(
98 'cannot create {} value object'.format(self._NAME.lower())
99 )
100
101
102 class _Value(_ValueConst):
103 _create_value_from_ptr = staticmethod(_create_from_ptr)
104 _create_value_from_ptr_and_get_ref = staticmethod(_create_from_ptr_and_get_ref)
105
106
107 @functools.total_ordering
108 class _NumericValueConst(_ValueConst):
109 @staticmethod
110 def _extract_value(other):
111 if isinstance(other, _BoolValueConst) or isinstance(other, bool):
112 return bool(other)
113
114 if isinstance(other, numbers.Integral):
115 return int(other)
116
117 if isinstance(other, numbers.Real):
118 return float(other)
119
120 if isinstance(other, numbers.Complex):
121 return complex(other)
122
123 raise TypeError(
124 "'{}' object is not a number object".format(other.__class__.__name__)
125 )
126
127 def __int__(self):
128 return int(self._value)
129
130 def __float__(self):
131 return float(self._value)
132
133 def __repr__(self):
134 return repr(self._value)
135
136 def __lt__(self, other):
137 return self._value < self._extract_value(other)
138
139 def __eq__(self, other):
140 try:
141 return self._value == self._extract_value(other)
142 except Exception:
143 return False
144
145 def __rmod__(self, other):
146 return self._extract_value(other) % self._value
147
148 def __mod__(self, other):
149 return self._value % self._extract_value(other)
150
151 def __rfloordiv__(self, other):
152 return self._extract_value(other) // self._value
153
154 def __floordiv__(self, other):
155 return self._value // self._extract_value(other)
156
157 def __round__(self, ndigits=None):
158 if ndigits is None:
159 return round(self._value)
160 else:
161 return round(self._value, ndigits)
162
163 def __ceil__(self):
164 return math.ceil(self._value)
165
166 def __floor__(self):
167 return math.floor(self._value)
168
169 def __trunc__(self):
170 return int(self._value)
171
172 def __abs__(self):
173 return abs(self._value)
174
175 def __add__(self, other):
176 return self._value + self._extract_value(other)
177
178 def __radd__(self, other):
179 return self.__add__(other)
180
181 def __neg__(self):
182 return -self._value
183
184 def __pos__(self):
185 return +self._value
186
187 def __mul__(self, other):
188 return self._value * self._extract_value(other)
189
190 def __rmul__(self, other):
191 return self.__mul__(other)
192
193 def __truediv__(self, other):
194 return self._value / self._extract_value(other)
195
196 def __rtruediv__(self, other):
197 return self._extract_value(other) / self._value
198
199 def __pow__(self, exponent):
200 return self._value ** self._extract_value(exponent)
201
202 def __rpow__(self, base):
203 return self._extract_value(base) ** self._value
204
205
206 class _NumericValue(_NumericValueConst, _Value):
207 pass
208
209
210 class _IntegralValueConst(_NumericValueConst, numbers.Integral):
211 def __lshift__(self, other):
212 return self._value << self._extract_value(other)
213
214 def __rlshift__(self, other):
215 return self._extract_value(other) << self._value
216
217 def __rshift__(self, other):
218 return self._value >> self._extract_value(other)
219
220 def __rrshift__(self, other):
221 return self._extract_value(other) >> self._value
222
223 def __and__(self, other):
224 return self._value & self._extract_value(other)
225
226 def __rand__(self, other):
227 return self._extract_value(other) & self._value
228
229 def __xor__(self, other):
230 return self._value ^ self._extract_value(other)
231
232 def __rxor__(self, other):
233 return self._extract_value(other) ^ self._value
234
235 def __or__(self, other):
236 return self._value | self._extract_value(other)
237
238 def __ror__(self, other):
239 return self._extract_value(other) | self._value
240
241 def __invert__(self):
242 return ~self._value
243
244
245 class _IntegralValue(_IntegralValueConst, _NumericValue):
246 pass
247
248
249 class _BoolValueConst(_IntegralValueConst):
250 _NAME = 'Const boolean'
251
252 def __bool__(self):
253 return self._value
254
255 def __repr__(self):
256 return repr(self._value)
257
258 @property
259 def _value(self):
260 value = native_bt.value_bool_get(self._ptr)
261 return value != 0
262
263
264 class BoolValue(_BoolValueConst, _IntegralValue):
265 _NAME = 'Boolean'
266
267 def __init__(self, value=None):
268 if value is None:
269 ptr = native_bt.value_bool_create()
270 else:
271 ptr = native_bt.value_bool_create_init(self._value_to_bool(value))
272
273 self._check_create_status(ptr)
274 super().__init__(ptr)
275
276 @classmethod
277 def _value_to_bool(cls, value):
278 if isinstance(value, _BoolValueConst):
279 value = value._value
280
281 if not isinstance(value, bool):
282 raise TypeError(
283 "'{}' object is not a 'bool', 'BoolValue', or '_BoolValueConst' object".format(
284 value.__class__
285 )
286 )
287
288 return value
289
290 def _set_value(self, value):
291 native_bt.value_bool_set(self._ptr, self._value_to_bool(value))
292
293 value = property(fset=_set_value)
294
295
296 class _IntegerValueConst(_IntegralValueConst):
297 @property
298 def _value(self):
299 return self._get_value(self._ptr)
300
301
302 class _IntegerValue(_IntegerValueConst, _IntegralValue):
303 def __init__(self, value=None):
304 if value is None:
305 ptr = self._create_default_value()
306 else:
307 ptr = self._create_value(self._value_to_int(value))
308
309 self._check_create_status(ptr)
310 super().__init__(ptr)
311
312 @classmethod
313 def _value_to_int(cls, value):
314 if not isinstance(value, numbers.Integral):
315 raise TypeError('expecting an integral number object')
316
317 value = int(value)
318 cls._check_int_range(value)
319 return value
320
321 def _prop_set_value(self, value):
322 self._set_value(self._ptr, self._value_to_int(value))
323
324 value = property(fset=_prop_set_value)
325
326
327 class _UnsignedIntegerValueConst(_IntegerValueConst):
328 _NAME = 'Const unsigned integer'
329 _get_value = staticmethod(native_bt.value_integer_unsigned_get)
330
331
332 class UnsignedIntegerValue(_UnsignedIntegerValueConst, _IntegerValue):
333 _NAME = 'Unsigned integer'
334 _check_int_range = staticmethod(utils._check_uint64)
335 _create_default_value = staticmethod(native_bt.value_integer_unsigned_create)
336 _create_value = staticmethod(native_bt.value_integer_unsigned_create_init)
337 _set_value = staticmethod(native_bt.value_integer_unsigned_set)
338
339
340 class _SignedIntegerValueConst(_IntegerValueConst):
341 _NAME = 'Const signed integer'
342 _get_value = staticmethod(native_bt.value_integer_signed_get)
343
344
345 class SignedIntegerValue(_SignedIntegerValueConst, _IntegerValue):
346 _NAME = 'Signed integer'
347 _check_int_range = staticmethod(utils._check_int64)
348 _create_default_value = staticmethod(native_bt.value_integer_signed_create)
349 _create_value = staticmethod(native_bt.value_integer_signed_create_init)
350 _set_value = staticmethod(native_bt.value_integer_signed_set)
351
352
353 class _RealValueConst(_NumericValueConst, numbers.Real):
354 _NAME = 'Const real number'
355
356 @property
357 def _value(self):
358 return native_bt.value_real_get(self._ptr)
359
360
361 class RealValue(_RealValueConst, _NumericValue):
362 _NAME = 'Real number'
363
364 def __init__(self, value=None):
365 if value is None:
366 ptr = native_bt.value_real_create()
367 else:
368 value = self._value_to_float(value)
369 ptr = native_bt.value_real_create_init(value)
370
371 self._check_create_status(ptr)
372 super().__init__(ptr)
373
374 @classmethod
375 def _value_to_float(cls, value):
376 if not isinstance(value, numbers.Real):
377 raise TypeError("expecting a real number object")
378
379 return float(value)
380
381 def _set_value(self, value):
382 native_bt.value_real_set(self._ptr, self._value_to_float(value))
383
384 value = property(fset=_set_value)
385
386
387 @functools.total_ordering
388 class _StringValueConst(collections.abc.Sequence, _Value):
389 _NAME = 'Const string'
390
391 @classmethod
392 def _value_to_str(cls, value):
393 if isinstance(value, _StringValueConst):
394 value = value._value
395
396 utils._check_str(value)
397 return value
398
399 @property
400 def _value(self):
401 return native_bt.value_string_get(self._ptr)
402
403 def __eq__(self, other):
404 try:
405 return self._value == self._value_to_str(other)
406 except Exception:
407 return False
408
409 def __lt__(self, other):
410 return self._value < self._value_to_str(other)
411
412 def __bool__(self):
413 return bool(self._value)
414
415 def __repr__(self):
416 return repr(self._value)
417
418 def __str__(self):
419 return self._value
420
421 def __getitem__(self, index):
422 return self._value[index]
423
424 def __len__(self):
425 return len(self._value)
426
427 def __contains__(self, item):
428 return self._value_to_str(item) in self._value
429
430
431 class StringValue(_StringValueConst, _Value):
432 _NAME = 'String'
433
434 def __init__(self, value=None):
435 if value is None:
436 ptr = native_bt.value_string_create()
437 else:
438 ptr = native_bt.value_string_create_init(self._value_to_str(value))
439
440 self._check_create_status(ptr)
441 super().__init__(ptr)
442
443 def _set_value(self, value):
444 status = native_bt.value_string_set(self._ptr, self._value_to_str(value))
445 utils._handle_func_status(status)
446
447 value = property(fset=_set_value)
448
449 def __iadd__(self, value):
450 curvalue = self._value
451 curvalue += self._value_to_str(value)
452 self.value = curvalue
453 return self
454
455
456 class _ContainerConst:
457 def __bool__(self):
458 return len(self) != 0
459
460
461 class _Container(_ContainerConst):
462 def __delitem__(self, index):
463 raise NotImplementedError
464
465
466 class _ArrayValueConst(_ContainerConst, collections.abc.Sequence, _ValueConst):
467 _NAME = 'Const array'
468 _borrow_element_by_index = staticmethod(
469 native_bt.value_array_borrow_element_by_index_const
470 )
471 _is_const = True
472
473 def __eq__(self, other):
474 if not isinstance(other, collections.abc.Sequence):
475 return False
476
477 if len(self) != len(other):
478 # early mismatch
479 return False
480
481 for self_elem, other_elem in zip(self, other):
482 if self_elem != other_elem:
483 return False
484
485 return True
486
487 def __len__(self):
488 size = native_bt.value_array_get_length(self._ptr)
489 assert size >= 0
490 return size
491
492 def _check_index(self, index):
493 # TODO: support slices also
494 if not isinstance(index, numbers.Integral):
495 raise TypeError(
496 "'{}' object is not an integral number object: invalid index".format(
497 index.__class__.__name__
498 )
499 )
500
501 index = int(index)
502
503 if index < 0 or index >= len(self):
504 raise IndexError('array value object index is out of range')
505
506 def __getitem__(self, index):
507 self._check_index(index)
508 ptr = self._borrow_element_by_index(self._ptr, index)
509 assert ptr
510 return self._create_value_from_ptr_and_get_ref(ptr)
511
512 def __repr__(self):
513 return '[{}]'.format(', '.join([repr(v) for v in self]))
514
515
516 class ArrayValue(_ArrayValueConst, _Container, collections.abc.MutableSequence, _Value):
517 _NAME = 'Array'
518 _borrow_element_by_index = staticmethod(
519 native_bt.value_array_borrow_element_by_index
520 )
521
522 def __init__(self, value=None):
523 ptr = native_bt.value_array_create()
524 self._check_create_status(ptr)
525 super().__init__(ptr)
526
527 # Python will raise a TypeError if there's anything wrong with
528 # the iterable protocol.
529 if value is not None:
530 for elem in value:
531 self.append(elem)
532
533 def __setitem__(self, index, value):
534 self._check_index(index)
535 value = create_value(value)
536
537 if value is None:
538 ptr = native_bt.value_null
539 else:
540 ptr = value._ptr
541
542 status = native_bt.value_array_set_element_by_index(self._ptr, index, ptr)
543 utils._handle_func_status(status)
544
545 def append(self, value):
546 value = create_value(value)
547
548 if value is None:
549 ptr = native_bt.value_null
550 else:
551 ptr = value._ptr
552
553 status = native_bt.value_array_append_element(self._ptr, ptr)
554 utils._handle_func_status(status)
555
556 def __iadd__(self, iterable):
557 # Python will raise a TypeError if there's anything wrong with
558 # the iterable protocol.
559 for elem in iterable:
560 self.append(elem)
561
562 return self
563
564 def insert(self, value):
565 raise NotImplementedError
566
567
568 class _MapValueKeyIterator(collections.abc.Iterator):
569 def __init__(self, map_obj):
570 self._map_obj = map_obj
571 self._at = 0
572 keys_ptr = native_bt.value_map_get_keys(map_obj._ptr)
573
574 if keys_ptr is None:
575 raise RuntimeError('unexpected error: cannot get map value object keys')
576
577 self._keys = _create_from_ptr(keys_ptr)
578
579 def __next__(self):
580 if self._at == len(self._map_obj):
581 raise StopIteration
582
583 key = self._keys[self._at]
584 self._at += 1
585 return str(key)
586
587
588 class _MapValueConst(_ContainerConst, collections.abc.Mapping, _ValueConst):
589 _NAME = 'Const map'
590 _borrow_entry_value_ptr = staticmethod(native_bt.value_map_borrow_entry_value_const)
591
592 def __ne__(self, other):
593 return _Value.__ne__(self, other)
594
595 def __eq__(self, other):
596 if not isinstance(other, collections.abc.Mapping):
597 return False
598
599 if len(self) != len(other):
600 # early mismatch
601 return False
602
603 for self_key in self:
604 if self_key not in other:
605 return False
606
607 if self[self_key] != other[self_key]:
608 return False
609
610 return True
611
612 def __len__(self):
613 size = native_bt.value_map_get_size(self._ptr)
614 assert size >= 0
615 return size
616
617 def __contains__(self, key):
618 self._check_key_type(key)
619 return native_bt.value_map_has_entry(self._ptr, key)
620
621 def _check_key_type(self, key):
622 utils._check_str(key)
623
624 def _check_key(self, key):
625 if key not in self:
626 raise KeyError(key)
627
628 def __getitem__(self, key):
629 self._check_key(key)
630 ptr = self._borrow_entry_value_ptr(self._ptr, key)
631 assert ptr
632 return self._create_value_from_ptr_and_get_ref(ptr)
633
634 def __iter__(self):
635 return _MapValueKeyIterator(self)
636
637 def __repr__(self):
638 items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()]
639 return '{{{}}}'.format(', '.join(items))
640
641
642 class MapValue(_MapValueConst, _Container, collections.abc.MutableMapping, _Value):
643 _NAME = 'Map'
644 _borrow_entry_value_ptr = staticmethod(native_bt.value_map_borrow_entry_value)
645
646 def __init__(self, value=None):
647 ptr = native_bt.value_map_create()
648 self._check_create_status(ptr)
649 super().__init__(ptr)
650
651 # Python will raise a TypeError if there's anything wrong with
652 # the iterable/mapping protocol.
653 if value is not None:
654 for key, elem in value.items():
655 self[key] = elem
656
657 def __setitem__(self, key, value):
658 self._check_key_type(key)
659 value = create_value(value)
660
661 if value is None:
662 ptr = native_bt.value_null
663 else:
664 ptr = value._ptr
665
666 status = native_bt.value_map_insert_entry(self._ptr, key, ptr)
667 utils._handle_func_status(status)
668
669
670 _TYPE_TO_OBJ = {
671 native_bt.VALUE_TYPE_BOOL: BoolValue,
672 native_bt.VALUE_TYPE_UNSIGNED_INTEGER: UnsignedIntegerValue,
673 native_bt.VALUE_TYPE_SIGNED_INTEGER: SignedIntegerValue,
674 native_bt.VALUE_TYPE_REAL: RealValue,
675 native_bt.VALUE_TYPE_STRING: StringValue,
676 native_bt.VALUE_TYPE_ARRAY: ArrayValue,
677 native_bt.VALUE_TYPE_MAP: MapValue,
678 }
679
680 _TYPE_TO_CONST_OBJ = {
681 native_bt.VALUE_TYPE_BOOL: _BoolValueConst,
682 native_bt.VALUE_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerValueConst,
683 native_bt.VALUE_TYPE_SIGNED_INTEGER: _SignedIntegerValueConst,
684 native_bt.VALUE_TYPE_REAL: _RealValueConst,
685 native_bt.VALUE_TYPE_STRING: _StringValueConst,
686 native_bt.VALUE_TYPE_ARRAY: _ArrayValueConst,
687 native_bt.VALUE_TYPE_MAP: _MapValueConst,
688 }
This page took 0.043467 seconds and 4 git commands to generate.