1 # SPDX-License-Identifier: MIT
3 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
5 from bt2
import native_bt
6 from bt2
import object as bt2_object
7 from bt2
import utils
as bt2_utils
8 from bt2
import error
as bt2_error
16 def _create_from_ptr_template(ptr
, object_map
):
20 # bt_value_null is translated to None. However, we are given a reference
21 # to it that we are not going to manage anymore, since we don't create a
22 # Python wrapper for it. Therefore put that reference immediately.
23 if ptr
== native_bt
.value_null
:
27 typeid
= native_bt
.value_get_type(ptr
)
28 return object_map
[typeid
]._create
_from
_ptr
(ptr
)
31 def _create_from_ptr(ptr
):
32 return _create_from_ptr_template(ptr
, _TYPE_TO_OBJ
)
35 def _create_from_const_ptr(ptr
):
36 return _create_from_ptr_template(ptr
, _TYPE_TO_CONST_OBJ
)
39 def _create_from_ptr_and_get_ref_template(ptr
, object_map
):
40 if ptr
is None or ptr
== native_bt
.value_null
:
43 typeid
= native_bt
.value_get_type(ptr
)
44 return object_map
[typeid
]._create
_from
_ptr
_and
_get
_ref
(ptr
)
47 def _create_from_ptr_and_get_ref(ptr
):
48 return _create_from_ptr_and_get_ref_template(ptr
, _TYPE_TO_OBJ
)
51 def _create_from_const_ptr_and_get_ref(ptr
):
52 return _create_from_ptr_and_get_ref_template(ptr
, _TYPE_TO_CONST_OBJ
)
55 def create_value(value
):
60 if isinstance(value
, _Value
):
63 if isinstance(value
, bool):
64 return BoolValue(value
)
66 if isinstance(value
, numbers
.Integral
):
67 return SignedIntegerValue(value
)
69 if isinstance(value
, numbers
.Real
):
70 return RealValue(value
)
72 if isinstance(value
, str):
73 return StringValue(value
)
75 if isinstance(value
, collections
.abc
.Sequence
):
76 return ArrayValue(value
)
78 if isinstance(value
, collections
.abc
.Mapping
):
79 return MapValue(value
)
82 "cannot create value object from '{}' object".format(value
.__class
__.__name
__)
86 class _ValueConst(bt2_object
._SharedObject
, metaclass
=abc
.ABCMeta
):
89 native_bt
.value_get_ref(ptr
)
93 native_bt
.value_put_ref(ptr
)
95 _create_value_from_ptr
= staticmethod(_create_from_const_ptr
)
96 _create_value_from_ptr_and_get_ref
= staticmethod(
97 _create_from_const_ptr_and_get_ref
100 def __ne__(self
, other
):
101 return not (self
== other
)
103 def _check_create_status(self
, ptr
):
105 raise bt2_error
._MemoryError
(
106 "cannot create {} value object".format(self
._NAME
.lower())
110 class _Value(_ValueConst
):
111 _create_value_from_ptr
= staticmethod(_create_from_ptr
)
112 _create_value_from_ptr_and_get_ref
= staticmethod(_create_from_ptr_and_get_ref
)
115 @functools.total_ordering
116 class _NumericValueConst(_ValueConst
):
118 def _extract_value(other
):
119 if isinstance(other
, _BoolValueConst
) or isinstance(other
, bool):
122 if isinstance(other
, numbers
.Integral
):
125 if isinstance(other
, numbers
.Real
):
128 if isinstance(other
, numbers
.Complex
):
129 return complex(other
)
132 "'{}' object is not a number object".format(other
.__class
__.__name
__)
136 return int(self
._value
)
139 return float(self
._value
)
142 return repr(self
._value
)
144 def __lt__(self
, other
):
145 return self
._value
< self
._extract
_value
(other
)
147 def __eq__(self
, other
):
149 return self
._value
== self
._extract
_value
(other
)
153 def __rmod__(self
, other
):
154 return self
._extract
_value
(other
) % self
._value
156 def __mod__(self
, other
):
157 return self
._value
% self
._extract
_value
(other
)
159 def __rfloordiv__(self
, other
):
160 return self
._extract
_value
(other
) // self
._value
162 def __floordiv__(self
, other
):
163 return self
._value
// self
._extract
_value
(other
)
165 def __round__(self
, ndigits
=None):
167 return round(self
._value
)
169 return round(self
._value
, ndigits
)
172 return math
.ceil(self
._value
)
175 return math
.floor(self
._value
)
178 return int(self
._value
)
181 return abs(self
._value
)
183 def __add__(self
, other
):
184 return self
._value
+ self
._extract
_value
(other
)
186 def __radd__(self
, other
):
187 return self
.__add
__(other
)
195 def __mul__(self
, other
):
196 return self
._value
* self
._extract
_value
(other
)
198 def __rmul__(self
, other
):
199 return self
.__mul
__(other
)
201 def __truediv__(self
, other
):
202 return self
._value
/ self
._extract
_value
(other
)
204 def __rtruediv__(self
, other
):
205 return self
._extract
_value
(other
) / self
._value
207 def __pow__(self
, exponent
):
208 return self
._value
** self
._extract
_value
(exponent
)
210 def __rpow__(self
, base
):
211 return self
._extract
_value
(base
) ** self
._value
214 class _NumericValue(_NumericValueConst
, _Value
):
218 class _IntegralValueConst(_NumericValueConst
, numbers
.Integral
):
219 def __lshift__(self
, other
):
220 return self
._value
<< self
._extract
_value
(other
)
222 def __rlshift__(self
, other
):
223 return self
._extract
_value
(other
) << self
._value
225 def __rshift__(self
, other
):
226 return self
._value
>> self
._extract
_value
(other
)
228 def __rrshift__(self
, other
):
229 return self
._extract
_value
(other
) >> self
._value
231 def __and__(self
, other
):
232 return self
._value
& self
._extract
_value
(other
)
234 def __rand__(self
, other
):
235 return self
._extract
_value
(other
) & self
._value
237 def __xor__(self
, other
):
238 return self
._value ^ self
._extract
_value
(other
)
240 def __rxor__(self
, other
):
241 return self
._extract
_value
(other
) ^ self
._value
243 def __or__(self
, other
):
244 return self
._value | self
._extract
_value
(other
)
246 def __ror__(self
, other
):
247 return self
._extract
_value
(other
) | self
._value
249 def __invert__(self
):
253 class _IntegralValue(_IntegralValueConst
, _NumericValue
):
257 class _BoolValueConst(_IntegralValueConst
):
258 _NAME
= "Const boolean"
264 return repr(self
._value
)
268 value
= native_bt
.value_bool_get(self
._ptr
)
272 class BoolValue(_BoolValueConst
, _IntegralValue
):
275 def __init__(self
, value
=None):
277 ptr
= native_bt
.value_bool_create()
279 ptr
= native_bt
.value_bool_create_init(self
._value
_to
_bool
(value
))
281 self
._check
_create
_status
(ptr
)
282 super().__init
__(ptr
)
285 def _value_to_bool(cls
, value
):
286 if isinstance(value
, _BoolValueConst
):
289 if not isinstance(value
, bool):
291 "'{}' object is not a 'bool', 'BoolValue', or '_BoolValueConst' object".format(
298 def _set_value(self
, value
):
299 native_bt
.value_bool_set(self
._ptr
, self
._value
_to
_bool
(value
))
301 value
= property(fset
=_set_value
)
304 class _IntegerValueConst(_IntegralValueConst
):
307 return self
._get
_value
(self
._ptr
)
310 class _IntegerValue(_IntegerValueConst
, _IntegralValue
):
311 def __init__(self
, value
=None):
313 ptr
= self
._create
_default
_value
()
315 ptr
= self
._create
_value
(self
._value
_to
_int
(value
))
317 self
._check
_create
_status
(ptr
)
318 super().__init
__(ptr
)
321 def _value_to_int(cls
, value
):
322 if not isinstance(value
, numbers
.Integral
):
323 raise TypeError("expecting an integral number object")
326 cls
._check
_int
_range
(value
)
329 def _prop_set_value(self
, value
):
330 self
._set
_value
(self
._ptr
, self
._value
_to
_int
(value
))
332 value
= property(fset
=_prop_set_value
)
335 class _UnsignedIntegerValueConst(_IntegerValueConst
):
336 _NAME
= "Const unsigned integer"
337 _get_value
= staticmethod(native_bt
.value_integer_unsigned_get
)
340 class UnsignedIntegerValue(_UnsignedIntegerValueConst
, _IntegerValue
):
341 _NAME
= "Unsigned integer"
342 _check_int_range
= staticmethod(bt2_utils
._check
_uint
64)
343 _create_default_value
= staticmethod(native_bt
.value_integer_unsigned_create
)
344 _create_value
= staticmethod(native_bt
.value_integer_unsigned_create_init
)
345 _set_value
= staticmethod(native_bt
.value_integer_unsigned_set
)
348 class _SignedIntegerValueConst(_IntegerValueConst
):
349 _NAME
= "Const signed integer"
350 _get_value
= staticmethod(native_bt
.value_integer_signed_get
)
353 class SignedIntegerValue(_SignedIntegerValueConst
, _IntegerValue
):
354 _NAME
= "Signed integer"
355 _check_int_range
= staticmethod(bt2_utils
._check
_int
64)
356 _create_default_value
= staticmethod(native_bt
.value_integer_signed_create
)
357 _create_value
= staticmethod(native_bt
.value_integer_signed_create_init
)
358 _set_value
= staticmethod(native_bt
.value_integer_signed_set
)
361 class _RealValueConst(_NumericValueConst
, numbers
.Real
):
362 _NAME
= "Const real number"
366 return native_bt
.value_real_get(self
._ptr
)
369 class RealValue(_RealValueConst
, _NumericValue
):
370 _NAME
= "Real number"
372 def __init__(self
, value
=None):
374 ptr
= native_bt
.value_real_create()
376 value
= self
._value
_to
_float
(value
)
377 ptr
= native_bt
.value_real_create_init(value
)
379 self
._check
_create
_status
(ptr
)
380 super().__init
__(ptr
)
383 def _value_to_float(cls
, value
):
384 if not isinstance(value
, numbers
.Real
):
385 raise TypeError("expecting a real number object")
389 def _set_value(self
, value
):
390 native_bt
.value_real_set(self
._ptr
, self
._value
_to
_float
(value
))
392 value
= property(fset
=_set_value
)
395 @functools.total_ordering
396 class _StringValueConst(collections
.abc
.Sequence
, _Value
):
397 _NAME
= "Const string"
400 def _value_to_str(cls
, value
):
401 if isinstance(value
, _StringValueConst
):
404 bt2_utils
._check
_str
(value
)
409 return native_bt
.value_string_get(self
._ptr
)
411 def __eq__(self
, other
):
413 return self
._value
== self
._value
_to
_str
(other
)
417 def __lt__(self
, other
):
418 return self
._value
< self
._value
_to
_str
(other
)
421 return bool(self
._value
)
424 return repr(self
._value
)
429 def __getitem__(self
, index
):
430 return self
._value
[index
]
433 return len(self
._value
)
435 def __contains__(self
, item
):
436 return self
._value
_to
_str
(item
) in self
._value
439 class StringValue(_StringValueConst
, _Value
):
442 def __init__(self
, value
=None):
444 ptr
= native_bt
.value_string_create()
446 ptr
= native_bt
.value_string_create_init(self
._value
_to
_str
(value
))
448 self
._check
_create
_status
(ptr
)
449 super().__init
__(ptr
)
451 def _set_value(self
, value
):
452 status
= native_bt
.value_string_set(self
._ptr
, self
._value
_to
_str
(value
))
453 bt2_utils
._handle
_func
_status
(status
)
455 value
= property(fset
=_set_value
)
457 def __iadd__(self
, value
):
458 curvalue
= self
._value
459 curvalue
+= self
._value
_to
_str
(value
)
460 self
.value
= curvalue
464 class _ContainerConst
:
466 return len(self
) != 0
469 class _Container(_ContainerConst
):
470 def __delitem__(self
, index
):
471 raise NotImplementedError
474 class _ArrayValueConst(_ContainerConst
, collections
.abc
.Sequence
, _ValueConst
):
475 _NAME
= "Const array"
476 _borrow_element_by_index
= staticmethod(
477 native_bt
.value_array_borrow_element_by_index_const
481 def __eq__(self
, other
):
482 if not isinstance(other
, collections
.abc
.Sequence
):
485 if len(self
) != len(other
):
489 for self_elem
, other_elem
in zip(self
, other
):
490 if self_elem
!= other_elem
:
496 size
= native_bt
.value_array_get_length(self
._ptr
)
500 def _check_index(self
, index
):
501 # TODO: support slices also
502 if not isinstance(index
, numbers
.Integral
):
504 "'{}' object is not an integral number object: invalid index".format(
505 index
.__class
__.__name
__
511 if index
< 0 or index
>= len(self
):
512 raise IndexError("array value object index is out of range")
514 def __getitem__(self
, index
):
515 self
._check
_index
(index
)
516 ptr
= self
._borrow
_element
_by
_index
(self
._ptr
, index
)
518 return self
._create
_value
_from
_ptr
_and
_get
_ref
(ptr
)
521 return "[{}]".format(", ".join([repr(v
) for v
in self
]))
524 class ArrayValue(_ArrayValueConst
, _Container
, collections
.abc
.MutableSequence
, _Value
):
526 _borrow_element_by_index
= staticmethod(
527 native_bt
.value_array_borrow_element_by_index
530 def __init__(self
, value
=None):
531 ptr
= native_bt
.value_array_create()
532 self
._check
_create
_status
(ptr
)
533 super().__init
__(ptr
)
535 # Python will raise a TypeError if there's anything wrong with
536 # the iterable protocol.
537 if value
is not None:
541 def __setitem__(self
, index
, value
):
542 self
._check
_index
(index
)
543 value
= create_value(value
)
546 ptr
= native_bt
.value_null
550 status
= native_bt
.value_array_set_element_by_index(self
._ptr
, index
, ptr
)
551 bt2_utils
._handle
_func
_status
(status
)
553 def append(self
, value
):
554 value
= create_value(value
)
557 ptr
= native_bt
.value_null
561 status
= native_bt
.value_array_append_element(self
._ptr
, ptr
)
562 bt2_utils
._handle
_func
_status
(status
)
564 def __iadd__(self
, iterable
):
565 # Python will raise a TypeError if there's anything wrong with
566 # the iterable protocol.
567 for elem
in iterable
:
572 def insert(self
, value
):
573 raise NotImplementedError
576 class _MapValueKeyIterator(collections
.abc
.Iterator
):
577 def __init__(self
, map_obj
):
578 self
._map
_obj
= map_obj
580 keys_ptr
= native_bt
.value_map_get_keys(map_obj
._ptr
)
583 raise RuntimeError("unexpected error: cannot get map value object keys")
585 self
._keys
= _create_from_ptr(keys_ptr
)
588 if self
._at
== len(self
._map
_obj
):
591 key
= self
._keys
[self
._at
]
596 class _MapValueConst(_ContainerConst
, collections
.abc
.Mapping
, _ValueConst
):
598 _borrow_entry_value_ptr
= staticmethod(native_bt
.value_map_borrow_entry_value_const
)
600 def __ne__(self
, other
):
601 return _Value
.__ne
__(self
, other
)
603 def __eq__(self
, other
):
604 if not isinstance(other
, collections
.abc
.Mapping
):
607 if len(self
) != len(other
):
611 for self_key
in self
:
612 if self_key
not in other
:
615 if self
[self_key
] != other
[self_key
]:
621 size
= native_bt
.value_map_get_size(self
._ptr
)
625 def __contains__(self
, key
):
626 self
._check
_key
_type
(key
)
627 return native_bt
.value_map_has_entry(self
._ptr
, key
)
629 def _check_key_type(self
, key
):
630 bt2_utils
._check
_str
(key
)
632 def _check_key(self
, key
):
636 def __getitem__(self
, key
):
638 ptr
= self
._borrow
_entry
_value
_ptr
(self
._ptr
, key
)
640 return self
._create
_value
_from
_ptr
_and
_get
_ref
(ptr
)
643 return _MapValueKeyIterator(self
)
646 items
= ["{}: {}".format(repr(k
), repr(v
)) for k
, v
in self
.items()]
647 return "{{{}}}".format(", ".join(items
))
650 class MapValue(_MapValueConst
, _Container
, collections
.abc
.MutableMapping
, _Value
):
652 _borrow_entry_value_ptr
= staticmethod(native_bt
.value_map_borrow_entry_value
)
654 def __init__(self
, value
=None):
655 ptr
= native_bt
.value_map_create()
656 self
._check
_create
_status
(ptr
)
657 super().__init
__(ptr
)
659 # Python will raise a TypeError if there's anything wrong with
660 # the iterable/mapping protocol.
661 if value
is not None:
662 for key
, elem
in value
.items():
665 def __setitem__(self
, key
, value
):
666 self
._check
_key
_type
(key
)
667 value
= create_value(value
)
670 ptr
= native_bt
.value_null
674 status
= native_bt
.value_map_insert_entry(self
._ptr
, key
, ptr
)
675 bt2_utils
._handle
_func
_status
(status
)
679 native_bt
.VALUE_TYPE_BOOL
: BoolValue
,
680 native_bt
.VALUE_TYPE_UNSIGNED_INTEGER
: UnsignedIntegerValue
,
681 native_bt
.VALUE_TYPE_SIGNED_INTEGER
: SignedIntegerValue
,
682 native_bt
.VALUE_TYPE_REAL
: RealValue
,
683 native_bt
.VALUE_TYPE_STRING
: StringValue
,
684 native_bt
.VALUE_TYPE_ARRAY
: ArrayValue
,
685 native_bt
.VALUE_TYPE_MAP
: MapValue
,
688 _TYPE_TO_CONST_OBJ
= {
689 native_bt
.VALUE_TYPE_BOOL
: _BoolValueConst
,
690 native_bt
.VALUE_TYPE_UNSIGNED_INTEGER
: _UnsignedIntegerValueConst
,
691 native_bt
.VALUE_TYPE_SIGNED_INTEGER
: _SignedIntegerValueConst
,
692 native_bt
.VALUE_TYPE_REAL
: _RealValueConst
,
693 native_bt
.VALUE_TYPE_STRING
: _StringValueConst
,
694 native_bt
.VALUE_TYPE_ARRAY
: _ArrayValueConst
,
695 native_bt
.VALUE_TYPE_MAP
: _MapValueConst
,