diff --git a/tests/test_leadership.py b/tests/test_leadership.py index 688c5e3..2ca7776 100644 --- a/tests/test_leadership.py +++ b/tests/test_leadership.py @@ -709,7 +709,7 @@ def test_groups_with_leader_importation(): maconfig = OptionDescription('toto', '', [interface1]) api = Config(maconfig) api.property.read_write() - api.value.importation((('ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0',), (None, (0, 1)), (('192.168.1.1', '192.168.1.0'), ('255.255.255.255', '255.255.255.0')), ('user', ('user', 'user')))) + api.value.importation([['ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'], [None, [0, 1]], [['192.168.1.1', '192.168.1.0'], ['255.255.255.255', '255.255.255.0']], ['user', ['user', 'user']]]) api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.1', '192.168.1.0'] api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == '255.255.255.255' api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == '255.255.255.0' diff --git a/tiramisu/api.py b/tiramisu/api.py index 6662ec4..16ed2ed 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -101,12 +101,12 @@ class CommonTiramisu(TiramisuHelp): raise APIError('index must be set only with a follower option') self._length = self._subconfig.cfgimpl_get_length_leadership(self._option_bag) if index >= self._length: - raise LeadershipError(_('index "{}" is higher than the leadership length "{}" ' + raise LeadershipError(_('index "{}" is greater than the leadership length "{}" ' 'for option "{}"').format(index, self._length, option.impl_get_display_name())) - if not self._allow_optiondescription and option.impl_is_optiondescription(): - raise APIError(_('option must not be an optiondescription')) + if not self._allow_optiondescription and option.impl_is_optiondescription(): + raise APIError(_('option must not be an optiondescription')) return option @@ -118,7 +118,8 @@ class CommonTiramisuOption(CommonTiramisu): def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], - option_bag: OptionBag) -> None: + option_bag: OptionBag, + config: 'Config'=None) -> None: self._option_bag = option_bag self._name = name self._subconfig = subconfig @@ -136,7 +137,7 @@ class CommonTiramisuOption(CommonTiramisu): raise APIError(_('index must be set with the follower option "{}"').format(self._option_bag.path)) def __getattr__(self, name): - raise APIError(_('unknown method {}').format(name)) + raise APIError(_('unknown method {} in {}').format(name, self.__class__.__name__)) class _TiramisuOptionOptionDescription(CommonTiramisuOption): @@ -144,6 +145,14 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption): _allow_optiondescription = True _follower_need_index = False + def __init__(self, + name: str, + subconfig: Union[KernelConfig, SubConfig], + option_bag: OptionBag, + config: "Subconfig") -> None: + super().__init__(name, subconfig, option_bag) + self._config = config + def get(self): """Get Tiramisu option""" return self._option_bag.option @@ -195,27 +204,22 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption): only_raises=False): """Get properties for an option""" settings = self._option_bag.config_bag.context.cfgimpl_get_settings() - properties = settings.getproperties(self._option_bag, - apply_requires=False) if not only_raises: - return properties + return settings.getproperties(self._option_bag, + apply_requires=False) # do not check cache properties/permissives which are not save (unrestraint, ...) - return settings.calc_raises_properties(properties, - settings.get_context_properties(), - settings.get_context_properties()) + return settings.calc_raises_properties(self._option_bag, + apply_requires=False) def __call__(self, - path: str, + name: str, index: Optional[int]=None) -> 'TiramisuOption': """Select an option by path""" - subpath = self._option_bag.option.impl_getname() + '.' + path - subconfig, name = self._subconfig.cfgimpl_get_home_by_path(subpath, - self._option_bag.config_bag) - path = self._option_bag.path + '.' + path + path = self._option_bag.path + '.' + name return TiramisuOption(name, path, index, - subconfig, + self._config, self._option_bag.config_bag) @@ -288,18 +292,18 @@ class TiramisuOptionOption(CommonTiramisuOption): def __new__(cls, name, subconfig, - option_bag): - option = subconfig.cfgimpl_get_description().get_child(name, - option_bag.config_bag, - subconfig.cfgimpl_get_path()) - if option.impl_is_optiondescription(): + option_bag, + config): + if option_bag.option.impl_is_optiondescription(): return _TiramisuOptionOptionDescription(name=name, subconfig=subconfig, - option_bag=option_bag) + option_bag=option_bag, + config=config) else: return _TiramisuOptionOption(name=name, subconfig=subconfig, - option_bag=option_bag) + option_bag=option_bag, + config=config) class TiramisuOptionOwner(CommonTiramisuOption): @@ -309,7 +313,8 @@ class TiramisuOptionOwner(CommonTiramisuOption): def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], - option_bag: OptionBag) -> None: + option_bag: OptionBag, + config: Optional['SubConfig']) -> None: super().__init__(name, subconfig, @@ -348,7 +353,8 @@ class TiramisuOptionProperty(CommonTiramisuOption): def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], - option_bag: OptionBag) -> None: + option_bag: OptionBag, + config: Optional['SubConfig']) -> None: super().__init__(name, subconfig, option_bag) @@ -360,13 +366,10 @@ class TiramisuOptionProperty(CommonTiramisuOption): """Get properties for an option""" option = self._option_bag.option self._test_follower_index() - properties = self._option_bag.properties if not only_raises: - return properties + return self._option_bag.properties # do not check cache properties/permissives which are not save (unrestraint, ...) - return self._settings.calc_raises_properties(properties, - self._settings.get_context_properties(), - self._settings.get_context_properties()) + return self._settings.calc_raises_properties(self._option_bag) def add(self, prop): """Add new property for an option""" @@ -406,7 +409,8 @@ class TiramisuOptionPermissive(CommonTiramisuOption): def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], - option_bag: OptionBag) -> None: + option_bag: OptionBag, + config: Optional['SubConfig']) -> None: super().__init__(name, subconfig, option_bag) @@ -554,6 +558,7 @@ class _TiramisuOptionValueChoiceOption: class _TiramisuOptionValueOptionDescription: + def dict(self, flatten=False, withvalue=undefined, @@ -583,32 +588,35 @@ class TiramisuOptionValue(CommonTiramisuOption): def __new__(cls, name, subconfig, - option_bag): - if subconfig is not None: + option_bag, + config): + types = [CommonTiramisuOption] + if option_bag.option and option_bag.option.impl_is_optiondescription(): + types.append(_TiramisuOptionValueOptionDescription) + elif subconfig is not None: option = subconfig.cfgimpl_get_description().get_child(name, option_bag.config_bag, subconfig.cfgimpl_get_path()) - else: - option = None - types = [CommonTiramisuOption] - if option: - if option.impl_is_optiondescription(): - types.append(_TiramisuOptionValueOptionDescription) - else: - types.append(_TiramisuOptionValueOption) - if isinstance(option, ChoiceOption): - types.append(_TiramisuOptionValueChoiceOption) - if option.impl_is_leader(): - types.append(_TiramisuOptionValueLeader) - elif option.impl_is_follower(): - types.append(_TiramisuOptionValueFollower) + types.append(_TiramisuOptionValueOption) + if isinstance(option, ChoiceOption): + types.append(_TiramisuOptionValueChoiceOption) + if option.impl_is_leader(): + types.append(_TiramisuOptionValueLeader) + elif option.impl_is_follower(): + types.append(_TiramisuOptionValueFollower) if option_bag.config_bag.context.impl_type == 'group': types.append(_TiramisuOptionValueGroup) new_type_dict = {'_allow_optiondescription': cls._allow_optiondescription, '_follower_need_index': cls._follower_need_index} - new_type = type('TiramisuOptionValue', tuple(types), new_type_dict)(name=name, - subconfig=subconfig, - option_bag=option_bag) + if option_bag.option and option_bag.option.impl_is_optiondescription(): + new_type = type('TiramisuOptionValue', tuple(types), new_type_dict)(name=name, + subconfig=subconfig, + option_bag=option_bag, + config=config) + else: + new_type = type('TiramisuOptionValue', tuple(types), new_type_dict)(name=name, + subconfig=subconfig, + option_bag=option_bag) new_type.__doc__ = cls.__doc__ return new_type @@ -642,14 +650,31 @@ class _TiramisuOption(CommonTiramisu): self._option_bag.index = self._index self._option_bag.config_bag = self._config_bag self._tiramisu_dict = None + self._config = None if not self._registers: _registers(self._registers, 'TiramisuOption') + def _get_config(self): + if self._config is None and self._subconfig is not None: + self._config = self._subconfig.get_subconfig(self._option_bag) + return self._config + def __getattr__(self, subfunc: str) -> Any: if subfunc in self._registers: + subconfig = self._subconfig + if subconfig: + option_bag = self._option_bag + option = self._get_option() + if option.impl_is_optiondescription() and subfunc == 'option': + config = self._get_config() + else: + config = None + else: + config = None return self._registers[subfunc](self._name, - self._subconfig, - self._option_bag) + subconfig, + self._option_bag, + config) raise APIError(_('please specify a valid sub function ({})').format(subfunc)) # pragma: no cover @@ -1240,7 +1265,7 @@ class _TiramisuContextConfigReset(): """Remove all datas to current config (informations, values, properties, ...)""" # Option's values context_owner = self._config_bag.context.cfgimpl_get_values().get_context_owner() - self._config_bag.context.cfgimpl_get_values()._p_.importation((tuple(), tuple(), tuple(), tuple())) + self._config_bag.context.cfgimpl_get_values()._p_.importation(([], [], [], [])) self._config_bag.context.cfgimpl_get_values()._p_.setvalue(None, None, context_owner, diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index ffdf87d..26868da 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -254,18 +254,20 @@ def carry_out_calculation(option, if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \ option.impl_is_follower(): if args or kwargs: - raise LeadershipError(_('function "{}" with arguments "{}" and "{}" ' - 'return the list "{}" for the follower option "{}"' + raise LeadershipError(_('the "{}" function with positional arguments "{}" ' + 'and keyword arguments "{}" must not return ' + 'a list ("{}") for the follower option "{}"' '').format(callback.__name__, args, kwargs, ret, option.impl_get_display_name())) else: - raise LeadershipError(_('function "{}" return the list "{}" for the follower option "{}"' + raise LeadershipError(_('the "{}" function must not return a list ("{}") ' + 'for the follower option "{}"' '').format(callback.__name__, ret, - option.impl_getname())) + option.impl_get_display_name())) return ret diff --git a/tiramisu/config.py b/tiramisu/config.py index 0776183..d399376 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -281,7 +281,8 @@ class SubConfig(object): def getattr(self, name, - option_bag): + option_bag, + from_follower=False): """ attribute notation mechanism for accessing the value of an option :param name: attribute name @@ -310,17 +311,18 @@ class SubConfig(object): return context.getattr(soption_bag.path, soption_bag) - self.cfgimpl_get_settings().validate_properties(option_bag) + if not from_follower or option_bag.option.impl_getrequires(): + self.cfgimpl_get_settings().validate_properties(option_bag) - if option.impl_is_follower(): + if option.impl_is_follower() and not from_follower: length = self.cfgimpl_get_length_leadership(option_bag) follower_len = self.cfgimpl_get_values()._p_.get_max_length(option_bag.path) if follower_len > length: - raise LeadershipError(_('follower option "{}" has higher length "{}" than the leader ' - 'length "{}"').format(option.impl_get_display_name(), - follower_len, - length, - option_bag.index)) + raise LeadershipError(_('the follower option "{}" has greater length ({}) than the leader ' + 'length ({})').format(option.impl_get_display_name(), + follower_len, + length, + option_bag.index)) if option.impl_is_follower() and option_bag.index is None: value = [] for idx in range(length): @@ -332,7 +334,8 @@ class SubConfig(object): soption_bag.fromconsistency = option_bag.fromconsistency.copy() try: value.append(self.getattr(name, - soption_bag)) + soption_bag, + from_follower=True)) except PropertiesOptionError as err: value.append(err) else: diff --git a/tiramisu/option/broadcastoption.py b/tiramisu/option/broadcastoption.py index cbb261f..a20054f 100644 --- a/tiramisu/option/broadcastoption.py +++ b/tiramisu/option/broadcastoption.py @@ -54,7 +54,7 @@ class BroadcastOption(Option): warnings_only, context): if len(vals) != 3: - raise ConfigError(_('invalid len for vals')) + raise ConfigError(_('invalid broadcast consistency, a network and a netmask are needed')) if None in vals: return broadcast, network, netmask = vals diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 6e8a438..165cd5e 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -121,6 +121,7 @@ class OptionBag: 'config_bag', 'ori_option', # original option (for example useful for symlinkoption) 'properties', # properties of current option + 'properties_setted', 'apply_requires', # apply requires or not for this option 'fromconsistency' # history for consistency ) @@ -151,8 +152,15 @@ class OptionBag: return self.option elif key == 'apply_requires': return True + elif key == 'properties_setted': + return False raise KeyError('unknown key {} for OptionBag'.format(key)) # pragma: no cover + def __setattr__(self, key, val): + super().__setattr__(key, val) + if key == 'properties': + self.properties_setted = True + def __delattr__(self, key): if key in ['properties', 'permissives']: try: @@ -379,12 +387,12 @@ class Settings(object): # get properties and permissive methods def get_context_properties(self): - is_cached, props = self._p_.getcache(None, - None, - None, - {}, - {}, - 'context_props') + is_cached, props, validated = self._p_.getcache(None, + None, + None, + {}, + {}, + 'context_props') if not is_cached: props = self._p_.getproperties(None, self.default_properties) @@ -392,7 +400,8 @@ class Settings(object): None, props, {}, - props) + props, + True) return props def getproperties(self, @@ -410,12 +419,12 @@ class Settings(object): if apply_requires: props = config_bag.properties - is_cached, props = self._p_.getcache(path, - config_bag.expiration_time, - index, - props, - {}, - 'self_props') + is_cached, props, validated = self._p_.getcache(path, + config_bag.expiration_time, + index, + props, + {}, + 'self_props') else: is_cached = False if not is_cached: @@ -431,7 +440,8 @@ class Settings(object): index, props, props, - config_bag.properties) + config_bag.properties, + True) return props def get_context_permissives(self): @@ -744,13 +754,18 @@ class Settings(object): #____________________________________________________________ # validate properties def calc_raises_properties(self, - option_properties, - config_properties, - config_permissives): - properties = option_properties & config_properties - SPECIAL_PROPERTIES + option_bag, + apply_requires=True): + raises_properties = option_bag.config_bag.properties - SPECIAL_PROPERTIES # remove global permissive properties - if properties and ('permissive' in config_properties): - properties -= config_permissives + if raises_properties and ('permissive' in raises_properties): + raises_properties -= option_bag.config_bag.permissives + if apply_requires and option_bag.properties_setted: + option_properties = option_bag.properties + else: + option_properties = self.getproperties(option_bag, + apply_requires=apply_requires) + properties = option_properties & raises_properties # at this point an option should not remain in properties return properties @@ -764,11 +779,9 @@ class Settings(object): was present """ config_bag = option_bag.config_bag - if not config_bag.properties: # pragma: no cover + if not config_bag.properties or config_bag.properties == frozenset(['cache']): # pragma: no cover return - properties = self.calc_raises_properties(option_bag.properties, - config_bag.properties, - config_bag.permissives) + properties = self.calc_raises_properties(option_bag) if properties != frozenset(): raise PropertiesOptionError(option_bag, properties, diff --git a/tiramisu/storage/cache/dictionary.py b/tiramisu/storage/cache/dictionary.py index 6bb3bf2..37fad81 100644 --- a/tiramisu/storage/cache/dictionary.py +++ b/tiramisu/storage/cache/dictionary.py @@ -22,8 +22,8 @@ class Cache(object): def __init__(self): self._cache = {} - def _setcache(self, path, index, val, time): - self._cache.setdefault(path, {})[index] = (val, int(time)) + def _setcache(self, path, index, val, time, validated): + self._cache.setdefault(path, {})[index] = (val, int(time), validated) def _getcache(self, path, index): values = self._cache.get(path) diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index 360a273..1f9df35 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -20,6 +20,8 @@ from ...setting import undefined from ...i18n import _ from ...log import log +from copy import deepcopy + class Values(Cache): __slots__ = ('_values', @@ -30,7 +32,7 @@ class Values(Cache): """init plugin means create values storage """ #(('path1',), (index1,), (value1,), ('owner1')) - self._values = (tuple(), tuple(), tuple(), tuple()) + self._values = ([], [], [], []) self._informations = {} # should init cache too super(Values, self).__init__(storage) @@ -38,33 +40,30 @@ class Values(Cache): def commit(self): pass - def _setvalue_info(self, nb, idx, value, values, index, vidx): - lst = list(self._values[nb]) - if idx is None: - if index is None or nb == 0: - lst.append(value) - else: - lst.append((value,)) + def _setvalue_info(self, nb, idx, value, index, follower_idx=None): + lst = self._values[nb] + if index is None or nb == 0: + # not follower or path + lst[idx] = value else: - if index is None or nb == 0: - lst[idx] = value + # follower + if nb == 1 and index in lst[idx]: + follower_idx = lst[idx].index(index) + tval = list(lst[idx]) + if follower_idx is None: + tval.append(value) else: - if nb == 1: - if index in lst[idx]: - vidx = lst[idx].index(index) - else: - vidx = None - if vidx is None: - tval = list(lst[idx]) - tval.append(value) - lst[idx] = tuple(tval) - elif nb != 1: - tval = list(lst[idx]) - tval[vidx] = value - lst[idx] = tuple(tval) - lst[idx] = tuple(lst[idx]) - values.append(tuple(lst)) - return vidx + tval[follower_idx] = value + lst[idx] = tval + return follower_idx + + def _add_new_value(self, index, nb, value): + if index is None or nb == 0: + # not follower or path + self._values[nb].append(value) + else: + # follower + self._values[nb].append([value]) # value def setvalue(self, @@ -77,20 +76,20 @@ class Values(Cache): a specified value must be associated to an owner """ log.debug('setvalue %s %s %s %s %s', path, value, owner, index, id(self)) - values = [] - vidx = None + #if isinstance(value, list): + # value = value if path in self._values[0]: idx = self._values[0].index(path) + self._setvalue_info(0, idx, path, index) + follower_idx = self._setvalue_info(1, idx, index, index) + self._setvalue_info(2, idx, value, index, follower_idx) + self._setvalue_info(3, idx, owner, index, follower_idx) else: - idx = None - vidx = self._setvalue_info(0, idx, path, values, index, vidx) - vidx = self._setvalue_info(1, idx, index, values, index, vidx) - if isinstance(value, list): - value = tuple(value) - vidx = self._setvalue_info(2, idx, value, values, index, vidx) - self._setvalue_info(3, idx, owner, values, index, vidx) - self._values = tuple(values) + self._add_new_value(index, 0, path) + self._add_new_value(index, 1, index) + self._add_new_value(index, 2, value) + self._add_new_value(index, 3, owner) def hasvalue(self, path, index=None): """if path has a value @@ -114,16 +113,8 @@ class Values(Cache): path_idx = self._values[0].index(path) # get the "index" position subidx = self._values[1][path_idx].index(index) - # transform tuple to list - values = list(self._values) - values_idx = list(values[1]) - lvalues = list(values_idx[path_idx]) # reduce to one the index - lvalues[subidx] = lvalues[subidx] - 1 - # store modification - values_idx[path_idx] = tuple(lvalues) - values[1] = tuple(values_idx) - self._values = tuple(values) + self._values[1][path_idx][subidx] -= 1 def resetvalue_index(self, path, @@ -131,23 +122,16 @@ class Values(Cache): commit): log.debug('resetvalue_index %s %s %s', path, index, id(self)) def _resetvalue(nb): - values_idx = list(values[nb]) - del(values_idx[path_idx]) - values[nb] = tuple(values_idx) + del self._values[nb][path_idx] def _resetvalue_index(nb): - values_idx = list(values[nb]) - lvalues = list(values_idx[path_idx]) - del(lvalues[subidx]) - values_idx[path_idx] = tuple(lvalues) - values[nb] = tuple(values_idx) + del self._values[nb][path_idx][subidx] path_idx = self._values[0].index(path) indexes = self._values[1][path_idx] if index in indexes: subidx = indexes.index(index) - values = list(self._values) - if len(values[1][path_idx]) == 1: + if len(self._values[1][path_idx]) == 1: _resetvalue(0) _resetvalue(1) _resetvalue(2) @@ -156,7 +140,6 @@ class Values(Cache): _resetvalue_index(1) _resetvalue_index(2) _resetvalue_index(3) - self._values = tuple(values) def resetvalue(self, path, @@ -165,17 +148,13 @@ class Values(Cache): """ log.debug('resetvalue %s %s', path, id(self)) def _resetvalue(nb): - lst = list(self._values[nb]) - lst.pop(idx) - values.append(tuple(lst)) - values = [] + self._values[nb].pop(idx) if path in self._values[0]: idx = self._values[0].index(path) _resetvalue(0) _resetvalue(1) _resetvalue(2) _resetvalue(3) - self._values = tuple(values) # owner def setowner(self, @@ -186,14 +165,10 @@ class Values(Cache): """ idx = self._values[0].index(path) if index is None: - vidx = None + follower_idx = None else: - vidx = self._values[1][idx].index(index) - values = [] - self._setvalue_info(3, idx, owner, values, index, vidx) - lst = list(self._values) - lst[3] = tuple(values[0]) - self._values = tuple(lst) + follower_idx = self._values[1][idx].index(index) + self._setvalue_info(3, idx, owner, index, follower_idx) def get_max_length(self, path): @@ -293,10 +268,10 @@ class Values(Cache): self._informations = {} def exportation(self): - return self._values + return deepcopy(self._values) def importation(self, export): - self._values = export + self._values = deepcopy(export) def delete_session(session_id): diff --git a/tiramisu/storage/util.py b/tiramisu/storage/util.py index 2d990ff..8b9e286 100644 --- a/tiramisu/storage/util.py +++ b/tiramisu/storage/util.py @@ -31,22 +31,16 @@ class Cache(DictCache): self._storage = storage super().__init__() - def setcache(self, path, index, val, self_props, props): + def setcache(self, path, index, val, self_props, props, validated): """add val in cache for a specified path if follower, add index """ if 'cache' in props or 'cache' in self_props: - log.debug('setcache {} with index {} and value {} in {} ({})'.format(path, index, val, - _display_classname(self), - id(self))) - self._setcache(path, index, val, time()) - log.debug('not setcache {} with index {} and value {} and props {} and {} in {} ({})'.format(path, - index, - val, - props, - self_props, - _display_classname(self), - id(self))) + log.debug('setcache %s with index %s and value %s in %s (%s)', + path, index, val, _display_classname(self), id(self)) + self._setcache(path, index, val, time(), validated) + log.debug('not setcache %s with index %s and value %s and props %s and %s in %s (%s)', + path, index, val, props, self_props, _display_classname(self), id(self)) def getcache(self, path, @@ -55,12 +49,12 @@ class Cache(DictCache): props, self_props, type_): - no_cache = False, None + no_cache = False, None, False if 'cache' in props or type_ == 'context_props': indexed = self._getcache(path, index) if indexed is None: return no_cache - value, timestamp = indexed + value, timestamp, validated = indexed if type_ == 'context_props': # cached value is settings properties so value is props props = value @@ -77,18 +71,18 @@ class Cache(DictCache): if timestamp + expiration_time >= ntime: log.debug('getcache in cache (1) %s %s %s %s %s', path, value, _display_classname(self), id(self), index) - return True, value - #else: - # log.debug('getcache expired value for path {} < {}'.format( - # timestamp + expiration_time, ntime)) - # # if expired, remove from cache - # #self.delcache(path) + return True, value, validated + else: + log.debug('getcache expired value for path %s < %s', + timestamp + expiration_time, ntime) + # if expired, remove from cache + # self.delcache(path) else: log.debug('getcache in cache (2) %s %s %s %s %s', path, value, _display_classname(self), id(self), index) - return True, value - log.debug('getcache {} with index {} not in {} cache'.format(path, index, - _display_classname(self))) + return True, value, validated + log.debug('getcache %s with index %s not in %s cache', + path, index, _display_classname(self)) return no_cache def delcache(self, path): @@ -108,5 +102,5 @@ class Cache(DictCache): please only use it in test purpose example: {'path1': {'index1': ('value1', 'time1')}, 'path2': {'index2': ('value2', 'time2', )}} """ - log.debug('get_chached {} for {} ({})'.format(self._cache, _display_classname(self), id(self))) + log.debug('get_chached %s for %s (%s)', self._cache, _display_classname(self), id(self)) return self._get_cached() diff --git a/tiramisu/value.py b/tiramisu/value.py index 04e369c..48d16dd 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -62,32 +62,32 @@ class Values(object): """ # try to retrive value in cache setting_properties = option_bag.config_bag.properties - is_cached, value = self._p_.getcache(option_bag.path, - option_bag.config_bag.expiration_time, - option_bag.index, - setting_properties, - option_bag.properties, - 'value') - - if not is_cached: + is_cached, value, validated = self._p_.getcache(option_bag.path, + option_bag.config_bag.expiration_time, + option_bag.index, + setting_properties, + option_bag.properties, + 'value') + if not validated: # no cached value so get value value = self.getvalue(option_bag) - # validate value - opt = option_bag.option - opt.impl_validate(value, - option_bag, - check_error=True) + # validate value + option_bag.option.impl_validate(value, + option_bag, + check_error=True) + # store value in cache + validator = 'validator' in option_bag.config_bag.properties + if not is_cached or validator: + self._p_.setcache(option_bag.path, + option_bag.index, + value, + option_bag.properties, + setting_properties, + validator) if 'warnings' in setting_properties: - opt.impl_validate(value, - option_bag, - check_error=False) - # store value in cache - if not is_cached: - self._p_.setcache(option_bag.path, - option_bag.index, - value, - option_bag.properties, - setting_properties) + option_bag.option.impl_validate(value, + option_bag, + check_error=False) if isinstance(value, list): # return a copy, so value cannot be modified return value.copy() @@ -174,12 +174,12 @@ class Values(object): def _reset_cache(_value): if not 'expire' in option_bag.properties: return - is_cache, cache_value = self._p_.getcache(option_bag.path, - None, - option_bag.index, - option_bag.config_bag.properties, - option_bag.properties, - 'value') + is_cache, cache_value, validated = self._p_.getcache(option_bag.path, + None, + option_bag.index, + option_bag.config_bag.properties, + option_bag.properties, + 'value') if not is_cache or cache_value == _value: # calculation return same value as previous value, # so do not invalidate cache @@ -508,7 +508,7 @@ class Values(object): current_value = self.get_cached_value(option_bag) length = len(current_value) if index >= length: - raise IndexError(_('index "{}" is higher than the length "{}" ' + raise IndexError(_('index {} is greater than the length {} ' 'for option "{}"').format(index, length, option_bag.option.impl_get_display_name()))