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_types
25 import collections
.abc
33 def _create_from_ptr(ptr
):
34 # recreate the field type wrapper of this field's type (the identity
35 # could be different, but the underlying address should be the
37 field_type_ptr
= native_bt
.ctf_field_get_type(ptr
)
38 utils
._handle
_ptr
(field_type_ptr
, "cannot get field object's type")
39 field_type
= bt2
.field_types
._create
_from
_ptr
(field_type_ptr
)
40 typeid
= native_bt
.ctf_field_type_get_type_id(field_type
._ptr
)
41 field
= _TYPE_ID_TO_OBJ
[typeid
]._create
_from
_ptr
(ptr
)
42 field
._field
_type
= field_type
46 class _Field(object._Object
, metaclass
=abc
.ABCMeta
):
48 ptr
= native_bt
.ctf_field_copy(self
._ptr
)
49 utils
._handle
_ptr
(ptr
, 'cannot copy {} field object'.format(self
._NAME
.lower()))
50 return _create_from_ptr(ptr
)
52 def __deepcopy__(self
, memo
):
59 return self
._field
_type
63 is_set
= native_bt
.ctf_field_value_is_set(self
._ptr
)
67 ret
= native_bt
.ctf_field_reset_value(self
._ptr
)
68 utils
._handle
_ret
(ret
, "cannot reset field object's value")
71 @functools.total_ordering
72 class _NumericField(_Field
):
74 def _extract_value(other
):
75 if other
is True or other
is False:
78 if isinstance(other
, numbers
.Integral
):
81 if isinstance(other
, numbers
.Real
):
84 if isinstance(other
, numbers
.Complex
):
87 raise TypeError("'{}' object is not a number object".format(other
.__class
__.__name
__))
90 return int(self
.value
)
93 return float(self
.value
)
96 return str(self
.value
)
98 def __lt__(self
, other
):
99 if not isinstance(other
, numbers
.Number
):
100 raise TypeError('unorderable types: {}() < {}()'.format(self
.__class
__.__name
__,
101 other
.__class
__.__name
__))
103 return self
.value
< float(other
)
105 def __le__(self
, other
):
106 if not isinstance(other
, numbers
.Number
):
107 raise TypeError('unorderable types: {}() <= {}()'.format(self
.__class
__.__name
__,
108 other
.__class
__.__name
__))
110 return self
.value
<= float(other
)
112 def __eq__(self
, other
):
113 if not isinstance(other
, numbers
.Number
):
116 return self
.value
== complex(other
)
118 def __rmod__(self
, other
):
119 return self
._extract
_value
(other
) % self
.value
121 def __mod__(self
, other
):
122 return self
.value
% self
._extract
_value
(other
)
124 def __rfloordiv__(self
, other
):
125 return self
._extract
_value
(other
) // self
.value
127 def __floordiv__(self
, other
):
128 return self
.value
// self
._extract
_value
(other
)
130 def __round__(self
, ndigits
=None):
132 return round(self
.value
)
134 return round(self
.value
, ndigits
)
137 return math
.ceil(self
.value
)
140 return math
.floor(self
.value
)
143 return int(self
.value
)
146 return abs(self
.value
)
148 def __add__(self
, other
):
149 return self
.value
+ self
._extract
_value
(other
)
151 def __radd__(self
, other
):
152 return self
.__add
__(other
)
160 def __mul__(self
, other
):
161 return self
.value
* self
._extract
_value
(other
)
163 def __rmul__(self
, other
):
164 return self
.__mul
__(other
)
166 def __truediv__(self
, other
):
167 return self
.value
/ self
._extract
_value
(other
)
169 def __rtruediv__(self
, other
):
170 return self
._extract
_value
(other
) / self
.value
172 def __pow__(self
, exponent
):
173 return self
.value
** self
._extract
_value
(exponent
)
175 def __rpow__(self
, base
):
176 return self
._extract
_value
(base
) ** self
.value
178 def __iadd__(self
, other
):
179 self
.value
= self
+ other
182 def __isub__(self
, other
):
183 self
.value
= self
- other
186 def __imul__(self
, other
):
187 self
.value
= self
* other
190 def __itruediv__(self
, other
):
191 self
.value
= self
/ other
194 def __ifloordiv__(self
, other
):
195 self
.value
= self
// other
198 def __imod__(self
, other
):
199 self
.value
= self
% other
202 def __ipow__(self
, other
):
203 self
.value
= self
** other
207 class _IntegralField(_NumericField
, numbers
.Integral
):
208 def __lshift__(self
, other
):
209 return self
.value
<< self
._extract
_value
(other
)
211 def __rlshift__(self
, other
):
212 return self
._extract
_value
(other
) << self
.value
214 def __rshift__(self
, other
):
215 return self
.value
>> self
._extract
_value
(other
)
217 def __rrshift__(self
, other
):
218 return self
._extract
_value
(other
) >> self
.value
220 def __and__(self
, other
):
221 return self
.value
& self
._extract
_value
(other
)
223 def __rand__(self
, other
):
224 return self
._extract
_value
(other
) & self
.value
226 def __xor__(self
, other
):
227 return self
.value ^ self
._extract
_value
(other
)
229 def __rxor__(self
, other
):
230 return self
._extract
_value
(other
) ^ self
.value
232 def __or__(self
, other
):
233 return self
.value | self
._extract
_value
(other
)
235 def __ror__(self
, other
):
236 return self
._extract
_value
(other
) | self
.value
238 def __invert__(self
):
241 def __ilshift__(self
, other
):
242 self
.value
= self
<< other
245 def __irshift__(self
, other
):
246 self
.value
= self
>> other
249 def __iand__(self
, other
):
250 self
.value
= self
& other
253 def __ixor__(self
, other
):
254 self
.value
= self ^ other
257 def __ior__(self
, other
):
258 self
.value
= self | other
262 class _RealField(_NumericField
, numbers
.Real
):
266 class _IntegerField(_IntegralField
):
269 def _value_to_int(self
, value
):
270 if not isinstance(value
, numbers
.Real
):
271 raise TypeError('expecting a real number object')
275 if self
.field_type
.is_signed
:
276 utils
._check
_int
64(value
)
278 utils
._check
_uint
64(value
)
284 if self
.field_type
.is_signed
:
285 ret
, value
= native_bt
.ctf_field_signed_integer_get_value(self
._ptr
)
287 ret
, value
= native_bt
.ctf_field_unsigned_integer_get_value(self
._ptr
)
296 def value(self
, value
):
297 value
= self
._value
_to
_int
(value
)
299 if self
.field_type
.is_signed
:
300 ret
= native_bt
.ctf_field_signed_integer_set_value(self
._ptr
, value
)
302 ret
= native_bt
.ctf_field_unsigned_integer_set_value(self
._ptr
, value
)
304 utils
._handle
_ret
(ret
, "cannot set integer field object's value")
307 class _FloatingPointNumberField(_RealField
):
308 _NAME
= 'Floating point number'
310 def _value_to_float(self
, value
):
311 if not isinstance(value
, numbers
.Real
):
312 raise TypeError("expecting a real number object")
318 ret
, value
= native_bt
.ctf_field_floating_point_get_value(self
._ptr
)
327 def value(self
, value
):
328 value
= self
._value
_to
_float
(value
)
329 ret
= native_bt
.ctf_field_floating_point_set_value(self
._ptr
, value
)
330 utils
._handle
_ret
(ret
, "cannot set floating point number field object's value")
333 class _EnumerationField(_IntegerField
):
334 _NAME
= 'Enumeration'
337 def integer_field(self
):
338 int_field_ptr
= native_bt
.ctf_field_enumeration_get_container(self
._ptr
)
339 assert(int_field_ptr
)
340 return _create_from_ptr(int_field_ptr
)
344 return self
.integer_field
.value
347 def value(self
, value
):
348 self
.integer_field
.value
= value
352 iter_ptr
= native_bt
.ctf_field_enumeration_get_mappings(self
._ptr
)
354 return bt2
.field_types
._EnumerationFieldTypeMappingIterator
(iter_ptr
,
355 self
.field_type
.is_signed
)
358 @functools.total_ordering
359 class _StringField(_Field
, collections
.abc
.Sequence
):
362 def _value_to_str(self
, value
):
363 if isinstance(value
, self
.__class
__):
366 if not isinstance(value
, str):
367 raise TypeError("expecting a 'str' object")
373 value
= native_bt
.ctf_field_string_get_value(self
._ptr
)
382 def value(self
, value
):
383 value
= self
._value
_to
_str
(value
)
384 ret
= native_bt
.ctf_field_string_set_value(self
._ptr
, value
)
385 utils
._handle
_ret
(ret
, "cannot set string field object's value")
387 def __eq__(self
, other
):
389 other
= self
._value
_to
_str
(other
)
393 return self
.value
== other
395 def __le__(self
, other
):
396 return self
.value
<= self
._value
_to
_str
(other
)
398 def __lt__(self
, other
):
399 return self
.value
< self
._value
_to
_str
(other
)
402 return bool(self
.value
)
407 def __getitem__(self
, index
):
408 return self
.value
[index
]
411 return len(self
.value
)
413 def __iadd__(self
, value
):
414 value
= self
._value
_to
_str
(value
)
415 ret
= native_bt
.ctf_field_string_append(self
._ptr
, value
)
416 utils
._handle
_ret
(ret
, "cannot append to string field object's value")
420 class _ContainerField(_Field
):
422 return len(self
) != 0
425 count
= self
._count
()
429 def __delitem__(self
, index
):
430 raise NotImplementedError
433 class _StructureField(_ContainerField
, collections
.abc
.MutableMapping
):
437 return len(self
.field_type
)
439 def __getitem__(self
, key
):
440 utils
._check
_str
(key
)
441 ptr
= native_bt
.ctf_field_structure_get_field_by_name(self
._ptr
, key
)
446 return _create_from_ptr(ptr
)
448 def __setitem__(self
, key
, value
):
449 # we can only set numbers and strings
450 if not isinstance(value
, (numbers
.Number
, str)):
451 raise TypeError('expecting number object or string')
453 # raises if index is somehow invalid
456 if not isinstance(field
, (_NumericField
, _StringField
)):
457 raise TypeError('can only set the value of a number or string field')
459 # the field's property does the appropriate conversion or raises
460 # the appropriate exception
463 def at_index(self
, index
):
464 utils
._check
_uint
64(index
)
466 if index
>= len(self
):
469 field_ptr
= native_bt
.ctf_field_structure_get_field_by_index(self
._ptr
, index
)
471 return _create_from_ptr(field_ptr
)
475 return iter(self
.field_type
)
477 def __eq__(self
, other
):
478 if not isinstance(other
, collections
.abc
.Mapping
):
481 if len(self
) != len(other
):
484 for self_key
, self_value
in self
.items():
485 if self_key
not in other
:
488 other_value
= other
[self_key
]
490 if self_value
!= other_value
:
497 return {key
: field
.value
for key
, field
in self
.items()}
500 def value(self
, values
):
501 if not hasattr(type(values
), '__getitem__'):
502 raise TypeError('expecting a Mapping collection')
504 for key
, value
in values
.items():
505 self
[key
].value
= value
508 class _VariantField(_Field
):
513 field_ptr
= native_bt
.ctf_field_variant_get_tag(self
._ptr
)
515 if field_ptr
is None:
518 return _create_from_ptr(field_ptr
)
521 def selected_field(self
):
524 def field(self
, tag_field
=None):
525 if tag_field
is None:
526 field_ptr
= native_bt
.ctf_field_variant_get_current_field(self
._ptr
)
528 if field_ptr
is None:
531 utils
._check
_type
(tag_field
, _EnumerationField
)
532 field_ptr
= native_bt
.ctf_field_variant_get_field(self
._ptr
, tag_field
._ptr
)
533 utils
._handle
_ptr
(field_ptr
, "cannot select variant field object's field")
535 return _create_from_ptr(field_ptr
)
537 def __eq__(self
, other
):
538 if type(other
) is not type(self
):
541 if self
.addr
== other
.addr
:
544 return self
.selected_field
== other
.selected_field
547 return bool(self
.selected_field
)
550 class _ArraySequenceField(_ContainerField
, collections
.abc
.MutableSequence
):
551 def __getitem__(self
, index
):
552 if not isinstance(index
, numbers
.Integral
):
553 raise TypeError("'{}' is not an integral number object: invalid index".format(index
.__class
__.__name
__))
557 if index
< 0 or index
>= len(self
):
558 raise IndexError('{} field object index is out of range'.format(self
._NAME
))
560 field_ptr
= self
._get
_field
_ptr
_at
_index
(index
)
562 return _create_from_ptr(field_ptr
)
564 def __setitem__(self
, index
, value
):
565 # we can only set numbers and strings
566 if not isinstance(value
, (numbers
.Number
, _StringField
, str)):
567 raise TypeError('expecting number or string object')
569 # raises if index is somehow invalid
572 if not isinstance(field
, (_NumericField
, _StringField
)):
573 raise TypeError('can only set the value of a number or string field')
575 # the field's property does the appropriate conversion or raises
576 # the appropriate exception
579 def insert(self
, index
, value
):
580 raise NotImplementedError
582 def __eq__(self
, other
):
583 if not isinstance(other
, collections
.abc
.Sequence
):
586 if len(self
) != len(other
):
589 for self_field
, other_field
in zip(self
, other
):
590 if self_field
!= other_field
:
597 return [field
.value
for field
in self
]
600 def value(self
, values
):
601 if not hasattr(type(values
), '__iter__'):
602 raise TypeError('expecting an iterable container (Sequence)')
604 if len(self
) != len(values
):
605 raise ValueError('expected length of value and field to match')
607 for index
, value
in enumerate(values
):
608 self
[index
].value
= value
611 class _ArrayField(_ArraySequenceField
):
615 return self
.field_type
.length
617 def _get_field_ptr_at_index(self
, index
):
618 return native_bt
.ctf_field_array_get_field(self
._ptr
, index
)
621 class _SequenceField(_ArraySequenceField
):
625 return self
.length_field
.value
628 def length_field(self
):
629 field_ptr
= native_bt
.ctf_field_sequence_get_length(self
._ptr
)
630 utils
._handle
_ptr
("cannot get sequence field object's length field")
631 return _create_from_ptr(field_ptr
)
634 def length_field(self
, length_field
):
635 utils
._check
_type
(length_field
, _IntegerField
)
636 ret
= native_bt
.ctf_field_sequence_set_length(self
._ptr
, length_field
._ptr
)
637 utils
._handle
_ret
(ret
, "cannot set sequence field object's length field")
639 def _get_field_ptr_at_index(self
, index
):
640 return native_bt
.ctf_field_sequence_get_field(self
._ptr
, index
)
644 native_bt
.CTF_FIELD_TYPE_ID_INTEGER
: _IntegerField
,
645 native_bt
.CTF_FIELD_TYPE_ID_FLOAT
: _FloatingPointNumberField
,
646 native_bt
.CTF_FIELD_TYPE_ID_ENUM
: _EnumerationField
,
647 native_bt
.CTF_FIELD_TYPE_ID_STRING
: _StringField
,
648 native_bt
.CTF_FIELD_TYPE_ID_STRUCT
: _StructureField
,
649 native_bt
.CTF_FIELD_TYPE_ID_ARRAY
: _ArrayField
,
650 native_bt
.CTF_FIELD_TYPE_ID_SEQUENCE
: _SequenceField
,
651 native_bt
.CTF_FIELD_TYPE_ID_VARIANT
: _VariantField
,