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 utils
._handle
_ptr
(field_class_ptr
, "cannot get field object's class")
35 typeid
= native_bt
.field_class_get_type(field_class_ptr
)
36 field
= _TYPE_ID_TO_OBJ
[typeid
]._create
_from
_ptr
_and
_get
_ref
(
37 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`
45 def _get_leaf_field(field
):
46 if not isinstance(field
, _VariantField
):
49 return _get_leaf_field(field
.selected_option
)
52 class _Field(object._UniqueObject
):
53 def __eq__(self
, other
):
54 other
= _get_leaf_field(other
)
55 return self
._spec
_eq
(other
)
58 def field_class(self
):
59 field_class_ptr
= native_bt
.field_borrow_class_const(self
._ptr
)
60 assert field_class_ptr
is not None
61 return bt2
.field_class
._create
_field
_class
_from
_ptr
_and
_get
_ref
(field_class_ptr
)
64 raise NotImplementedError
70 @functools.total_ordering
71 class _NumericField(_Field
):
73 def _extract_value(other
):
74 if other
is True or other
is False:
77 if isinstance(other
, numbers
.Integral
):
80 if isinstance(other
, numbers
.Real
):
83 if isinstance(other
, numbers
.Complex
):
86 raise TypeError("'{}' object is not a number object".format(other
.__class
__.__name
__))
89 return int(self
._value
)
92 return float(self
._value
)
95 return repr(self
._value
)
97 def __lt__(self
, other
):
98 if not isinstance(other
, numbers
.Number
):
99 raise TypeError('unorderable types: {}() < {}()'.format(self
.__class
__.__name
__,
100 other
.__class
__.__name
__))
102 return self
._value
< float(other
)
104 def _spec_eq(self
, other
):
105 if not isinstance(other
, numbers
.Number
):
106 return NotImplemented
108 return self
._value
== complex(other
)
110 def __rmod__(self
, other
):
111 return self
._extract
_value
(other
) % self
._value
113 def __mod__(self
, other
):
114 return self
._value
% self
._extract
_value
(other
)
116 def __rfloordiv__(self
, other
):
117 return self
._extract
_value
(other
) // self
._value
119 def __floordiv__(self
, other
):
120 return self
._value
// self
._extract
_value
(other
)
122 def __round__(self
, ndigits
=None):
124 return round(self
._value
)
126 return round(self
._value
, ndigits
)
129 return math
.ceil(self
._value
)
132 return math
.floor(self
._value
)
135 return int(self
._value
)
138 return abs(self
._value
)
140 def __add__(self
, other
):
141 return self
._value
+ self
._extract
_value
(other
)
143 def __radd__(self
, other
):
144 return self
.__add
__(other
)
152 def __mul__(self
, other
):
153 return self
._value
* self
._extract
_value
(other
)
155 def __rmul__(self
, other
):
156 return self
.__mul
__(other
)
158 def __truediv__(self
, other
):
159 return self
._value
/ self
._extract
_value
(other
)
161 def __rtruediv__(self
, other
):
162 return self
._extract
_value
(other
) / self
._value
164 def __pow__(self
, exponent
):
165 return self
._value
** self
._extract
_value
(exponent
)
167 def __rpow__(self
, base
):
168 return self
._extract
_value
(base
) ** self
._value
170 def __iadd__(self
, other
):
171 self
.value
= self
+ other
174 def __isub__(self
, other
):
175 self
.value
= self
- other
178 def __imul__(self
, other
):
179 self
.value
= self
* other
182 def __itruediv__(self
, other
):
183 self
.value
= self
/ other
186 def __ifloordiv__(self
, other
):
187 self
.value
= self
// other
190 def __imod__(self
, other
):
191 self
.value
= self
% other
194 def __ipow__(self
, other
):
195 self
.value
= self
** other
199 class _IntegralField(_NumericField
, 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
):
233 def __ilshift__(self
, other
):
234 self
.value
= self
<< other
237 def __irshift__(self
, other
):
238 self
.value
= self
>> other
241 def __iand__(self
, other
):
242 self
.value
= self
& other
245 def __ixor__(self
, other
):
246 self
.value
= self ^ other
249 def __ior__(self
, other
):
250 self
.value
= self | other
253 def __lt__(self
, other
):
254 if not isinstance(other
, numbers
.Integral
):
255 return super().__lt
__(other
);
257 return self
._value
< int(other
)
259 def _spec_eq(self
, other
):
260 if not isinstance(other
, numbers
.Integral
):
261 return super()._spec
_eq
(other
);
263 return self
._value
== int(other
)
266 class _IntegerField(_IntegralField
, _Field
):
270 class _UnsignedIntegerField(_IntegerField
, _Field
):
271 _NAME
= 'Unsigned integer'
273 def _value_to_int(self
, value
):
274 if not isinstance(value
, numbers
.Real
):
275 raise TypeError('expecting a real number object')
278 utils
._check
_uint
64(value
)
284 return native_bt
.field_unsigned_integer_get_value(self
._ptr
)
286 def _set_value(self
, value
):
287 value
= self
._value
_to
_int
(value
)
288 native_bt
.field_unsigned_integer_set_value(self
._ptr
, value
)
290 value
= property(fset
=_set_value
)
293 class _SignedIntegerField(_IntegerField
, _Field
):
294 _NAME
= 'Signed integer'
296 def _value_to_int(self
, value
):
297 if not isinstance(value
, numbers
.Real
):
298 raise TypeError('expecting a real number object')
301 utils
._check
_int
64(value
)
307 return native_bt
.field_signed_integer_get_value(self
._ptr
)
309 def _set_value(self
, value
):
310 value
= self
._value
_to
_int
(value
)
311 native_bt
.field_signed_integer_set_value(self
._ptr
, value
)
313 value
= property(fset
=_set_value
)
316 class _RealField(_NumericField
, numbers
.Real
):
319 def _value_to_float(self
, value
):
320 if not isinstance(value
, numbers
.Real
):
321 raise TypeError("expecting a real number object")
327 return native_bt
.field_real_get_value(self
._ptr
)
329 def _set_value(self
, value
):
330 value
= self
._value
_to
_float
(value
)
331 native_bt
.field_real_set_value(self
._ptr
, value
)
333 value
= property(fset
=_set_value
)
336 class _EnumerationField(_IntegerField
):
338 return '{} ({})'.format(self
._value
, ', '.join(self
.labels
))
342 ret
, labels
= self
._get
_mapping
_labels
(self
._ptr
)
343 utils
._handle
_ret
(ret
, "cannot get label for enumeration field")
345 assert labels
is not None
349 class _UnsignedEnumerationField(_EnumerationField
, _UnsignedIntegerField
):
350 _NAME
= 'Unsigned Enumeration'
351 _get_mapping_labels
= staticmethod(native_bt
.field_unsigned_enumeration_get_mapping_labels
)
354 class _SignedEnumerationField(_EnumerationField
, _SignedIntegerField
):
355 _NAME
= 'Signed Enumeration'
356 _get_mapping_labels
= staticmethod(native_bt
.field_signed_enumeration_get_mapping_labels
)
359 @functools.total_ordering
360 class _StringField(_Field
):
363 def _value_to_str(self
, value
):
364 if isinstance(value
, self
.__class
__):
367 if not isinstance(value
, str):
368 raise TypeError("expecting a 'str' object")
374 return native_bt
.field_string_get_value(self
._ptr
)
376 def _set_value(self
, value
):
377 value
= self
._value
_to
_str
(value
)
378 native_bt
.field_string_set_value(self
._ptr
, value
)
380 value
= property(fset
=_set_value
)
382 def _spec_eq(self
, other
):
384 other
= self
._value
_to
_str
(other
)
388 return self
._value
== other
390 def __lt__(self
, other
):
391 return self
._value
< self
._value
_to
_str
(other
)
394 return bool(self
._value
)
397 return repr(self
._value
)
400 return str(self
._value
)
402 def __getitem__(self
, index
):
403 return self
._value
[index
]
406 return native_bt
.field_string_get_length(self
._ptr
)
408 def __iadd__(self
, value
):
409 value
= self
._value
_to
_str
(value
)
410 ret
= native_bt
.field_string_append(self
._ptr
, value
)
411 utils
._handle
_ret
(ret
, "cannot append to string field object's value")
415 class _ContainerField(_Field
):
417 return len(self
) != 0
420 count
= self
._count
()
424 def __delitem__(self
, index
):
425 raise NotImplementedError
428 class _StructureField(_ContainerField
, collections
.abc
.MutableMapping
):
432 return len(self
.field_class
)
434 def __setitem__(self
, key
, value
):
435 # raises if key is somehow invalid
438 # the field's property does the appropriate conversion or raises
439 # the appropriate exception
444 return iter(self
.field_class
)
446 def _spec_eq(self
, other
):
448 if len(self
) != len(other
):
451 for self_key
, self_value
in self
.items():
452 if self_key
not in other
:
455 other_value
= other
[self_key
]
457 if self_value
!= other_value
:
464 def _set_value(self
, values
):
466 for key
, value
in values
.items():
467 self
[key
].value
= value
471 value
= property(fset
=_set_value
)
474 items
= ['{}: {}'.format(repr(k
), repr(v
)) for k
, v
in self
.items()]
475 return '{{{}}}'.format(', '.join(items
))
477 def __getitem__(self
, key
):
478 utils
._check
_str
(key
)
479 field_ptr
= native_bt
.field_structure_borrow_member_field_by_name(self
._ptr
, key
)
481 if field_ptr
is None:
484 return _create_field_from_ptr(field_ptr
, self
._owner
_ptr
,
488 def member_at_index(self
, index
):
489 utils
._check
_uint
64(index
)
491 if index
>= len(self
):
494 field_ptr
= native_bt
.field_structure_borrow_member_field_by_index(self
._ptr
, index
)
495 assert field_ptr
is not None
496 return _create_field_from_ptr(field_ptr
, self
._owner
_ptr
,
501 class _VariantField(_ContainerField
, _Field
):
505 def selected_option_index(self
):
506 return native_bt
.field_variant_get_selected_option_field_index(self
._ptr
)
508 @selected_option_index.setter
509 def selected_option_index(self
, index
):
510 native_bt
.field_variant_select_option_field(self
._ptr
, index
)
513 def selected_option(self
):
514 field_ptr
= native_bt
.field_variant_borrow_selected_option_field(self
._ptr
)
515 utils
._handle
_ptr
(field_ptr
, "cannot get variant field's selected option")
517 return _create_field_from_ptr(field_ptr
, self
._owner
_ptr
,
521 def _spec_eq(self
, other
):
522 new_self
= _get_leaf_field(self
)
523 return new_self
== other
526 raise NotImplementedError
529 return str(self
.selected_option
)
532 return repr(self
.selected_option
)
534 def _set_value(self
, value
):
535 self
.selected_option
.value
= value
537 value
= property(fset
=_set_value
)
540 class _ArrayField(_ContainerField
, _Field
):
542 def _get_length(self
):
543 return native_bt
.field_array_get_length(self
._ptr
)
545 length
= property(fget
=_get_length
)
547 def __getitem__(self
, index
):
548 if not isinstance(index
, numbers
.Integral
):
549 raise TypeError("'{}' is not an integral number object: invalid index".format(index
.__class
__.__name
__))
553 if index
< 0 or index
>= len(self
):
554 raise IndexError('{} field object index is out of range'.format(self
._NAME
))
556 field_ptr
= native_bt
.field_array_borrow_element_field_by_index(self
._ptr
, index
)
558 return _create_field_from_ptr(field_ptr
, self
._owner
_ptr
,
562 def __setitem__(self
, index
, value
):
563 # we can only set numbers and strings
564 if not isinstance(value
, (numbers
.Number
, _StringField
, str)):
565 raise TypeError('expecting number or string object')
567 # raises if index is somehow invalid
570 if not isinstance(field
, (_NumericField
, _StringField
)):
571 raise TypeError('can only set the value of a number or string field')
573 # the field's property does the appropriate conversion or raises
574 # the appropriate exception
577 def insert(self
, index
, value
):
578 raise NotImplementedError
580 def _spec_eq(self
, other
):
582 if len(self
) != len(other
):
585 for self_field
, other_field
in zip(self
, other
):
586 if self_field
!= other_field
:
594 return '[{}]'.format(', '.join([repr(v
) for v
in self
]))
597 class _StaticArrayField(_ArrayField
, _Field
):
598 _NAME
= 'Static array'
601 return native_bt
.field_array_get_length(self
._ptr
)
603 def _set_value(self
, values
):
604 if len(self
) != len(values
):
606 'expected length of value and array field to match')
608 for index
, value
in enumerate(values
):
609 if value
is not None:
610 self
[index
].value
= value
612 value
= property(fset
=_set_value
)
615 class _DynamicArrayField(_ArrayField
, _Field
):
616 _NAME
= 'Dynamic array'
621 def _set_length(self
, length
):
622 utils
._check
_uint
64(length
)
623 ret
= native_bt
.field_dynamic_array_set_length(self
._ptr
, length
)
624 utils
._handle
_ret
(ret
, "cannot set dynamic array length")
626 length
= property(fget
=_ArrayField
._get
_length
, fset
=_set_length
)
628 def _set_value(self
, values
):
629 if len(values
) != self
.length
:
630 self
.length
= len(values
)
632 for index
, value
in enumerate(values
):
633 if value
is not None:
634 self
[index
].value
= value
636 value
= property(fset
=_set_value
)
640 native_bt
.FIELD_CLASS_TYPE_UNSIGNED_INTEGER
: _UnsignedIntegerField
,
641 native_bt
.FIELD_CLASS_TYPE_SIGNED_INTEGER
: _SignedIntegerField
,
642 native_bt
.FIELD_CLASS_TYPE_REAL
: _RealField
,
643 native_bt
.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION
: _UnsignedEnumerationField
,
644 native_bt
.FIELD_CLASS_TYPE_SIGNED_ENUMERATION
: _SignedEnumerationField
,
645 native_bt
.FIELD_CLASS_TYPE_STRING
: _StringField
,
646 native_bt
.FIELD_CLASS_TYPE_STRUCTURE
: _StructureField
,
647 native_bt
.FIELD_CLASS_TYPE_STATIC_ARRAY
: _StaticArrayField
,
648 native_bt
.FIELD_CLASS_TYPE_DYNAMIC_ARRAY
: _DynamicArrayField
,
649 native_bt
.FIELD_CLASS_TYPE_VARIANT
: _VariantField
,