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 from bt2
import field_class
as bt2_field_class
25 import collections
.abc
31 def _create_field_from_ptr(ptr
, owner_ptr
, owner_get_ref
, owner_put_ref
):
32 field_class_ptr
= native_bt
.field_borrow_class_const(ptr
)
33 typeid
= native_bt
.field_class_get_type(field_class_ptr
)
34 field
= _TYPE_ID_TO_OBJ
[typeid
]._create
_from
_ptr
_and
_get
_ref
(
35 ptr
, owner_ptr
, owner_get_ref
, owner_put_ref
40 # Get the "effective" field of `field`. If `field` is a variant, return
41 # the currently selected field. If `field` is an option, return the
42 # content field. If `field` is of any other type, return `field`
46 def _get_leaf_field(field
):
47 if isinstance(field
, _VariantField
):
48 return _get_leaf_field(field
.selected_option
)
50 if isinstance(field
, _OptionField
):
51 return _get_leaf_field(field
.field
)
56 class _Field(object._UniqueObject
):
57 def __eq__(self
, other
):
58 other
= _get_leaf_field(other
)
59 return self
._spec
_eq
(other
)
63 field_class_ptr
= native_bt
.field_borrow_class_const(self
._ptr
)
64 assert field_class_ptr
is not None
65 return bt2_field_class
._create
_field
_class
_from
_ptr
_and
_get
_ref
(field_class_ptr
)
68 raise NotImplementedError
74 class _BitArrayField(_Field
):
78 def value_as_integer(self
):
79 return native_bt
.field_bit_array_get_value_as_integer(self
._ptr
)
81 @value_as_integer.setter
82 def value_as_integer(self
, value
):
83 utils
._check
_uint
64(value
)
84 native_bt
.field_bit_array_set_value_as_integer(self
._ptr
, value
)
86 def _spec_eq(self
, other
):
87 if type(other
) is not type(self
):
90 return self
.value_as_integer
== other
.value_as_integer
93 return repr(self
.value_as_integer
)
96 return str(self
.value_as_integer
)
99 return self
.cls
.length
102 @functools.total_ordering
103 class _NumericField(_Field
):
105 def _extract_value(other
):
106 if isinstance(other
, _BoolField
) or isinstance(other
, bool):
109 if isinstance(other
, numbers
.Integral
):
112 if isinstance(other
, numbers
.Real
):
115 if isinstance(other
, numbers
.Complex
):
116 return complex(other
)
119 "'{}' 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 if not isinstance(other
, numbers
.Number
):
134 'unorderable types: {}() < {}()'.format(
135 self
.__class
__.__name
__, other
.__class
__.__name
__
139 return self
._value
< self
._extract
_value
(other
)
141 def _spec_eq(self
, other
):
143 return self
._value
== self
._extract
_value
(other
)
147 def __rmod__(self
, other
):
148 return self
._extract
_value
(other
) % self
._value
150 def __mod__(self
, other
):
151 return self
._value
% self
._extract
_value
(other
)
153 def __rfloordiv__(self
, other
):
154 return self
._extract
_value
(other
) // self
._value
156 def __floordiv__(self
, other
):
157 return self
._value
// self
._extract
_value
(other
)
159 def __round__(self
, ndigits
=None):
161 return round(self
._value
)
163 return round(self
._value
, ndigits
)
166 return math
.ceil(self
._value
)
169 return math
.floor(self
._value
)
172 return int(self
._value
)
175 return abs(self
._value
)
177 def __add__(self
, other
):
178 return self
._value
+ self
._extract
_value
(other
)
180 def __radd__(self
, other
):
181 return self
.__add
__(other
)
189 def __mul__(self
, other
):
190 return self
._value
* self
._extract
_value
(other
)
192 def __rmul__(self
, other
):
193 return self
.__mul
__(other
)
195 def __truediv__(self
, other
):
196 return self
._value
/ self
._extract
_value
(other
)
198 def __rtruediv__(self
, other
):
199 return self
._extract
_value
(other
) / self
._value
201 def __pow__(self
, exponent
):
202 return self
._value
** self
._extract
_value
(exponent
)
204 def __rpow__(self
, base
):
205 return self
._extract
_value
(base
) ** self
._value
208 class _IntegralField(_NumericField
, numbers
.Integral
):
209 def __lshift__(self
, other
):
210 return self
._value
<< self
._extract
_value
(other
)
212 def __rlshift__(self
, other
):
213 return self
._extract
_value
(other
) << self
._value
215 def __rshift__(self
, other
):
216 return self
._value
>> self
._extract
_value
(other
)
218 def __rrshift__(self
, other
):
219 return self
._extract
_value
(other
) >> self
._value
221 def __and__(self
, other
):
222 return self
._value
& self
._extract
_value
(other
)
224 def __rand__(self
, other
):
225 return self
._extract
_value
(other
) & self
._value
227 def __xor__(self
, other
):
228 return self
._value ^ self
._extract
_value
(other
)
230 def __rxor__(self
, other
):
231 return self
._extract
_value
(other
) ^ self
._value
233 def __or__(self
, other
):
234 return self
._value | self
._extract
_value
(other
)
236 def __ror__(self
, other
):
237 return self
._extract
_value
(other
) | self
._value
239 def __invert__(self
):
243 class _BoolField(_IntegralField
, _Field
):
249 def _value_to_bool(self
, value
):
250 if isinstance(value
, _BoolField
):
253 if not isinstance(value
, bool):
255 "'{}' object is not a 'bool' or '_BoolField' object".format(
264 return bool(native_bt
.field_bool_get_value(self
._ptr
))
266 def _set_value(self
, value
):
267 value
= self
._value
_to
_bool
(value
)
268 native_bt
.field_bool_set_value(self
._ptr
, value
)
270 value
= property(fset
=_set_value
)
273 class _IntegerField(_IntegralField
, _Field
):
277 class _UnsignedIntegerField(_IntegerField
, _Field
):
278 _NAME
= 'Unsigned integer'
280 def _value_to_int(self
, value
):
281 if not isinstance(value
, numbers
.Integral
):
282 raise TypeError('expecting an integral number object')
285 utils
._check
_uint
64(value
)
291 return native_bt
.field_integer_unsigned_get_value(self
._ptr
)
293 def _set_value(self
, value
):
294 value
= self
._value
_to
_int
(value
)
295 native_bt
.field_integer_unsigned_set_value(self
._ptr
, value
)
297 value
= property(fset
=_set_value
)
300 class _SignedIntegerField(_IntegerField
, _Field
):
301 _NAME
= 'Signed integer'
303 def _value_to_int(self
, value
):
304 if not isinstance(value
, numbers
.Integral
):
305 raise TypeError('expecting an integral number object')
308 utils
._check
_int
64(value
)
314 return native_bt
.field_integer_signed_get_value(self
._ptr
)
316 def _set_value(self
, value
):
317 value
= self
._value
_to
_int
(value
)
318 native_bt
.field_integer_signed_set_value(self
._ptr
, value
)
320 value
= property(fset
=_set_value
)
323 class _RealField(_NumericField
, numbers
.Real
):
326 def _value_to_float(self
, value
):
327 if not isinstance(value
, numbers
.Real
):
328 raise TypeError("expecting a real number object")
334 return native_bt
.field_real_get_value(self
._ptr
)
336 def _set_value(self
, value
):
337 value
= self
._value
_to
_float
(value
)
338 native_bt
.field_real_set_value(self
._ptr
, value
)
340 value
= property(fset
=_set_value
)
343 class _EnumerationField(_IntegerField
):
345 return '{} ({})'.format(self
._value
, ', '.join(self
.labels
))
349 status
, labels
= self
._get
_mapping
_labels
(self
._ptr
)
350 utils
._handle
_func
_status
(status
, "cannot get label for enumeration field")
352 assert labels
is not None
356 class _UnsignedEnumerationField(_EnumerationField
, _UnsignedIntegerField
):
357 _NAME
= 'Unsigned Enumeration'
358 _get_mapping_labels
= staticmethod(
359 native_bt
.field_enumeration_unsigned_get_mapping_labels
363 class _SignedEnumerationField(_EnumerationField
, _SignedIntegerField
):
364 _NAME
= 'Signed Enumeration'
365 _get_mapping_labels
= staticmethod(
366 native_bt
.field_enumeration_signed_get_mapping_labels
370 @functools.total_ordering
371 class _StringField(_Field
):
374 def _value_to_str(self
, value
):
375 if isinstance(value
, self
.__class
__):
378 if not isinstance(value
, str):
379 raise TypeError("expecting a 'str' object")
385 return native_bt
.field_string_get_value(self
._ptr
)
387 def _set_value(self
, value
):
388 value
= self
._value
_to
_str
(value
)
389 native_bt
.field_string_set_value(self
._ptr
, value
)
391 value
= property(fset
=_set_value
)
393 def _spec_eq(self
, other
):
395 return self
._value
== self
._value
_to
_str
(other
)
399 def __lt__(self
, other
):
400 return self
._value
< self
._value
_to
_str
(other
)
403 return bool(self
._value
)
406 return repr(self
._value
)
409 return str(self
._value
)
411 def __getitem__(self
, index
):
412 return self
._value
[index
]
415 return native_bt
.field_string_get_length(self
._ptr
)
417 def __iadd__(self
, value
):
418 value
= self
._value
_to
_str
(value
)
419 status
= native_bt
.field_string_append(self
._ptr
, value
)
420 utils
._handle
_func
_status
(
421 status
, "cannot append to string field object's value"
426 class _ContainerField(_Field
):
428 return len(self
) != 0
431 count
= self
._count
()
435 def __delitem__(self
, index
):
436 raise NotImplementedError
439 class _StructureField(_ContainerField
, collections
.abc
.MutableMapping
):
445 def __setitem__(self
, key
, value
):
446 # raises if key is somehow invalid
449 # the field's property does the appropriate conversion or raises
450 # the appropriate exception
455 return iter(self
.cls
)
457 def _spec_eq(self
, other
):
458 if not isinstance(other
, collections
.abc
.Mapping
):
461 if len(self
) != len(other
):
465 for self_key
in self
:
466 if self_key
not in other
:
469 if self
[self_key
] != other
[self_key
]:
474 def _set_value(self
, values
):
476 for key
, value
in values
.items():
477 self
[key
].value
= value
481 value
= property(fset
=_set_value
)
484 items
= ['{}: {}'.format(repr(k
), repr(v
)) for k
, v
in self
.items()]
485 return '{{{}}}'.format(', '.join(items
))
487 def __getitem__(self
, key
):
488 utils
._check
_str
(key
)
489 field_ptr
= native_bt
.field_structure_borrow_member_field_by_name(
493 if field_ptr
is None:
496 return _create_field_from_ptr(
497 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
500 def member_at_index(self
, index
):
501 utils
._check
_uint
64(index
)
503 if index
>= len(self
):
506 field_ptr
= native_bt
.field_structure_borrow_member_field_by_index(
509 assert field_ptr
is not None
510 return _create_field_from_ptr(
511 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
515 class _OptionField(_Field
):
520 field_ptr
= native_bt
.field_option_borrow_field_const(self
._ptr
)
522 if field_ptr
is None:
525 return _create_field_from_ptr(
526 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
531 return self
.field
is not None
534 def has_field(self
, value
):
535 utils
._check
_bool
(value
)
536 native_bt
.field_option_set_has_field(self
._ptr
, value
)
538 def _spec_eq(self
, other
):
539 return _get_leaf_field(self
) == other
542 return self
.has_field
545 return str(self
.field
)
548 return repr(self
.field
)
550 def _set_value(self
, value
):
551 self
.has_field
= True
553 assert field
is not None
556 value
= property(fset
=_set_value
)
559 class _VariantField(_ContainerField
, _Field
):
566 def selected_option_index(self
):
567 return native_bt
.field_variant_get_selected_option_field_index(self
._ptr
)
569 @selected_option_index.setter
570 def selected_option_index(self
, index
):
571 native_bt
.field_variant_select_option_field_by_index(self
._ptr
, index
)
574 def selected_option(self
):
575 # TODO: Is there a way to check if the variant field has a selected_option,
576 # so we can raise an exception instead of hitting a pre-condition check?
577 # If there is something, that check should be added to selected_option_index too.
578 field_ptr
= native_bt
.field_variant_borrow_selected_option_field(self
._ptr
)
580 return _create_field_from_ptr(
581 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
584 def _spec_eq(self
, other
):
585 return _get_leaf_field(self
) == other
588 raise NotImplementedError
591 return str(self
.selected_option
)
594 return repr(self
.selected_option
)
596 def _set_value(self
, value
):
597 self
.selected_option
.value
= value
599 value
= property(fset
=_set_value
)
602 class _ArrayField(_ContainerField
, _Field
, collections
.abc
.MutableSequence
):
603 def _get_length(self
):
604 return native_bt
.field_array_get_length(self
._ptr
)
606 length
= property(fget
=_get_length
)
608 def __getitem__(self
, index
):
609 if not isinstance(index
, numbers
.Integral
):
611 "'{}' is not an integral number object: invalid index".format(
612 index
.__class
__.__name
__
618 if index
< 0 or index
>= len(self
):
619 raise IndexError('{} field object index is out of range'.format(self
._NAME
))
621 field_ptr
= native_bt
.field_array_borrow_element_field_by_index(
625 return _create_field_from_ptr(
626 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
629 def __setitem__(self
, index
, value
):
630 # raises if index is somehow invalid
633 if not isinstance(field
, (_NumericField
, _StringField
)):
634 raise TypeError('can only set the value of a number or string field')
636 # the field's property does the appropriate conversion or raises
637 # the appropriate exception
640 def insert(self
, index
, value
):
641 raise NotImplementedError
643 def _spec_eq(self
, other
):
644 if not isinstance(other
, collections
.abc
.Sequence
):
647 if len(self
) != len(other
):
651 for self_elem
, other_elem
in zip(self
, other
):
652 if self_elem
!= other_elem
:
658 return '[{}]'.format(', '.join([repr(v
) for v
in self
]))
661 class _StaticArrayField(_ArrayField
, _Field
):
662 _NAME
= 'Static array'
665 return native_bt
.field_array_get_length(self
._ptr
)
667 def _set_value(self
, values
):
668 if len(self
) != len(values
):
669 raise ValueError('expected length of value and array field to match')
671 for index
, value
in enumerate(values
):
672 if value
is not None:
673 self
[index
].value
= value
675 value
= property(fset
=_set_value
)
678 class _DynamicArrayField(_ArrayField
, _Field
):
679 _NAME
= 'Dynamic array'
684 def _set_length(self
, length
):
685 utils
._check
_uint
64(length
)
686 status
= native_bt
.field_array_dynamic_set_length(self
._ptr
, length
)
687 utils
._handle
_func
_status
(status
, "cannot set dynamic array length")
689 length
= property(fget
=_ArrayField
._get
_length
, fset
=_set_length
)
691 def _set_value(self
, values
):
692 if len(values
) != self
.length
:
693 self
.length
= len(values
)
695 for index
, value
in enumerate(values
):
696 if value
is not None:
697 self
[index
].value
= value
699 value
= property(fset
=_set_value
)
703 native_bt
.FIELD_CLASS_TYPE_BOOL
: _BoolField
,
704 native_bt
.FIELD_CLASS_TYPE_BIT_ARRAY
: _BitArrayField
,
705 native_bt
.FIELD_CLASS_TYPE_UNSIGNED_INTEGER
: _UnsignedIntegerField
,
706 native_bt
.FIELD_CLASS_TYPE_SIGNED_INTEGER
: _SignedIntegerField
,
707 native_bt
.FIELD_CLASS_TYPE_REAL
: _RealField
,
708 native_bt
.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION
: _UnsignedEnumerationField
,
709 native_bt
.FIELD_CLASS_TYPE_SIGNED_ENUMERATION
: _SignedEnumerationField
,
710 native_bt
.FIELD_CLASS_TYPE_STRING
: _StringField
,
711 native_bt
.FIELD_CLASS_TYPE_STRUCTURE
: _StructureField
,
712 native_bt
.FIELD_CLASS_TYPE_STATIC_ARRAY
: _StaticArrayField
,
713 native_bt
.FIELD_CLASS_TYPE_DYNAMIC_ARRAY
: _DynamicArrayField
,
714 native_bt
.FIELD_CLASS_TYPE_OPTION
: _OptionField
,
715 native_bt
.FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR
: _VariantField
,
716 native_bt
.FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR
: _VariantField
,
717 native_bt
.FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR
: _VariantField
,