better dynoption support

This commit is contained in:
Emmanuel Garette 2018-04-09 21:37:49 +02:00
parent c3be5e82ba
commit 9ea373efdf
17 changed files with 810 additions and 924 deletions

View file

@ -1742,7 +1742,8 @@ def test_options(paths):
# if callback and default_multi: # if callback and default_multi:
# continue # continue
# for default in (False,): # for default in (False,):
# for multi in (False,): # for multi in (True,):
# print(meta, callback, consistency, require, default_multi, symlink, default, multi)
if multi is submulti and default: if multi is submulti and default:
continue continue
if multi is submulti and consistency: if multi is submulti and consistency:

View file

@ -210,7 +210,7 @@ def test_reset_cache():
api.option('u1').value.get() api.option('u1').value.get()
assert 'u1' in values._p_.get_cached() assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() assert 'u1' in settings._p_.get_cached()
c.cfgimpl_reset_cache() c.cfgimpl_reset_cache(None, None, None)
assert 'u1' not in values._p_.get_cached() assert 'u1' not in values._p_.get_cached()
assert 'u1' not in settings._p_.get_cached() assert 'u1' not in settings._p_.get_cached()
api.option('u1').value.get() api.option('u1').value.get()
@ -222,7 +222,7 @@ def test_reset_cache():
assert 'u1' in settings._p_.get_cached() assert 'u1' in settings._p_.get_cached()
assert 'u2' in values._p_.get_cached() assert 'u2' in values._p_.get_cached()
assert 'u2' in settings._p_.get_cached() assert 'u2' in settings._p_.get_cached()
c.cfgimpl_reset_cache() c.cfgimpl_reset_cache(None, None, None)
assert 'u1' not in values._p_.get_cached() assert 'u1' not in values._p_.get_cached()
assert 'u1' not in settings._p_.get_cached() assert 'u1' not in settings._p_.get_cached()
assert 'u2' not in values._p_.get_cached() assert 'u2' not in values._p_.get_cached()
@ -237,62 +237,62 @@ def test_reset_cache():
# values = c.cfgimpl_get_values() # values = c.cfgimpl_get_values()
# api.option('od1.u1').value.get() # api.option('od1.u1').value.get()
# assert 'od1.u1' in values._p_.get_cached() # assert 'od1.u1' in values._p_.get_cached()
# c.od1.cfgimpl_reset_cache() # c.od1.cfgimpl_reset_cache(None, None, None)
# assert 'od1.u1' not in values._p_.get_cached() # assert 'od1.u1' not in values._p_.get_cached()
def test_reset_cache_only_expired(): #def test_reset_cache_only_expired():
od1 = make_description() # od1 = make_description()
c = Config(od1) # c = Config(od1)
api = getapi(c) # api = getapi(c)
api.property.add('expire') # api.property.add('expire')
values = c.cfgimpl_get_values() # values = c.cfgimpl_get_values()
settings = c.cfgimpl_get_settings() # settings = c.cfgimpl_get_settings()
api.option('u1').value.get() # api.option('u1').value.get()
assert 'u1' in values._p_.get_cached() # assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() # assert 'u1' in settings._p_.get_cached()
c.cfgimpl_reset_cache(True) # c.cfgimpl_reset_cache(True)
assert 'u1' in values._p_.get_cached() # assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() # assert 'u1' in settings._p_.get_cached()
sleep(1) # sleep(1)
api.option('u1').value.get() # api.option('u1').value.get()
sleep(1) # sleep(1)
api.option('u2').value.get() # api.option('u2').value.get()
assert 'u1' in values._p_.get_cached() # assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() # assert 'u1' in settings._p_.get_cached()
assert 'u2' in values._p_.get_cached() # assert 'u2' in values._p_.get_cached()
assert 'u2' in settings._p_.get_cached() # assert 'u2' in settings._p_.get_cached()
c.cfgimpl_reset_cache(True) # c.cfgimpl_reset_cache(True)
assert 'u1' not in values._p_.get_cached() # assert 'u1' not in values._p_.get_cached()
assert 'u1' not in settings._p_.get_cached() # assert 'u1' not in settings._p_.get_cached()
assert 'u2' in values._p_.get_cached() # assert 'u2' in values._p_.get_cached()
assert 'u2' in settings._p_.get_cached() # assert 'u2' in settings._p_.get_cached()
def test_cache_not_expire(): #def test_cache_not_expire():
od1 = make_description() # od1 = make_description()
c = Config(od1) # c = Config(od1)
api = getapi(c) # api = getapi(c)
values = c.cfgimpl_get_values() # values = c.cfgimpl_get_values()
settings = c.cfgimpl_get_settings() # settings = c.cfgimpl_get_settings()
#api.property.pop('expire') # #api.property.pop('expire')
api.option('u1').value.get() # api.option('u1').value.get()
assert 'u1' in values._p_.get_cached() # assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() # assert 'u1' in settings._p_.get_cached()
c.cfgimpl_reset_cache(True) # c.cfgimpl_reset_cache(True)
assert 'u1' in values._p_.get_cached() # assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() # assert 'u1' in settings._p_.get_cached()
sleep(1) # sleep(1)
api.option('u2').value.get() # api.option('u2').value.get()
assert 'u1' in values._p_.get_cached() # assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() # assert 'u1' in settings._p_.get_cached()
assert 'u2' in values._p_.get_cached() # assert 'u2' in values._p_.get_cached()
assert 'u2' in settings._p_.get_cached() # assert 'u2' in settings._p_.get_cached()
c.cfgimpl_reset_cache(True) # c.cfgimpl_reset_cache(True)
assert 'u1' in values._p_.get_cached() # assert 'u1' in values._p_.get_cached()
assert 'u1' in settings._p_.get_cached() # assert 'u1' in settings._p_.get_cached()
assert 'u2' in values._p_.get_cached() # assert 'u2' in values._p_.get_cached()
assert 'u2' in settings._p_.get_cached() # assert 'u2' in settings._p_.get_cached()
def test_cache_not_cache(): def test_cache_not_cache():

