def test_sequence_type(): """ Test sequence type. """ # With default sequence builder. checker = parsing.SequenceType(int) checker.validate((1, 2, 3)) checker.validate([1, 2, 3]) checker.validate({1, 2, 3}) checker.validate(SortedSet(int)) checker.validate(SortedSet(int, (1, 2, 3))) assert_raises(lambda: checker.validate((1, 2, 3.0)), exceptions.TypeException) assert_raises(lambda: checker.validate((1.0, 2.0, 3.0)), exceptions.TypeException) assert isinstance(checker.to_type((1, 2, 3)), list) # With SortedSet as sequence builder. checker = parsing.SequenceType(float) checker.validate((1.0, 2.0, 3.0)) checker.validate([1.0, 2.0, 3.0]) checker.validate({1.0, 2.0, 3.0}) assert_raises(lambda: checker.validate((1, 2, 3.0)), exceptions.TypeException) assert_raises(lambda: checker.validate((1.0, 2.0, 3)), exceptions.TypeException) checker = parsing.SequenceType(int, sequence_builder=SortedSet.builder(int)) initial_list = (1, 2, 7, 7, 1) checker.validate(initial_list) updated_list = checker.update(initial_list) assert isinstance(updated_list, SortedSet) and updated_list.element_type is int assert updated_list == SortedSet(int, (1, 2, 7)) assert checker.to_json(updated_list) == [1, 2, 7] assert checker.to_type([7, 2, 1, 1, 7, 1, 7]) == updated_list
def test_init_bool_and_len(): """ Test SortedSet initialization, length and conversion to boolean. """ sorted_set = SortedSet(int) assert not sorted_set sorted_set = SortedSet(int, (2, 4, 99)) assert sorted_set assert len(sorted_set) == 3
def __init__(self, key_type, val_type, kwargs=None): """ Initialize a typed SortedDict. :param key_type: expected type for keys. :param val_type: expected type for values. :param kwargs: (optional) dictionary-like object: initial values for sorted dict. """ self.__val_type = val_type self.__keys = SortedSet(key_type) self.__couples = {} if kwargs is not None: assert is_dictionary(kwargs) for key, value in kwargs.items(): self.put(key, value)
class MyJsonable(Jsonable): """ Example of class derived from Jsonable. """ __slots__ = ('field_a', 'field_b', 'field_c', 'field_d', 'field_e', 'field_f', 'field_g') model = { 'field_a': bool, 'field_b': str, 'field_c': parsing.OptionalValueType(float), 'field_d': parsing.DefaultValueType(str, 'super'), 'field_e': parsing.SequenceType(int), 'field_f': parsing.SequenceType(float, sequence_builder=SortedSet.builder(float)), 'field_g': parsing.DefaultValueType( parsing.DictType(str, int, SortedDict.builder(str, int)), {'x': -1}) } def __init__(self, **kwargs): """ Constructor """ self.field_a = None self.field_b = None self.field_c = None self.field_d = None self.field_e = None self.field_f = None self.field_g = {} super(MyJsonable, self).__init__(**kwargs)
def test_iteration(): """ Test SortedSet iteration. """ expected_sorted_values = ['cat', 'lion', 'panthera', 'serval', 'tiger'] sorted_set = SortedSet(str, ('lion', 'tiger', 'panthera', 'cat', 'serval')) computed_sorted_values = [key for key in sorted_set] assert_equals(expected_sorted_values, computed_sorted_values)
def test_builder_and_property(): """ Test SortedSet builder and property element_type. """ builder_float = SortedSet.builder(float) sorted_set = builder_float((2.5, 2.7, 2.9)) assert isinstance(sorted_set, SortedSet) and sorted_set.element_type is float
def test_index(): """ Test SortedSet method index(). """ sorted_set = SortedSet(int, (2, 5, 1, 9, 4, 5, 20, 0, 6, 17, 8, 3, 7, 0, 4)) sorted_set.remove(8) sorted_set.remove(4) index_of_2 = sorted_set.index(2) index_of_17 = sorted_set.index(17) assert index_of_2 == 2 assert sorted_set.index(4) is None assert sorted_set.index(8) is None assert index_of_17 == len(sorted_set) - 2 assert sorted_set.pop(index_of_2) == 2
def test_item_add_get_and_contains(): """ Test SortedSet methods add(), __getitem__(), and __contains__(). """ expected_values = ['cat', 'lion', 'panthera', 'serval', 'tiger'] sorted_set = SortedSet(str, ('lion', 'tiger')) # Test setter. sorted_set.add('panthera') sorted_set.add('cat') sorted_set.add('serval') # Test __contains__. assert 'lions' not in sorted_set assert all(key in sorted_set for key in expected_values) # Test getter. assert sorted_set[0] == 'cat' assert sorted_set[1] == 'lion' assert sorted_set[2] == 'panthera' assert sorted_set[3] == 'serval' assert sorted_set[4] == 'tiger' # Test add then getter. sorted_set.add('onca') assert sorted_set[1] == 'lion' assert sorted_set[2] == 'onca' assert sorted_set[3] == 'panthera'
def test_pop_and_remove(): """ Test SortedSet methods remove() and pop(). """ sorted_set = SortedSet(str, ('lion', 'tiger', 'panthera', 'cat', 'serval')) assert len(sorted_set) == 5 assert 'serval' in sorted_set sorted_set.remove('serval') assert len(sorted_set) == 4 assert 'serval' not in sorted_set assert sorted_set.remove('tiger') == 'tiger' assert len(sorted_set) == 3 assert 'tiger' not in sorted_set assert sorted_set.remove('tiger') is None assert sorted_set.remove('key not in set') is None index_of_panthera = sorted_set.index('panthera') assert index_of_panthera == 2 assert sorted_set.pop(index_of_panthera) == 'panthera' assert len(sorted_set) == 2 assert 'panthera' not in sorted_set assert 'cat' in sorted_set assert 'lion' in sorted_set
def test_equality(): """ Test SortedSet equality. """ empty_sorted_set_float = SortedSet(float) empty_sorted_set_int = SortedSet(int) another_empty_sorted_set_int = SortedSet(int) sorted_set_float_1 = SortedSet(float, (2.5, 3.3, -5.7)) sorted_set_float_2 = SortedSet(float, (2.5, 3.3, -5.7)) sorted_set_float_3 = SortedSet(float, (2.5, 3.3, 5.7)) assert empty_sorted_set_float != empty_sorted_set_int assert empty_sorted_set_int == another_empty_sorted_set_int assert sorted_set_float_1 == sorted_set_float_2 assert sorted_set_float_1 != sorted_set_float_3
def test_jsonable_parsing(): """ Test parsing for Jsonable. """ attributes = ('field_a', 'field_b', 'field_c', 'field_d', 'field_e', 'field_f', 'field_g') # Building and validating my_jsonable = MyJsonable(field_a=False, field_b='test', field_e={1}, field_f=[6.5]) for attribute_name in attributes: assert hasattr(my_jsonable, attribute_name) assert isinstance(my_jsonable.field_a, bool) assert isinstance(my_jsonable.field_b, str) assert my_jsonable.field_c is None assert isinstance(my_jsonable.field_d, str), my_jsonable.field_d assert isinstance(my_jsonable.field_e, list) assert isinstance(my_jsonable.field_f, SortedSet) assert isinstance(my_jsonable.field_g, SortedDict) assert my_jsonable.field_d == 'super' assert my_jsonable.field_e == [1] assert my_jsonable.field_f == SortedSet(float, (6.5, )) assert len(my_jsonable.field_g) == 1 and my_jsonable.field_g['x'] == -1 # Building from its json representation and validating from_json = MyJsonable.from_dict( json.loads(json.dumps(my_jsonable.to_dict()))) for attribute_name in attributes: assert hasattr(from_json, attribute_name), attribute_name assert from_json.field_a == my_jsonable.field_a assert from_json.field_b == my_jsonable.field_b assert from_json.field_c == my_jsonable.field_c assert from_json.field_d == my_jsonable.field_d assert from_json.field_e == my_jsonable.field_e assert from_json.field_f == my_jsonable.field_f assert from_json.field_g == my_jsonable.field_g
class SortedDict(): """ Dict with sorted keys. """ __slots__ = ['__val_type', '__keys', '__couples'] def __init__(self, key_type, val_type, kwargs=None): """ Initialize a typed SortedDict. :param key_type: expected type for keys. :param val_type: expected type for values. :param kwargs: (optional) dictionary-like object: initial values for sorted dict. """ self.__val_type = val_type self.__keys = SortedSet(key_type) self.__couples = {} if kwargs is not None: assert is_dictionary(kwargs) for key, value in kwargs.items(): self.put(key, value) @staticmethod def builder(key_type, val_type): """ Return a function to build sorted dicts from a dictionary-like object. Returned function expects a dictionary parameter (an object with method items()). builder_fn = SortedDict.builder(str, int) my_sorted_dict = builder_fn({'a': 1, 'b': 2}) :param key_type: expected type for keys. :param val_type: expected type for values. :return: callable """ return lambda dictionary: SortedDict(key_type, val_type, dictionary) @property def key_type(self): """ Get key type. """ return self.__keys.element_type @property def val_type(self): """ Get value type. """ return self.__val_type def __str__(self): return 'SortedDict{%s}' % ', '.join('%s:%s' % (k, self.__couples[k]) for k in self.__keys) def __bool__(self): return bool(self.__keys) def __len__(self): return len(self.__keys) def __eq__(self, other): """ Return True if self and other are equal. Note that self and other must also have same key and value types. """ assert isinstance(other, SortedDict) return (self.key_type is other.key_type and self.val_type is other.val_type and len(self) == len(other) and all(key in other and self[key] == other[key] for key in self.__keys)) def __getitem__(self, key): return self.__couples[key] def __setitem__(self, key, value): self.put(key, value) def __delitem__(self, key): self.remove(key) def __iter__(self): return self.__keys.__iter__() def __contains__(self, key): return key in self.__couples def get(self, key, default=None): """ Return value associated with key, or default value if key not found. """ return self.__couples.get(key, default) def put(self, key, value): """ Add a key with a value to the dict. """ if not isinstance(value, self.__val_type): raise TypeError('Expected value type %s, got %s' % (self.__val_type, type(value))) if key not in self.__keys: self.__keys.add(key) self.__couples[key] = value def remove(self, key): """ Pop (remove and return) value associated with given key, or None if key not found. """ if key in self.__couples: self.__keys.remove(key) return self.__couples.pop(key, None) def first_key(self): """ Get the lowest key from the dict. """ return self.__keys[0] def first_value(self): """ Get the value associated to lowest key in the dict. """ return self.__couples[self.__keys[0]] def last_key(self): """ Get the highest key from the dict. """ return self.__keys[-1] def last_value(self): """ Get the value associated to highest key in the dict. """ return self.__couples[self.__keys[-1]] def last_item(self): """ Get the item (key-value pair) for the highest key in the dict. """ return self.__keys[-1], self.__couples[self.__keys[-1]] def keys(self): """ Get an iterator to the keys in the dict. """ return iter(self.__keys) def values(self): """ Get an iterator to the values in the dict. """ return (self.__couples[k] for k in self.__keys) def reversed_values(self): """ Get an iterator to the values in the dict in reversed order or keys. """ return (self.__couples[k] for k in reversed(self.__keys)) def items(self): """ Get an iterator to the items in the dict. """ return ((k, self.__couples[k]) for k in self.__keys) def reversed_items(self): """ Get an iterator to the items in the dict in reversed order of keys. """ return ((k, self.__couples[k]) for k in reversed(self.__keys)) def sub_keys(self, key_from=None, key_to=None): """ Return list of keys between key_from and key_to (both bounds included). """ position_from, position_to = self._get_keys_interval(key_from, key_to) return self.__keys[position_from:(position_to + 1)] def sub(self, key_from=None, key_to=None): """ Return a list of values associated to keys between key_from and key_to (both bounds included). If key_from is None, lowest key in dict is used. If key_to is None, greatest key in dict is used. If key_from is not in dict, lowest key in dict greater than key_from is used. If key_to is not in dict, greatest key in dict less than key_to is used. If dict is empty, return empty list. With keys (None, None) return a copy of all values. With keys (None, key_to), return values from first to the one associated to key_to. With keys (key_from, None), return values from the one associated to key_from to the last value. :param key_from: start key :param key_to: end key :return: list: values in closed keys interval [key_from; key_to] """ position_from, position_to = self._get_keys_interval(key_from, key_to) return [ self.__couples[k] for k in self.__keys[position_from:(position_to + 1)] ] def remove_sub(self, key_from=None, key_to=None): """ Remove values associated to keys between key_from and key_to (both bounds included). See sub() doc about key_from and key_to. :param key_from: start key :param key_to: end key :return: nothing """ position_from, position_to = self._get_keys_interval(key_from, key_to) keys_to_remove = self.__keys[position_from:(position_to + 1)] for key in keys_to_remove: self.remove(key) def key_from_index(self, index): """ Return key matching given position in sorted dict, or None for invalid position. """ return self.__keys[index] if -len(self.__keys) <= index < len( self.__keys) else None def get_previous_key(self, key): """ Return greatest key lower than given key, or None if not exists. """ return self.__keys.get_previous_value(key) def get_next_key(self, key): """ Return smallest key greater then given key, or None if not exists. """ return self.__keys.get_next_value(key) def _get_keys_interval(self, key_from, key_to): """ Get a couple of internal key positions (index of key_from, index of key_to) allowing to easily retrieve values in closed interval [index of key_from; index of key_to] corresponding to Python slice [index of key_from : (index of key_to + 1)] If dict is empty, return (0, -1), so that python slice [0 : -1 + 1] corresponds to empty interval. If key_from is None, lowest key in dict is used. If key_to is None, greatest key in dict is used. If key_from is not in dict, lowest key in dict greater than key_from is used. If key_to is not in dict, greatest key in dict less than key_to is used. Thus: - With keys (None, None), we get interval of all values. - With keys (key_from, None), we get interval for values from key_from to the last key. - With keys (None, key_to), we get interval for values from the first key to key_to. :param key_from: start key :param key_to: end key :return: (int, int): couple of integers: (index of key_from, index of key_to). """ if not self: return 0, -1 if key_from is not None and key_from not in self.__couples: key_from = self.__keys.get_next_value(key_from) if key_from is None: return 0, -1 if key_to is not None and key_to not in self.__couples: key_to = self.__keys.get_previous_value(key_to) if key_to is None: return 0, -1 if key_from is None and key_to is None: key_from = self.first_key() key_to = self.last_key() elif key_from is not None and key_to is None: key_to = self.last_key() elif key_from is None and key_to is not None: key_from = self.first_key() if key_from > key_to: raise IndexError('expected key_from <= key_to (%s vs %s)' % (key_from, key_to)) position_from = self.__keys.index(key_from) position_to = self.__keys.index(key_to) assert position_from is not None and position_to is not None return position_from, position_to def clear(self): """ Remove all items from dict. """ self.__couples.clear() self.__keys.clear() def fill(self, dct): """ Add given dict to this sorted dict. """ if dct: assert is_dictionary(dct) for key, value in dct.items(): self.put(key, value) def copy(self): """ Return a copy of this sorted dict. """ return SortedDict(self.__keys.element_type, self.__val_type, self.__couples)
def test_common_utils_with_sorted_set(): """Check sorted set with is_sequence() and is_dictionary().""" assert common.is_sequence(SortedSet(int, (1, 2, 3))) assert common.is_sequence(SortedSet(int)) assert not common.is_dictionary(SortedSet(int, (1, 2, 3))) assert not common.is_dictionary(SortedSet(int))
def test_getters_around_values(): """Test SortedSet methods get_next_value() and get_previous_value().""" sorted_set = SortedSet(int, (2, 5, 1, 9, 4, 5, 20, 0, 6, 17, 8, 3, 7, 0, 4)) expected = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 17, 20) assert sorted_set assert len(sorted_set) == len(expected) assert all(expected[i] == sorted_set[i] for i in range(len(expected))) assert all(e in sorted_set for e in expected) assert sorted_set.get_next_value(0) == 1 assert sorted_set.get_next_value(5) == 6 assert sorted_set.get_next_value(9) == 17 assert sorted_set.get_next_value(-1) == 0 assert sorted_set.get_next_value(20) is None assert sorted_set.get_previous_value(0) is None assert sorted_set.get_previous_value(17) == 9 assert sorted_set.get_previous_value(20) == 17 assert sorted_set.get_previous_value(1) == 0 assert sorted_set.get_previous_value(6) == 5 assert sorted_set.get_next_value(3) == 4 assert sorted_set.get_next_value(4) == 5 assert sorted_set.get_next_value(7) == 8 assert sorted_set.get_next_value(8) == 9 assert sorted_set.get_previous_value(5) == 4 assert sorted_set.get_previous_value(4) == 3 assert sorted_set.get_previous_value(9) == 8 assert sorted_set.get_previous_value(8) == 7 sorted_set.remove(8) assert len(sorted_set) == len(expected) - 1 assert 8 not in sorted_set sorted_set.remove(4) assert len(sorted_set) == len(expected) - 2 assert 4 not in sorted_set assert sorted_set.get_next_value(3) == 5 assert sorted_set.get_next_value(4) == 5 assert sorted_set.get_next_value(7) == 9 assert sorted_set.get_next_value(8) == 9 assert sorted_set.get_previous_value(5) == 3 assert sorted_set.get_previous_value(4) == 3 assert sorted_set.get_previous_value(9) == 7 assert sorted_set.get_previous_value(8) == 7