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 bt2
.field_class
25 import collections
.abc
32 def _create_field_from_ptr(ptr
, owner_ptr
, owner_get_ref
, owner_put_ref
):
33 field_class_ptr
= native_bt
.field_borrow_class_const(ptr
)
34 typeid
= native_bt
.field_class_get_type(field_class_ptr
)
35 field
= _TYPE_ID_TO_OBJ
[typeid
]._create
_from
_ptr
_and
_get
_ref
(
36 ptr
, owner_ptr
, owner_get_ref
, owner_put_ref
41 # Get the "effective" field of `field`. If `field` is a variant, return the
42 # currently selected field. If `field` is of any other type, return `field`
46 def _get_leaf_field(field
):
47 if not isinstance(field
, _VariantField
):
50 return _get_leaf_field(field
.selected_option
)
53 class _Field(object._UniqueObject
):
54 def __eq__(self
, other
):
55 other
= _get_leaf_field(other
)
56 return self
._spec
_eq
(other
)
59 def field_class(self
):
60 field_class_ptr
= native_bt
.field_borrow_class_const(self
._ptr
)
61 assert field_class_ptr
is not None
62 return bt2
.field_class
._create
_field
_class
_from
_ptr
_and
_get
_ref
(field_class_ptr
)
65 raise NotImplementedError
71 @functools.total_ordering
72 class _NumericField(_Field
):
74 def _extract_value(other
):
75 if isinstance(other
, bool):
78 if isinstance(other
, numbers
.Integral
):
81 if isinstance(other
, numbers
.Real
):
84 if isinstance(other
, numbers
.Complex
):
88 "'{}' object is not a number object".format(other
.__class
__.__name
__)
92 return int(self
._value
)
95 return float(self
._value
)
98 return repr(self
._value
)
100 def __lt__(self
, other
):
101 if not isinstance(other
, numbers
.Number
):
103 'unorderable types: {}() < {}()'.format(
104 self
.__class
__.__name
__, other
.__class
__.__name
__
108 return self
._value
< self
._extract
_value
(other
)
110 def _spec_eq(self
, other
):
112 return self
._value
== self
._extract
_value
(other
)
116 def __rmod__(self
, other
):
117 return self
._extract
_value
(other
) % self
._value
119 def __mod__(self
, other
):
120 return self
._value
% self
._extract
_value
(other
)
122 def __rfloordiv__(self
, other
):
123 return self
._extract
_value
(other
) // self
._value
125 def __floordiv__(self
, other
):
126 return self
._value
// self
._extract
_value
(other
)
128 def __round__(self
, ndigits
=None):
130 return round(self
._value
)
132 return round(self
._value
, ndigits
)
135 return math
.ceil(self
._value
)
138 return math
.floor(self
._value
)
141 return int(self
._value
)
144 return abs(self
._value
)
146 def __add__(self
, other
):
147 return self
._value
+ self
._extract
_value
(other
)
149 def __radd__(self
, other
):
150 return self
.__add
__(other
)
158 def __mul__(self
, other
):
159 return self
._value
* self
._extract
_value
(other
)
161 def __rmul__(self
, other
):
162 return self
.__mul
__(other
)
164 def __truediv__(self
, other
):
165 return self
._value
/ self
._extract
_value
(other
)
167 def __rtruediv__(self
, other
):
168 return self
._extract
_value
(other
) / self
._value
170 def __pow__(self
, exponent
):
171 return self
._value
** self
._extract
_value
(exponent
)
173 def __rpow__(self
, base
):
174 return self
._extract
_value
(base
) ** self
._value
177 class _IntegralField(_NumericField
, numbers
.Integral
):
178 def __lshift__(self
, other
):
179 return self
._value
<< self
._extract
_value
(other
)
181 def __rlshift__(self
, other
):
182 return self
._extract
_value
(other
) << self
._value
184 def __rshift__(self
, other
):
185 return self
._value
>> self
._extract
_value
(other
)
187 def __rrshift__(self
, other
):
188 return self
._extract
_value
(other
) >> self
._value
190 def __and__(self
, other
):
191 return self
._value
& self
._extract
_value
(other
)
193 def __rand__(self
, other
):
194 return self
._extract
_value
(other
) & self
._value
196 def __xor__(self
, other
):
197 return self
._value ^ self
._extract
_value
(other
)
199 def __rxor__(self
, other
):
200 return self
._extract
_value
(other
) ^ self
._value
202 def __or__(self
, other
):
203 return self
._value | self
._extract
_value
(other
)
205 def __ror__(self
, other
):
206 return self
._extract
_value
(other
) | self
._value
208 def __invert__(self
):
212 class _IntegerField(_IntegralField
, _Field
):
216 class _UnsignedIntegerField(_IntegerField
, _Field
):
217 _NAME
= 'Unsigned integer'
219 def _value_to_int(self
, value
):
220 if not isinstance(value
, numbers
.Integral
):
221 raise TypeError('expecting an integral number object')
224 utils
._check
_uint
64(value
)
230 return native_bt
.field_integer_unsigned_get_value(self
._ptr
)
232 def _set_value(self
, value
):
233 value
= self
._value
_to
_int
(value
)
234 native_bt
.field_integer_unsigned_set_value(self
._ptr
, value
)
236 value
= property(fset
=_set_value
)
239 class _SignedIntegerField(_IntegerField
, _Field
):
240 _NAME
= 'Signed integer'
242 def _value_to_int(self
, value
):
243 if not isinstance(value
, numbers
.Integral
):
244 raise TypeError('expecting an integral number object')
247 utils
._check
_int
64(value
)
253 return native_bt
.field_integer_signed_get_value(self
._ptr
)
255 def _set_value(self
, value
):
256 value
= self
._value
_to
_int
(value
)
257 native_bt
.field_integer_signed_set_value(self
._ptr
, value
)
259 value
= property(fset
=_set_value
)
262 class _RealField(_NumericField
, numbers
.Real
):
265 def _value_to_float(self
, value
):
266 if not isinstance(value
, numbers
.Real
):
267 raise TypeError("expecting a real number object")
273 return native_bt
.field_real_get_value(self
._ptr
)
275 def _set_value(self
, value
):
276 value
= self
._value
_to
_float
(value
)
277 native_bt
.field_real_set_value(self
._ptr
, value
)
279 value
= property(fset
=_set_value
)
282 class _EnumerationField(_IntegerField
):
284 return '{} ({})'.format(self
._value
, ', '.join(self
.labels
))
288 status
, labels
= self
._get
_mapping
_labels
(self
._ptr
)
289 utils
._handle
_func
_status
(status
, "cannot get label for enumeration field")
291 assert labels
is not None
295 class _UnsignedEnumerationField(_EnumerationField
, _UnsignedIntegerField
):
296 _NAME
= 'Unsigned Enumeration'
297 _get_mapping_labels
= staticmethod(
298 native_bt
.field_enumeration_unsigned_get_mapping_labels
302 class _SignedEnumerationField(_EnumerationField
, _SignedIntegerField
):
303 _NAME
= 'Signed Enumeration'
304 _get_mapping_labels
= staticmethod(
305 native_bt
.field_enumeration_signed_get_mapping_labels
309 @functools.total_ordering
310 class _StringField(_Field
):
313 def _value_to_str(self
, value
):
314 if isinstance(value
, self
.__class
__):
317 if not isinstance(value
, str):
318 raise TypeError("expecting a 'str' object")
324 return native_bt
.field_string_get_value(self
._ptr
)
326 def _set_value(self
, value
):
327 value
= self
._value
_to
_str
(value
)
328 native_bt
.field_string_set_value(self
._ptr
, value
)
330 value
= property(fset
=_set_value
)
332 def _spec_eq(self
, other
):
334 return self
._value
== self
._value
_to
_str
(other
)
338 def __lt__(self
, other
):
339 return self
._value
< self
._value
_to
_str
(other
)
342 return bool(self
._value
)
345 return repr(self
._value
)
348 return str(self
._value
)
350 def __getitem__(self
, index
):
351 return self
._value
[index
]
354 return native_bt
.field_string_get_length(self
._ptr
)
356 def __iadd__(self
, value
):
357 value
= self
._value
_to
_str
(value
)
358 status
= native_bt
.field_string_append(self
._ptr
, value
)
359 utils
._handle
_func
_status
(
360 status
, "cannot append to string field object's value"
365 class _ContainerField(_Field
):
367 return len(self
) != 0
370 count
= self
._count
()
374 def __delitem__(self
, index
):
375 raise NotImplementedError
378 class _StructureField(_ContainerField
, collections
.abc
.MutableMapping
):
382 return len(self
.field_class
)
384 def __setitem__(self
, key
, value
):
385 # raises if key is somehow invalid
388 # the field's property does the appropriate conversion or raises
389 # the appropriate exception
394 return iter(self
.field_class
)
396 def _spec_eq(self
, other
):
397 if not isinstance(other
, collections
.abc
.Mapping
):
400 if len(self
) != len(other
):
404 for self_key
in self
:
405 if self_key
not in other
:
408 if self
[self_key
] != other
[self_key
]:
413 def _set_value(self
, values
):
415 for key
, value
in values
.items():
416 self
[key
].value
= value
420 value
= property(fset
=_set_value
)
423 items
= ['{}: {}'.format(repr(k
), repr(v
)) for k
, v
in self
.items()]
424 return '{{{}}}'.format(', '.join(items
))
426 def __getitem__(self
, key
):
427 utils
._check
_str
(key
)
428 field_ptr
= native_bt
.field_structure_borrow_member_field_by_name(
432 if field_ptr
is None:
435 return _create_field_from_ptr(
436 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
439 def member_at_index(self
, index
):
440 utils
._check
_uint
64(index
)
442 if index
>= len(self
):
445 field_ptr
= native_bt
.field_structure_borrow_member_field_by_index(
448 assert field_ptr
is not None
449 return _create_field_from_ptr(
450 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
454 class _VariantField(_ContainerField
, _Field
):
458 def selected_option_index(self
):
459 return native_bt
.field_variant_get_selected_option_field_index(self
._ptr
)
461 @selected_option_index.setter
462 def selected_option_index(self
, index
):
463 native_bt
.field_variant_select_option_field_by_index(self
._ptr
, index
)
466 def selected_option(self
):
467 # TODO: Is there a way to check if the variant field has a selected_option,
468 # so we can raise an exception instead of hitting a pre-condition check?
469 # If there is something, that check should be added to selected_option_index too.
470 field_ptr
= native_bt
.field_variant_borrow_selected_option_field(self
._ptr
)
472 return _create_field_from_ptr(
473 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
476 def _spec_eq(self
, other
):
477 return _get_leaf_field(self
) == other
480 raise NotImplementedError
483 return str(self
.selected_option
)
486 return repr(self
.selected_option
)
488 def _set_value(self
, value
):
489 self
.selected_option
.value
= value
491 value
= property(fset
=_set_value
)
494 class _ArrayField(_ContainerField
, _Field
, collections
.abc
.MutableSequence
):
495 def _get_length(self
):
496 return native_bt
.field_array_get_length(self
._ptr
)
498 length
= property(fget
=_get_length
)
500 def __getitem__(self
, index
):
501 if not isinstance(index
, numbers
.Integral
):
503 "'{}' is not an integral number object: invalid index".format(
504 index
.__class
__.__name
__
510 if index
< 0 or index
>= len(self
):
511 raise IndexError('{} field object index is out of range'.format(self
._NAME
))
513 field_ptr
= native_bt
.field_array_borrow_element_field_by_index(
517 return _create_field_from_ptr(
518 field_ptr
, self
._owner
_ptr
, self
._owner
_get
_ref
, self
._owner
_put
_ref
521 def __setitem__(self
, index
, value
):
522 # raises if index is somehow invalid
525 if not isinstance(field
, (_NumericField
, _StringField
)):
526 raise TypeError('can only set the value of a number or string field')
528 # the field's property does the appropriate conversion or raises
529 # the appropriate exception
532 def insert(self
, index
, value
):
533 raise NotImplementedError
535 def _spec_eq(self
, other
):
536 if not isinstance(other
, collections
.abc
.Sequence
):
539 if len(self
) != len(other
):
543 for self_elem
, other_elem
in zip(self
, other
):
544 if self_elem
!= other_elem
:
550 return '[{}]'.format(', '.join([repr(v
) for v
in self
]))
553 class _StaticArrayField(_ArrayField
, _Field
):
554 _NAME
= 'Static array'
557 return native_bt
.field_array_get_length(self
._ptr
)
559 def _set_value(self
, values
):
560 if len(self
) != len(values
):
561 raise ValueError('expected length of value and array field to match')
563 for index
, value
in enumerate(values
):
564 if value
is not None:
565 self
[index
].value
= value
567 value
= property(fset
=_set_value
)
570 class _DynamicArrayField(_ArrayField
, _Field
):
571 _NAME
= 'Dynamic array'
576 def _set_length(self
, length
):
577 utils
._check
_uint
64(length
)
578 status
= native_bt
.field_array_dynamic_set_length(self
._ptr
, length
)
579 utils
._handle
_func
_status
(status
, "cannot set dynamic array length")
581 length
= property(fget
=_ArrayField
._get
_length
, fset
=_set_length
)
583 def _set_value(self
, values
):
584 if len(values
) != self
.length
:
585 self
.length
= len(values
)
587 for index
, value
in enumerate(values
):
588 if value
is not None:
589 self
[index
].value
= value
591 value
= property(fset
=_set_value
)
595 native_bt
.FIELD_CLASS_TYPE_UNSIGNED_INTEGER
: _UnsignedIntegerField
,
596 native_bt
.FIELD_CLASS_TYPE_SIGNED_INTEGER
: _SignedIntegerField
,
597 native_bt
.FIELD_CLASS_TYPE_REAL
: _RealField
,
598 native_bt
.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION
: _UnsignedEnumerationField
,
599 native_bt
.FIELD_CLASS_TYPE_SIGNED_ENUMERATION
: _SignedEnumerationField
,
600 native_bt
.FIELD_CLASS_TYPE_STRING
: _StringField
,
601 native_bt
.FIELD_CLASS_TYPE_STRUCTURE
: _StructureField
,
602 native_bt
.FIELD_CLASS_TYPE_STATIC_ARRAY
: _StaticArrayField
,
603 native_bt
.FIELD_CLASS_TYPE_DYNAMIC_ARRAY
: _DynamicArrayField
,
604 native_bt
.FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR
: _VariantField
,
605 native_bt
.FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR
: _VariantField
,
606 native_bt
.FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR
: _VariantField
,