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