File diff suppressed because it is too large Load diff

View file

@ -604,3 +604,23 @@ def test_wrong_index():
raises(APIError, "api.option('od.ip_admin_eth0', 0).option.get()") raises(APIError, "api.option('od.ip_admin_eth0', 0).option.get()")
assert api.option('od').option.get() assert api.option('od').option.get()
raises(APIError, "api.option('od', 0).option.get()") raises(APIError, "api.option('od', 0).option.get()")
def test_without_master_or_slave():
raises(ValueError, "MasterSlaves('ip_admin_eth0', '', [])")
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
raises(ValueError, "MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0])")
#empty optiondescription is allowed
OptionDescription('ip_admin_eth0', '', [])
def test_master_not_multi():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
raises(ValueError, "MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])")
def test_slave_not_multi():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
raises(ValueError, "MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])")

View file

@ -704,6 +704,19 @@ def test_callback_master_and_slaves_master_list():
assert api.option('val1.val2', 0).value.get() == None assert api.option('val1.val2', 0).value.get() == None
def test_callback_master_and_slaves_master_slave_list():
val1 = StrOption('val1', "", multi=True)
val2 = StrOption('val2', "", multi=True, callback=return_list)
interface1 = MasterSlaves('val1', '', [val1, val2])
#interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1])
api = getapi(Config(maconfig))
api.property.read_write()
assert api.option('val1.val1').value.get() == []
api.option('val1.val1').value.set(['val1'])
raises(SlaveError, "api.option('val1.val2', 0).value.get()")
def test_callback_master_and_slaves_slave(): def test_callback_master_and_slaves_slave():
val1 = StrOption('val1', "", multi=True) val1 = StrOption('val1', "", multi=True)
val2 = StrOption('val2', "", multi=True, callback=return_val) val2 = StrOption('val2', "", multi=True, callback=return_val)

View file

@ -429,8 +429,9 @@ class TiramisuOptionProperty(CommonTiramisuOption):
def reset(self): def reset(self):
"""reset all personalised properties""" """reset all personalised properties"""
self._get_option() self._get_option()
self.settings.reset(opt=self.config_bag.option, self.settings.reset(self.config_bag.option,
path=self.path) self.path,
self.config_bag)
class TiramisuOptionPermissive(CommonTiramisuOption): class TiramisuOptionPermissive(CommonTiramisuOption):
@ -470,12 +471,14 @@ class TiramisuOptionPermissive(CommonTiramisuOption):
opt = self._opt opt = self._opt
self.settings.setpermissive(opt=opt, self.settings.setpermissive(opt=opt,
path=path, path=path,
config_bag=self.config_bag,
permissive=permissives) permissive=permissives)
else: else:
path = self.path path = self.path
opt = self._get_option() opt = self._get_option()
self.settings.setpermissive(opt=opt, self.settings.setpermissive(opt=opt,
path=path, path=path,
config_bag=self.config_bag,
permissives=permissives) permissives=permissives)
@count @count
@ -815,7 +818,9 @@ class TiramisuContextProperty(TiramisuContext):
@count @count
def reset(self): def reset(self):
"""remove configuration properties""" """remove configuration properties"""
self.config_bag.config.cfgimpl_get_settings().reset() self.config_bag.config.cfgimpl_get_settings().reset(None,
None,
None)
@count @count
def exportation(self): def exportation(self):

View file

