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_field_from_ptr(ptr
, owner_ptr
, owner_get_ref
, owner_put_ref
):
41 field_class_ptr
= native_bt
.field_borrow_class_const(ptr
)
42 utils
._handle
_ptr
(field_class_ptr
, "cannot get field object's class")
43 typeid
= native_bt
.field_class_get_type(field_class_ptr
)
44 field
= _TYPE_ID_TO_OBJ
[typeid
]._create
_from
_ptr
_and
_get
_ref
(
45 ptr
, owner_ptr
, owner_get_ref
, owner_put_ref
)
49 class _Field(object._UniqueObject
, metaclass
=abc
.ABCMeta
):
51 ptr
= native_bt
.field_copy(self
._ptr
)
52 utils
._handle
_ptr
(ptr
, 'cannot copy {} field object'.format(self
._NAME
.lower()))
53 return _create_from_ptr(ptr
)
55 def __deepcopy__(self
, memo
):
60 def __eq__(self
, other
):
61 # special case: two unset fields with the same field class are equal
62 if isinstance(other
, _Field
):
63 if not self
.is_set
or not other
.is_set
:
64 if not self
.is_set
and not other
.is_set
and self
.field_class
== other
.field_class
:
68 other
= _get_leaf_field(other
)
69 return self
._spec
_eq
(other
)
72 def field_class(self
):
73 return self
._field
_class
77 is_set
= native_bt
.field_is_set(self
._ptr
)
81 ret
= native_bt
.field_reset(self
._ptr
)
82 utils
._handle
_ret
(ret
, "cannot reset field object's value")
85 raise NotImplementedError
88 return self
._repr
() if self
.is_set
else 'Unset'
91 @functools.total_ordering
92 class _NumericField(_Field
):
94 def _extract_value(other
):
95 if other
is True or other
is False:
98 if isinstance(other
, numbers
.Integral
):
101 if isinstance(other
, numbers
.Real
):
104 if isinstance(other
, numbers
.Complex
):
105 return complex(other
)
107 raise TypeError("'{}' object is not a number object".format(other
.__class
__.__name
__))
110 return int(self
._value
)
113 return float(self
._value
)
116 return repr(self
._value
)
118 def __lt__(self
, other
):
119 if not isinstance(other
, numbers
.Number
):
120 raise TypeError('unorderable types: {}() < {}()'.format(self
.__class
__.__name
__,
121 other
.__class
__.__name
__))
123 return self
._value
< float(other
)
125 def __le__(self
, other
):
126 if not isinstance(other
, numbers
.Number
):
127 raise TypeError('unorderable types: {}() <= {}()'.format(self
.__class
__.__name
__,
128 other
.__class
__.__name
__))
130 return self
._value
<= float(other
)
132 def _spec_eq(self
, other
):
133 if not isinstance(other
, numbers
.Number
):
136 return self
._value
== complex(other
)
138 def __rmod__(self
, other
):
139 return self
._extract
_value
(other
) % self
._value
141 def __mod__(self
, other
):
142 return self
._value
% self
._extract
_value
(other
)
144 def __rfloordiv__(self
, other
):
145 return self
._extract
_value
(other
) // self
._value
147 def __floordiv__(self
, other
):
148 return self
._value
// self
._extract
_value
(other
)
150 def __round__(self
, ndigits
=None):
152 return round(self
._value
)
154 return round(self
._value
, ndigits
)
157 return math
.ceil(self
._value
)
160 return math
.floor(self
._value
)
163 return int(self
._value
)
166 return abs(self
._value
)
168 def __add__(self
, other
):
169 return self
._value
+ self
._extract
_value
(other
)
171 def __radd__(self
, other
):
172 return self
.__add
__(other
)
180 def __mul__(self
, other
):
181 return self
._value
* self
._extract
_value
(other
)
183 def __rmul__(self
, other
):
184 return self
.__mul
__(other
)
186 def __truediv__(self
, other
):
187 return self
._value
/ self
._extract
_value
(other
)
189 def __rtruediv__(self
, other
):
190 return self
._extract
_value
(other
) / self
._value
192 def __pow__(self
, exponent
):
193 return self
._value
** self
._extract
_value
(exponent
)
195 def __rpow__(self
, base
):
196 return self
._extract
_value
(base
) ** self
._value
198 def __iadd__(self
, other
):
199 self
.value
= self
+ other
202 def __isub__(self
, other
):
203 self
.value
= self
- other
206 def __imul__(self
, other
):
207 self
.value
= self
* other
210 def __itruediv__(self
, other
):
211 self
.value
= self
/ other
214 def __ifloordiv__(self
, other
):
215 self
.value
= self
// other
218 def __imod__(self
, other
):
219 self
.value
= self
% other
222 def __ipow__(self
, other
):
223 self
.value
= self
** other
227 class _IntegralField(_NumericField
, numbers
.Integral
):
228 def __lshift__(self
, other
):
229 return self
._value
<< self
._extract
_value
(other
)
231 def __rlshift__(self
, other
):
232 return self
._extract
_value
(other
) << self
._value
234 def __rshift__(self
, other
):
235 return self
._value
>> self
._extract
_value
(other
)
237 def __rrshift__(self
, other
):
238 return self
._extract
_value
(other
) >> self
._value
240 def __and__(self
, other
):
241 return self
._value
& self
._extract
_value
(other
)
243 def __rand__(self
, other
):
244 return self
._extract
_value
(other
) & self
._value
246 def __xor__(self
, other
):
247 return self
._value ^ self
._extract
_value
(other
)
249 def __rxor__(self
, other
):
250 return self
._extract
_value
(other
) ^ self
._value
252 def __or__(self
, other
):
253 return self
._value | self
._extract
_value
(other
)
255 def __ror__(self
, other
):
256 return self
._extract
_value
(other
) | self
._value
258 def __invert__(self
):
261 def __ilshift__(self
, other
):
262 self
.value
= self
<< other
265 def __irshift__(self
, other
):
266 self
.value
= self
>> other
269 def __iand__(self
, other
):
270 self
.value
= self
& other
273 def __ixor__(self
, other
):
274 self
.value
= self ^ other
277 def __ior__(self
, other
):
278 self
.value
= self | other
282 class _IntegerField(_IntegralField
, _Field
):
286 class _UnsignedIntegerField(_IntegerField
, _Field
):
287 _NAME
= 'Unsigned integer'
288 def _value_to_int(self
, value
):
289 if not isinstance(value
, numbers
.Real
):
290 raise TypeError('expecting a real number object')
293 utils
._check
_uint
64(value
)
299 return native_bt
.field_unsigned_integer_get_value(self
._ptr
)
301 def _set_value(self
, value
):
302 value
= self
._value
_to
_int
(value
)
303 native_bt
.field_unsigned_integer_set_value(self
._ptr
, value
)
305 value
= property(fset
=_set_value
)
308 class _SignedIntegerField(_IntegerField
, _Field
):
309 _NAME
= 'Signed integer'
310 def _value_to_int(self
, value
):
311 if not isinstance(value
, numbers
.Real
):
312 raise TypeError('expecting a real number object')
315 utils
._check
_int
64(value
)
321 return native_bt
.field_signed_integer_get_value(self
._ptr
)
323 def _set_value(self
, value
):
324 value
= self
._value
_to
_int
(value
)
325 native_bt
.field_signed_integer_set_value(self
._ptr
, value
)
327 value
= property(fset
=_set_value
)
330 class _RealField(_NumericField
, numbers
.Real
):
333 def _value_to_float(self
, value
):
334 if not isinstance(value
, numbers
.Real
):
335 raise TypeError("expecting a real number object")
341 return native_bt
.field_real_get_value(self
._ptr
)
343 def _set_value(self
, value
):
344 value
= self
._value
_to
_float
(value
)
345 native_bt
.field_real_set_value(self
._ptr
, value
)
347 value
= property(fset
=_set_value
)
350 class _EnumerationField(_IntegerField
):
351 _NAME
= 'Enumeration'
354 def integer_field(self
):
355 int_field_ptr
= native_bt
.field_enumeration_get_container(self
._ptr
)
356 assert(int_field_ptr
)
357 return _create_from_ptr(int_field_ptr
)
359 def _set_value(self
, value
):
360 self
.integer_field
.value
= value
363 labels
= [repr(v
.name
) for v
in self
.mappings
]
364 return '{} ({})'.format(self
._value
, ', '.join(labels
))
366 value
= property(fset
=_set_value
)
370 return self
.integer_field
._value
374 iter_ptr
= native_bt
.field_enumeration_get_mappings(self
._ptr
)
376 return bt2
.field_class
._EnumerationFieldClassMappingIterator
(iter_ptr
,
377 self
.field_class
.is_signed
)
380 @functools.total_ordering
381 class _StringField(_Field
, collections
.abc
.Sequence
):
384 def _value_to_str(self
, value
):
385 if isinstance(value
, self
.__class
__):
388 if not isinstance(value
, str):
389 raise TypeError("expecting a 'str' object")
395 value
= native_bt
.field_string_get_value(self
._ptr
)
398 def _set_value(self
, value
):
399 value
= self
._value
_to
_str
(value
)
400 ret
= native_bt
.field_string_set_value(self
._ptr
, value
)
401 utils
._handle
_ret
(ret
, "cannot set string field object's value")
403 value
= property(fset
=_set_value
)
405 def _spec_eq(self
, other
):
407 other
= self
._value
_to
_str
(other
)
411 return self
._value
== other
413 def __le__(self
, other
):
414 return self
._value
<= self
._value
_to
_str
(other
)
416 def __lt__(self
, other
):
417 return self
._value
< self
._value
_to
_str
(other
)
420 return bool(self
._value
)
423 return repr(self
._value
)
426 return self
._value
if self
.is_set
else repr(self
)
428 def __getitem__(self
, index
):
429 return self
._value
[index
]
432 return len(self
._value
)
434 def __iadd__(self
, value
):
435 value
= self
._value
_to
_str
(value
)
436 ret
= native_bt
.field_string_append(self
._ptr
, value
)
437 utils
._handle
_ret
(ret
, "cannot append to string field object's value")
441 class _ContainerField(_Field
):
443 return len(self
) != 0
446 count
= self
._count
()
450 def __delitem__(self
, index
):
451 raise NotImplementedError
454 class _StructureField(_ContainerField
, collections
.abc
.MutableMapping
):
458 return len(self
.field_class
)
460 def __getitem__(self
, key
):
461 utils
._check
_str
(key
)
462 field_ptr
= native_bt
.field_structure_borrow_member_field_by_name(self
._ptr
, key
)
464 if field_ptr
is None:
467 return _create_field_from_ptr(field_ptr
, self
._owner
_ptr
,
471 def __setitem__(self
, key
, value
):
472 # raises if key is somehow invalid
475 # the field's property does the appropriate conversion or raises
476 # the appropriate exception
479 def at_index(self
, index
):
480 utils
._check
_uint
64(index
)
482 if index
>= len(self
):
485 field_ptr
= native_bt
.field_structure_get_field_by_index(self
._ptr
, index
)
487 return _create_from_ptr(field_ptr
)
491 return iter(self
.field_class
)
493 def _spec_eq(self
, other
):
495 if len(self
) != len(other
):
498 for self_key
, self_value
in self
.items():
499 if self_key
not in other
:
502 other_value
= other
[self_key
]
504 if self_value
!= other_value
:
513 return {key
: value
._value
for key
, value
in self
.items()}
515 def _set_value(self
, values
):
516 original_values
= self
._value
519 for key
, value
in values
.items():
520 self
[key
].value
= value
522 self
.value
= original_values
525 value
= property(fset
=_set_value
)
528 items
= ['{}: {}'.format(repr(k
), repr(v
)) for k
, v
in self
.items()]
529 return '{{{}}}'.format(', '.join(items
))
532 class _VariantField(_Field
):
537 field_ptr
= native_bt
.field_variant_get_tag(self
._ptr
)
539 if field_ptr
is None:
542 return _create_from_ptr(field_ptr
)
545 def selected_field(self
):
548 def field(self
, tag_field
=None):
549 if tag_field
is None:
550 field_ptr
= native_bt
.field_variant_get_current_field(self
._ptr
)
552 if field_ptr
is None:
555 utils
._check
_type
(tag_field
, _EnumerationField
)
556 field_ptr
= native_bt
.field_variant_get_field(self
._ptr
, tag_field
._ptr
)
557 utils
._handle
_ptr
(field_ptr
, "cannot select variant field object's field")
559 return _create_from_ptr(field_ptr
)
561 def _spec_eq(self
, other
):
562 return _get_leaf_field(self
) == other
565 return bool(self
.selected_field
)
568 return str(self
.selected_field
) if self
.is_set
else repr(self
)
571 return repr(self
.selected_field
)
575 if self
.selected_field
is not None:
576 return self
.selected_field
._value
578 def _set_value(self
, value
):
579 self
.selected_field
.value
= value
581 value
= property(fset
=_set_value
)
584 class _ArraySequenceField(_ContainerField
, collections
.abc
.MutableSequence
):
585 def __getitem__(self
, index
):
586 if not isinstance(index
, numbers
.Integral
):
587 raise TypeError("'{}' is not an integral number object: invalid index".format(index
.__class
__.__name
__))
591 if index
< 0 or index
>= len(self
):
592 raise IndexError('{} field object index is out of range'.format(self
._NAME
))
594 field_ptr
= self
._get
_field
_ptr
_at
_index
(index
)
596 return _create_from_ptr(field_ptr
)
598 def __setitem__(self
, index
, value
):
599 # we can only set numbers and strings
600 if not isinstance(value
, (numbers
.Number
, _StringField
, str)):
601 raise TypeError('expecting number or string object')
603 # raises if index is somehow invalid
606 if not isinstance(field
, (_NumericField
, _StringField
)):
607 raise TypeError('can only set the value of a number or string field')
609 # the field's property does the appropriate conversion or raises
610 # the appropriate exception
613 def insert(self
, index
, value
):
614 raise NotImplementedError
616 def _spec_eq(self
, other
):
618 if len(self
) != len(other
):
621 for self_field
, other_field
in zip(self
, other
):
622 if self_field
!= other_field
:
631 return [field
._value
for field
in self
]
634 return '[{}]'.format(', '.join([repr(v
) for v
in self
]))
637 class _ArrayField(_ArraySequenceField
):
641 return self
.field_class
.length
643 def _get_field_ptr_at_index(self
, index
):
644 return native_bt
.field_array_get_field(self
._ptr
, index
)
646 def _set_value(self
, values
):
647 if len(self
) != len(values
):
649 'expected length of value and array field to match')
651 original_values
= self
._value
653 for index
, value
in enumerate(values
):
654 if value
is not None:
655 self
[index
].value
= value
659 self
.value
= original_values
662 value
= property(fset
=_set_value
)
665 class _SequenceField(_ArraySequenceField
):
669 return int(self
.length_field
)
672 def length_field(self
):
673 field_ptr
= native_bt
.field_sequence_get_length(self
._ptr
)
674 if field_ptr
is None:
676 return _create_from_ptr(field_ptr
)
679 def length_field(self
, length_field
):
680 utils
._check
_type
(length_field
, _IntegerField
)
681 ret
= native_bt
.field_sequence_set_length(self
._ptr
, length_field
._ptr
)
682 utils
._handle
_ret
(ret
, "cannot set sequence field object's length field")
684 def _get_field_ptr_at_index(self
, index
):
685 return native_bt
.field_sequence_get_field(self
._ptr
, index
)
687 def _set_value(self
, values
):
688 original_length_field
= self
.length_field
689 if original_length_field
is not None:
690 original_values
= self
._value
692 if len(values
) != self
.length_field
:
693 if self
.length_field
is not None:
694 length_fc
= self
.length_field
.field_class
696 length_fc
= bt2
.IntegerFieldClass(size
=64, is_signed
=False)
697 self
.length_field
= length_fc(len(values
))
700 for index
, value
in enumerate(values
):
701 if value
is not None:
702 self
[index
].value
= value
706 if original_length_field
is not None:
707 self
.length_field
= original_length_field
708 self
.value
= original_values
713 value
= property(fset
=_set_value
)
717 native_bt
.FIELD_CLASS_TYPE_UNSIGNED_INTEGER
: _UnsignedIntegerField
,
718 native_bt
.FIELD_CLASS_TYPE_SIGNED_INTEGER
: _SignedIntegerField
,
719 native_bt
.FIELD_CLASS_TYPE_REAL
: _RealField
,
720 native_bt
.FIELD_CLASS_TYPE_STRING
: _StringField
,
721 native_bt
.FIELD_CLASS_TYPE_STRUCTURE
: _StructureField
,