X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=src%2Fbindings%2Fpython%2Fbt2%2Fbt2%2Fvalue.py;h=c938ff9046bba925db8b2d09b9f73f572c3aa4d4;hb=5995b304e5601bf9b97ffa661b21874bec6c0e3a;hp=4fbe799286683b2ab3f0c2bd4fb289b0c0802392;hpb=da911a377b4b4840a0a9b9d654df01fc2a8f94d5;p=babeltrace.git diff --git a/src/bindings/python/bt2/bt2/value.py b/src/bindings/python/bt2/bt2/value.py index 4fbe7992..c938ff90 100644 --- a/src/bindings/python/bt2/bt2/value.py +++ b/src/bindings/python/bt2/bt2/value.py @@ -1,55 +1,56 @@ -# The MIT License (MIT) +# SPDX-License-Identifier: MIT # # Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import collections.abc -import functools -import numbers -import math + import abc -import bt2 +import math +import numbers +import functools +import collections.abc + +from bt2 import error as bt2_error +from bt2 import utils as bt2_utils +from bt2 import object as bt2_object +from bt2 import native_bt + +def _create_from_ptr_template(ptr, object_map): + if ptr is None: + return -def _handle_status(status, obj_name): - if status >= 0: + # bt_value_null is translated to None. However, we are given a reference + # to it that we are not going to manage anymore, since we don't create a + # Python wrapper for it. Therefore put that reference immediately. + if ptr == native_bt.value_null: + _Value._put_ref(ptr) return - else: - raise RuntimeError('unexpected error') + + typeid = native_bt.value_get_type(ptr) + return object_map[typeid]._create_from_ptr(ptr) def _create_from_ptr(ptr): + return _create_from_ptr_template(ptr, _TYPE_TO_OBJ) + + +def _create_from_const_ptr(ptr): + return _create_from_ptr_template(ptr, _TYPE_TO_CONST_OBJ) + + +def _create_from_ptr_and_get_ref_template(ptr, object_map): if ptr is None or ptr == native_bt.value_null: return typeid = native_bt.value_get_type(ptr) - return _TYPE_TO_OBJ[typeid]._create_from_ptr(ptr) + return object_map[typeid]._create_from_ptr_and_get_ref(ptr) def _create_from_ptr_and_get_ref(ptr): - if ptr is None or ptr == native_bt.value_null: - return + return _create_from_ptr_and_get_ref_template(ptr, _TYPE_TO_OBJ) - typeid = native_bt.value_get_type(ptr) - return _TYPE_TO_OBJ[typeid]._create_from_ptr_and_get_ref(ptr) + +def _create_from_const_ptr_and_get_ref(ptr): + return _create_from_ptr_and_get_ref_template(ptr, _TYPE_TO_CONST_OBJ) def create_value(value): @@ -63,49 +64,60 @@ def create_value(value): if isinstance(value, bool): return BoolValue(value) - if isinstance(value, int): + if isinstance(value, numbers.Integral): return SignedIntegerValue(value) - if isinstance(value, float): + if isinstance(value, numbers.Real): return RealValue(value) if isinstance(value, str): return StringValue(value) - try: + if isinstance(value, collections.abc.Sequence): + return ArrayValue(value) + + if isinstance(value, collections.abc.Mapping): return MapValue(value) - except: - pass - try: - return ArrayValue(value) - except: - pass + raise TypeError( + "cannot create value object from '{}' object".format(value.__class__.__name__) + ) - raise TypeError("cannot create value object from '{}' object".format(value.__class__.__name__)) +class _ValueConst(bt2_object._SharedObject, metaclass=abc.ABCMeta): + @staticmethod + def _get_ref(ptr): + native_bt.value_get_ref(ptr) + + @staticmethod + def _put_ref(ptr): + native_bt.value_put_ref(ptr) -class _Value(object._SharedObject, metaclass=abc.ABCMeta): - _get_ref = staticmethod(native_bt.value_get_ref) - _put_ref = staticmethod(native_bt.value_put_ref) + _create_value_from_ptr = staticmethod(_create_from_const_ptr) + _create_value_from_ptr_and_get_ref = staticmethod( + _create_from_const_ptr_and_get_ref + ) def __ne__(self, other): return not (self == other) - def _handle_status(self, status): - _handle_status(status, self._NAME) - def _check_create_status(self, ptr): if ptr is None: - raise bt2.CreationError( - 'cannot create {} value object'.format(self._NAME.lower())) + raise bt2_error._MemoryError( + "cannot create {} value object".format(self._NAME.lower()) + ) + + +class _Value(_ValueConst): + _create_value_from_ptr = staticmethod(_create_from_ptr) + _create_value_from_ptr_and_get_ref = staticmethod(_create_from_ptr_and_get_ref) @functools.total_ordering -class _NumericValue(_Value): +class _NumericValueConst(_ValueConst): @staticmethod def _extract_value(other): - if isinstance(other, BoolValue) or isinstance(other, bool): + if isinstance(other, _BoolValueConst) or isinstance(other, bool): return bool(other) if isinstance(other, numbers.Integral): @@ -117,7 +129,9 @@ class _NumericValue(_Value): if isinstance(other, numbers.Complex): return complex(other) - raise TypeError("'{}' object is not a number object".format(other.__class__.__name__)) + raise TypeError( + "'{}' object is not a number object".format(other.__class__.__name__) + ) def __int__(self): return int(self._value) @@ -134,7 +148,7 @@ class _NumericValue(_Value): def __eq__(self, other): try: return self._value == self._extract_value(other) - except: + except Exception: return False def __rmod__(self, other): @@ -198,7 +212,11 @@ class _NumericValue(_Value): return self._extract_value(base) ** self._value -class _IntegralValue(_NumericValue, numbers.Integral): +class _NumericValue(_NumericValueConst, _Value): + pass + + +class _IntegralValueConst(_NumericValueConst, numbers.Integral): def __lshift__(self, other): return self._value << self._extract_value(other) @@ -233,12 +251,27 @@ class _IntegralValue(_NumericValue, numbers.Integral): return ~self._value -class _RealValue(_NumericValue, numbers.Real): +class _IntegralValue(_IntegralValueConst, _NumericValue): pass -class BoolValue(_IntegralValue): - _NAME = 'Boolean' +class _BoolValueConst(_IntegralValueConst): + _NAME = "Const boolean" + + def __bool__(self): + return self._value + + def __repr__(self): + return repr(self._value) + + @property + def _value(self): + value = native_bt.value_bool_get(self._ptr) + return value != 0 + + +class BoolValue(_BoolValueConst, _IntegralValue): + _NAME = "Boolean" def __init__(self, value=None): if value is None: @@ -249,33 +282,33 @@ class BoolValue(_IntegralValue): self._check_create_status(ptr) super().__init__(ptr) - def __bool__(self): - return self._value - - def __repr__(self): - return repr(self._value) - - def _value_to_bool(self, value): - if isinstance(value, BoolValue): + @classmethod + def _value_to_bool(cls, value): + if isinstance(value, _BoolValueConst): value = value._value if not isinstance(value, bool): - raise TypeError("'{}' object is not a 'bool' or 'BoolValue' object".format(value.__class__)) + raise TypeError( + "'{}' object is not a 'bool', 'BoolValue', or '_BoolValueConst' object".format( + value.__class__ + ) + ) return value - @property - def _value(self): - value = native_bt.value_bool_get(self._ptr) - return value != 0 - def _set_value(self, value): native_bt.value_bool_set(self._ptr, self._value_to_bool(value)) value = property(fset=_set_value) -class _IntegerValue(_IntegralValue): +class _IntegerValueConst(_IntegralValueConst): + @property + def _value(self): + return self._get_value(self._ptr) + + +class _IntegerValue(_IntegerValueConst, _IntegralValue): def __init__(self, value=None): if value is None: ptr = self._create_default_value() @@ -285,42 +318,57 @@ class _IntegerValue(_IntegralValue): self._check_create_status(ptr) super().__init__(ptr) - def _value_to_int(self, value): - if not isinstance(value, numbers.Real): - raise TypeError('expecting a number object') + @classmethod + def _value_to_int(cls, value): + if not isinstance(value, numbers.Integral): + raise TypeError("expecting an integral number object") value = int(value) - self._check_int_range(value) + cls._check_int_range(value) return value - @property - def _value(self): - return self._get_value(self._ptr) - def _prop_set_value(self, value): self._set_value(self._ptr, self._value_to_int(value)) value = property(fset=_prop_set_value) -class UnsignedIntegerValue(_IntegerValue): - _check_int_range = staticmethod(utils._check_uint64) - _create_default_value = staticmethod(native_bt.value_unsigned_integer_create) - _create_value = staticmethod(native_bt.value_unsigned_integer_create_init) - _set_value = staticmethod(native_bt.value_unsigned_integer_set) - _get_value = staticmethod(native_bt.value_unsigned_integer_get) +class _UnsignedIntegerValueConst(_IntegerValueConst): + _NAME = "Const unsigned integer" + _get_value = staticmethod(native_bt.value_integer_unsigned_get) + + +class UnsignedIntegerValue(_UnsignedIntegerValueConst, _IntegerValue): + _NAME = "Unsigned integer" + _check_int_range = staticmethod(bt2_utils._check_uint64) + _create_default_value = staticmethod(native_bt.value_integer_unsigned_create) + _create_value = staticmethod(native_bt.value_integer_unsigned_create_init) + _set_value = staticmethod(native_bt.value_integer_unsigned_set) + + +class _SignedIntegerValueConst(_IntegerValueConst): + _NAME = "Const signed integer" + _get_value = staticmethod(native_bt.value_integer_signed_get) + + +class SignedIntegerValue(_SignedIntegerValueConst, _IntegerValue): + _NAME = "Signed integer" + _check_int_range = staticmethod(bt2_utils._check_int64) + _create_default_value = staticmethod(native_bt.value_integer_signed_create) + _create_value = staticmethod(native_bt.value_integer_signed_create_init) + _set_value = staticmethod(native_bt.value_integer_signed_set) -class SignedIntegerValue(_IntegerValue): - _check_int_range = staticmethod(utils._check_int64) - _create_default_value = staticmethod(native_bt.value_signed_integer_create) - _create_value = staticmethod(native_bt.value_signed_integer_create_init) - _set_value = staticmethod(native_bt.value_signed_integer_set) - _get_value = staticmethod(native_bt.value_signed_integer_get) +class _RealValueConst(_NumericValueConst, numbers.Real): + _NAME = "Const real number" + @property + def _value(self): + return native_bt.value_real_get(self._ptr) -class RealValue(_RealValue): - _NAME = 'Real number' + +class RealValue(_RealValueConst, _NumericValue): + _NAME = "Real number" def __init__(self, value=None): if value is None: @@ -332,16 +380,13 @@ class RealValue(_RealValue): self._check_create_status(ptr) super().__init__(ptr) - def _value_to_float(self, value): + @classmethod + def _value_to_float(cls, value): if not isinstance(value, numbers.Real): raise TypeError("expecting a real number object") return float(value) - @property - def _value(self): - return native_bt.value_real_get(self._ptr) - def _set_value(self, value): native_bt.value_real_set(self._ptr, self._value_to_float(value)) @@ -349,39 +394,25 @@ class RealValue(_RealValue): @functools.total_ordering -class StringValue(collections.abc.Sequence, _Value): - _NAME = 'String' +class _StringValueConst(collections.abc.Sequence, _Value): + _NAME = "Const string" - def __init__(self, value=None): - if value is None: - ptr = native_bt.value_string_create() - else: - ptr = native_bt.value_string_create_init(self._value_to_str(value)) - - self._check_create_status(ptr) - super().__init__(ptr) - - def _value_to_str(self, value): - if isinstance(value, self.__class__): + @classmethod + def _value_to_str(cls, value): + if isinstance(value, _StringValueConst): value = value._value - utils._check_str(value) + bt2_utils._check_str(value) return value @property def _value(self): return native_bt.value_string_get(self._ptr) - def _set_value(self, value): - status = native_bt.value_string_set(self._ptr, self._value_to_str(value)) - self._handle_status(status) - - value = property(fset=_set_value) - def __eq__(self, other): try: return self._value == self._value_to_str(other) - except: + except Exception: return False def __lt__(self, other): @@ -402,6 +433,28 @@ class StringValue(collections.abc.Sequence, _Value): def __len__(self): return len(self._value) + def __contains__(self, item): + return self._value_to_str(item) in self._value + + +class StringValue(_StringValueConst, _Value): + _NAME = "String" + + def __init__(self, value=None): + if value is None: + ptr = native_bt.value_string_create() + else: + ptr = native_bt.value_string_create_init(self._value_to_str(value)) + + self._check_create_status(ptr) + super().__init__(ptr) + + def _set_value(self, value): + status = native_bt.value_string_set(self._ptr, self._value_to_str(value)) + bt2_utils._handle_func_status(status) + + value = property(fset=_set_value) + def __iadd__(self, value): curvalue = self._value curvalue += self._value_to_str(value) @@ -409,27 +462,22 @@ class StringValue(collections.abc.Sequence, _Value): return self -class _Container: +class _ContainerConst: def __bool__(self): return len(self) != 0 + +class _Container(_ContainerConst): def __delitem__(self, index): raise NotImplementedError -class ArrayValue(_Container, collections.abc.MutableSequence, _Value): - _NAME = 'Array' - - def __init__(self, value=None): - ptr = native_bt.value_array_create() - self._check_create_status(ptr) - super().__init__(ptr) - - # Python will raise a TypeError if there's anything wrong with - # the iterable protocol. - if value is not None: - for elem in value: - self.append(elem) +class _ArrayValueConst(_ContainerConst, collections.abc.Sequence, _ValueConst): + _NAME = "Const array" + _borrow_element_by_index = staticmethod( + native_bt.value_array_borrow_element_by_index_const + ) + _is_const = True def __eq__(self, other): if not isinstance(other, collections.abc.Sequence): @@ -446,25 +494,50 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): return True def __len__(self): - size = native_bt.value_array_get_size(self._ptr) - assert(size >= 0) + size = native_bt.value_array_get_length(self._ptr) + assert size >= 0 return size def _check_index(self, index): # TODO: support slices also if not isinstance(index, numbers.Integral): - raise TypeError("'{}' object is not an integral number object: invalid index".format(index.__class__.__name__)) + raise TypeError( + "'{}' object is not an integral number object: invalid index".format( + index.__class__.__name__ + ) + ) index = int(index) if index < 0 or index >= len(self): - raise IndexError('array value object index is out of range') + raise IndexError("array value object index is out of range") def __getitem__(self, index): self._check_index(index) - ptr = native_bt.value_array_borrow_element_by_index(self._ptr, index) - assert(ptr) - return _create_from_ptr_and_get_ref(ptr) + ptr = self._borrow_element_by_index(self._ptr, index) + assert ptr + return self._create_value_from_ptr_and_get_ref(ptr) + + def __repr__(self): + return "[{}]".format(", ".join([repr(v) for v in self])) + + +class ArrayValue(_ArrayValueConst, _Container, collections.abc.MutableSequence, _Value): + _NAME = "Array" + _borrow_element_by_index = staticmethod( + native_bt.value_array_borrow_element_by_index + ) + + def __init__(self, value=None): + ptr = native_bt.value_array_create() + self._check_create_status(ptr) + super().__init__(ptr) + + # Python will raise a TypeError if there's anything wrong with + # the iterable protocol. + if value is not None: + for elem in value: + self.append(elem) def __setitem__(self, index, value): self._check_index(index) @@ -475,9 +548,8 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): else: ptr = value._ptr - status = native_bt.value_array_set_element_by_index( - self._ptr, index, ptr) - self._handle_status(status) + status = native_bt.value_array_set_element_by_index(self._ptr, index, ptr) + bt2_utils._handle_func_status(status) def append(self, value): value = create_value(value) @@ -488,7 +560,7 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): ptr = value._ptr status = native_bt.value_array_append_element(self._ptr, ptr) - self._handle_status(status) + bt2_utils._handle_func_status(status) def __iadd__(self, iterable): # Python will raise a TypeError if there's anything wrong with @@ -498,9 +570,6 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): return self - def __repr__(self): - return '[{}]'.format(', '.join([repr(v) for v in self])) - def insert(self, value): raise NotImplementedError @@ -512,7 +581,7 @@ class _MapValueKeyIterator(collections.abc.Iterator): keys_ptr = native_bt.value_map_get_keys(map_obj._ptr) if keys_ptr is None: - raise RuntimeError('unexpected error: cannot get map value object keys') + raise RuntimeError("unexpected error: cannot get map value object keys") self._keys = _create_from_ptr(keys_ptr) @@ -525,19 +594,9 @@ class _MapValueKeyIterator(collections.abc.Iterator): return str(key) -class MapValue(_Container, collections.abc.MutableMapping, _Value): - _NAME = 'Map' - - def __init__(self, value=None): - ptr = native_bt.value_map_create() - self._check_create_status(ptr) - super().__init__(ptr) - - # Python will raise a TypeError if there's anything wrong with - # the iterable/mapping protocol. - if value is not None: - for key, elem in value.items(): - self[key] = elem +class _MapValueConst(_ContainerConst, collections.abc.Mapping, _ValueConst): + _NAME = "Const map" + _borrow_entry_value_ptr = staticmethod(native_bt.value_map_borrow_entry_value_const) def __ne__(self, other): return _Value.__ne__(self, other) @@ -561,7 +620,7 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): def __len__(self): size = native_bt.value_map_get_size(self._ptr) - assert(size >= 0) + assert size >= 0 return size def __contains__(self, key): @@ -569,7 +628,7 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): return native_bt.value_map_has_entry(self._ptr, key) def _check_key_type(self, key): - utils._check_str(key) + bt2_utils._check_str(key) def _check_key(self, key): if key not in self: @@ -577,13 +636,33 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): def __getitem__(self, key): self._check_key(key) - ptr = native_bt.value_map_borrow_entry_value(self._ptr, key) - assert(ptr) - return _create_from_ptr_and_get_ref(ptr) + ptr = self._borrow_entry_value_ptr(self._ptr, key) + assert ptr + return self._create_value_from_ptr_and_get_ref(ptr) def __iter__(self): return _MapValueKeyIterator(self) + def __repr__(self): + items = ["{}: {}".format(repr(k), repr(v)) for k, v in self.items()] + return "{{{}}}".format(", ".join(items)) + + +class MapValue(_MapValueConst, _Container, collections.abc.MutableMapping, _Value): + _NAME = "Map" + _borrow_entry_value_ptr = staticmethod(native_bt.value_map_borrow_entry_value) + + def __init__(self, value=None): + ptr = native_bt.value_map_create() + self._check_create_status(ptr) + super().__init__(ptr) + + # Python will raise a TypeError if there's anything wrong with + # the iterable/mapping protocol. + if value is not None: + for key, elem in value.items(): + self[key] = elem + def __setitem__(self, key, value): self._check_key_type(key) value = create_value(value) @@ -594,11 +673,7 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): ptr = value._ptr status = native_bt.value_map_insert_entry(self._ptr, key, ptr) - self._handle_status(status) - - def __repr__(self): - items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] - return '{{{}}}'.format(', '.join(items)) + bt2_utils._handle_func_status(status) _TYPE_TO_OBJ = { @@ -610,3 +685,13 @@ _TYPE_TO_OBJ = { native_bt.VALUE_TYPE_ARRAY: ArrayValue, native_bt.VALUE_TYPE_MAP: MapValue, } + +_TYPE_TO_CONST_OBJ = { + native_bt.VALUE_TYPE_BOOL: _BoolValueConst, + native_bt.VALUE_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerValueConst, + native_bt.VALUE_TYPE_SIGNED_INTEGER: _SignedIntegerValueConst, + native_bt.VALUE_TYPE_REAL: _RealValueConst, + native_bt.VALUE_TYPE_STRING: _StringValueConst, + native_bt.VALUE_TYPE_ARRAY: _ArrayValueConst, + native_bt.VALUE_TYPE_MAP: _MapValueConst, +}