Commit | Line | Data |
---|---|---|
81447b5b PP |
1 | # The MIT License (MIT) |
2 | # | |
811644b8 | 3 | # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com> |
81447b5b PP |
4 | # |
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: | |
11 | # | |
12 | # The above copyright notice and this permission notice shall be included in | |
13 | # all copies or substantial portions of the Software. | |
14 | # | |
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 | |
21 | # THE SOFTWARE. | |
22 | ||
23 | from bt2 import native_bt, object, utils | |
3fb99a22 | 24 | from bt2 import field_class as bt2_field_class |
81447b5b PP |
25 | import collections.abc |
26 | import functools | |
27 | import numbers | |
28 | import math | |
81447b5b PP |
29 | |
30 | ||
2ae9f48c SM |
31 | def _create_field_from_ptr(ptr, owner_ptr, owner_get_ref, owner_put_ref): |
32 | field_class_ptr = native_bt.field_borrow_class_const(ptr) | |
2ae9f48c SM |
33 | typeid = native_bt.field_class_get_type(field_class_ptr) |
34 | field = _TYPE_ID_TO_OBJ[typeid]._create_from_ptr_and_get_ref( | |
cfbd7cf3 FD |
35 | ptr, owner_ptr, owner_get_ref, owner_put_ref |
36 | ) | |
81447b5b PP |
37 | return field |
38 | ||
39 | ||
cec0261d PP |
40 | # Get the "effective" field of `field`. If `field` is a variant, return |
41 | # the currently selected field. If `field` is an option, return the | |
42 | # content field. If `field` is of any other type, return `field` | |
1eccc498 | 43 | # directly. |
81447b5b | 44 | |
cfbd7cf3 | 45 | |
1eccc498 | 46 | def _get_leaf_field(field): |
cec0261d PP |
47 | if isinstance(field, _VariantField): |
48 | return _get_leaf_field(field.selected_option) | |
1eccc498 | 49 | |
cec0261d PP |
50 | if isinstance(field, _OptionField): |
51 | return _get_leaf_field(field.field) | |
52 | ||
53 | return field | |
81447b5b | 54 | |
e1c6bebd | 55 | |
1eccc498 SM |
56 | class _Field(object._UniqueObject): |
57 | def __eq__(self, other): | |
e1c6bebd JG |
58 | other = _get_leaf_field(other) |
59 | return self._spec_eq(other) | |
60 | ||
81447b5b | 61 | @property |
d8e2073c | 62 | def cls(self): |
838a5a52 SM |
63 | field_class_ptr = native_bt.field_borrow_class_const(self._ptr) |
64 | assert field_class_ptr is not None | |
3fb99a22 | 65 | return bt2_field_class._create_field_class_from_ptr_and_get_ref(field_class_ptr) |
81447b5b | 66 | |
12bf0d88 JG |
67 | def _repr(self): |
68 | raise NotImplementedError | |
69 | ||
70 | def __repr__(self): | |
1eccc498 | 71 | return self._repr() |
12bf0d88 | 72 | |
81447b5b | 73 | |
ead8c3d4 PP |
74 | class _BitArrayField(_Field): |
75 | _NAME = 'Bit array' | |
76 | ||
77 | @property | |
78 | def value_as_integer(self): | |
79 | return native_bt.field_bit_array_get_value_as_integer(self._ptr) | |
80 | ||
81 | @value_as_integer.setter | |
82 | def value_as_integer(self, value): | |
83 | utils._check_uint64(value) | |
84 | native_bt.field_bit_array_set_value_as_integer(self._ptr, value) | |
85 | ||
86 | def _spec_eq(self, other): | |
87 | if type(other) is not type(self): | |
88 | return False | |
89 | ||
90 | return self.value_as_integer == other.value_as_integer | |
91 | ||
92 | def _repr(self): | |
93 | return repr(self.value_as_integer) | |
94 | ||
95 | def __str__(self): | |
96 | return str(self.value_as_integer) | |
97 | ||
98 | def __len__(self): | |
d8e2073c | 99 | return self.cls.length |
ead8c3d4 PP |
100 | |
101 | ||
81447b5b PP |
102 | @functools.total_ordering |
103 | class _NumericField(_Field): | |
104 | @staticmethod | |
105 | def _extract_value(other): | |
aae30e61 PP |
106 | if isinstance(other, _BoolField) or isinstance(other, bool): |
107 | return bool(other) | |
81447b5b PP |
108 | |
109 | if isinstance(other, numbers.Integral): | |
110 | return int(other) | |
111 | ||
112 | if isinstance(other, numbers.Real): | |
113 | return float(other) | |
114 | ||
115 | if isinstance(other, numbers.Complex): | |
116 | return complex(other) | |
117 | ||
cfbd7cf3 FD |
118 | raise TypeError( |
119 | "'{}' object is not a number object".format(other.__class__.__name__) | |
120 | ) | |
81447b5b PP |
121 | |
122 | def __int__(self): | |
e1c6bebd | 123 | return int(self._value) |
81447b5b PP |
124 | |
125 | def __float__(self): | |
e1c6bebd | 126 | return float(self._value) |
81447b5b | 127 | |
12bf0d88 | 128 | def _repr(self): |
5abb9e33 | 129 | return repr(self._value) |
81447b5b PP |
130 | |
131 | def __lt__(self, other): | |
132 | if not isinstance(other, numbers.Number): | |
cfbd7cf3 FD |
133 | raise TypeError( |
134 | 'unorderable types: {}() < {}()'.format( | |
135 | self.__class__.__name__, other.__class__.__name__ | |
136 | ) | |
137 | ) | |
81447b5b | 138 | |
09a926c1 | 139 | return self._value < self._extract_value(other) |
81447b5b | 140 | |
e1c6bebd | 141 | def _spec_eq(self, other): |
f11ed062 PP |
142 | try: |
143 | return self._value == self._extract_value(other) | |
4c4935bf | 144 | except Exception: |
f11ed062 | 145 | return False |
81447b5b PP |
146 | |
147 | def __rmod__(self, other): | |
e1c6bebd | 148 | return self._extract_value(other) % self._value |
81447b5b PP |
149 | |
150 | def __mod__(self, other): | |
e1c6bebd | 151 | return self._value % self._extract_value(other) |
81447b5b PP |
152 | |
153 | def __rfloordiv__(self, other): | |
e1c6bebd | 154 | return self._extract_value(other) // self._value |
81447b5b PP |
155 | |
156 | def __floordiv__(self, other): | |
e1c6bebd | 157 | return self._value // self._extract_value(other) |
81447b5b PP |
158 | |
159 | def __round__(self, ndigits=None): | |
160 | if ndigits is None: | |
e1c6bebd | 161 | return round(self._value) |
81447b5b | 162 | else: |
e1c6bebd | 163 | return round(self._value, ndigits) |
81447b5b PP |
164 | |
165 | def __ceil__(self): | |
e1c6bebd | 166 | return math.ceil(self._value) |
81447b5b PP |
167 | |
168 | def __floor__(self): | |
e1c6bebd | 169 | return math.floor(self._value) |
81447b5b PP |
170 | |
171 | def __trunc__(self): | |
e1c6bebd | 172 | return int(self._value) |
81447b5b PP |
173 | |
174 | def __abs__(self): | |
e1c6bebd | 175 | return abs(self._value) |
81447b5b PP |
176 | |
177 | def __add__(self, other): | |
e1c6bebd | 178 | return self._value + self._extract_value(other) |
81447b5b PP |
179 | |
180 | def __radd__(self, other): | |
181 | return self.__add__(other) | |
182 | ||
183 | def __neg__(self): | |
e1c6bebd | 184 | return -self._value |
81447b5b PP |
185 | |
186 | def __pos__(self): | |
e1c6bebd | 187 | return +self._value |
81447b5b PP |
188 | |
189 | def __mul__(self, other): | |
e1c6bebd | 190 | return self._value * self._extract_value(other) |
81447b5b PP |
191 | |
192 | def __rmul__(self, other): | |
193 | return self.__mul__(other) | |
194 | ||
195 | def __truediv__(self, other): | |
e1c6bebd | 196 | return self._value / self._extract_value(other) |
81447b5b PP |
197 | |
198 | def __rtruediv__(self, other): | |
e1c6bebd | 199 | return self._extract_value(other) / self._value |
81447b5b PP |
200 | |
201 | def __pow__(self, exponent): | |
e1c6bebd | 202 | return self._value ** self._extract_value(exponent) |
81447b5b PP |
203 | |
204 | def __rpow__(self, base): | |
e1c6bebd | 205 | return self._extract_value(base) ** self._value |
81447b5b | 206 | |
81447b5b PP |
207 | |
208 | class _IntegralField(_NumericField, numbers.Integral): | |
209 | def __lshift__(self, other): | |
e1c6bebd | 210 | return self._value << self._extract_value(other) |
81447b5b PP |
211 | |
212 | def __rlshift__(self, other): | |
e1c6bebd | 213 | return self._extract_value(other) << self._value |
81447b5b PP |
214 | |
215 | def __rshift__(self, other): | |
e1c6bebd | 216 | return self._value >> self._extract_value(other) |
81447b5b PP |
217 | |
218 | def __rrshift__(self, other): | |
e1c6bebd | 219 | return self._extract_value(other) >> self._value |
81447b5b PP |
220 | |
221 | def __and__(self, other): | |
e1c6bebd | 222 | return self._value & self._extract_value(other) |
81447b5b PP |
223 | |
224 | def __rand__(self, other): | |
e1c6bebd | 225 | return self._extract_value(other) & self._value |
81447b5b PP |
226 | |
227 | def __xor__(self, other): | |
e1c6bebd | 228 | return self._value ^ self._extract_value(other) |
81447b5b PP |
229 | |
230 | def __rxor__(self, other): | |
e1c6bebd | 231 | return self._extract_value(other) ^ self._value |
81447b5b PP |
232 | |
233 | def __or__(self, other): | |
e1c6bebd | 234 | return self._value | self._extract_value(other) |
81447b5b PP |
235 | |
236 | def __ror__(self, other): | |
e1c6bebd | 237 | return self._extract_value(other) | self._value |
81447b5b PP |
238 | |
239 | def __invert__(self): | |
e1c6bebd | 240 | return ~self._value |
81447b5b | 241 | |
81447b5b | 242 | |
aae30e61 PP |
243 | class _BoolField(_IntegralField, _Field): |
244 | _NAME = 'Boolean' | |
245 | ||
246 | def __bool__(self): | |
247 | return self._value | |
248 | ||
249 | def _value_to_bool(self, value): | |
250 | if isinstance(value, _BoolField): | |
251 | value = value._value | |
252 | ||
253 | if not isinstance(value, bool): | |
254 | raise TypeError( | |
255 | "'{}' object is not a 'bool' or '_BoolField' object".format( | |
256 | value.__class__ | |
257 | ) | |
258 | ) | |
259 | ||
260 | return value | |
261 | ||
262 | @property | |
263 | def _value(self): | |
264 | return bool(native_bt.field_bool_get_value(self._ptr)) | |
265 | ||
266 | def _set_value(self, value): | |
267 | value = self._value_to_bool(value) | |
268 | native_bt.field_bool_set_value(self._ptr, value) | |
269 | ||
270 | value = property(fset=_set_value) | |
271 | ||
272 | ||
2ae9f48c | 273 | class _IntegerField(_IntegralField, _Field): |
81447b5b PP |
274 | pass |
275 | ||
276 | ||
2ae9f48c SM |
277 | class _UnsignedIntegerField(_IntegerField, _Field): |
278 | _NAME = 'Unsigned integer' | |
1eccc498 | 279 | |
81447b5b | 280 | def _value_to_int(self, value): |
25bb9bab PP |
281 | if not isinstance(value, numbers.Integral): |
282 | raise TypeError('expecting an integral number object') | |
81447b5b PP |
283 | |
284 | value = int(value) | |
2ae9f48c | 285 | utils._check_uint64(value) |
81447b5b PP |
286 | |
287 | return value | |
288 | ||
289 | @property | |
e1c6bebd | 290 | def _value(self): |
9c08c816 | 291 | return native_bt.field_integer_unsigned_get_value(self._ptr) |
81447b5b | 292 | |
2ae9f48c SM |
293 | def _set_value(self, value): |
294 | value = self._value_to_int(value) | |
9c08c816 | 295 | native_bt.field_integer_unsigned_set_value(self._ptr, value) |
2ae9f48c SM |
296 | |
297 | value = property(fset=_set_value) | |
298 | ||
299 | ||
300 | class _SignedIntegerField(_IntegerField, _Field): | |
301 | _NAME = 'Signed integer' | |
1eccc498 | 302 | |
2ae9f48c | 303 | def _value_to_int(self, value): |
25bb9bab PP |
304 | if not isinstance(value, numbers.Integral): |
305 | raise TypeError('expecting an integral number object') | |
e1c6bebd | 306 | |
2ae9f48c SM |
307 | value = int(value) |
308 | utils._check_int64(value) | |
811644b8 | 309 | |
81447b5b PP |
310 | return value |
311 | ||
2ae9f48c SM |
312 | @property |
313 | def _value(self): | |
9c08c816 | 314 | return native_bt.field_integer_signed_get_value(self._ptr) |
2ae9f48c | 315 | |
e1c6bebd | 316 | def _set_value(self, value): |
81447b5b | 317 | value = self._value_to_int(value) |
9c08c816 | 318 | native_bt.field_integer_signed_set_value(self._ptr, value) |
81447b5b | 319 | |
e1c6bebd | 320 | value = property(fset=_set_value) |
81447b5b | 321 | |
0b03f63e | 322 | |
2ae9f48c SM |
323 | class _RealField(_NumericField, numbers.Real): |
324 | _NAME = 'Real' | |
81447b5b PP |
325 | |
326 | def _value_to_float(self, value): | |
327 | if not isinstance(value, numbers.Real): | |
328 | raise TypeError("expecting a real number object") | |
329 | ||
330 | return float(value) | |
331 | ||
332 | @property | |
e1c6bebd | 333 | def _value(self): |
2ae9f48c | 334 | return native_bt.field_real_get_value(self._ptr) |
81447b5b | 335 | |
e1c6bebd | 336 | def _set_value(self, value): |
81447b5b | 337 | value = self._value_to_float(value) |
2ae9f48c | 338 | native_bt.field_real_set_value(self._ptr, value) |
81447b5b | 339 | |
e1c6bebd | 340 | value = property(fset=_set_value) |
81447b5b | 341 | |
0b03f63e | 342 | |
81447b5b | 343 | class _EnumerationField(_IntegerField): |
1eccc498 SM |
344 | def _repr(self): |
345 | return '{} ({})'.format(self._value, ', '.join(self.labels)) | |
81447b5b PP |
346 | |
347 | @property | |
1eccc498 | 348 | def labels(self): |
d24d5663 | 349 | status, labels = self._get_mapping_labels(self._ptr) |
cfbd7cf3 | 350 | utils._handle_func_status(status, "cannot get label for enumeration field") |
81447b5b | 351 | |
1eccc498 SM |
352 | assert labels is not None |
353 | return labels | |
81447b5b | 354 | |
4addd228 | 355 | |
1eccc498 SM |
356 | class _UnsignedEnumerationField(_EnumerationField, _UnsignedIntegerField): |
357 | _NAME = 'Unsigned Enumeration' | |
cfbd7cf3 | 358 | _get_mapping_labels = staticmethod( |
9c08c816 | 359 | native_bt.field_enumeration_unsigned_get_mapping_labels |
cfbd7cf3 | 360 | ) |
e1c6bebd | 361 | |
e1c6bebd | 362 | |
1eccc498 SM |
363 | class _SignedEnumerationField(_EnumerationField, _SignedIntegerField): |
364 | _NAME = 'Signed Enumeration' | |
cfbd7cf3 | 365 | _get_mapping_labels = staticmethod( |
9c08c816 | 366 | native_bt.field_enumeration_signed_get_mapping_labels |
cfbd7cf3 | 367 | ) |
81447b5b PP |
368 | |
369 | ||
370 | @functools.total_ordering | |
1eccc498 | 371 | class _StringField(_Field): |
81447b5b PP |
372 | _NAME = 'String' |
373 | ||
374 | def _value_to_str(self, value): | |
375 | if isinstance(value, self.__class__): | |
e1c6bebd | 376 | value = value._value |
81447b5b PP |
377 | |
378 | if not isinstance(value, str): | |
379 | raise TypeError("expecting a 'str' object") | |
380 | ||
381 | return value | |
382 | ||
383 | @property | |
e1c6bebd | 384 | def _value(self): |
1eccc498 | 385 | return native_bt.field_string_get_value(self._ptr) |
81447b5b | 386 | |
e1c6bebd | 387 | def _set_value(self, value): |
81447b5b | 388 | value = self._value_to_str(value) |
1eccc498 | 389 | native_bt.field_string_set_value(self._ptr, value) |
81447b5b | 390 | |
e1c6bebd JG |
391 | value = property(fset=_set_value) |
392 | ||
393 | def _spec_eq(self, other): | |
81447b5b | 394 | try: |
f11ed062 | 395 | return self._value == self._value_to_str(other) |
4c4935bf | 396 | except Exception: |
81447b5b PP |
397 | return False |
398 | ||
81447b5b | 399 | def __lt__(self, other): |
e1c6bebd | 400 | return self._value < self._value_to_str(other) |
81447b5b PP |
401 | |
402 | def __bool__(self): | |
e1c6bebd | 403 | return bool(self._value) |
81447b5b | 404 | |
12bf0d88 | 405 | def _repr(self): |
d623d2e9 JG |
406 | return repr(self._value) |
407 | ||
81447b5b | 408 | def __str__(self): |
1eccc498 | 409 | return str(self._value) |
81447b5b PP |
410 | |
411 | def __getitem__(self, index): | |
e1c6bebd | 412 | return self._value[index] |
81447b5b PP |
413 | |
414 | def __len__(self): | |
1eccc498 | 415 | return native_bt.field_string_get_length(self._ptr) |
81447b5b PP |
416 | |
417 | def __iadd__(self, value): | |
418 | value = self._value_to_str(value) | |
d24d5663 | 419 | status = native_bt.field_string_append(self._ptr, value) |
cfbd7cf3 FD |
420 | utils._handle_func_status( |
421 | status, "cannot append to string field object's value" | |
422 | ) | |
81447b5b PP |
423 | return self |
424 | ||
425 | ||
426 | class _ContainerField(_Field): | |
427 | def __bool__(self): | |
428 | return len(self) != 0 | |
429 | ||
430 | def __len__(self): | |
431 | count = self._count() | |
1eccc498 | 432 | assert count >= 0 |
81447b5b PP |
433 | return count |
434 | ||
435 | def __delitem__(self, index): | |
436 | raise NotImplementedError | |
437 | ||
438 | ||
439 | class _StructureField(_ContainerField, collections.abc.MutableMapping): | |
440 | _NAME = 'Structure' | |
441 | ||
442 | def _count(self): | |
d8e2073c | 443 | return len(self.cls) |
81447b5b | 444 | |
81447b5b | 445 | def __setitem__(self, key, value): |
236355c2 | 446 | # raises if key is somehow invalid |
81447b5b PP |
447 | field = self[key] |
448 | ||
81447b5b PP |
449 | # the field's property does the appropriate conversion or raises |
450 | # the appropriate exception | |
451 | field.value = value | |
452 | ||
81447b5b PP |
453 | def __iter__(self): |
454 | # same name iterator | |
d8e2073c | 455 | return iter(self.cls) |
81447b5b | 456 | |
e1c6bebd | 457 | def _spec_eq(self, other): |
f11ed062 PP |
458 | if not isinstance(other, collections.abc.Mapping): |
459 | return False | |
81447b5b | 460 | |
f11ed062 PP |
461 | if len(self) != len(other): |
462 | # early mismatch | |
463 | return False | |
81447b5b | 464 | |
f11ed062 PP |
465 | for self_key in self: |
466 | if self_key not in other: | |
467 | return False | |
81447b5b | 468 | |
f11ed062 PP |
469 | if self[self_key] != other[self_key]: |
470 | return False | |
e1c6bebd | 471 | |
f11ed062 | 472 | return True |
81447b5b | 473 | |
e1c6bebd | 474 | def _set_value(self, values): |
e1c6bebd JG |
475 | try: |
476 | for key, value in values.items(): | |
477 | self[key].value = value | |
1eccc498 | 478 | except Exception: |
e1c6bebd | 479 | raise |
7c54e2e7 | 480 | |
e1c6bebd | 481 | value = property(fset=_set_value) |
81447b5b | 482 | |
12bf0d88 | 483 | def _repr(self): |
ac7e2dc6 JG |
484 | items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] |
485 | return '{{{}}}'.format(', '.join(items)) | |
486 | ||
1eccc498 SM |
487 | def __getitem__(self, key): |
488 | utils._check_str(key) | |
cfbd7cf3 FD |
489 | field_ptr = native_bt.field_structure_borrow_member_field_by_name( |
490 | self._ptr, key | |
491 | ) | |
0b03f63e | 492 | |
1eccc498 SM |
493 | if field_ptr is None: |
494 | raise KeyError(key) | |
81447b5b | 495 | |
cfbd7cf3 FD |
496 | return _create_field_from_ptr( |
497 | field_ptr, self._owner_ptr, self._owner_get_ref, self._owner_put_ref | |
498 | ) | |
811644b8 | 499 | |
1eccc498 SM |
500 | def member_at_index(self, index): |
501 | utils._check_uint64(index) | |
811644b8 | 502 | |
1eccc498 SM |
503 | if index >= len(self): |
504 | raise IndexError | |
505 | ||
cfbd7cf3 FD |
506 | field_ptr = native_bt.field_structure_borrow_member_field_by_index( |
507 | self._ptr, index | |
508 | ) | |
1eccc498 | 509 | assert field_ptr is not None |
cfbd7cf3 FD |
510 | return _create_field_from_ptr( |
511 | field_ptr, self._owner_ptr, self._owner_get_ref, self._owner_put_ref | |
512 | ) | |
1eccc498 SM |
513 | |
514 | ||
cec0261d PP |
515 | class _OptionField(_Field): |
516 | _NAME = 'Option' | |
517 | ||
518 | @property | |
519 | def field(self): | |
520 | field_ptr = native_bt.field_option_borrow_field_const(self._ptr) | |
521 | ||
522 | if field_ptr is None: | |
523 | return | |
524 | ||
525 | return _create_field_from_ptr( | |
526 | field_ptr, self._owner_ptr, self._owner_get_ref, self._owner_put_ref | |
527 | ) | |
528 | ||
529 | @property | |
530 | def has_field(self): | |
531 | return self.field is not None | |
532 | ||
533 | @has_field.setter | |
534 | def has_field(self, value): | |
535 | utils._check_bool(value) | |
536 | native_bt.field_option_set_has_field(self._ptr, value) | |
537 | ||
538 | def _spec_eq(self, other): | |
539 | return _get_leaf_field(self) == other | |
540 | ||
541 | def __bool__(self): | |
542 | return self.has_field | |
543 | ||
544 | def __str__(self): | |
545 | return str(self.field) | |
546 | ||
547 | def _repr(self): | |
548 | return repr(self.field) | |
549 | ||
550 | def _set_value(self, value): | |
551 | self.has_field = True | |
552 | field = self.field | |
553 | assert field is not None | |
554 | field.value = value | |
555 | ||
556 | value = property(fset=_set_value) | |
557 | ||
558 | ||
1eccc498 SM |
559 | class _VariantField(_ContainerField, _Field): |
560 | _NAME = 'Variant' | |
81447b5b | 561 | |
2b9aa00b FD |
562 | def _count(self): |
563 | return len(self.cls) | |
564 | ||
81447b5b | 565 | @property |
1eccc498 SM |
566 | def selected_option_index(self): |
567 | return native_bt.field_variant_get_selected_option_field_index(self._ptr) | |
81447b5b | 568 | |
1eccc498 SM |
569 | @selected_option_index.setter |
570 | def selected_option_index(self, index): | |
d431b4de FD |
571 | if index < 0 or index >= len(self): |
572 | raise IndexError('{} field object index is out of range'.format(self._NAME)) | |
573 | ||
45c51519 | 574 | native_bt.field_variant_select_option_field_by_index(self._ptr, index) |
811644b8 | 575 | |
1eccc498 SM |
576 | @property |
577 | def selected_option(self): | |
5ae9f1bf SM |
578 | # TODO: Is there a way to check if the variant field has a selected_option, |
579 | # so we can raise an exception instead of hitting a pre-condition check? | |
580 | # If there is something, that check should be added to selected_option_index too. | |
1eccc498 | 581 | field_ptr = native_bt.field_variant_borrow_selected_option_field(self._ptr) |
81447b5b | 582 | |
cfbd7cf3 FD |
583 | return _create_field_from_ptr( |
584 | field_ptr, self._owner_ptr, self._owner_get_ref, self._owner_put_ref | |
585 | ) | |
81447b5b | 586 | |
e1c6bebd | 587 | def _spec_eq(self, other): |
f11ed062 | 588 | return _get_leaf_field(self) == other |
811644b8 PP |
589 | |
590 | def __bool__(self): | |
1eccc498 | 591 | raise NotImplementedError |
81447b5b | 592 | |
12bf0d88 | 593 | def __str__(self): |
1eccc498 | 594 | return str(self.selected_option) |
12bf0d88 JG |
595 | |
596 | def _repr(self): | |
1eccc498 | 597 | return repr(self.selected_option) |
e1c6bebd JG |
598 | |
599 | def _set_value(self, value): | |
1eccc498 | 600 | self.selected_option.value = value |
e1c6bebd JG |
601 | |
602 | value = property(fset=_set_value) | |
81447b5b | 603 | |
0b03f63e | 604 | |
c2ea62f5 | 605 | class _ArrayField(_ContainerField, _Field, collections.abc.MutableSequence): |
1eccc498 SM |
606 | def _get_length(self): |
607 | return native_bt.field_array_get_length(self._ptr) | |
608 | ||
609 | length = property(fget=_get_length) | |
610 | ||
81447b5b PP |
611 | def __getitem__(self, index): |
612 | if not isinstance(index, numbers.Integral): | |
cfbd7cf3 FD |
613 | raise TypeError( |
614 | "'{}' is not an integral number object: invalid index".format( | |
615 | index.__class__.__name__ | |
616 | ) | |
617 | ) | |
81447b5b PP |
618 | |
619 | index = int(index) | |
620 | ||
621 | if index < 0 or index >= len(self): | |
622 | raise IndexError('{} field object index is out of range'.format(self._NAME)) | |
623 | ||
cfbd7cf3 FD |
624 | field_ptr = native_bt.field_array_borrow_element_field_by_index( |
625 | self._ptr, index | |
626 | ) | |
627 | assert field_ptr | |
628 | return _create_field_from_ptr( | |
629 | field_ptr, self._owner_ptr, self._owner_get_ref, self._owner_put_ref | |
630 | ) | |
81447b5b PP |
631 | |
632 | def __setitem__(self, index, value): | |
81447b5b PP |
633 | # raises if index is somehow invalid |
634 | field = self[index] | |
635 | ||
636 | if not isinstance(field, (_NumericField, _StringField)): | |
637 | raise TypeError('can only set the value of a number or string field') | |
638 | ||
639 | # the field's property does the appropriate conversion or raises | |
640 | # the appropriate exception | |
641 | field.value = value | |
642 | ||
643 | def insert(self, index, value): | |
644 | raise NotImplementedError | |
645 | ||
e1c6bebd | 646 | def _spec_eq(self, other): |
f11ed062 PP |
647 | if not isinstance(other, collections.abc.Sequence): |
648 | return False | |
7c54e2e7 | 649 | |
f11ed062 PP |
650 | if len(self) != len(other): |
651 | # early mismatch | |
e1c6bebd | 652 | return False |
7c54e2e7 | 653 | |
f11ed062 PP |
654 | for self_elem, other_elem in zip(self, other): |
655 | if self_elem != other_elem: | |
656 | return False | |
657 | ||
658 | return True | |
659 | ||
12bf0d88 | 660 | def _repr(self): |
2bc21382 JG |
661 | return '[{}]'.format(', '.join([repr(v) for v in self])) |
662 | ||
81447b5b | 663 | |
1eccc498 SM |
664 | class _StaticArrayField(_ArrayField, _Field): |
665 | _NAME = 'Static array' | |
81447b5b PP |
666 | |
667 | def _count(self): | |
1eccc498 | 668 | return native_bt.field_array_get_length(self._ptr) |
81447b5b | 669 | |
e1c6bebd JG |
670 | def _set_value(self, values): |
671 | if len(self) != len(values): | |
cfbd7cf3 | 672 | raise ValueError('expected length of value and array field to match') |
e1c6bebd | 673 | |
1eccc498 SM |
674 | for index, value in enumerate(values): |
675 | if value is not None: | |
676 | self[index].value = value | |
e1c6bebd JG |
677 | |
678 | value = property(fset=_set_value) | |
679 | ||
81447b5b | 680 | |
1eccc498 SM |
681 | class _DynamicArrayField(_ArrayField, _Field): |
682 | _NAME = 'Dynamic array' | |
81447b5b PP |
683 | |
684 | def _count(self): | |
1eccc498 | 685 | return self.length |
81447b5b | 686 | |
1eccc498 SM |
687 | def _set_length(self, length): |
688 | utils._check_uint64(length) | |
9c08c816 | 689 | status = native_bt.field_array_dynamic_set_length(self._ptr, length) |
d24d5663 | 690 | utils._handle_func_status(status, "cannot set dynamic array length") |
81447b5b | 691 | |
1eccc498 | 692 | length = property(fget=_ArrayField._get_length, fset=_set_length) |
81447b5b | 693 | |
e1c6bebd | 694 | def _set_value(self, values): |
1eccc498 SM |
695 | if len(values) != self.length: |
696 | self.length = len(values) | |
e1c6bebd | 697 | |
1eccc498 SM |
698 | for index, value in enumerate(values): |
699 | if value is not None: | |
700 | self[index].value = value | |
e1c6bebd JG |
701 | |
702 | value = property(fset=_set_value) | |
81447b5b | 703 | |
0b03f63e | 704 | |
81447b5b | 705 | _TYPE_ID_TO_OBJ = { |
aae30e61 | 706 | native_bt.FIELD_CLASS_TYPE_BOOL: _BoolField, |
ead8c3d4 | 707 | native_bt.FIELD_CLASS_TYPE_BIT_ARRAY: _BitArrayField, |
2ae9f48c SM |
708 | native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerField, |
709 | native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerField, | |
710 | native_bt.FIELD_CLASS_TYPE_REAL: _RealField, | |
1eccc498 SM |
711 | native_bt.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: _UnsignedEnumerationField, |
712 | native_bt.FIELD_CLASS_TYPE_SIGNED_ENUMERATION: _SignedEnumerationField, | |
2ae9f48c SM |
713 | native_bt.FIELD_CLASS_TYPE_STRING: _StringField, |
714 | native_bt.FIELD_CLASS_TYPE_STRUCTURE: _StructureField, | |
1eccc498 SM |
715 | native_bt.FIELD_CLASS_TYPE_STATIC_ARRAY: _StaticArrayField, |
716 | native_bt.FIELD_CLASS_TYPE_DYNAMIC_ARRAY: _DynamicArrayField, | |
cec0261d | 717 | native_bt.FIELD_CLASS_TYPE_OPTION: _OptionField, |
45c51519 PP |
718 | native_bt.FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR: _VariantField, |
719 | native_bt.FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR: _VariantField, | |
720 | native_bt.FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR: _VariantField, | |
81447b5b | 721 | } |