@ -149,7 +149,7 @@ def carry_out_calculation(option,
# if callback_params has a callback, launch several time calculate() # if callback_params has a callback, launch several time calculate()
master_slave = False master_slave = False
# multi's option should have same value for all option # multi's option should have same value for all option
if option._is_subdyn(): if option.issubdyn():
kwargs['suffix'] = option.impl_getsuffix() kwargs['suffix'] = option.impl_getsuffix()
for key, callbacks in callback_params.items(): for key, callbacks in callback_params.items():
for callbk in callbacks: for callbk in callbacks:
@ -167,7 +167,7 @@ def carry_out_calculation(option,
else: else:
# callbk is something link (opt, True|False) # callbk is something link (opt, True|False)
opt, force_permissive = callbk opt, force_permissive = callbk
if opt._is_subdyn(): if opt.issubdyn():
opt = DynSymLinkOption(opt, opt = DynSymLinkOption(opt,
option._rootpath, option._rootpath,
option.impl_getsuffix()) option.impl_getsuffix())
@ -224,18 +224,18 @@ def carry_out_calculation(option,
if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \ if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \
option.impl_is_master_slaves('slave'): option.impl_is_master_slaves('slave'):
if args or kwargs: if args or kwargs:
raise SlaveError(_('function "{}" return the list "{}" for the slave option "{}"'
'').format(callback.__name__,
ret,
option.impl_getname()))
else:
raise SlaveError(_('function "{}" with arguments "{}" and "{}" ' raise SlaveError(_('function "{}" with arguments "{}" and "{}" '
'return the list "{}" for the slave option "{}"' 'return the list "{}" for the slave option "{}"'
'').format(callback.__name__, '').format(callback.__name__,
args, args,
kwargs, kwargs,
value, ret,
option.impl_get_display_name())) option.impl_get_display_name()))
else:
raise SlaveError(_('function "{}" return the list "{}" for the slave option "{}"'
'').format(callback.__name__,
ret,
option.impl_getname()))
return ret return ret

View file

@ -83,59 +83,77 @@ class SubConfig(object):
return self._impl_length return self._impl_length
def reset_one_option_cache(self, def reset_one_option_cache(self,
desc,
values, values,
settings, settings,
resetted_opts, resetted_opts,
config_bag,
opt, opt,
path): path):
opt.reset_cache(opt, if path in resetted_opts:
path, return
resetted_opts.append(path)
for woption in opt._get_dependencies(self):
option = woption()
if option.impl_is_dynoptiondescription():
for doption in option.get_syndynoptiondescriptions(config_bag):
doption_path = doption.impl_getpath(self)
self.reset_one_option_cache(desc,
values,
settings,
resetted_opts,
config_bag,
doption,
doption_path)
elif option.issubdyn():
for doption in desc.build_dynoptions(option, config_bag):
doption_path = doption.impl_getpath(self)
self.reset_one_option_cache(desc,
values,
settings,
resetted_opts,
config_bag,
doption,
doption_path)
else:
option_path = option.impl_getpath(self)
self.reset_one_option_cache(desc,
values,
settings,
resetted_opts,
config_bag,
option,
option_path)
del option
opt.reset_cache(path,
values, values,
settings, settings,
resetted_opts) resetted_opts)
for woption in opt._get_dependencies(self):
option = woption()
option_path = option.impl_getpath(self)
if option_path in resetted_opts:
continue
self.reset_one_option_cache(values,
settings,
resetted_opts,
option,
option_path)
del option
def cfgimpl_reset_cache(self, def cfgimpl_reset_cache(self,
only_expired=False, opt,
opt=None, path,
path=None, config_bag,
resetted_opts=None): resetted_opts=None):
"""reset all settings in cache """reset all settings in cache
:param only_expired: if True reset only expired cached values
:type only_expired: boolean
""" """
if resetted_opts is None: if resetted_opts is None:
resetted_opts = [] resetted_opts = []
context = self.cfgimpl_get_context() context = self.cfgimpl_get_context()
desc = context.cfgimpl_get_description()
values = context.cfgimpl_get_values() values = context.cfgimpl_get_values()
settings = context.cfgimpl_get_settings() settings = context.cfgimpl_get_settings()
if not None in (opt, path): if not None in (opt, path):
if path not in resetted_opts: self.reset_one_option_cache(desc,
self.reset_one_option_cache(values, values,
settings, settings,
resetted_opts, resetted_opts,
opt, config_bag,
path) opt,
path)
elif only_expired:
# reset cache for expired cache value ony
datetime = int(time())
values._p_.reset_expired_cache(datetime)
settings._p_.reset_expired_cache(datetime)
else: else:
values._p_.reset_all_cache() values._p_.reset_all_cache()
settings._p_.reset_all_cache() settings._p_.reset_all_cache()
@ -862,21 +880,25 @@ class GroupConfig(_CommonConfig):
return self._impl_children return self._impl_children
def cfgimpl_reset_cache(self, def cfgimpl_reset_cache(self,
only_expired=False, opt,
opt=None, path,
path=None, config_bag,
resetted_opts=None): resetted_opts=None):
if resetted_opts is None: if resetted_opts is None:
resetted_opts = [] resetted_opts = []
if isinstance(self, MetaConfig): if isinstance(self, MetaConfig):
super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired, super(GroupConfig, self).cfgimpl_reset_cache(opt,
opt=opt, path,
path=path, config_bag=config_bag,
resetted_opts=copy(resetted_opts)) resetted_opts=copy(resetted_opts))
for child in self._impl_children: for child in self._impl_children:
child.cfgimpl_reset_cache(only_expired=only_expired, if config_bag is None:
opt=opt, nconfig_bag = config_bag
path=path, else:
nconfig_bag = config_bag.copy('nooption')
child.cfgimpl_reset_cache(opt,
path,
config_bag=nconfig_bag,
resetted_opts=copy(resetted_opts)) resetted_opts=copy(resetted_opts))
def set_value(self, def set_value(self,

View file

@ -369,10 +369,16 @@ class Base(object):
if extra is not None: if extra is not None:
_setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())])) _setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())]))
def _impl_setsubdyn(self, def _setsubdyn(self,
subdyn): subdyn):
self._subdyn = weakref.ref(subdyn) self._subdyn = weakref.ref(subdyn)
def issubdyn(self):
return getattr(self, '_subdyn', None) is not None
def getsubdyn(self):
return self._subdyn()
def impl_getrequires(self): def impl_getrequires(self):
return getattr(self, '_requires', STATIC_TUPLE) return getattr(self, '_requires', STATIC_TUPLE)
@ -477,9 +483,6 @@ class BaseOption(Base):
"to know if a callback has been defined or not" "to know if a callback has been defined or not"
return self.impl_get_callback()[0] is not None return self.impl_get_callback()[0] is not None
def _is_subdyn(self):
return getattr(self, '_subdyn', None) is not None
def _impl_valid_string(self, def _impl_valid_string(self,
value): value):
if not isinstance(value, str): if not isinstance(value, str):
@ -496,18 +499,14 @@ class BaseOption(Base):
return name return name
def reset_cache(self, def reset_cache(self,
opt,
path, path,
values, values,
settings, settings,
resetted_opts): resetted_opts):
if opt in resetted_opts:
return
settings._p_.delcache(path) settings._p_.delcache(path)
settings._pp_.delcache(path) settings._pp_.delcache(path)
if not opt.impl_is_optiondescription(): if not self.impl_is_optiondescription():
values._p_.delcache(path) values._p_.delcache(path)
resetted_opts.append(path)
def impl_is_symlinkoption(self): def impl_is_symlinkoption(self):
return False return False

