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 _handle_status(status
, obj_name
):
36 raise RuntimeError('unexpected error')
39 def _create_from_ptr(ptr
):
40 if ptr
is None or ptr
== native_bt
.value_null
:
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
, int):
67 return SignedIntegerValue(value
)
69 if isinstance(value
, float):
70 return RealValue(value
)
72 if isinstance(value
, str):
73 return StringValue(value
)
76 return MapValue(value
)
81 return ArrayValue(value
)
85 raise TypeError("cannot create value object from '{}' object".format(value
.__class
__.__name
__))
88 class _Value(object._SharedObject
, metaclass
=abc
.ABCMeta
):
89 _get_ref
= staticmethod(native_bt
.value_get_ref
)
90 _put_ref
= staticmethod(native_bt
.value_put_ref
)
92 def __ne__(self
, other
):
93 return not (self
== other
)
95 def _handle_status(self
, status
):
96 _handle_status(status
, self
._NAME
)
98 def _check_create_status(self
, ptr
):
100 raise bt2
.CreationError(
101 'cannot create {} value object'.format(self
._NAME
.lower()))
104 @functools.total_ordering
105 class _NumericValue(_Value
):
107 def _extract_value(other
):
108 if isinstance(other
, BoolValue
) or isinstance(other
, bool):
111 if isinstance(other
, numbers
.Integral
):
114 if isinstance(other
, numbers
.Real
):
117 if isinstance(other
, numbers
.Complex
):
118 return complex(other
)
120 raise TypeError("'{}' object is not a number object".format(other
.__class
__.__name
__))
123 return int(self
._value
)
126 return float(self
._value
)
129 return repr(self
._value
)
131 def __lt__(self
, other
):
132 return self
._value
< self
._extract
_value
(other
)
134 def __eq__(self
, other
):
136 return self
._value
== self
._extract
_value
(other
)
140 def __rmod__(self
, other
):
141 return self
._extract
_value
(other
) % self
._value
143 def __mod__(self
, other
):
144 return self
._value
% self
._extract
_value
(other
)
146 def __rfloordiv__(self
, other
):
147 return self
._extract
_value
(other
) // self
._value
149 def __floordiv__(self
, other
):
150 return self
._value
// self
._extract
_value
(other
)
152 def __round__(self
, ndigits
=None):
154 return round(self
._value
)
156 return round(self
._value
, ndigits
)
159 return math
.ceil(self
._value
)
162 return math
.floor(self
._value
)
165 return int(self
._value
)
168 return abs(self
._value
)
170 def __add__(self
, other
):
171 return self
._value
+ self
._extract
_value
(other
)
173 def __radd__(self
, other
):
174 return self
.__add
__(other
)
182 def __mul__(self
, other
):
183 return self
._value
* self
._extract
_value
(other
)
185 def __rmul__(self
, other
):
186 return self
.__mul
__(other
)
188 def __truediv__(self
, other
):
189 return self
._value
/ self
._extract
_value
(other
)
191 def __rtruediv__(self
, other
):
192 return self
._extract
_value
(other
) / self
._value
194 def __pow__(self
, exponent
):
195 return self
._value
** self
._extract
_value
(exponent
)
197 def __rpow__(self
, base
):
198 return self
._extract
_value
(base
) ** self
._value
200 def __iadd__(self
, other
):
201 self
.value
= self
+ other
204 def __isub__(self
, other
):
205 self
.value
= self
- other
208 def __imul__(self
, other
):
209 self
.value
= self
* other
212 def __itruediv__(self
, other
):
213 self
.value
= self
/ other
216 def __ifloordiv__(self
, other
):
217 self
.value
= self
// other
220 def __imod__(self
, other
):
221 self
.value
= self
% other
224 def __ipow__(self
, other
):
225 self
.value
= self
** other
229 class _IntegralValue(_NumericValue
, numbers
.Integral
):
230 def __lshift__(self
, other
):
231 return self
._value
<< self
._extract
_value
(other
)
233 def __rlshift__(self
, other
):
234 return self
._extract
_value
(other
) << self
._value
236 def __rshift__(self
, other
):
237 return self
._value
>> self
._extract
_value
(other
)
239 def __rrshift__(self
, other
):
240 return self
._extract
_value
(other
) >> self
._value
242 def __and__(self
, other
):
243 return self
._value
& self
._extract
_value
(other
)
245 def __rand__(self
, other
):
246 return self
._extract
_value
(other
) & self
._value
248 def __xor__(self
, other
):
249 return self
._value ^ self
._extract
_value
(other
)
251 def __rxor__(self
, other
):
252 return self
._extract
_value
(other
) ^ self
._value
254 def __or__(self
, other
):
255 return self
._value | self
._extract
_value
(other
)
257 def __ror__(self
, other
):
258 return self
._extract
_value
(other
) | self
._value
260 def __invert__(self
):
263 def __ilshift__(self
, other
):
264 self
.value
= self
<< other
267 def __irshift__(self
, other
):
268 self
.value
= self
>> other
271 def __iand__(self
, other
):
272 self
.value
= self
& other
275 def __ixor__(self
, other
):
276 self
.value
= self ^ other
279 def __ior__(self
, other
):
280 self
.value
= self | other
284 class _RealValue(_NumericValue
, numbers
.Real
):
288 class BoolValue(_Value
):
291 def __init__(self
, value
=None):
293 ptr
= native_bt
.value_bool_create()
295 ptr
= native_bt
.value_bool_create_init(self
._value
_to
_bool
(value
))
297 self
._check
_create
_status
(ptr
)
298 super().__init
__(ptr
)
300 def __eq__(self
, other
):
302 return self
._value
== self
._value
_to
_bool
(other
)
310 return repr(self
._value
)
312 def _value_to_bool(self
, value
):
313 if isinstance(value
, BoolValue
):
316 if not isinstance(value
, bool):
317 raise TypeError("'{}' object is not a 'bool' or 'BoolValue' object".format(value
.__class
__))
323 value
= native_bt
.value_bool_get(self
._ptr
)
326 def _set_value(self
, value
):
327 native_bt
.value_bool_set(self
._ptr
, self
._value
_to
_bool
(value
))
329 value
= property(fset
=_set_value
)
332 class _IntegerValue(_IntegralValue
):
333 def __init__(self
, value
=None):
335 ptr
= self
._create
_default
_value
()
337 ptr
= self
._create
_value
(self
._value
_to
_int
(value
))
339 self
._check
_create
_status
(ptr
)
340 super().__init
__(ptr
)
342 def _value_to_int(self
, value
):
343 if not isinstance(value
, numbers
.Real
):
344 raise TypeError('expecting a number object')
347 self
._check
_int
_range
(value
)
352 return self
._get
_value
(self
._ptr
)
354 def _prop_set_value(self
, value
):
355 self
._set
_value
(self
._ptr
, self
._value
_to
_int
(value
))
357 value
= property(fset
=_prop_set_value
)
360 class UnsignedIntegerValue(_IntegerValue
):
361 _check_int_range
= staticmethod(utils
._check
_uint
64)
362 _create_default_value
= staticmethod(native_bt
.value_unsigned_integer_create
)
363 _create_value
= staticmethod(native_bt
.value_unsigned_integer_create_init
)
364 _set_value
= staticmethod(native_bt
.value_unsigned_integer_set
)
365 _get_value
= staticmethod(native_bt
.value_unsigned_integer_get
)
368 class SignedIntegerValue(_IntegerValue
):
369 _check_int_range
= staticmethod(utils
._check
_int
64)
370 _create_default_value
= staticmethod(native_bt
.value_signed_integer_create
)
371 _create_value
= staticmethod(native_bt
.value_signed_integer_create_init
)
372 _set_value
= staticmethod(native_bt
.value_signed_integer_set
)
373 _get_value
= staticmethod(native_bt
.value_signed_integer_get
)
376 class RealValue(_RealValue
):
377 _NAME
= 'Real number'
379 def __init__(self
, value
=None):
381 ptr
= native_bt
.value_real_create()
383 value
= self
._value
_to
_float
(value
)
384 ptr
= native_bt
.value_real_create_init(value
)
386 self
._check
_create
_status
(ptr
)
387 super().__init
__(ptr
)
389 def _value_to_float(self
, value
):
390 if not isinstance(value
, numbers
.Real
):
391 raise TypeError("expecting a real number object")
397 return native_bt
.value_real_get(self
._ptr
)
399 def _set_value(self
, value
):
400 native_bt
.value_real_set(self
._ptr
, self
._value
_to
_float
(value
))
402 value
= property(fset
=_set_value
)
405 @functools.total_ordering
406 class StringValue(collections
.abc
.Sequence
, _Value
):
409 def __init__(self
, value
=None):
411 ptr
= native_bt
.value_string_create()
413 ptr
= native_bt
.value_string_create_init(self
._value
_to
_str
(value
))
415 self
._check
_create
_status
(ptr
)
416 super().__init
__(ptr
)
418 def _value_to_str(self
, value
):
419 if isinstance(value
, self
.__class
__):
422 utils
._check
_str
(value
)
427 return native_bt
.value_string_get(self
._ptr
)
429 def _set_value(self
, value
):
430 status
= native_bt
.value_string_set(self
._ptr
, self
._value
_to
_str
(value
))
431 self
._handle
_status
(status
)
433 value
= property(fset
=_set_value
)
435 def __eq__(self
, other
):
437 return self
._value
== self
._value
_to
_str
(other
)
441 def __lt__(self
, other
):
442 return self
._value
< self
._value
_to
_str
(other
)
445 return bool(self
._value
)
448 return repr(self
._value
)
453 def __getitem__(self
, index
):
454 return self
._value
[index
]
457 return len(self
._value
)
459 def __iadd__(self
, value
):
460 curvalue
= self
._value
461 curvalue
+= self
._value
_to
_str
(value
)
462 self
.value
= curvalue
468 return len(self
) != 0
470 def __delitem__(self
, index
):
471 raise NotImplementedError
474 class ArrayValue(_Container
, collections
.abc
.MutableSequence
, _Value
):
477 def __init__(self
, value
=None):
478 ptr
= native_bt
.value_array_create()
479 self
._check
_create
_status
(ptr
)
480 super().__init
__(ptr
)
482 # Python will raise a TypeError if there's anything wrong with
483 # the iterable protocol.
484 if value
is not None:
488 def __eq__(self
, other
):
489 if not isinstance(other
, collections
.abc
.Sequence
):
492 if len(self
) != len(other
):
496 for self_elem
, other_elem
in zip(self
, other
):
497 if self_elem
!= other_elem
:
503 size
= native_bt
.value_array_get_size(self
._ptr
)
507 def _check_index(self
, index
):
508 # TODO: support slices also
509 if not isinstance(index
, numbers
.Integral
):
510 raise TypeError("'{}' object is not an integral number object: invalid index".format(index
.__class
__.__name
__))
514 if index
< 0 or index
>= len(self
):
515 raise IndexError('array value object index is out of range')
517 def __getitem__(self
, index
):
518 self
._check
_index
(index
)
519 ptr
= native_bt
.value_array_borrow_element_by_index(self
._ptr
, index
)
521 return _create_from_ptr_and_get_ref(ptr
)
523 def __setitem__(self
, index
, value
):
524 self
._check
_index
(index
)
525 value
= create_value(value
)
528 ptr
= native_bt
.value_null
532 status
= native_bt
.value_array_set_element_by_index(
533 self
._ptr
, index
, ptr
)
534 self
._handle
_status
(status
)
536 def append(self
, value
):
537 value
= create_value(value
)
540 ptr
= native_bt
.value_null
544 status
= native_bt
.value_array_append_element(self
._ptr
, ptr
)
545 self
._handle
_status
(status
)
547 def __iadd__(self
, iterable
):
548 # Python will raise a TypeError if there's anything wrong with
549 # the iterable protocol.
550 for elem
in iterable
:
556 return '[{}]'.format(', '.join([repr(v
) for v
in self
]))
558 def insert(self
, value
):
559 raise NotImplementedError
562 class _MapValueKeyIterator(collections
.abc
.Iterator
):
563 def __init__(self
, map_obj
):
564 self
._map
_obj
= map_obj
566 keys_ptr
= native_bt
.value_map_get_keys(map_obj
._ptr
)
569 raise RuntimeError('unexpected error: cannot get map value object keys')
571 self
._keys
= _create_from_ptr(keys_ptr
)
574 if self
._at
== len(self
._map
_obj
):
577 key
= self
._keys
[self
._at
]
582 class MapValue(_Container
, collections
.abc
.MutableMapping
, _Value
):
585 def __init__(self
, value
=None):
586 ptr
= native_bt
.value_map_create()
587 self
._check
_create
_status
(ptr
)
588 super().__init
__(ptr
)
590 # Python will raise a TypeError if there's anything wrong with
591 # the iterable/mapping protocol.
592 if value
is not None:
593 for key
, elem
in value
.items():
596 def __ne__(self
, other
):
597 return _Value
.__ne
__(self
, other
)
599 def __eq__(self
, other
):
600 if not isinstance(other
, collections
.abc
.Mapping
):
603 if len(self
) != len(other
):
607 for self_key
in self
:
608 if self_key
not in other
:
611 if self
[self_key
] != other
[self_key
]:
617 size
= native_bt
.value_map_get_size(self
._ptr
)
621 def __contains__(self
, key
):
622 self
._check
_key
_type
(key
)
623 return native_bt
.value_map_has_entry(self
._ptr
, key
)
625 def _check_key_type(self
, key
):
626 utils
._check
_str
(key
)
628 def _check_key(self
, key
):
632 def __getitem__(self
, key
):
634 ptr
= native_bt
.value_map_borrow_entry_value(self
._ptr
, key
)
636 return _create_from_ptr_and_get_ref(ptr
)
639 return _MapValueKeyIterator(self
)
641 def __setitem__(self
, key
, value
):
642 self
._check
_key
_type
(key
)
643 value
= create_value(value
)
646 ptr
= native_bt
.value_null
650 status
= native_bt
.value_map_insert_entry(self
._ptr
, key
, ptr
)
651 self
._handle
_status
(status
)
654 items
= ['{}: {}'.format(repr(k
), repr(v
)) for k
, v
in self
.items()]
655 return '{{{}}}'.format(', '.join(items
))
659 native_bt
.VALUE_TYPE_BOOL
: BoolValue
,
660 native_bt
.VALUE_TYPE_UNSIGNED_INTEGER
: UnsignedIntegerValue
,
661 native_bt
.VALUE_TYPE_SIGNED_INTEGER
: SignedIntegerValue
,
662 native_bt
.VALUE_TYPE_REAL
: RealValue
,
663 native_bt
.VALUE_TYPE_STRING
: StringValue
,
664 native_bt
.VALUE_TYPE_ARRAY
: ArrayValue
,
665 native_bt
.VALUE_TYPE_MAP
: MapValue
,