This commit is contained in:
Emmanuel Garette 2019-06-12 08:45:56 +02:00
parent 4df36a5548
commit 620e8bbf2e
10 changed files with 231 additions and 219 deletions

View File

@ -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'

View File

@ -101,7 +101,7 @@ 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()))
@ -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,18 +588,15 @@ 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)
@ -606,6 +608,12 @@ class TiramisuOptionValue(CommonTiramisuOption):
types.append(_TiramisuOptionValueGroup)
new_type_dict = {'_allow_optiondescription': cls._allow_optiondescription,
'_follower_need_index': cls._follower_need_index}
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)
@ -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,

View File

@ -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

View File

@ -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,14 +311,15 @@ class SubConfig(object):
return context.getattr(soption_bag.path,
soption_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(),
raise LeadershipError(_('the follower option "{}" has greater length ({}) than the leader '
'length ({})').format(option.impl_get_display_name(),
follower_len,
length,
option_bag.index))
@ -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:

View File

@ -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

View File

@ -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,7 +387,7 @@ class Settings(object):
# get properties and permissive methods
def get_context_properties(self):
is_cached, props = self._p_.getcache(None,
is_cached, props, validated = self._p_.getcache(None,
None,
None,
{},
@ -392,7 +400,8 @@ class Settings(object):
None,
props,
{},
props)
props,
True)
return props
def getproperties(self,
@ -410,7 +419,7 @@ class Settings(object):
if apply_requires:
props = config_bag.properties
is_cached, props = self._p_.getcache(path,
is_cached, props, validated = self._p_.getcache(path,
config_bag.expiration_time,
index,
props,
@ -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,

View File

@ -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)

View File

@ -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,))
else:
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 nb == 1:
if index in lst[idx]:
vidx = lst[idx].index(index)
else:
vidx = None
if vidx is None:
# 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)
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
else:
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):

View File

@ -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()

View File

@ -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,
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 is_cached:
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.option.impl_validate(value,
option_bag,
check_error=True)
if 'warnings' in setting_properties:
opt.impl_validate(value,
option_bag,
check_error=False)
# store value in cache
if not is_cached:
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)
setting_properties,
validator)
if 'warnings' in 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,7 +174,7 @@ 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,
is_cache, cache_value, validated = self._p_.getcache(option_bag.path,
None,
option_bag.index,
option_bag.config_bag.properties,
@ -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()))