View file

@ -26,6 +26,7 @@ from .optiondescription import OptionDescription
from ..setting import groups, undefined from ..setting import groups, undefined
from ..error import ConfigError from ..error import ConfigError
from ..autolib import carry_out_calculation from ..autolib import carry_out_calculation
from .syndynoptiondescription import SynDynOptionDescription
NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$') NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
@ -42,11 +43,11 @@ class DynOptionDescription(OptionDescription):
callback=None, callback=None,
callback_params=None): callback_params=None):
super(DynOptionDescription, self).__init__(name, super().__init__(name,
doc, doc,
children, children,
requires, requires,
properties) properties)
# check children + set relation to this dynoptiondescription # check children + set relation to this dynoptiondescription
for child in children: for child in children:
if isinstance(child, OptionDescription): if isinstance(child, OptionDescription):
@ -54,11 +55,11 @@ class DynOptionDescription(OptionDescription):
raise ConfigError(_('cannot set optiondescription in a ' raise ConfigError(_('cannot set optiondescription in a '
'dynoptiondescription')) 'dynoptiondescription'))
for chld in child.impl_getchildren(config_bag=undefined): for chld in child.impl_getchildren(config_bag=undefined):
chld._impl_setsubdyn(self) chld._setsubdyn(self)
if child.impl_is_symlinkoption(): if child.impl_is_symlinkoption():
raise ConfigError(_('cannot set symlinkoption in a ' raise ConfigError(_('cannot set symlinkoption in a '
'dynoptiondescription')) 'dynoptiondescription'))
child._impl_setsubdyn(self) child._setsubdyn(self)
# add callback # add callback
self.impl_set_callback(callback, self.impl_set_callback(callback,
callback_params) callback_params)
@ -91,3 +92,10 @@ class DynOptionDescription(OptionDescription):
'').format(val, '').format(val,
self.impl_get_display_name())) self.impl_get_display_name()))
return values return values
def get_syndynoptiondescriptions(self, config_bag):
subpath = self.impl_getpath(config_bag.config).rsplit('.', 1)[0]
for suffix in self._impl_get_suffixes(config_bag):
yield SynDynOptionDescription(self,
subpath,
suffix)

View file

