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
33 def _get_leaf_field(obj
):
34 if type(obj
) is not _VariantField
:
37 return _get_leaf_field(obj
.selected_field
)
40 def _create_from_ptr(ptr
):
41 # recreate the field class wrapper of this field's type (the identity
42 # could be different, but the underlying address should be the
44 field_class_ptr
= native_bt
.field_get_type(ptr
)
45 utils
._handle
_ptr
(field_class_ptr
, "cannot get field object's type")
46 field_class
= bt2
.field_class
._create
_from
_ptr
(field_class_ptr
)
47 typeid
= native_bt
.field_class_get_type_id(field_class
._ptr
)
48 field
= _TYPE_ID_TO_OBJ
[typeid
]._create
_from
_ptr
(ptr
)
49 field
._field
_class
= field_class
53 class _Field(object._Object
, metaclass
=abc
.ABCMeta
):
55 ptr
= native_bt
.field_copy(self
._ptr
)
56 utils
._handle
_ptr
(ptr
, 'cannot copy {} field object'.format(self
._NAME
.lower()))
57 return _create_from_ptr(ptr
)
59 def __deepcopy__(self
, memo
):
64 def __eq__(self
, other
):
65 # special case: two unset fields with the same field class are equal
66 if isinstance(other
, _Field
):
67 if not self
.is_set
or not other
.is_set
:
68 if not self
.is_set
and not other
.is_set
and self
.field_class
== other
.field_class
:
72 other
= _get_leaf_field(other
)
73 return self
._spec
_eq
(other
)
76 def field_class(self
):
77 return self
._field
_class
81 is_set
= native_bt
.field_is_set(self
._ptr
)
85 ret
= native_bt
.field_reset(self
._ptr
)
86 utils
._handle
_ret
(ret
, "cannot reset field object's value")
89 raise NotImplementedError
92 return self
._repr
() if self
.is_set
else 'Unset'
95 @functools.total_ordering
96 class _NumericField(_Field
):
98 def _extract_value(other
):
99 if other
is True or other
is False:
102 if isinstance(other
, numbers
.Integral
):
105 if isinstance(other
, numbers
.Real
):
108 if isinstance(other
, numbers
.Complex
):
109 return complex(other
)
111 raise TypeError("'{}' object is not a number object".format(other
.__class
__.__name
__))
114 return int(self
._value
)
117 return float(self
._value
)
120 return repr(self
._value
)
122 def __lt__(self
, other
):
123 if not isinstance(other
, numbers
.Number
):
124 raise TypeError('unorderable types: {}() < {}()'.format(self
.__class
__.__name
__,
125 other
.__class
__.__name
__))
127 return self
._value
< float(other
)
129 def __le__(self
, other
):
130 if not isinstance(other
, numbers
.Number
):
131 raise TypeError('unorderable types: {}() <= {}()'.format(self
.__class
__.__name
__,
132 other
.__class
__.__name
__))
134 return self
._value
<= float(other
)
136 def _spec_eq(self
, other
):
137 if not isinstance(other
, numbers
.Number
):
140 return self
._value
== complex(other
)
142 def __rmod__(self
, other
):
143 return self
._extract
_value
(other
) % self
._value
145 def __mod__(self
, other
):
146 return self
._value
% self
._extract
_value
(other
)
148 def __rfloordiv__(self
, other
):
149 return self
._extract
_value
(other
) // self
._value
151 def __floordiv__(self
, other
):
152 return self
._value
// self
._extract
_value
(other
)
154 def __round__(self
, ndigits
=None):
156 return round(self
._value
)
158 return round(self
._value
, ndigits
)
161 return math
.ceil(self
._value
)
164 return math
.floor(self
._value
)
167 return int(self
._value
)
170 return abs(self
._value
)
172 def __add__(self
, other
):
173 return self
._value
+ self
._extract
_value
(other
)
175 def __radd__(self
, other
):
176 return self
.__add
__(other
)
184 def __mul__(self
, other
):
185 return self
._value
* self
._extract
_value
(other
)
187 def __rmul__(self
, other
):
188 return self
.__mul
__(other
)
190 def __truediv__(self
, other
):
191 return self
._value
/ self
._extract
_value
(other
)
193 def __rtruediv__(self
, other
):
194 return self
._extract
_value
(other
) / self
._value
196 def __pow__(self
, exponent
):
197 return self
._value
** self
._extract
_value
(exponent
)
199 def __rpow__(self
, base
):
200 return self
._extract
_value
(base
) ** self
._value
202 def __iadd__(self
, other
):
203 self
.value
= self
+ other
206 def __isub__(self
, other
):
207 self
.value
= self
- other
210 def __imul__(self
, other
):
211 self
.value
= self
* other
214 def __itruediv__(self
, other
):
215 self
.value
= self
/ other
218 def __ifloordiv__(self
, other
):
219 self
.value
= self
// other
222 def __imod__(self
, other
):
223 self
.value
= self
% other
226 def __ipow__(self
, other
):
227 self
.value
= self
** other
231 class _IntegralField(_NumericField
, numbers
.Integral
):
232 def __lshift__(self
, other
):
233 return self
._value
<< self
._extract
_value
(other
)
235 def __rlshift__(self
, other
):
236 return self
._extract
_value
(other
) << self
._value
238 def __rshift__(self
, other
):
239 return self
._value
>> self
._extract
_value
(other
)
241 def __rrshift__(self
, other
):
242 return self
._extract
_value
(other
) >> self
._value
244 def __and__(self
, other
):
245 return self
._value
& self
._extract
_value
(other
)
247 def __rand__(self
, other
):
248 return self
._extract
_value
(other
) & self
._value
250 def __xor__(self
, other
):
251 return self
._value ^ self
._extract
_value
(other
)
253 def __rxor__(self
, other
):
254 return self
._extract
_value
(other
) ^ self
._value
256 def __or__(self
, other
):
257 return self
._value | self
._extract
_value
(other
)
259 def __ror__(self
, other
):
260 return self
._extract
_value
(other
) | self
._value
262 def __invert__(self
):
265 def __ilshift__(self
, other
):
266 self
.value
= self
<< other
269 def __irshift__(self
, other
):
270 self
.value
= self
>> other
273 def __iand__(self
, other
):
274 self
.value
= self
& other
277 def __ixor__(self
, other
):
278 self
.value
= self ^ other
281 def __ior__(self
, other
):
282 self
.value
= self | other
286 class _RealField(_NumericField
, numbers
.Real
):
290 class _IntegerField(_IntegralField
):
293 def _value_to_int(self
, value
):
294 if not isinstance(value
, numbers
.Real
):
295 raise TypeError('expecting a real number object')
299 if self
.field_class
.is_signed
:
300 utils
._check
_int
64(value
)
302 utils
._check
_uint
64(value
)
308 if self
.field_class
.is_signed
:
309 ret
, value
= native_bt
.field_signed_integer_get_value(self
._ptr
)
311 ret
, value
= native_bt
.field_unsigned_integer_get_value(self
._ptr
)
317 utils
._handle
_ret
(ret
, "cannot get integer field's value")
321 def _set_value(self
, value
):
322 value
= self
._value
_to
_int
(value
)
324 if self
.field_class
.is_signed
:
325 ret
= native_bt
.field_signed_integer_set_value(self
._ptr
, value
)
327 ret
= native_bt
.field_unsigned_integer_set_value(self
._ptr
, value
)
329 utils
._handle
_ret
(ret
, "cannot set integer field object's value")
331 value
= property(fset
=_set_value
)
334 class _FloatingPointNumberField(_RealField
):
335 _NAME
= 'Floating point number'
337 def _value_to_float(self
, value
):
338 if not isinstance(value
, numbers
.Real
):
339 raise TypeError("expecting a real number object")
345 ret
, value
= native_bt
.field_floating_point_get_value(self
._ptr
)
351 utils
._handle
_ret
(ret
, "cannot get floating point number field's value")
355 def _set_value(self
, value
):
356 value
= self
._value
_to
_float
(value
)
357 ret
= native_bt
.field_floating_point_set_value(self
._ptr
, value
)
358 utils
._handle
_ret
(ret
, "cannot set floating point number field object's value")
360 value
= property(fset
=_set_value
)
363 class _EnumerationField(_IntegerField
):
364 _NAME
= 'Enumeration'
367 def integer_field(self
):
368 int_field_ptr
= native_bt
.field_enumeration_get_container(self
._ptr
)
369 assert(int_field_ptr
)
370 return _create_from_ptr(int_field_ptr
)
372 def _set_value(self
, value
):
373 self
.integer_field
.value
= value
376 labels
= [repr(v
.name
) for v
in self
.mappings
]
377 return '{} ({})'.format(self
._value
, ', '.join(labels
))
379 value
= property(fset
=_set_value
)
383 return self
.integer_field
._value
387 iter_ptr
= native_bt
.field_enumeration_get_mappings(self
._ptr
)
389 return bt2
.field_class
._EnumerationFieldClassMappingIterator
(iter_ptr
,
390 self
.field_class
.is_signed
)
393 @functools.total_ordering
394 class _StringField(_Field
, collections
.abc
.Sequence
):
397 def _value_to_str(self
, value
):
398 if isinstance(value
, self
.__class
__):
401 if not isinstance(value
, str):
402 raise TypeError("expecting a 'str' object")
408 value
= native_bt
.field_string_get_value(self
._ptr
)
411 def _set_value(self
, value
):
412 value
= self
._value
_to
_str
(value
)
413 ret
= native_bt
.field_string_set_value(self
._ptr
, value
)
414 utils
._handle
_ret
(ret
, "cannot set string field object's value")
416 value
= property(fset
=_set_value
)
418 def _spec_eq(self
, other
):
420 other
= self
._value
_to
_str
(other
)
424 return self
._value
== other
426 def __le__(self
, other
):
427 return self
._value
<= self
._value
_to
_str
(other
)
429 def __lt__(self
, other
):
430 return self
._value
< self
._value
_to
_str
(other
)
433 return bool(self
._value
)
436 return repr(self
._value
)
439 return self
._value
if self
.is_set
else repr(self
)
441 def __getitem__(self
, index
):
442 return self
._value
[index
]
445 return len(self
._value
)
447 def __iadd__(self
, value
):
448 value
= self
._value
_to
_str
(value
)
449 ret
= native_bt
.field_string_append(self
._ptr
, value
)
450 utils
._handle
_ret
(ret
, "cannot append to string field object's value")
454 class _ContainerField(_Field
):
456 return len(self
) != 0
459 count
= self
._count
()
463 def __delitem__(self
, index
):
464 raise NotImplementedError
467 class _StructureField(_ContainerField
, collections
.abc
.MutableMapping
):
471 return len(self
.field_class
)
473 def __getitem__(self
, key
):
474 utils
._check
_str
(key
)
475 ptr
= native_bt
.field_structure_get_field_by_name(self
._ptr
, key
)
480 return _create_from_ptr(ptr
)
482 def __setitem__(self
, key
, value
):
483 # raises if key is somehow invalid
486 # the field's property does the appropriate conversion or raises
487 # the appropriate exception
490 def at_index(self
, index
):
491 utils
._check
_uint
64(index
)
493 if index
>= len(self
):
496 field_ptr
= native_bt
.field_structure_get_field_by_index(self
._ptr
, index
)
498 return _create_from_ptr(field_ptr
)
502 return iter(self
.field_class
)
504 def _spec_eq(self
, other
):
506 if len(self
) != len(other
):
509 for self_key
, self_value
in self
.items():
510 if self_key
not in other
:
513 other_value
= other
[self_key
]
515 if self_value
!= other_value
:
524 return {key
: value
._value
for key
, value
in self
.items()}
526 def _set_value(self
, values
):
527 original_values
= self
._value
530 for key
, value
in values
.items():
531 self
[key
].value
= value
533 self
.value
= original_values
536 value
= property(fset
=_set_value
)
539 items
= ['{}: {}'.format(repr(k
), repr(v
)) for k
, v
in self
.items()]
540 return '{{{}}}'.format(', '.join(items
))
543 class _VariantField(_Field
):
548 field_ptr
= native_bt
.field_variant_get_tag(self
._ptr
)
550 if field_ptr
is None:
553 return _create_from_ptr(field_ptr
)
556 def selected_field(self
):
559 def field(self
, tag_field
=None):
560 if tag_field
is None:
561 field_ptr
= native_bt
.field_variant_get_current_field(self
._ptr
)
563 if field_ptr
is None:
566 utils
._check
_type
(tag_field
, _EnumerationField
)
567 field_ptr
= native_bt
.field_variant_get_field(self
._ptr
, tag_field
._ptr
)
568 utils
._handle
_ptr
(field_ptr
, "cannot select variant field object's field")
570 return _create_from_ptr(field_ptr
)
572 def _spec_eq(self
, other
):
573 return _get_leaf_field(self
) == other
576 return bool(self
.selected_field
)
579 return str(self
.selected_field
) if self
.is_set
else repr(self
)
582 return repr(self
.selected_field
)
586 if self
.selected_field
is not None:
587 return self
.selected_field
._value
589 def _set_value(self
, value
):
590 self
.selected_field
.value
= value
592 value
= property(fset
=_set_value
)
595 class _ArraySequenceField(_ContainerField
, collections
.abc
.MutableSequence
):
596 def __getitem__(self
, index
):
597 if not isinstance(index
, numbers
.Integral
):
598 raise TypeError("'{}' is not an integral number object: invalid index".format(index
.__class
__.__name
__))
602 if index
< 0 or index
>= len(self
):
603 raise IndexError('{} field object index is out of range'.format(self
._NAME
))
605 field_ptr
= self
._get
_field
_ptr
_at
_index
(index
)
607 return _create_from_ptr(field_ptr
)
609 def __setitem__(self
, index
, value
):
610 # we can only set numbers and strings
611 if not isinstance(value
, (numbers
.Number
, _StringField
, str)):
612 raise TypeError('expecting number or string object')
614 # raises if index is somehow invalid
617 if not isinstance(field
, (_NumericField
, _StringField
)):
618 raise TypeError('can only set the value of a number or string field')
620 # the field's property does the appropriate conversion or raises
621 # the appropriate exception
624 def insert(self
, index
, value
):
625 raise NotImplementedError
627 def _spec_eq(self
, other
):
629 if len(self
) != len(other
):
632 for self_field
, other_field
in zip(self
, other
):
633 if self_field
!= other_field
:
642 return [field
._value
for field
in self
]
645 return '[{}]'.format(', '.join([repr(v
) for v
in self
]))
648 class _ArrayField(_ArraySequenceField
):
652 return self
.field_class
.length
654 def _get_field_ptr_at_index(self
, index
):
655 return native_bt
.field_array_get_field(self
._ptr
, index
)
657 def _set_value(self
, values
):
658 if len(self
) != len(values
):
660 'expected length of value and array field to match')
662 original_values
= self
._value
664 for index
, value
in enumerate(values
):
665 if value
is not None:
666 self
[index
].value
= value
670 self
.value
= original_values
673 value
= property(fset
=_set_value
)
676 class _SequenceField(_ArraySequenceField
):
680 return int(self
.length_field
)
683 def length_field(self
):
684 field_ptr
= native_bt
.field_sequence_get_length(self
._ptr
)
685 if field_ptr
is None:
687 return _create_from_ptr(field_ptr
)
690 def length_field(self
, length_field
):
691 utils
._check
_type
(length_field
, _IntegerField
)
692 ret
= native_bt
.field_sequence_set_length(self
._ptr
, length_field
._ptr
)
693 utils
._handle
_ret
(ret
, "cannot set sequence field object's length field")
695 def _get_field_ptr_at_index(self
, index
):
696 return native_bt
.field_sequence_get_field(self
._ptr
, index
)
698 def _set_value(self
, values
):
699 original_length_field
= self
.length_field
700 if original_length_field
is not None:
701 original_values
= self
._value
703 if len(values
) != self
.length_field
:
704 if self
.length_field
is not None:
705 length_fc
= self
.length_field
.field_class
707 length_fc
= bt2
.IntegerFieldClass(size
=64, is_signed
=False)
708 self
.length_field
= length_fc(len(values
))
711 for index
, value
in enumerate(values
):
712 if value
is not None:
713 self
[index
].value
= value
717 if original_length_field
is not None:
718 self
.length_field
= original_length_field
719 self
.value
= original_values
724 value
= property(fset
=_set_value
)