1 # The MIT License (MIT)
3 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
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:
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
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
23 from bt2
import native_bt
, object, utils
24 import collections
.abc
32 def _create_from_ptr(ptr
):
36 # bt_value_null is translated to None. However, we are given a reference
37 # to it that we are not going to manage anymore, since we don't create a
38 # Python wrapper for it. Therefore put that reference immediately.
39 if ptr
== native_bt
.value_null
:
40 bt2
.value
._Value
._put
_ref
(ptr
)
43 typeid
= native_bt
.value_get_type(ptr
)
44 return _TYPE_TO_OBJ
[typeid
]._create
_from
_ptr
(ptr
)
47 def _create_from_ptr_and_get_ref(ptr
):
48 if ptr
is None or ptr
== native_bt
.value_null
:
51 typeid
= native_bt
.value_get_type(ptr
)
52 return _TYPE_TO_OBJ
[typeid
]._create
_from
_ptr
_and
_get
_ref
(ptr
)
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 _Value(object._SharedObject
, metaclass
=abc
.ABCMeta
):
87 _get_ref
= staticmethod(native_bt
.value_get_ref
)
88 _put_ref
= staticmethod(native_bt
.value_put_ref
)
90 def __ne__(self
, other
):
91 return not (self
== other
)
93 def _check_create_status(self
, ptr
):
95 raise bt2
.CreationError(
96 'cannot create {} value object'.format(self
._NAME
.lower())
100 @functools.total_ordering
101 class _NumericValue(_Value
):
103 def _extract_value(other
):
104 if isinstance(other
, BoolValue
) or isinstance(other
, bool):
107 if isinstance(other
, numbers
.Integral
):
110 if isinstance(other
, numbers
.Real
):
113 if isinstance(other
, numbers
.Complex
):
114 return complex(other
)
117 "'{}' object is not a number object".format(other
.__class
__.__name
__)
121 return int(self
._value
)
124 return float(self
._value
)
127 return repr(self
._value
)
129 def __lt__(self
, other
):
130 return self
._value
< self
._extract
_value
(other
)
132 def __eq__(self
, other
):
134 return self
._value
== self
._extract
_value
(other
)
138 def __rmod__(self
, other
):
139 return self
._extract
_value
(other
) % self
._value
141 def __mod__(self
, other
):
142 return self
._value
% self
._extract
_value
(other
)
144 def __rfloordiv__(self
, other
):
145 return self
._extract
_value
(other
) // self
._value
147 def __floordiv__(self
, other
):
148 return self
._value
// self
._extract
_value
(other
)
150 def __round__(self
, ndigits
=None):
152 return round(self
._value
)
154 return round(self
._value
, ndigits
)
157 return math
.ceil(self
._value
)
160 return math
.floor(self
._value
)
163 return int(self
._value
)
166 return abs(self
._value
)
168 def __add__(self
, other
):
169 return self
._value
+ self
._extract
_value
(other
)
171 def __radd__(self
, other
):
172 return self
.__add
__(other
)
180 def __mul__(self
, other
):
181 return self
._value
* self
._extract
_value
(other
)
183 def __rmul__(self
, other
):
184 return self
.__mul
__(other
)
186 def __truediv__(self
, other
):
187 return self
._value
/ self
._extract
_value
(other
)
189 def __rtruediv__(self
, other
):
190 return self
._extract
_value
(other
) / self
._value
192 def __pow__(self
, exponent
):
193 return self
._value
** self
._extract
_value
(exponent
)
195 def __rpow__(self
, base
):
196 return self
._extract
_value
(base
) ** self
._value
199 class _IntegralValue(_NumericValue
, numbers
.Integral
):
200 def __lshift__(self
, other
):
201 return self
._value
<< self
._extract
_value
(other
)
203 def __rlshift__(self
, other
):
204 return self
._extract
_value
(other
) << self
._value
206 def __rshift__(self
, other
):
207 return self
._value
>> self
._extract
_value
(other
)
209 def __rrshift__(self
, other
):
210 return self
._extract
_value
(other
) >> self
._value
212 def __and__(self
, other
):
213 return self
._value
& self
._extract
_value
(other
)
215 def __rand__(self
, other
):
216 return self
._extract
_value
(other
) & self
._value
218 def __xor__(self
, other
):
219 return self
._value ^ self
._extract
_value
(other
)
221 def __rxor__(self
, other
):
222 return self
._extract
_value
(other
) ^ self
._value
224 def __or__(self
, other
):
225 return self
._value | self
._extract
_value
(other
)
227 def __ror__(self
, other
):
228 return self
._extract
_value
(other
) | self
._value
230 def __invert__(self
):
234 class _RealValue(_NumericValue
, numbers
.Real
):
238 class BoolValue(_IntegralValue
):
241 def __init__(self
, value
=None):
243 ptr
= native_bt
.value_bool_create()
245 ptr
= native_bt
.value_bool_create_init(self
._value
_to
_bool
(value
))
247 self
._check
_create
_status
(ptr
)
248 super().__init
__(ptr
)
254 return repr(self
._value
)
256 def _value_to_bool(self
, value
):
257 if isinstance(value
, BoolValue
):
260 if not isinstance(value
, bool):
262 "'{}' object is not a 'bool' or 'BoolValue' object".format(
271 value
= native_bt
.value_bool_get(self
._ptr
)
274 def _set_value(self
, value
):
275 native_bt
.value_bool_set(self
._ptr
, self
._value
_to
_bool
(value
))
277 value
= property(fset
=_set_value
)
280 class _IntegerValue(_IntegralValue
):
281 def __init__(self
, value
=None):
283 ptr
= self
._create
_default
_value
()
285 ptr
= self
._create
_value
(self
._value
_to
_int
(value
))
287 self
._check
_create
_status
(ptr
)
288 super().__init
__(ptr
)
290 def _value_to_int(self
, value
):
291 if not isinstance(value
, numbers
.Integral
):
292 raise TypeError('expecting an integral number object')
295 self
._check
_int
_range
(value
)
300 return self
._get
_value
(self
._ptr
)
302 def _prop_set_value(self
, value
):
303 self
._set
_value
(self
._ptr
, self
._value
_to
_int
(value
))
305 value
= property(fset
=_prop_set_value
)
308 class UnsignedIntegerValue(_IntegerValue
):
309 _check_int_range
= staticmethod(utils
._check
_uint
64)
310 _create_default_value
= staticmethod(native_bt
.value_unsigned_integer_create
)
311 _create_value
= staticmethod(native_bt
.value_unsigned_integer_create_init
)
312 _set_value
= staticmethod(native_bt
.value_unsigned_integer_set
)
313 _get_value
= staticmethod(native_bt
.value_unsigned_integer_get
)
316 class SignedIntegerValue(_IntegerValue
):
317 _check_int_range
= staticmethod(utils
._check
_int
64)
318 _create_default_value
= staticmethod(native_bt
.value_signed_integer_create
)
319 _create_value
= staticmethod(native_bt
.value_signed_integer_create_init
)
320 _set_value
= staticmethod(native_bt
.value_signed_integer_set
)
321 _get_value
= staticmethod(native_bt
.value_signed_integer_get
)
324 class RealValue(_RealValue
):
325 _NAME
= 'Real number'
327 def __init__(self
, value
=None):
329 ptr
= native_bt
.value_real_create()
331 value
= self
._value
_to
_float
(value
)
332 ptr
= native_bt
.value_real_create_init(value
)
334 self
._check
_create
_status
(ptr
)
335 super().__init
__(ptr
)
337 def _value_to_float(self
, value
):
338 if not isinstance(value
, numbers
.Real
):
339 raise TypeError("expecting a real number object")
345 return native_bt
.value_real_get(self
._ptr
)
347 def _set_value(self
, value
):
348 native_bt
.value_real_set(self
._ptr
, self
._value
_to
_float
(value
))
350 value
= property(fset
=_set_value
)
353 @functools.total_ordering
354 class StringValue(collections
.abc
.Sequence
, _Value
):
357 def __init__(self
, value
=None):
359 ptr
= native_bt
.value_string_create()
361 ptr
= native_bt
.value_string_create_init(self
._value
_to
_str
(value
))
363 self
._check
_create
_status
(ptr
)
364 super().__init
__(ptr
)
366 def _value_to_str(self
, value
):
367 if isinstance(value
, self
.__class
__):
370 utils
._check
_str
(value
)
375 return native_bt
.value_string_get(self
._ptr
)
377 def _set_value(self
, value
):
378 status
= native_bt
.value_string_set(self
._ptr
, self
._value
_to
_str
(value
))
379 utils
._handle
_func
_status
(status
)
381 value
= property(fset
=_set_value
)
383 def __eq__(self
, other
):
385 return self
._value
== self
._value
_to
_str
(other
)
389 def __lt__(self
, other
):
390 return self
._value
< self
._value
_to
_str
(other
)
393 return bool(self
._value
)
396 return repr(self
._value
)
401 def __getitem__(self
, index
):
402 return self
._value
[index
]
405 return len(self
._value
)
407 def __iadd__(self
, value
):
408 curvalue
= self
._value
409 curvalue
+= self
._value
_to
_str
(value
)
410 self
.value
= curvalue
416 return len(self
) != 0
418 def __delitem__(self
, index
):
419 raise NotImplementedError
422 class ArrayValue(_Container
, collections
.abc
.MutableSequence
, _Value
):
425 def __init__(self
, value
=None):
426 ptr
= native_bt
.value_array_create()
427 self
._check
_create
_status
(ptr
)
428 super().__init
__(ptr
)
430 # Python will raise a TypeError if there's anything wrong with
431 # the iterable protocol.
432 if value
is not None:
436 def __eq__(self
, other
):
437 if not isinstance(other
, collections
.abc
.Sequence
):
440 if len(self
) != len(other
):
444 for self_elem
, other_elem
in zip(self
, other
):
445 if self_elem
!= other_elem
:
451 size
= native_bt
.value_array_get_size(self
._ptr
)
455 def _check_index(self
, index
):
456 # TODO: support slices also
457 if not isinstance(index
, numbers
.Integral
):
459 "'{}' object is not an integral number object: invalid index".format(
460 index
.__class
__.__name
__
466 if index
< 0 or index
>= len(self
):
467 raise IndexError('array value object index is out of range')
469 def __getitem__(self
, index
):
470 self
._check
_index
(index
)
471 ptr
= native_bt
.value_array_borrow_element_by_index(self
._ptr
, index
)
473 return _create_from_ptr_and_get_ref(ptr
)
475 def __setitem__(self
, index
, value
):
476 self
._check
_index
(index
)
477 value
= create_value(value
)
480 ptr
= native_bt
.value_null
484 status
= native_bt
.value_array_set_element_by_index(self
._ptr
, index
, ptr
)
485 utils
._handle
_func
_status
(status
)
487 def append(self
, value
):
488 value
= create_value(value
)
491 ptr
= native_bt
.value_null
495 status
= native_bt
.value_array_append_element(self
._ptr
, ptr
)
496 utils
._handle
_func
_status
(status
)
498 def __iadd__(self
, iterable
):
499 # Python will raise a TypeError if there's anything wrong with
500 # the iterable protocol.
501 for elem
in iterable
:
507 return '[{}]'.format(', '.join([repr(v
) for v
in self
]))
509 def insert(self
, value
):
510 raise NotImplementedError
513 class _MapValueKeyIterator(collections
.abc
.Iterator
):
514 def __init__(self
, map_obj
):
515 self
._map
_obj
= map_obj
517 keys_ptr
= native_bt
.value_map_get_keys(map_obj
._ptr
)
520 raise RuntimeError('unexpected error: cannot get map value object keys')
522 self
._keys
= _create_from_ptr(keys_ptr
)
525 if self
._at
== len(self
._map
_obj
):
528 key
= self
._keys
[self
._at
]
533 class MapValue(_Container
, collections
.abc
.MutableMapping
, _Value
):
536 def __init__(self
, value
=None):
537 ptr
= native_bt
.value_map_create()
538 self
._check
_create
_status
(ptr
)
539 super().__init
__(ptr
)
541 # Python will raise a TypeError if there's anything wrong with
542 # the iterable/mapping protocol.
543 if value
is not None:
544 for key
, elem
in value
.items():
547 def __ne__(self
, other
):
548 return _Value
.__ne
__(self
, other
)
550 def __eq__(self
, other
):
551 if not isinstance(other
, collections
.abc
.Mapping
):
554 if len(self
) != len(other
):
558 for self_key
in self
:
559 if self_key
not in other
:
562 if self
[self_key
] != other
[self_key
]:
568 size
= native_bt
.value_map_get_size(self
._ptr
)
572 def __contains__(self
, key
):
573 self
._check
_key
_type
(key
)
574 return native_bt
.value_map_has_entry(self
._ptr
, key
)
576 def _check_key_type(self
, key
):
577 utils
._check
_str
(key
)
579 def _check_key(self
, key
):
583 def __getitem__(self
, key
):
585 ptr
= native_bt
.value_map_borrow_entry_value(self
._ptr
, key
)
587 return _create_from_ptr_and_get_ref(ptr
)
590 return _MapValueKeyIterator(self
)
592 def __setitem__(self
, key
, value
):
593 self
._check
_key
_type
(key
)
594 value
= create_value(value
)
597 ptr
= native_bt
.value_null
601 status
= native_bt
.value_map_insert_entry(self
._ptr
, key
, ptr
)
602 utils
._handle
_func
_status
(status
)
605 items
= ['{}: {}'.format(repr(k
), repr(v
)) for k
, v
in self
.items()]
606 return '{{{}}}'.format(', '.join(items
))
610 native_bt
.VALUE_TYPE_BOOL
: BoolValue
,
611 native_bt
.VALUE_TYPE_UNSIGNED_INTEGER
: UnsignedIntegerValue
,
612 native_bt
.VALUE_TYPE_SIGNED_INTEGER
: SignedIntegerValue
,
613 native_bt
.VALUE_TYPE_REAL
: RealValue
,
614 native_bt
.VALUE_TYPE_STRING
: StringValue
,
615 native_bt
.VALUE_TYPE_ARRAY
: ArrayValue
,
616 native_bt
.VALUE_TYPE_MAP
: MapValue
,