@ -23,7 +23,7 @@ import weakref
from ..i18n import _ from ..i18n import _
from ..setting import groups, undefined, log, debug from ..setting import groups, undefined
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .option import Option from .option import Option
from ..error import SlaveError, PropertiesOptionError from ..error import SlaveError, PropertiesOptionError
@ -46,15 +46,10 @@ class MasterSlaves(OptionDescription):
properties=properties) properties=properties)
self._group_type = groups.master self._group_type = groups.master
slaves = [] slaves = []
if len(children) < 2:
raise ValueError(_('a master and a slave and mandatory in masterslaves "{}"').format(name))
master = children[0] master = children[0]
if not children:
raise ValueError(_('children is mandatory in masterslaves "{}"').format(name))
for idx, child in enumerate(children): for idx, child in enumerate(children):
if idx != 0 and child.impl_getdefault() != []:
raise ValueError(_('not allowed default value for option "{0}" '
'in master/slave object "{1}"'
'').format(child.impl_get_display_name(),
self.impl_get_display_name()))
if child.impl_is_symlinkoption(): # pragma: optional cover if child.impl_is_symlinkoption(): # pragma: optional cover
raise ValueError(_('master group "{0}" shall not have ' raise ValueError(_('master group "{0}" shall not have '
"a symlinkoption").format(self.impl_get_display_name())) "a symlinkoption").format(self.impl_get_display_name()))
@ -62,9 +57,12 @@ class MasterSlaves(OptionDescription):
raise ValueError(_('master group "{0}" shall not have ' raise ValueError(_('master group "{0}" shall not have '
'a subgroup').format(self.impl_get_display_name())) 'a subgroup').format(self.impl_get_display_name()))
if not child.impl_is_multi(): # pragma: optional cover if not child.impl_is_multi(): # pragma: optional cover
raise ValueError(_('not allowed option "{0}" ' raise ValueError(_('only multi option allowed in master group "{0}" but option '
'in group "{1}"' '"{1}" is not a multi').format(self.impl_get_display_name(),
': this option is not a multi' child.impl_get_display_name()))
if idx != 0 and child.impl_getdefault() != []:
raise ValueError(_('not allowed default value for option "{0}" '
'in master/slave object "{1}"'
'').format(child.impl_get_display_name(), '').format(child.impl_get_display_name(),
self.impl_get_display_name())) self.impl_get_display_name()))
# no empty property for save # no empty property for save
@ -144,27 +142,37 @@ class MasterSlaves(OptionDescription):
idx) idx)
def reset_cache(self, def reset_cache(self,
opt,
path, path,
values, values,
settings, settings,
resetted_opts): resetted_opts):
master = self.getmaster()
slaves = self.getslaves()
self._reset_cache(master,
slaves,
values,
settings,
resetted_opts)
def _reset_cache(self,
master,
slaves,
values,
settings,
resetted_opts):
context = values._getcontext() context = values._getcontext()
#FIXME pb avec dyn, devrait etre une option mpath = master.impl_getpath(context)
mopt = self.getmaster() master.reset_cache(mpath,
mpath = mopt.impl_getpath(context) values,
mopt.reset_cache(mopt, settings,
mpath, None)
values, for slave in slaves:
settings,
resetted_opts)
for slave in self.getslaves():
spath = slave.impl_getpath(context) spath = slave.impl_getpath(context)
slave.reset_cache(slave, slave.reset_cache(spath,
spath,
values, values,
settings, settings,
resetted_opts) None)
resetted_opts.append(spath)
def impl_validate_value(self, def impl_validate_value(self,
option, option,

View file

@ -375,8 +375,8 @@ class Option(OnlyOption):
other_opts, other_opts,
init=True, init=True,
func=None): func=None):
if self._is_subdyn(): if self.issubdyn():
dynod = self._subdyn() dynod = self.getsubdyn()
else: else:
dynod = None dynod = None
if self.impl_is_submulti(): if self.impl_is_submulti():
@ -389,11 +389,11 @@ class Option(OnlyOption):
raise ConfigError(_('cannot add consistency with submulti option')) raise ConfigError(_('cannot add consistency with submulti option'))
if not isinstance(opt, Option): if not isinstance(opt, Option):
raise ConfigError(_('consistency must be set with an option, not {}').format(opt)) raise ConfigError(_('consistency must be set with an option, not {}').format(opt))
if opt._is_subdyn(): if opt.issubdyn():
if dynod is None: if dynod is None:
raise ConfigError(_('almost one option in consistency is ' raise ConfigError(_('almost one option in consistency is '
'in a dynoptiondescription but not all')) 'in a dynoptiondescription but not all'))
subod = opt._subdyn() subod = opt.getsubdyn()
if dynod != subod: if dynod != subod:
raise ConfigError(_('option in consistency must be in same' raise ConfigError(_('option in consistency must be in same'
' dynoptiondescription')) ' dynoptiondescription'))

View file

@ -23,7 +23,7 @@ from copy import copy
from ..i18n import _ from ..i18n import _
from ..setting import ConfigBag, groups, undefined, owners from ..setting import ConfigBag, groups, undefined, owners
from .baseoption import BaseOption from .baseoption import BaseOption, OnlyOption
from .option import ALLOWED_CONST_LIST, DynSymLinkOption from .option import ALLOWED_CONST_LIST, DynSymLinkOption
from .syndynoptiondescription import SynDynOptionDescription from .syndynoptiondescription import SynDynOptionDescription
from ..error import ConfigError, ConflictError from ..error import ConfigError, ConflictError
@ -172,7 +172,7 @@ class CacheOptionDescription(BaseOption):
# problem with index # problem with index
raise ConfigError(_('a slave ({0}) cannot have ' raise ConfigError(_('a slave ({0}) cannot have '
'force_store_value property').format(subpath)) 'force_store_value property').format(subpath))
if option._is_subdyn(): if option.issubdyn():
raise ConfigError(_('a dynoption ({0}) cannot have ' raise ConfigError(_('a dynoption ({0}) cannot have '
'force_store_value property').format(subpath)) 'force_store_value property').format(subpath))
if not values._p_.hasvalue(subpath): if not values._p_.hasvalue(subpath):
@ -225,6 +225,31 @@ class CacheOptionDescription(BaseOption):
class OptionDescriptionWalk(CacheOptionDescription): class OptionDescriptionWalk(CacheOptionDescription):
__slots__ = ('_children',) __slots__ = ('_children',)
def build_dynoptions(self,
option,
config_bag):
dynopt = option.getsubdyn()
rootpath = self.impl_get_path_by_opt(dynopt)
if rootpath == '':
ori_index = 0
else:
ori_index = len(rootpath) + 1
subpaths = option.impl_getpath(config_bag.config)[ori_index:].split('.')[:-1]
if subpaths != ['']:
subpaths = [rootpath] + subpaths
else:
subpaths = [rootpath]
for suffix in dynopt._impl_get_suffixes(config_bag):
subpath = '.'.join([subp + suffix for subp in subpaths])
if isinstance(option, OnlyOption):
yield DynSymLinkOption(option,
subpath,
suffix)
else:
yield SynDynOptionDescription(option,
subpath,
suffix)
def impl_get_options_paths(self, def impl_get_options_paths(self,
bytype, bytype,
byname, byname,
@ -233,51 +258,24 @@ class OptionDescriptionWalk(CacheOptionDescription):
config_bag): config_bag):
find_results = [] find_results = []
def _rebuild_dynpath(path,
suffix,
dynopt):
found = False
spath = path.split('.')
for length in range(1, len(spath)):
subpath = '.'.join(spath[0:length])
subopt = self.impl_get_opt_by_path(subpath)
if dynopt == subopt:
found = True
break
if not found: # pragma: no cover
raise ConfigError(_('cannot find dynpath'))
subpath = subpath + suffix
for slength in range(length, len(spath)):
subpath = subpath + '.' + spath[slength] + suffix
return subpath
def _filter_by_name(path, def _filter_by_name(path,
option): option):
name = option.impl_getname() name = option.impl_getname()
if option._is_subdyn(): if option.issubdyn():
found = False found = False
if byname.startswith(name): if byname.startswith(name):
subdyn = option._subdyn() for doption in self.build_dynoptions(option, config_bag):
for suffix in subdyn._impl_get_suffixes(config_bag): if byname == doption.impl_getname():
if byname == name + suffix: dpath = doption.impl_getpath(config_bag.config)
find_results.append((dpath, doption))
found = True found = True
path = _rebuild_dynpath(path,
suffix,
subdyn)
if '.' in path:
subpath = path.rsplit('.', 1)[0]
else:
subpath = ''
option = DynSymLinkOption(option,
subpath,
suffix)
break break
if not found: if not found:
return False return False
else: else:
if not byname == name: if not byname == name:
return False return False
find_results.append((path, option)) find_results.append((path, option))
return True return True
def _filter_by_type(path, def _filter_by_type(path,
@ -287,20 +285,10 @@ class OptionDescriptionWalk(CacheOptionDescription):
#if byname is not None, check option byname in _filter_by_name #if byname is not None, check option byname in _filter_by_name
#not here #not here
if byname is None: if byname is None:
if option._is_subdyn(): if option.issubdyn():
name = option.impl_getname() for doption in self.build_dynoptions(option, config_bag):
for suffix in option._subdyn._impl_get_suffixes(config_bag): dpath = doption.impl_getname(config_bag.config)
path = _rebuild_dynpath(path, find_results.append((dpath, doption))
suffix,
option._subdyn)
if '.' in path:
subpath = path.rsplit('.', 1)[0]
else:
subpath = ''
doption = DynSymLinkOption(option,
subpath,
suffix)
find_results.append((subpath, doption))
else: else:
find_results.append((path, option)) find_results.append((path, option))
return True return True
@ -322,22 +310,12 @@ class OptionDescriptionWalk(CacheOptionDescription):
if _subpath is not None and not path.startswith(_subpath + '.'): if _subpath is not None and not path.startswith(_subpath + '.'):
continue continue
if bytype == byname is None: if bytype == byname is None:
if option._is_subdyn(): if option.issubdyn():
name = option.impl_getname() for doption in self.build_dynoptions(option, config_bag):
for suffix in option._subdyn._impl_get_suffixes(config_bag): dpath = doption.impl_getpath(config_bag.config)
path = _rebuild_dynpath(path, find_results.append((dpath, doption))
suffix,
option._subdyn)
if '.' in path:
subpath = path.rsplit('.', 1)[0]
else:
subpath = ''
doption = DynSymLinkOption(option,
subpath,
suffix)
find_results.append((path, doption))
else: else:
find_results.append((path, option)) find_results.append((dpath, option))
else: else:
if _filter(path, option) is False: if _filter(path, option) is False:
continue continue
@ -381,24 +359,23 @@ class OptionDescriptionWalk(CacheOptionDescription):
def impl_getchildren(self, def impl_getchildren(self,
config_bag, config_bag,
context=None,
dyn=True): dyn=True):
cname = None subpath = None
for child in self._impl_st_getchildren(): for child in self._impl_st_getchildren():
if dyn and child.impl_is_dynoptiondescription(): if dyn and child.impl_is_dynoptiondescription():
if context is None: if config_bag.config is None:
raise ConfigError(_('need context')) raise ConfigError(_('need context'))
if cname is None: if subpath is None:
if context.cfgimpl_get_description() == self: if config_bag.config.cfgimpl_get_description() == self:
cname = '' subpath = ''
else: else:
cname = self.impl_getpath(context) subpath = self.impl_getpath(config_bag.config)
sconfig_bag = config_bag.copy('nooption') sconfig_bag = config_bag.copy('nooption')
sconfig_bag.option = child sconfig_bag.option = child
for value in child._impl_get_suffixes(sconfig_bag): for suffix in child._impl_get_suffixes(sconfig_bag):
yield SynDynOptionDescription(child, yield SynDynOptionDescription(child,
cname, subpath,
value) suffix)
else: else:
yield child yield child

View file

@ -55,37 +55,12 @@ class SymLinkOption(OnlyOption):
def impl_getopt(self): def impl_getopt(self):
return self._opt return self._opt
#def impl_get_information(self,
# key,
# default=undefined):
# return self._opt.impl_get_information(key, default)
def impl_is_readonly(self): def impl_is_readonly(self):
return True return True
#def impl_getproperties(self):
# return self._opt.impl_getproperties()
#def impl_get_callback(self):
# return self._opt.impl_get_callback()
#def impl_has_callback(self):
# "to know if a callback has been defined or not"
# return self._opt.impl_has_callback()
#def impl_is_multi(self):
# return self._opt.impl_is_multi()
#def _is_subdyn(self):
# return getattr(self._opt, '_subdyn', None) is not None
def get_consistencies(self): def get_consistencies(self):
return () return ()
def _has_consistencies(self,
context):
return option._opt._has_consistencies(context)
class DynSymLinkOption(object): class DynSymLinkOption(object):
__slots__ = ('_rootpath', __slots__ = ('_rootpath',
@ -110,7 +85,6 @@ class DynSymLinkOption(object):
return self._opt == left._opt and \ return self._opt == left._opt and \
self._rootpath == left._rootpath and \ self._rootpath == left._rootpath and \
self._suffix == left._suffix self._suffix == left._suffix
return True
def impl_getname(self): def impl_getname(self):
return self._opt.impl_getname() + self._suffix return self._opt.impl_getname() + self._suffix
@ -126,8 +100,6 @@ class DynSymLinkOption(object):
def impl_getpath(self, def impl_getpath(self,
context): context):
if self._rootpath == '':
return self.impl_getname()
return self._rootpath + '.' + self.impl_getname() return self._rootpath + '.' + self.impl_getname()
def impl_validate(self, def impl_validate(self,

View file

@ -19,7 +19,7 @@
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from ..i18n import _ from ..i18n import _
from ..setting import undefined from ..setting import groups, undefined
from .symlinkoption import DynSymLinkOption from .symlinkoption import DynSymLinkOption
@ -32,7 +32,6 @@ class SynDynOptionDescription(object):
opt, opt,
subpath, subpath,
suffix): suffix):
self._opt = opt self._opt = opt
self._subpath = subpath self._subpath = subpath
self._suffix = suffix self._suffix = suffix
@ -68,13 +67,13 @@ class SynDynOptionDescription(object):
config_bag, config_bag,
dyn=True): dyn=True):
children = [] children = []
subpath = self.impl_getpath() subpath = self.impl_getpath(config_bag.config)
for child in self._opt.impl_getchildren(config_bag): for child in self._opt.impl_getchildren(config_bag):
yield(self._opt._impl_get_dynchild(child, yield(self._opt._impl_get_dynchild(child,
self._suffix, self._suffix,
subpath)) subpath))
def impl_getpath(self): def impl_getpath(self, context):
subpath = self._subpath subpath = self._subpath
if subpath != '': if subpath != '':
subpath += '.' subpath += '.'
@ -83,16 +82,35 @@ class SynDynOptionDescription(object):
def getmaster(self): def getmaster(self):
master = self._opt.getmaster() master = self._opt.getmaster()
return DynSymLinkOption(master, return DynSymLinkOption(master,
self.impl_getpath(), self.impl_getpath(None),
self._suffix) self._suffix)
def getslaves(self): def getslaves(self):
subpath = self.impl_getpath() subpath = self.impl_getpath(None)
for slave in self._opt.getslaves(): for slave in self._opt.getslaves():
yield DynSymLinkOption(slave, yield DynSymLinkOption(slave,
subpath, subpath,
self._suffix) self._suffix)
def reset_cache(self,
path,
values,
settings,
resetted_opts):
if self.impl_get_group_type() == groups.master:
master = self.getmaster()
slaves = self.getslaves()
self._reset_cache(master,
slaves,
values,
settings,
resetted_opts)
else:
self._opt.reset_cache(path,
values,
settings,
resetted_opts)
def pop(self, def pop(self,
*args, *args,
**kwargs): **kwargs):

View file

@ -600,15 +600,21 @@ class Settings(object):
self._p_.setproperties(path, self._p_.setproperties(path,
properties) properties)
#values too because of slave values could have a PropertiesOptionError has value #values too because of slave values could have a PropertiesOptionError has value
self._getcontext().cfgimpl_reset_cache(opt=opt, self._getcontext().cfgimpl_reset_cache(opt,
path=path) path,
config_bag)
def set_context_permissive(self, permissive): def set_context_permissive(self,
self.setpermissive(None, None, permissive) permissive):
self.setpermissive(None,
None,
None,
permissive)
def setpermissive(self, def setpermissive(self,
opt, opt,
path, path,
config_bag,
permissives): permissives):
""" """
enables us to put the permissives in the storage enables us to put the permissives in the storage
@ -631,15 +637,17 @@ class Settings(object):
raise ConfigError(_('cannot add those permissives: {0}').format( raise ConfigError(_('cannot add those permissives: {0}').format(
' '.join(forbidden_permissives))) ' '.join(forbidden_permissives)))
self._pp_.setpermissive(path, permissives) self._pp_.setpermissive(path, permissives)
self._getcontext().cfgimpl_reset_cache(opt=opt, self._getcontext().cfgimpl_reset_cache(opt,
path=path) path,
config_bag)
#____________________________________________________________ #____________________________________________________________
# reset methods # reset methods
def reset(self, def reset(self,
opt=None, opt,
path=None, path,
config_bag,
all_properties=False): all_properties=False):
if self._getcontext().cfgimpl_get_meta() is not None: if self._getcontext().cfgimpl_get_meta() is not None:
raise ConfigError(_('cannot change property with metaconfig')) raise ConfigError(_('cannot change property with metaconfig'))
@ -655,8 +663,9 @@ class Settings(object):
if opt is not None and path is None: if opt is not None and path is None:
path = opt.impl_getpath(self._getcontext()) path = opt.impl_getpath(self._getcontext())
self._p_.delproperties(path) self._p_.delproperties(path)
self._getcontext().cfgimpl_reset_cache(opt=opt, self._getcontext().cfgimpl_reset_cache(opt,
path=path) path,
config_bag)
#____________________________________________________________ #____________________________________________________________
# validate properties # validate properties

View file

@ -205,8 +205,9 @@ class Values(object):
if is_cache and cache_value == _value: if is_cache and cache_value == _value:
return return
# calculated value is a new value, so reset cache # calculated value is a new value, so reset cache
context.cfgimpl_reset_cache(opt=opt, context.cfgimpl_reset_cache(opt,
path=path) path,
config_bag)
if opt.impl_is_master_slaves('slave'): if opt.impl_is_master_slaves('slave'):
index_ = index index_ = index
@ -409,8 +410,9 @@ class Values(object):
config_bag, config_bag,
commit=True): commit=True):
self._getcontext().cfgimpl_reset_cache(opt=config_bag.option, self._getcontext().cfgimpl_reset_cache(config_bag.option,
path=path) path,
config_bag)
if isinstance(value, list): if isinstance(value, list):
# copy # copy
value = list(value) value = list(value)
@ -601,8 +603,9 @@ class Values(object):
else: else:
self._p_.resetvalue(path, self._p_.resetvalue(path,
_commit) _commit)
context.cfgimpl_reset_cache(opt=opt, context.cfgimpl_reset_cache(config_bag.option,
path=path) path,
config_bag)
def reset_slave(self, def reset_slave(self,
path, path,
@ -627,8 +630,9 @@ class Values(object):
value, value,
config_bag) config_bag)
self._p_.resetvalue_index(path, index) self._p_.resetvalue_index(path, index)
context.cfgimpl_reset_cache(opt=config_bag.option, context.cfgimpl_reset_cache(config_bag.option,
path=path) path,
config_bag)
def reset_master(self, def reset_master(self,
subconfig, subconfig,