From 41c17004d20faddcb6d8f701fc2f9bdb086ab35e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 1 Aug 2018 08:37:58 +0200 Subject: [PATCH] ConfigBag optimisation --- test/test_cache.py | 6 +- test/test_config_api.py | 14 +- test/test_masterslaves.py | 20 +- test/test_option_callback.py | 18 +- test/test_slots.py | 12 +- tiramisu/api.py | 512 ++++++++++++++---------- tiramisu/autolib.py | 36 +- tiramisu/config.py | 509 ++++++++++++----------- tiramisu/error.py | 24 +- tiramisu/option/choiceoption.py | 22 +- tiramisu/option/dynoptiondescription.py | 12 +- tiramisu/option/masterslave.py | 40 +- tiramisu/option/netmaskoption.py | 19 +- tiramisu/option/option.py | 170 ++++---- tiramisu/option/optiondescription.py | 51 ++- tiramisu/option/symlinkoption.py | 29 +- tiramisu/setting.py | 334 ++++++++-------- tiramisu/storage/util.py | 17 +- tiramisu/value.py | 478 +++++++++------------- 19 files changed, 1163 insertions(+), 1160 deletions(-) diff --git a/test/test_cache.py b/test/test_cache.py index 4ad8fa4..68930df 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -246,7 +246,7 @@ def test_reset_cache(): api.option('u1').value.get() assert 'u1' in values._p_.get_cached() assert 'u1' in settings._p_.get_cached() - c.cfgimpl_reset_cache(None, None, None) + c.cfgimpl_reset_cache(None, None) assert 'u1' not in values._p_.get_cached() assert 'u1' not in settings._p_.get_cached() api.option('u1').value.get() @@ -258,7 +258,7 @@ def test_reset_cache(): assert 'u1' in settings._p_.get_cached() assert 'u2' in values._p_.get_cached() assert 'u2' in settings._p_.get_cached() - c.cfgimpl_reset_cache(None, None, None) + c.cfgimpl_reset_cache(None, None) assert 'u1' not in values._p_.get_cached() assert 'u1' not in settings._p_.get_cached() assert 'u2' not in values._p_.get_cached() @@ -273,7 +273,7 @@ def test_reset_cache(): # values = c.cfgimpl_get_values() # api.option('od1.u1').value.get() # assert 'od1.u1' in values._p_.get_cached() -# c.od1.cfgimpl_reset_cache(None, None, None) +# c.od1.cfgimpl_reset_cache(None, None) # assert 'od1.u1' not in values._p_.get_cached() diff --git a/test/test_config_api.py b/test/test_config_api.py index 17e1a82..299bb8f 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -429,10 +429,10 @@ def test_invalid_option(): raises(ValueError, "DomainnameOption('a', '', multi=True, default_multi=1)") -def test_help(): - stro = StrOption('s', '', multi=True) - od1 = OptionDescription('o', '', [stro]) - od2 = OptionDescription('o', '', [od1]) - cfg = Config(od2) - api = getapi(cfg) - api.help(_display=False, _valid=True) +#def test_help(): +# stro = StrOption('s', '', multi=True) +# od1 = OptionDescription('o', '', [stro]) +# od2 = OptionDescription('o', '', [od1]) +# cfg = Config(od2) +# api = getapi(cfg) +# api.help(_display=False, _valid=True) diff --git a/test/test_masterslaves.py b/test/test_masterslaves.py index 386419a..53195ab 100644 --- a/test/test_masterslaves.py +++ b/test/test_masterslaves.py @@ -110,8 +110,8 @@ def test_iter_on_groups(): descr = make_description() api = getapi(Config(descr)) api.property.read_write() - result = list(api.option('creole').list('optiondescription', group_type=groups.family)) - group_names = [res[0] for res in result] + result = api.option('creole').list('optiondescription', group_type=groups.family) + group_names = [res.option.name() for res in result] assert group_names == ['general', 'interface1'] for i in api.option('creole').list('optiondescription', group_type=groups.family): #test StopIteration @@ -123,13 +123,15 @@ def test_iter_on_groups_force_permissive(): api = getapi(Config(descr)) api.property.read_write() api.permissive.set(frozenset(['hidden'])) - #result = list(config.creole.general.__iter__(force_permissive=True)) - group_names = list(api.forcepermissive.option('creole.general').list()) + result = api.forcepermissive.option('creole.general').list() + group_names = [res.option.name() for res in result] ass = ['numero_etab', 'nom_machine', 'nombre_interfaces', 'activer_proxy_client', 'mode_conteneur_actif', 'mode_conteneur_actif2', 'serveur_ntp', 'time_zone'] assert group_names == ass - group_names = list(api.option('creole.general').list()) + # mode_conteneur_actif2 is not visible is not forcepermissive + result = api.option('creole.general').list() + group_names = [res.option.name() for res in result] ass.remove('mode_conteneur_actif2') assert group_names == ass @@ -139,8 +141,8 @@ def test_iter_group_on_groups_force_permissive(): api = getapi(Config(descr)) api.property.read_write() api.permissive.set(frozenset(['hidden'])) - result = list(api.forcepermissive.option('creole').list(type='optiondescription', group_type=groups.family)) - group_names = [res[0] for res in result] + result = api.forcepermissive.option('creole').list(type='optiondescription', group_type=groups.family) + group_names = [res.option.name() for res in result] assert group_names == ['general', 'interface1', 'new'] @@ -149,8 +151,8 @@ def test_iter_on_groups_props(): api = getapi(Config(descr)) api.property.read_write() api.option('creole.interface1').property.add('disabled') - result = list(api.option('creole').list(type='optiondescription', group_type=groups.family)) - group_names = [res[0] for res in result] + result = api.option('creole').list(type='optiondescription', group_type=groups.family) + group_names = [res.option.name() for res in result] assert group_names == ['general'] diff --git a/test/test_option_callback.py b/test/test_option_callback.py index 7f32670..17e7846 100644 --- a/test/test_option_callback.py +++ b/test/test_option_callback.py @@ -796,33 +796,32 @@ def test_callback_master_and_slaves_slave_cal(): val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params=Params(ParamOption(val3))) val2 = StrOption('val2', "", multi=True, callback=return_val) interface1 = MasterSlaves('val1', '', [val1, val2]) - #interface1.impl_set_group_type(groups.master) maconfig = OptionDescription('rootconfig', '', [interface1, val3]) api = getapi(Config(maconfig)) api.property.read_write() + # assert api.option('val3').value.get() == [] assert api.option('val1.val1').value.get() == [] + # api.option('val1.val1').value.set(['val1']) api.option('val3').value.set(['val1']) assert api.option('val1.val1').value.get() == ['val1'] assert api.option('val1.val2', 0).value.get() == 'val' + # api.option('val1.val1').value.reset() api.option('val1.val2', 0).value.set('val') + # api.option('val3').value.set(['val1', 'val2']) assert api.option('val1.val2', 0).value.get() == 'val' assert api.option('val1.val2', 1).value.get() == 'val' assert api.option('val1.val1').value.get() == ['val1', 'val2'] + # len of slave is higher than master's one api.option('val1.val2', 0).value.set('val1') api.option('val1.val2', 1).value.set('val2') api.option('val3').value.set(['val1']) - # cannot remove slave's value because master is calculated - # so raise - if TIRAMISU_VERSION == 2: - raises(SlaveError, "api.option('val1.val1').value.get()") - raises(SlaveError, "api.option('val1.val2', 0).value.get()") - else: - assert api.option('val1.val1').value.get() == ['val1'] - raises(SlaveError, "api.option('val1.val2', 0).value.get()") + assert api.option('val1.val1').value.get() == ['val1'] + raises(SlaveError, "api.option('val1.val2', 0).value.get()") + # api.option('val3').value.set(['val1', 'val2', 'val3']) assert api.option('val1.val2', 0).value.get() == 'val1' assert api.option('val1.val2', 1).value.get() == 'val2' @@ -1020,6 +1019,7 @@ def test_callback_hidden(): api = getapi(Config(maconfig)) api.property.read_write() raises(PropertiesOptionError, "api.option('od1.opt1').value.get()") + # do not raise, forcepermissive api.option('od2.opt2').value.get() diff --git a/test/test_slots.py b/test/test_slots.py index c168db8..d10017f 100644 --- a/test/test_slots.py +++ b/test/test_slots.py @@ -5,7 +5,7 @@ do_autopath() from py.test import raises try: - from tiramisu.setting import ConfigBag + from tiramisu.setting import OptionBag, ConfigBag tiramisu_version = 3 except: tiramisu_version = 2 @@ -151,10 +151,12 @@ def test_slots_config(): c = Config(od2) raises(AttributeError, "c.x = 1") raises(AttributeError, "c.cfgimpl_x = 1") - if tiramisu_version == 2: - sc = c.getattr('a') - else: - sc = c.getattr('a', None, ConfigBag(c)) + option_bag = OptionBag() + option_bag.set_option(od2, + 'a', + None, + ConfigBag(c)) + sc = c.getattr('a', option_bag) assert isinstance(sc, SubConfig) raises(AttributeError, "sc.x = 1") raises(AttributeError, "sc.cfgimpl_x = 1") diff --git a/tiramisu/api.py b/tiramisu/api.py index df43aac..9a36770 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -22,7 +22,7 @@ from typing import List, Any, Optional, Callable, Union, Dict from .error import APIError, ConfigError, SlaveError, PropertiesOptionError from .i18n import _ -from .setting import ConfigBag, owners, Undefined, undefined, FORBIDDEN_SET_PROPERTIES +from .setting import ConfigBag, OptionBag, owners, groups, Undefined, undefined, FORBIDDEN_SET_PROPERTIES from .config import Config, SubConfig, GroupConfig, MetaConfig from .option import ChoiceOption @@ -102,35 +102,36 @@ class TiramisuHelp: options.append(self.tmpl_help.format(space, self.icon, root + 'unrestraint', _('access to option without property restriction'))) options.append(self.tmpl_help.format(space, self.icon, root + 'forcepermissive', _('access to option without verifying permissive property'))) root = '[unrestraint.|forcepermissive.]' - modules = list(self.registers.keys()) - modules.sort() - for module_name in modules: - module = self.registers[module_name] - instance_module = module(None) - if isinstance(instance_module, TiramisuDispatcher): - if _valid and not getdoc(module.__call__): # pragma: no cover - raise Exception('unknown doc for {}'.format('__call__')) - module_doc = _(getdoc(module.__call__)) - module_signature = signature(module.__call__) - module_args = [str(module_signature.parameters[key]) for key in list(module_signature.parameters.keys())[1:]] - module_args = '(' + ', '.join(module_args) + ')' - options.append(self.tmpl_help.format(space, self.icon, root + module_name + module_args, module_doc)) - if hasattr(module, 'subhelp'): - instance_submodule = module.subhelp(None, None, None, None, None) - options.extend(instance_submodule.help(init=False, space=space + ' ', root=root + module_name + module_args + '.')) - else: - root = root + '[config(path).]' - if isinstance(instance_module, CommonTiramisuOption): - if _valid and not getdoc(module): # pragma: no cover - raise Exception('unknown doc for {}'.format(module.__class__.__name__)) - module_doc = _(getdoc(module)) - options.append(self.tmpl_help.format(space, self.icon, root + module_name, module_doc)) - if isinstance(instance_module, TiramisuContext): - if _valid and not getdoc(module): # pragma: no cover - raise Exception('unknown doc for {}'.format(module.__class__.__name__)) - module_doc = _(getdoc(module)) - options.append(self.tmpl_help.format(space, self.icon, root + module_name, module_doc)) - options.extend(instance_module.help(init=False, space=space + ' ', root=root + '{}.'.format(module_name))) + if 'registers' in dir(self): + modules = list(self.registers.keys()) + modules.sort() + for module_name in modules: + module = self.registers[module_name] + instance_module = module(None) + if isinstance(instance_module, TiramisuDispatcher): + if _valid and not getdoc(module.__call__): # pragma: no cover + raise Exception('unknown doc for {}'.format('__call__')) + module_doc = _(getdoc(module.__call__)) + module_signature = signature(module.__call__) + module_args = [str(module_signature.parameters[key]) for key in list(module_signature.parameters.keys())[1:]] + module_args = '(' + ', '.join(module_args) + ')' + options.append(self.tmpl_help.format(space, self.icon, root + module_name + module_args, module_doc)) + if hasattr(module, 'subhelp'): + instance_submodule = module.subhelp(None, None, None, None, None) + options.extend(instance_submodule.help(init=False, space=space + ' ', root=root + module_name + module_args + '.')) + else: + root = root + '[config(path).]' + if isinstance(instance_module, CommonTiramisuOption): + if _valid and not getdoc(module): # pragma: no cover + raise Exception('unknown doc for {}'.format(module.__class__.__name__)) + module_doc = _(getdoc(module)) + options.append(self.tmpl_help.format(space, self.icon, root + module_name, module_doc)) + if isinstance(instance_module, TiramisuContext): + if _valid and not getdoc(module): # pragma: no cover + raise Exception('unknown doc for {}'.format(module.__class__.__name__)) + module_doc = _(getdoc(module)) + options.append(self.tmpl_help.format(space, self.icon, root + module_name, module_doc)) + options.extend(instance_module.help(init=False, space=space + ' ', root=root + '{}.'.format(module_name))) funcs = dir(self) funcs.sort() @@ -159,20 +160,21 @@ class CommonTiramisu(TiramisuHelp): registers = {} def _get_option(self) -> Any: - option = self.config_bag.option + option = self.option_bag.option if option is None: option = self.subconfig.cfgimpl_get_description().impl_getchild(self._name, self.config_bag, self.subconfig) - self.config_bag.option = option + self.option_bag.set_option(option, + self._path, + self.index, + self.config_bag) if self.config_bag.setting_properties: - self.config_bag.config.cfgimpl_get_settings().validate_properties(self._path, - self.index, - self.config_bag) + self.config_bag.config.cfgimpl_get_settings().validate_properties(self.option_bag) if self.index is not None: if option.impl_is_optiondescription() or not option.impl_is_master_slaves('slave'): raise APIError('index must be set only with a slave option') - self._length = self.subconfig.cfgimpl_get_length_slave(option, self.config_bag) + self._length = self.subconfig.cfgimpl_get_length_slave(self.option_bag) if self.index >= self._length: raise SlaveError(_('index "{}" is higher than the master length "{}" ' 'for option "{}"').format(self.index, @@ -188,14 +190,16 @@ class CommonTiramisuOption(CommonTiramisu): slave_need_index = True def __init__(self, - name: Optional[str], - path: Optional[str]=None, - index: Optional[int]=None, - subconfig: Any=None, - config_bag: Optional[ConfigBag]=None) -> None: + name: str, + path: str, + index: int, + subconfig: Union[Config, SubConfig], + config_bag: ConfigBag, + option_bag: OptionBag) -> None: self._path = path self.index = index self.config_bag = config_bag + self.option_bag = option_bag self._name = name self.subconfig = subconfig if config_bag is not None and self.slave_need_index: @@ -310,7 +314,8 @@ class TiramisuOptionOption(CommonTiramisuOption): return option.impl_getrequires() def __getattr__(self, name: str) -> Callable: - if not self._get_option().impl_is_optiondescription() and name != 'get_option': + #if not self._get_option().impl_is_optiondescription() and name != 'get_option': + if not self._get_option().impl_is_optiondescription(): subkey = '_' + name if subkey in dir(self): func = getattr(self, subkey) @@ -327,17 +332,19 @@ class TiramisuOptionOwner(CommonTiramisuOption): """manager option's owner""" def __init__(self, - name: Optional[str], - path: Optional[str]=None, - index: Optional[int]=None, - subconfig: Union[None, Config, SubConfig]=None, - config_bag: Optional[ConfigBag]=None) -> None: + name: str, + path: str, + index: int, + subconfig: Union[Config, SubConfig], + config_bag: ConfigBag, + option_bag: OptionBag) -> None: super().__init__(name, path, index, subconfig, - config_bag) + config_bag, + option_bag) if config_bag: self.values = self.config_bag.config.cfgimpl_get_values() @@ -345,17 +352,13 @@ class TiramisuOptionOwner(CommonTiramisuOption): def get(self): """get owner for a specified option""" option = self._get_option() - return self.values.getowner(self._path, - self.index, - self.config_bag) + return self.values.getowner(self.option_bag) @count def isdefault(self): """is option has defaut value""" self._get_option() - return self.values.is_default_owner(self._path, - self.index, - self.config_bag) + return self.values.is_default_owner(self.option_bag) @count def set(self, owner): @@ -369,10 +372,8 @@ class TiramisuOptionOwner(CommonTiramisuOption): except AttributeError: owners.addowner(owner) obj_owner = getattr(owners, owner) - self.values.setowner(self._path, - self.index, - obj_owner, - self.config_bag) + self.values.setowner(obj_owner, + self.option_bag) class TiramisuOptionProperty(CommonTiramisuOption): @@ -381,16 +382,18 @@ class TiramisuOptionProperty(CommonTiramisuOption): slave_need_index = False def __init__(self, - name: Optional[str], - path: Optional[str]=None, - index: Optional[int]=None, - subconfig: Union[None, Config, SubConfig]=None, - config_bag: Optional[ConfigBag]=None) -> None: + name: str, + path: str, + index: int, + subconfig: Union[Config, SubConfig], + config_bag: ConfigBag, + option_bag: OptionBag) -> None: super().__init__(name, path, index, subconfig, - config_bag) + config_bag, + option_bag) if config_bag: self.settings = config_bag.config.cfgimpl_get_settings() @@ -400,12 +403,9 @@ class TiramisuOptionProperty(CommonTiramisuOption): self._get_option() if apply_requires: self._test_slave_index() - properties = self.settings.getproperties(self._path, - self.index, - self.config_bag, - apply_requires) - if TIRAMISU_VERSION == 2: - properties = properties.get() + else: + self.option_bag.apply_requires = False + properties = self.option_bag.properties return set(properties) @count @@ -415,33 +415,27 @@ class TiramisuOptionProperty(CommonTiramisuOption): if prop in FORBIDDEN_SET_PROPERTIES: raise ConfigError(_('cannot add this property: "{0}"').format( ' '.join(prop))) - props = self.settings.getproperties(self._path, - None, - self.config_bag, + props = self.settings.getproperties(self.option_bag, apply_requires=False) self.settings.setproperties(self._path, props | {prop}, - self.config_bag) + self.option_bag) @count def pop(self, prop): """remove new property for an option""" self._get_option() - props = self.settings.getproperties(self._path, - self.index, - self.config_bag, + props = self.settings.getproperties(self.option_bag, apply_requires=False) self.settings.setproperties(self._path, props - {prop}, - self.config_bag) + self.option_bag) @count def reset(self): """reset all personalised properties""" self._get_option() - self.settings.reset(self.config_bag.option, - self._path, - self.config_bag) + self.settings.reset(self.option_bag) class TiramisuOptionPermissive(CommonTiramisuOption): @@ -450,16 +444,18 @@ class TiramisuOptionPermissive(CommonTiramisuOption): slave_need_index = False def __init__(self, - name: Optional[str], - path: Optional[str]=None, - index: Optional[int]=None, - subconfig: Union[None, Config, SubConfig]=None, - config_bag: Optional[ConfigBag]=None) -> None: + name: str, + path: str, + index: int, + subconfig: Union[Config, SubConfig], + config_bag: ConfigBag, + option_bag: OptionBag) -> None: super().__init__(name, path, index, subconfig, - config_bag) + config_bag, + option_bag) if config_bag: self.settings = config_bag.config.cfgimpl_get_settings() @@ -475,21 +471,9 @@ class TiramisuOptionPermissive(CommonTiramisuOption): @count def set(self, permissives): """set permissives value""" - if TIRAMISU_VERSION == 2: - permissives = tuple(permissives) - path = self._path - opt = self._opt - self.settings.setpermissive(opt=opt, - path=path, - config_bag=self.config_bag, - permissive=permissives) - else: - path = self._path - opt = self._get_option() - self.settings.setpermissive(opt=opt, - path=path, - config_bag=self.config_bag, - permissives=permissives) + self._get_option() + self.settings.setpermissive(self.option_bag, + permissives=permissives) @count def reset(self): @@ -526,12 +510,10 @@ class TiramisuOptionValue(CommonTiramisuOption): @count def get(self): """get option's value""" - self._get_option() self._test_slave_index() settings = self.config_bag.config.cfgimpl_get_settings() value = self.subconfig.getattr(self._name, - self.index, - self.config_bag) + self.option_bag) #if isinstance(value, Multi): # value = list(value) return value @@ -545,35 +527,35 @@ class TiramisuOptionValue(CommonTiramisuOption): if isinstance(value, list): while undefined in value: idx = value.index(undefined) - value[idx] = values.getdefaultvalue(self._path, - idx, - self.config_bag) + option_bag = OptionBag() + option_bag.set_option(self.option_bag.option, + self.option_bag.path, + idx, + self.config_bag) + value[idx] = values.getdefaultvalue(option_bag) else: if value == undefined: - value = values.getdefaultvalue(self._path, - self.index, - self.config_bag) - self.subconfig.setattr(self._name, - self.index, - value, - self.config_bag) + value = values.getdefaultvalue(self.option_bag) + self.subconfig.setattr(value, + self.option_bag) @count def _pop(self, index): """pop value for a master option (only for master option)""" self._get_option() - self.config_bag.config.delattr(self._path, - index, - self.config_bag) + if self.option_bag.option.impl_is_symlinkoption(): + raise TypeError(_("can't delete a SymLinkOption")) + self.config_bag.config.cfgimpl_get_values().reset_master(index, + self.option_bag, + self.subconfig) @count def reset(self): """reset value for a value""" self._get_option() self._test_slave_index() - self.config_bag.config.delattr(self._path, - self.index, - self.config_bag) + #self.config_bag.config.delattr(self.option_bag) + self.subconfig.delattr(self.option_bag) @count def _len_master(self): @@ -590,7 +572,7 @@ class TiramisuOptionValue(CommonTiramisuOption): option = self._get_option() # for example if index is None if '_length' not in vars(self): - self._length = self.subconfig.cfgimpl_get_length_slave(option, self.config_bag) + self._length = self.subconfig.cfgimpl_get_length_slave(self.option_bag) return self._length def __getattr__(self, name: str) -> Callable: @@ -608,7 +590,8 @@ class TiramisuOptionValue(CommonTiramisuOption): @count def _list(self): """all values available for an option (only for choiceoption)""" - return self.config_bag.option.impl_get_values(self.config_bag) + self._get_option() + return self._get_option().impl_get_values(self.option_bag) def registers(registers: Dict[str, type], prefix: str) -> None: @@ -620,19 +603,27 @@ def registers(registers: Dict[str, type], prefix: str) -> None: class TiramisuOption(CommonTiramisu): + registers = {} def __init__(self, name: Optional[str], path: Optional[str]=None, index: Optional[int]=None, subconfig: Union[None, Config, SubConfig]=None, - config_bag: Optional[ConfigBag]=None) -> None: + config_bag: Optional[ConfigBag]=None, + option_bag: Optional[OptionBag]=None) -> None: self._name = name self.subconfig = subconfig + if subconfig == None: + raise Exception() self._path = path self.index = index self.config_bag = config_bag - self.registers = {} - registers(self.registers, self.__class__.__name__) + if option_bag: + self.option_bag = option_bag + else: + self.option_bag = OptionBag() + if not self.registers: + registers(self.registers, self.__class__.__name__) def __getattr__(self, subfunc: str) -> Any: if subfunc in self.registers: @@ -640,11 +631,14 @@ class TiramisuOption(CommonTiramisu): self._path, self.index, self.subconfig, - self.config_bag) + self.config_bag, + self.option_bag) elif subfunc == 'make_dict' and self._get_option().impl_is_optiondescription(): return self._make_dict elif subfunc == 'find' and self._get_option().impl_is_optiondescription(): return self._find + elif subfunc == 'get' and self._get_option().impl_is_optiondescription(): + return self._get elif subfunc == 'list' and self._get_option().impl_is_optiondescription(): return self._list elif subfunc == 'group_type' and self._get_option().impl_is_optiondescription(): @@ -659,9 +653,9 @@ class TiramisuOption(CommonTiramisu): withoption=None, fullpath=False): """return dict with path as key and value for an optiondescription (only for optiondescription)""" + self._get_option() return self.config_bag.config.getattr(self._path, - None, - self.config_bag).make_dict(config_bag=self.config_bag, + self.option_bag).make_dict(config_bag=self.config_bag, flatten=flatten, fullpath=fullpath, withoption=withoption, @@ -680,19 +674,42 @@ class TiramisuOption(CommonTiramisu): bytype=type, _subpath=self._path, config_bag=self.config_bag): - config_bag = self.config_bag.copy('nooption') - subconfig, name = config_bag.config.cfgimpl_get_home_by_path(path, - config_bag) + subconfig, name = self.config_bag.config.cfgimpl_get_home_by_path(path, + self.config_bag) t_option = TiramisuOption(name, path, None, # index for a slave ? subconfig, - config_bag) + self.config_bag) if first: return t_option ret.append(t_option) return ret + @count + def _get(self, name): + self._get_option() + current_option = self.option_bag.option.impl_getchild(name, + self.config_bag, + self.subconfig) + path = self.option_bag.path + '.' + name + option_bag= OptionBag() + option_bag.set_option(current_option, + path, + None, + self.config_bag) + if current_option.impl_is_optiondescription(): + subconfig = self.subconfig.getattr(name, + option_bag) + else: + subconfig = self.subconfig + return TiramisuOption(name, + path, + None, + subconfig, + self.config_bag, + option_bag) + @count def _group_type(self): """get type for an optiondescription (only for optiondescription)""" @@ -703,26 +720,57 @@ class TiramisuOption(CommonTiramisu): type='all', group_type=None): """list options in an optiondescription (only for optiondescription)""" - if type == 'optiondescription': - return self.config_bag.config.getattr(self._path, - None, - self.config_bag - ).iter_groups(self.config_bag, group_type) - elif type == 'all': - return self.config_bag.config.getattr(self._path, - None, - self.config_bag - ).cfgimpl_get_children(self.config_bag) - else: + if type not in ('all', 'optiondescription'): raise APIError(_('unknown list type {}').format(type)) + if group_type is not None and not isinstance(group_type, + groups.GroupType): + raise TypeError(_("unknown group_type: {0}").format(group_type)) + def _filter(opt): + if not self.config_bag.force_unrestraint: + name = opt.impl_getname() + path = subconfig._get_subpath(name) + option_bag = OptionBag() + option_bag.set_option(opt, + path, + None, + self.config_bag) + self.subconfig.getattr(name, + option_bag) + + option = self._get_option() + name = option.impl_getname() + path = self.subconfig._get_subpath(name) + option_bag = OptionBag() + option_bag.set_option(option, + path, + None, + self.config_bag) + subconfig = self.subconfig.getattr(name, + option_bag) + for opt in option.impl_getchildren(self.config_bag): + try: + subsubconfig = _filter(opt) + except PropertiesOptionError: + continue + if opt.impl_is_optiondescription(): + if type == 'optiondescription' and \ + (group_type and opt.impl_get_group_type() != group_type): + continue + else: + if type == 'optiondescription': + continue + name = opt.impl_getname() + yield TiramisuOption(name, + subconfig._get_subpath(name), + None, + subconfig, + self.config_bag) class TiramisuContext(TiramisuHelp): def __init__(self, config_bag: Optional[ConfigBag]) -> None: self.config_bag = config_bag - self.registers = {} - registers(self.registers, self.__class__.__name__) class TiramisuContextInformation(TiramisuContext): @@ -778,7 +826,8 @@ class TiramisuContextValue(TiramisuContext): def reset(self, path): """reset value for a GroupConfig or a MetaConfig""" - self.config_bag.config.reset(path, self.config_bag) + self.config_bag.config.reset(path, + self.config_bag) @count def exportation(self): @@ -789,7 +838,7 @@ class TiramisuContextValue(TiramisuContext): def importation(self, values): """import values""" self.config_bag.config.cfgimpl_get_values()._p_.importation(values) - self.config_bag.config.cfgimpl_reset_cache(None, None, None) + self.config_bag.config.cfgimpl_reset_cache(None, None) class TiramisuContextOwner(TiramisuContext): @@ -819,7 +868,10 @@ class TiramisuContextProperty(TiramisuContext): """set configuration to read only mode""" settings = self.config_bag.config.cfgimpl_get_settings() settings.read_only() - self.config_bag.delete('setting_properties') + try: + del self.config_bag.setting_properties + except AttributeError: + pass @count def read_write(self): @@ -828,7 +880,10 @@ class TiramisuContextProperty(TiramisuContext): settings.read_write() # #FIXME ? settings.set_context_permissive(frozenset(['hidden'])) - self.config_bag.delete('setting_properties') + try: + del self.config_bag.setting_properties + except AttributeError: + pass #/FIXME ? @count @@ -859,9 +914,7 @@ class TiramisuContextProperty(TiramisuContext): @count def reset(self): """remove configuration properties""" - self.config_bag.config.cfgimpl_get_settings().reset(None, - None, - None) + self.config_bag.config.cfgimpl_get_settings().reset(None) @count def exportation(self): @@ -872,7 +925,8 @@ class TiramisuContextProperty(TiramisuContext): def importation(self, properties): """import configuration properties""" self.config_bag.config.cfgimpl_get_settings()._p_.importation(properties) - self.config_bag.config.cfgimpl_reset_cache(None, None, None) + self.config_bag.config.cfgimpl_reset_cache(None, + None) class TiramisuContextPermissive(TiramisuContext): @@ -897,7 +951,8 @@ class TiramisuContextPermissive(TiramisuContext): def importation(self, permissives): """import configuration permissives""" self.config_bag.config.cfgimpl_get_settings()._pp_.importation(permissives) - self.config_bag.config.cfgimpl_reset_cache(None, None, None) + self.config_bag.config.cfgimpl_reset_cache(None, + None) @@ -918,28 +973,28 @@ class TiramisuContextOption(TiramisuContext): bytype=type, #_subpath=self._path, config_bag=self.config_bag): - config_bag = self.config_bag.copy('nooption') - subconfig, name = config_bag.config.cfgimpl_get_home_by_path(path, - config_bag) + subconfig, name = self.config_bag.config.cfgimpl_get_home_by_path(path, + self.config_bag) t_option = TiramisuOption(name, path, None, # index for a slave ? subconfig, - config_bag) + self.config_bag) if first: return t_option ret.append(t_option) return ret - #@count - #def get(self, path): - # """""" - # config_bag = self.config_bag.copy() - # config_bag.validate = False - # config_bag.force_unrestraint = True - # config_bag.setting_properties = None - # return self.config_bag.config.unwrap_from_path(path, - # config_bag) + @count + def get(self, name): + option = self.config_bag.config.cfgimpl_get_description().impl_getchild(name, + self.config_bag, + self.config_bag.config) + return TiramisuOption(name, + name, + None, + self.config_bag.config, + self.config_bag) @count def make_dict(self, @@ -960,11 +1015,24 @@ class TiramisuContextOption(TiramisuContext): group_type=None, recursive=False): """list content of an optiondescription""" + # FIXME should return TiramisuOption !!! + if group_type is not None and not isinstance(group_type, + groups.GroupType): + raise TypeError(_("unknown group_type: {0}").format(group_type)) + if type == 'optiondescription': if recursive: raise APIError(_('not implemented yet')) else: - return self.config_bag.config.iter_groups(self.config_bag, group_type) + if not self.config_bag.force_unrestraint: + option = self.config_bag.config.cfgimpl_get_description() + for opt in option.impl_getchildren(self.config_bag): + if type == 'optiondescription' and not opt.impl_is_optiondescription(): + continue + yield opt.impl_getname() + else: + # FIXME itergroups !!! + return self.config_bag.config.iter_groups(self.config_bag, group_type) elif type == 'all': if group_type: raise APIError(_('not implemented yet')) @@ -998,68 +1066,80 @@ class TiramisuDispatcher: pass +class TiramisuAPI(TiramisuHelp): + registers = {} + + def __init__(self, + config: Union[Config, GroupConfig, MetaConfig, ConfigBag]) -> None: + self._config = config + if not self.registers: + registers(self.registers, 'TiramisuContext') + registers(self.registers, 'TiramisuDispatcher') + + def __getattr__(self, subfunc: str) -> Any: + if subfunc == 'forcepermissive': + if isinstance(self._config, ConfigBag): + config = self._config.config + force = self._config.force_unrestraint + else: + config = self._config + force = None + config_bag = ConfigBag(config=config, + force_permissive=True) + if force is not None: + config_bag.force_unrestraint = force + return TiramisuAPI(config_bag) + elif subfunc == 'unrestraint': + if isinstance(self._config, ConfigBag): + config = self._config.config + force = self._config.force_permissive + else: + config = self._config + force = None + config_bag = ConfigBag(config=config, + force_unrestraint=True) + if force is not None: + config_bag.force_permissive = force + return TiramisuAPI(config_bag) + elif subfunc in self.registers: + if not isinstance(self._config, ConfigBag): + config_bag = ConfigBag(config=self._config) + else: + config_bag = self._config + return self.registers[subfunc](config_bag) + else: + raise APIError(_('please specify a valid sub function ({})').format(subfunc)) + + class TiramisuDispatcherConfig(TiramisuDispatcher, TiramisuContextConfig): - def __call__(self, path: Optional[str]): + def __call__(self, + path: Optional[str]) -> TiramisuAPI: """select a child Tiramisu configuration (only with MetaConfig or GroupConfig)""" - config = self.config_bag.config if path is None: - return TiramisuAPI(config, - force_permissive=self.config_bag.force_permissive, - force_unrestraint=self.config_bag.force_unrestraint) + return TiramisuAPI(self.config_bag) spaths = path.split('.') + config = self.config_bag.config for spath in spaths: config = config.getconfig(spath) - return TiramisuAPI(config, - force_permissive=self.config_bag.force_permissive, - force_unrestraint=self.config_bag.force_unrestraint) + config_bag = ConfigBag(config=config, + force_unrestraint=self.config_bag.force_unrestraint, + force_permissive=self.config_bag.force_permissive) + return TiramisuAPI(config_bag) class TiramisuDispatcherOption(TiramisuDispatcher, TiramisuContextOption): subhelp = TiramisuOption - def __call__(self, path: str, index: Optional[int]=None) -> TiramisuOption: + def __call__(self, + path: str, + index: Optional[int]=None) -> TiramisuOption: """select a option (index only for slave option)""" - config_bag = self.config_bag.copy() - validate = not config_bag.force_unrestraint - if not validate: - config_bag.setting_properties = None - subconfig, name = config_bag.config.cfgimpl_get_home_by_path(path, - config_bag) + subconfig, name = self.config_bag.config.cfgimpl_get_home_by_path(path, + self.config_bag) return TiramisuOption(name, path, index, subconfig, - config_bag) - - -class TiramisuAPI(TiramisuHelp): - - def __init__(self, - config: Union[Config, GroupConfig, MetaConfig], - force_permissive: bool=False, - force_unrestraint: bool=False) -> None: - self._config = config - self.force_permissive = force_permissive - self.force_unrestraint = force_unrestraint - self.registers = {} - registers(self.registers, 'TiramisuContext') - registers(self.registers, 'TiramisuDispatcher') - - def __getattr__(self, subfunc: str) -> Any: - if subfunc == 'forcepermissive': - return TiramisuAPI(config=self._config, - force_permissive=True, - force_unrestraint=self.force_unrestraint) - elif subfunc == 'unrestraint': - return TiramisuAPI(config=self._config, - force_permissive=self.force_permissive, - force_unrestraint=True) - elif subfunc in self.registers: - config_bag = ConfigBag(config=self._config, - force_permissive=self.force_permissive, - force_unrestraint=self.force_unrestraint) - return self.registers[subfunc](config_bag) - else: - raise APIError(_('please specify a valid sub function ({})').format(subfunc)) + self.config_bag) @count diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index f5c902b..7006b3d 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -23,7 +23,7 @@ from typing import Any, Optional, Union, Callable, Dict, List from .error import PropertiesOptionError, ConfigError, SlaveError from .i18n import _ -from .setting import undefined, ConfigBag, Undefined +from .setting import undefined, ConfigBag, OptionBag, Undefined from .option.symlinkoption import DynSymLinkOption from .storage import get_default_values_storages, get_default_settings_storages from .function import ParamValue, ParamContext, ParamIndex, ParamOption, Params @@ -34,7 +34,7 @@ def manager_callback(callbk: Union[ParamOption, ParamValue], option, index: Optional[int], orig_value, - config_bag: ConfigBag, + option_bag: OptionBag, context) -> Any: """replace Param by true value""" if isinstance(callbk, ParamValue): @@ -55,10 +55,6 @@ def manager_callback(callbk: Union[ParamOption, ParamValue], path = opt.impl_getpath(context) else: path = context.cfgimpl_get_description().impl_get_path_by_opt(opt) - # don't validate if option is option that we tried to validate - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = opt - sconfig_bag.force_permissive = True if index is not None and opt.impl_is_master_slaves() and \ opt.impl_get_master_slaves().in_same_group(option): if opt == option: @@ -76,15 +72,25 @@ def manager_callback(callbk: Union[ParamOption, ParamValue], if opt == option and orig_value is not undefined and \ (not opt.impl_is_master_slaves('slave') or index is None): return orig_value + # don't validate if option is option that we tried to validate + config_bag = ConfigBag(config=option_bag.config_bag.config, + _setting_properties=option_bag.config_bag._setting_properties, + force_permissive=True, + force_unrestraint=option_bag.config_bag.force_unrestraint, + _validate=option_bag.config_bag._validate) + soption_bag = OptionBag() + soption_bag.set_option(opt, + path, + index_, + config_bag) + soption_bag.fromconsistency = option_bag.fromconsistency.copy() if opt == option: - sconfig_bag.setting_properties = None - sconfig_bag.force_unrestraint= False - sconfig_bag.validate = False + soption_bag.config_bag.force_unrestraint = True + soption_bag.config_bag.validate = False try: # get value value = context.getattr(path, - index_, - sconfig_bag) + soption_bag) if with_index: return value[index] return value @@ -100,7 +106,7 @@ def carry_out_calculation(option, callback: Callable, callback_params: Optional[Params], index: Optional[int], - config_bag: Optional[ConfigBag], + option_bag: Optional[OptionBag], orig_value=undefined, is_validator: int=False): @@ -217,7 +223,7 @@ def carry_out_calculation(option, kwargs = {} # if callback_params has a callback, launch several time calculate() if option.issubdyn(): - #FIXME why here? should be ParamSuffix ! + #FIXME why here? should be a ParamSuffix ! kwargs['suffix'] = option.impl_getsuffix() if callback_params: for callbk in callback_params.args: @@ -226,7 +232,7 @@ def carry_out_calculation(option, option, index, orig_value, - config_bag, + option_bag, context) if value is undefined: return undefined @@ -239,7 +245,7 @@ def carry_out_calculation(option, option, index, orig_value, - config_bag, + option_bag, context) if value is undefined: return undefined diff --git a/tiramisu/config.py b/tiramisu/config.py index f2f1748..cd52d85 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -20,7 +20,6 @@ # ____________________________________________________________ "options handler global entry point" import weakref -from time import time from copy import copy @@ -29,7 +28,7 @@ from .option.syndynoptiondescription import SynDynOptionDescription from .option.dynoptiondescription import DynOptionDescription from .option.masterslave import MasterSlaves from .option.baseoption import BaseOption, valid_name -from .setting import ConfigBag, groups, Settings, undefined +from .setting import OptionBag, ConfigBag, groups, Settings, undefined from .storage import get_storages, get_default_values_storages from .value import Values # , Multi from .i18n import _ @@ -50,7 +49,8 @@ class SubConfig(object): descr, context, config_bag, - subpath=None): + subpath=None, + fromconsistency=None): """ Configuration option management master class :param descr: describes the configuration schema @@ -73,23 +73,38 @@ class SubConfig(object): descr.impl_get_group_type() == groups.master: master = descr.getmaster() masterpath = master.impl_getname() - mconfig_bag = config_bag.copy('nooption') - mconfig_bag.option = master - mconfig_bag.validate = False + full_masterpath = self._get_subpath(masterpath) + cconfig_bag = ConfigBag(config=config_bag.config, + _setting_properties=config_bag._setting_properties, + force_permissive=config_bag.force_permissive, + force_unrestraint=config_bag.force_unrestraint, + _validate=False) + moption_bag = OptionBag() + moption_bag.set_option(master, + full_masterpath, + None, + cconfig_bag) + if fromconsistency: + moption_bag.fromconsistency = fromconsistency value = self.getattr(masterpath, - None, - mconfig_bag) + moption_bag) self._impl_length = len(value) def cfgimpl_get_length(self): return self._impl_length - def cfgimpl_get_length_slave(self, option, config_bag): - if option.impl_is_symlinkoption(): + def cfgimpl_get_length_slave(self, + option_bag): + if option_bag.option.impl_is_symlinkoption(): context = self.cfgimpl_get_context() - sconfig_bag = config_bag.copy('nooption') - subconfig, _ = context.cfgimpl_get_home_by_path(option.impl_getopt().impl_getpath(context), - sconfig_bag) + #soption_bag = OptionBag() + #soption_bag.set_option(option_bag.option.impl_getopt(), + # option_bag.option.impl_getopt().impl_getpath(context), + # None, + # option_bag.config_bag) + path = option_bag.option.impl_getopt().impl_getpath(context) + subconfig, _ = context.cfgimpl_get_home_by_path(path, + option_bag.config_bag) return subconfig.cfgimpl_get_length() else: return self.cfgimpl_get_length() @@ -99,54 +114,65 @@ class SubConfig(object): values, settings, resetted_opts, - config_bag, - opt, - path): + option_bag): - if path in resetted_opts: + if option_bag.path in resetted_opts: return - resetted_opts.append(path) - for woption in opt._get_dependencies(self): + resetted_opts.append(option_bag.path) + for woption in option_bag.option._get_dependencies(self): option = woption() if option.impl_is_dynoptiondescription(): - for doption in option.get_syndynoptiondescriptions(config_bag): + for doption in option.get_syndynoptiondescriptions(option_bag): doption_path = doption.impl_getpath(self) + doption_bag = OptionBag() + doption_bag.set_option(doption, + doption_path, + option_bag.index, + option_bag.config_bag) self.reset_one_option_cache(desc, values, settings, resetted_opts, - config_bag, - doption, - doption_path) + doption_bag) elif option.issubdyn(): - for doption in desc.build_dynoptions(option, config_bag): + doption_bag = OptionBag() + doption_path = option.impl_getpath(self) + doption_bag.set_option(option, + doption_path, + option_bag.index, + option_bag.config_bag) + for doption in desc.build_dynoptions(doption_bag): doption_path = doption.impl_getpath(self) + doption_bag = OptionBag() + doption_bag.set_option(doption, + doption_path, + option_bag.index, + option_bag.config_bag) self.reset_one_option_cache(desc, values, settings, resetted_opts, - config_bag, - doption, - doption_path) + doption_bag) else: option_path = option.impl_getpath(self) + doption_bag = OptionBag() + doption_bag.set_option(option, + option_path, + option_bag.index, + option_bag.config_bag) self.reset_one_option_cache(desc, values, settings, resetted_opts, - config_bag, - option, - option_path) + doption_bag) del option - opt.reset_cache(path, - values, - settings, - resetted_opts) + option_bag.option.reset_cache(option_bag.path, + values, + settings, + resetted_opts) def cfgimpl_reset_cache(self, - opt, - path, - config_bag, + option_bag, resetted_opts=None): """reset all settings in cache """ @@ -158,79 +184,40 @@ class SubConfig(object): values = context.cfgimpl_get_values() settings = context.cfgimpl_get_settings() - if not None in (opt, path): + if option_bag is not None: self.reset_one_option_cache(desc, values, settings, resetted_opts, - config_bag, - opt, - path) + option_bag) else: values._p_.reset_all_cache() settings._p_.reset_all_cache() def cfgimpl_get_home_by_path(self, path, - config_bag): + config_bag, + fromconsistency=None): """:returns: tuple (config, name)""" path = path.split('.') for step in path[:-1]: - sconfig_bag = config_bag.copy('nooption') + option_bag = OptionBag() + option = self.cfgimpl_get_description().impl_getchild(step, + config_bag, + self) + subpath = self._get_subpath(step) + option_bag.set_option(option, + subpath, + None, + config_bag) + if fromconsistency is not None: + option_bag.fromconsistency = fromconsistency self = self.getattr(step, - None, - sconfig_bag) + option_bag) if not isinstance(self, SubConfig): raise AttributeError(_('unknown option {}').format(path[-1])) return self, path[-1] - # ______________________________________________________________________ - def iter_groups(self, - config_bag, - group_type=None): - """iteration on groups objects only. - All groups are returned if `group_type` is `None`, otherwise the groups - can be filtered by categories (families, or whatever). - - :param group_type: if defined, is an instance of `groups.GroupType` - or `groups.MasterGroupType` that lives in - `setting.groups` - """ - if group_type is not None and not isinstance(group_type, - groups.GroupType): - raise TypeError(_("unknown group_type: {0}").format(group_type)) - for child in self.cfgimpl_get_description().impl_getchildren(config_bag): - if child.impl_is_optiondescription(): - nconfig_bag = config_bag.copy('nooption') - nconfig_bag.option = child - try: - if group_type is None or (group_type is not None and - child.impl_get_group_type() - == group_type): - name = child.impl_getname() - yield name, self.getattr(name, - None, - nconfig_bag) - except PropertiesOptionError: - pass - - def cfgimpl_get_children(self, - config_bag): - context = self.cfgimpl_get_context() - for opt in self.cfgimpl_get_description().impl_getchildren(config_bag): - nconfig_bag = config_bag.copy('nooption') - nconfig_bag.option = opt - name = opt.impl_getname() - if nconfig_bag.setting_properties is not None: - subpath = self._get_subpath(name) - try: - context.cfgimpl_get_settings().validate_properties(subpath, - None, - nconfig_bag) - except PropertiesOptionError: - continue - yield name - # ______________________________________________________________________ def cfgimpl_get_context(self): """context could be None, we need to test it @@ -245,7 +232,7 @@ class SubConfig(object): def cfgimpl_get_description(self): if self._impl_descr is None: - raise ConfigError(_('no option description found for this config' + raise ConfigError(_('there is no option description for this config' ' (may be GroupConfig)')) else: return self._impl_descr @@ -257,63 +244,39 @@ class SubConfig(object): return self.cfgimpl_get_context()._impl_values def setattr(self, - name, - index, value, - config_bag, + option_bag, _commit=True): - context = self.cfgimpl_get_context() - if '.' in name: - # when set_value - self, name = self.cfgimpl_get_home_by_path(name, - config_bag) - if config_bag.option is None: - config_bag.option = self.cfgimpl_get_description().impl_getchild(name, - config_bag, - self) - if config_bag.option.impl_is_symlinkoption(): + #self, name = self.cfgimpl_get_home_by_path(option_bag.path, + # option_bag.config_bag) + #if config_bag.option is None: + # config_bag.option = self.cfgimpl_get_description().impl_getchild(name, + # config_bag, + # self) + if option_bag.option.impl_is_symlinkoption(): raise ConfigError(_("can't assign to a SymLinkOption")) else: - path = self._get_subpath(name) - if config_bag.setting_properties: - context.cfgimpl_get_settings().validate_properties(path, - index, - config_bag) - self.cfgimpl_get_description().impl_validate_value(config_bag.option, + context = self.cfgimpl_get_context() + if option_bag.config_bag.setting_properties: + context.cfgimpl_get_settings().validate_properties(option_bag) + self.cfgimpl_get_description().impl_validate_value(option_bag.option, value, self) - return context.cfgimpl_get_values().setvalue(path, - index, - value, - config_bag, + return context.cfgimpl_get_values().setvalue(value, + option_bag, _commit) def delattr(self, - name, - index, - config_bag): - if '.' in name: - self, name = self.cfgimpl_get_home_by_path(name, - config_bag) - option = config_bag.option + option_bag): + option = option_bag.option if option.impl_is_symlinkoption(): raise TypeError(_("can't delete a SymLinkOption")) - subpath = self._get_subpath(name) values = self.cfgimpl_get_values() - if index is not None: - if option.impl_is_master_slaves('master'): - values.reset_master(self, - subpath, - index, - config_bag) - elif option.impl_is_master_slaves('slave'): - values.reset_slave(subpath, - index, - config_bag) + if option_bag.index is not None: + values.reset_slave(option_bag) else: - values.reset(subpath, - config_bag) + values.reset(option_bag) def _get_subpath(self, name): if self._impl_path is None: @@ -324,71 +287,73 @@ class SubConfig(object): def getattr(self, name, - index, - config_bag): + option_bag): """ attribute notation mechanism for accessing the value of an option :param name: attribute name :return: option's value if name is an option name, OptionDescription otherwise """ + config_bag = option_bag.config_bag if '.' in name: + if option_bag.fromconsistency: + fromconsistency = option_bag.fromconsistency.copy() + else: + fromconsistency = None self, name = self.cfgimpl_get_home_by_path(name, - config_bag) + config_bag, + fromconsistency) - context = self.cfgimpl_get_context() - option = config_bag.option - if option is None: - option = self.cfgimpl_get_description().impl_getchild(name, - config_bag, - self) - config_bag.option = option + option = option_bag.option if option.impl_is_symlinkoption(): - opt = option.impl_getopt() - path = context.cfgimpl_get_description().impl_get_path_by_opt(opt) - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.ori_option = option - sconfig_bag.option = opt - return context.getattr(path, - index, - sconfig_bag) + soption_bag = OptionBag() + soption_bag.set_option(option.impl_getopt(), + None, + option_bag.index, + config_bag) + soption_bag.ori_option = option + context = self.cfgimpl_get_context() + return context.getattr(soption_bag.path, + soption_bag) - subpath = self._get_subpath(name) if config_bag.setting_properties: - self.cfgimpl_get_settings().validate_properties(subpath, - index, - config_bag) + self.cfgimpl_get_settings().validate_properties(option_bag) if option.impl_is_optiondescription(): + if option_bag.fromconsistency: + fromconsistency = option_bag.fromconsistency.copy() + else: + fromconsistency = None return SubConfig(option, self._impl_context, config_bag, - subpath) + option_bag.path, + fromconsistency) if option.impl_is_master_slaves('slave'): - length = self.cfgimpl_get_length_slave(option, config_bag) - slave_len = self.cfgimpl_get_values()._p_.get_max_length(subpath) + length = self.cfgimpl_get_length_slave(option_bag) + slave_len = self.cfgimpl_get_values()._p_.get_max_length(option_bag.path) if slave_len > length: raise SlaveError(_('slave option "{}" has higher length "{}" than the master ' 'length "{}"').format(option.impl_get_display_name(), slave_len, length, - subpath)) - if option.impl_is_master_slaves('slave') and index is None: + option_bag.index)) + if option.impl_is_master_slaves('slave') and option_bag.index is None: value = [] for idx in range(length): - config_bag.properties = None + soption_bag = OptionBag() + soption_bag.set_option(option, + option_bag.path, + idx, + config_bag) + soption_bag.fromconsistency = option_bag.fromconsistency.copy() value.append(self.getattr(name, - idx, - config_bag)) + soption_bag)) else: - value = self.cfgimpl_get_values().get_cached_value(subpath, - index, - config_bag) + value = self.cfgimpl_get_values().get_cached_value(option_bag) if config_bag.validate_properties: - self.cfgimpl_get_settings().validate_mandatory(subpath, - index, - value, - config_bag) + self.cfgimpl_get_settings().validate_mandatory(value, + option_bag) return value def find(self, @@ -406,11 +371,10 @@ class SubConfig(object): :param first: return only one option if True, a list otherwise :return: find list or an exception if nothing has been found """ - def _filter_by_value(sconfig_bag): + def _filter_by_value(soption_bag): try: value = self.getattr(path, - None, - sconfig_bag) + soption_bag) except PropertiesOptionError: return False if isinstance(value, list): @@ -427,18 +391,19 @@ class SubConfig(object): _subpath, config_bag) for path, option in options: - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = option - if byvalue is not undefined and not _filter_by_value(sconfig_bag): + option_bag = OptionBag() + option_bag.set_option(option, + path, + None, + config_bag) + if byvalue is not undefined and not _filter_by_value(option_bag): continue - elif sconfig_bag.validate_properties: + elif config_bag.validate_properties: #remove option with propertyerror, ... try: self.unwrap_from_path(path, - sconfig_bag) - self.cfgimpl_get_settings().validate_properties(path, - None, - sconfig_bag) + config_bag) + self.cfgimpl_get_settings().validate_properties(option_bag) except PropertiesOptionError: continue found = True @@ -513,8 +478,11 @@ class SubConfig(object): path = '.'.join(path.split('.')[:-1]) opt = context.unwrap_from_path(path, config_bag) - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = opt + soption_bag = OptionBag() + soption_bag.set_option(opt, + path, + None, + config_bag) if mypath is not None: if mypath == path: withoption = None @@ -531,19 +499,24 @@ class SubConfig(object): pathsvalues, _currpath, flatten, - sconfig_bag, + soption_bag, fullpath=fullpath) #withoption can be set to None below ! if withoption is None: for opt in self.cfgimpl_get_description().impl_getchildren(config_bag, context): - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = opt - path = opt.impl_getname() - self._make_sub_dict(path, + name = opt.impl_getname() + path = self._get_subpath(name) + soption_bag = OptionBag() + soption_bag.set_option(opt, + path, + None, + config_bag) + #path = self._get_subpath(name) + self._make_sub_dict(name, pathsvalues, _currpath, flatten, - sconfig_bag, + soption_bag, fullpath=fullpath) if _currpath == []: options = dict(pathsvalues) @@ -555,33 +528,32 @@ class SubConfig(object): pathsvalues, _currpath, flatten, - config_bag, + option_bag, fullpath=False): try: - option = config_bag.option + option = option_bag.option if not option.impl_is_optiondescription() and option.impl_is_master_slaves('slave'): ret = [] - length = self.cfgimpl_get_length_slave(option, config_bag) + length = self.cfgimpl_get_length_slave(option_bag) if length: for idx in range(length): - config_bag.properties = None + soption_bag = OptionBag() + soption_bag.set_option(option, + option_bag.path, + idx, + option_bag.config_bag) ret.append(self.getattr(name, - idx, - config_bag)) - elif config_bag.setting_properties: - path = self._get_subpath(name) - self.cfgimpl_get_settings().validate_properties(path, - None, - config_bag) + soption_bag)) + elif option_bag.config_bag.setting_properties: + self.cfgimpl_get_settings().validate_properties(option_bag) else: ret = self.getattr(name, - None, - config_bag) + option_bag) except PropertiesOptionError: pass else: if option.impl_is_optiondescription(): - pathsvalues += ret.make_dict(config_bag, + pathsvalues += ret.make_dict(option_bag.config_bag, flatten=flatten, _currpath=_currpath + [name], fullpath=fullpath) @@ -638,12 +610,12 @@ class _CommonConfig(SubConfig): true_option = option.impl_getopt() context = self.cfgimpl_get_context() true_path = true_option.impl_getpath(context) - self, path = context.cfgimpl_get_home_by_path(true_path, + self, path = context.cfgimpl_get_home_by_path(path, config_bag) config_bag.option = true_option else: true_path = path - config_bag.option = option + #config_bag.option = option return option def cfgimpl_get_path(self, dyn=True): @@ -816,25 +788,15 @@ class GroupConfig(_CommonConfig): return self._impl_children def cfgimpl_reset_cache(self, - opt, - path, - config_bag, + option_bag, resetted_opts=None): if resetted_opts is None: resetted_opts = [] if isinstance(self, MetaConfig): - super(GroupConfig, self).cfgimpl_reset_cache(opt, - path, - config_bag=config_bag, + super(GroupConfig, self).cfgimpl_reset_cache(option_bag, resetted_opts=copy(resetted_opts)) for child in self._impl_children: - if config_bag is None: - nconfig_bag = config_bag - else: - nconfig_bag = config_bag.copy('nooption') - child.cfgimpl_reset_cache(opt, - path, - config_bag=nconfig_bag, + child.cfgimpl_reset_cache(option_bag, resetted_opts=copy(resetted_opts)) def set_value(self, @@ -857,16 +819,21 @@ class GroupConfig(_CommonConfig): only_config=only_config, _commit=False)) else: - nconfig_bag = config_bag.copy('nooption') - child.setattr(path, - index, - value, - nconfig_bag, + subconfig, name = child.cfgimpl_get_home_by_path(path, + config_bag) + option = subconfig.cfgimpl_get_description().impl_getchild(name, + config_bag, + child) + option_bag = OptionBag() + option_bag.set_option(option, + path, + index, + config_bag) + child.setattr(value, + option_bag, _commit=False) except PropertiesOptionError as err: - ret.append(PropertiesOptionError(err._path, - err._index, - err._config_bag, + ret.append(PropertiesOptionError(err._option_bag, err.proptype, err._settings, err._opt_type, @@ -905,8 +872,6 @@ class GroupConfig(_CommonConfig): ret = [] for child in self._impl_children: - nconfig_bag = config_bag.copy('nooption') - nconfig_bag.option = child if isinstance(child, GroupConfig): ret.extend(child.find_firsts(byname=byname, bypath=bypath, @@ -1011,59 +976,77 @@ class MetaConfig(GroupConfig): ' cannot be set together')) opt = self.cfgimpl_get_description().impl_get_opt_by_path(path) for child in self._impl_children: - nconfig_bag = config_bag.copy('nooption') - nconfig_bag.option = opt + subconfig, name = child.cfgimpl_get_home_by_path(path, + config_bag) + option = subconfig.cfgimpl_get_description().impl_getchild(name, + config_bag, + child) + option_bag = OptionBag() + option_bag.set_option(option, + path, + index, + config_bag) if force_default_if_same: if not child.cfgimpl_get_values()._p_.hasvalue(path): child_value = undefined else: - child_value = child.getattr(path, - None, - nconfig_bag) + child_value = child.getattr(name, + option_bag) if force_default or (force_default_if_same and value == child_value): - child.cfgimpl_get_values().reset(path, - nconfig_bag, + child.cfgimpl_get_values().reset(option_bag, _commit=False) continue if force_dont_change_value: try: - child_value = child.getattr(path, - None, - nconfig_bag) + child_value = child.getattr(name, + option_bag) if value != child_value: - child.setattr(path, - None, - child_value, - nconfig_bag, + child.setattr(child_value, + option_bag, _commit=False) except (PropertiesOptionError, ValueError, SlaveError) as err: ret.append(err) - nconfig_bag = config_bag.copy('nooption') try: - self.setattr(path, - index, - value, - nconfig_bag, - _commit=_commit) + subconfig, name = self.cfgimpl_get_home_by_path(path, + config_bag) + option = subconfig.cfgimpl_get_description().impl_getchild(name, + config_bag, + self) + option_bag = OptionBag() + option_bag.set_option(option, + path, + index, + config_bag) + self.setattr(value, + option_bag, + _commit=False) except (PropertiesOptionError, ValueError, SlaveError) as err: ret.append(err) return ret - def reset(self, path, config_bag): - #FIXME not working with DynSymLinkOption - #FIXME fonctionne avec sous metaconfig ?? - opt = self.cfgimpl_get_description().impl_get_opt_by_path(path) - config_bag.option = opt - config_bag.validate = False + def reset(self, + path, + config_bag): + rconfig_bag = ConfigBag(config=config_bag.config, + _setting_properties=config_bag._setting_properties, + force_permissive=config_bag.force_permissive, + force_unrestraint=config_bag.force_unrestraint, + _validate=False) + subconfig, name = self.cfgimpl_get_home_by_path(path, + config_bag) + option = subconfig.cfgimpl_get_description().impl_getchild(name, + config_bag, + self) + option_bag = OptionBag() + option_bag.set_option(option, + path, + option, + rconfig_bag) for child in self._impl_children: - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = opt - child.cfgimpl_get_values().reset(path, - sconfig_bag, + child.cfgimpl_get_values().reset(option_bag, _commit=False) - self.cfgimpl_get_values().reset(path, - config_bag) + self.cfgimpl_get_values().reset(option_bag) def new_config(self, session_id, diff --git a/tiramisu/error.py b/tiramisu/error.py index ed371a0..1bcb4a7 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -53,31 +53,27 @@ def display_list(lst, separator='and', add_quote=False): class PropertiesOptionError(AttributeError): "attempt to access to an option with a property that is not allowed" def __init__(self, - path, - index, - config_bag, + option_bag, proptype, settings, opt_type=None, requires=None, name=None, orig_opt=None): - self._path = path - self._index = index if opt_type: self._opt_type = opt_type self._requires = requires self._name = name self._orig_opt = orig_opt else: - if config_bag.option.impl_is_optiondescription(): + if option_bag.option.impl_is_optiondescription(): self._opt_type = 'optiondescription' else: self._opt_type = 'option' - self._requires = config_bag.option.impl_getrequires() - self._name = config_bag.option.impl_get_display_name() + self._requires = option_bag.option.impl_getrequires() + self._name = option_bag.option.impl_get_display_name() self._orig_opt = None - self._config_bag = config_bag.copy() + self._option_bag = option_bag self.proptype = proptype self._settings = settings self.msg = None @@ -90,12 +86,8 @@ class PropertiesOptionError(AttributeError): #this part is a bit slow, so only execute when display if self.msg: return self.msg - req = self._settings.apply_requires(self._path, - self._requires, - self._index, - True, - self._config_bag, - self._name) + req = self._settings.apply_requires(self._option_bag, + True) #if req != {} or self._orig_opt is not None: if req != {}: only_one = len(req) == 1 @@ -123,7 +115,7 @@ class PropertiesOptionError(AttributeError): self._name, prop_msg, msg)) - del self._path, self._index, self._requires, self._opt_type, self._name, self._config_bag + del self._requires, self._opt_type, self._name, self._option_bag del self._settings, self._orig_opt return self.msg diff --git a/tiramisu/option/choiceoption.py b/tiramisu/option/choiceoption.py index 08295bc..34c5064 100644 --- a/tiramisu/option/choiceoption.py +++ b/tiramisu/option/choiceoption.py @@ -81,22 +81,26 @@ class ChoiceOption(Option): warnings_only=warnings_only) def impl_get_values(self, - config_bag, + option_bag, current_opt=undefined): if current_opt is undefined: current_opt = self #FIXME cache? but in context... values = self._choice_values if isinstance(values, FunctionType): - if config_bag is undefined: + if option_bag is undefined: values = undefined else: + if option_bag.config_bag == undefined: + config = undefined + else: + config = option_bag.config_bag.config values = carry_out_calculation(current_opt, - config_bag.config, - values, - getattr(self, '_choice_values_params', {}), - None, - config_bag) + context=config, + callback=values, + callback_params=getattr(self, '_choice_values_params', {}), + index=None, + option_bag=option_bag) if values is not undefined and not isinstance(values, list): raise ConfigError(_('calculated values for {0} is not a list' '').format(self.impl_getname())) @@ -105,9 +109,9 @@ class ChoiceOption(Option): def _validate(self, value, - config_bag, + option_bag, current_opt=undefined): - values = self.impl_get_values(config_bag, + values = self.impl_get_values(option_bag, current_opt=current_opt) if values is not undefined and not value in values: if len(values) == 1: diff --git a/tiramisu/option/dynoptiondescription.py b/tiramisu/option/dynoptiondescription.py index 491c1fc..3109e11 100644 --- a/tiramisu/option/dynoptiondescription.py +++ b/tiramisu/option/dynoptiondescription.py @@ -72,14 +72,14 @@ class DynOptionDescription(OptionDescription): '').format(self.impl_get_display_name())) def _impl_get_suffixes(self, - config_bag): + option_bag): callback, callback_params = self.impl_get_callback() values = carry_out_calculation(self, - config_bag.config, + option_bag.config_bag.config, callback, callback_params, None, - config_bag) + option_bag) if not isinstance(values, list): raise ValueError(_('DynOptionDescription callback for option "{}", is not a list ({})' '').format(self.impl_get_display_name(), values)) @@ -92,9 +92,9 @@ class DynOptionDescription(OptionDescription): self.impl_get_display_name())) 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): + def get_syndynoptiondescriptions(self, option_bag): + subpath = self.impl_getpath(option_bag.config_bag.config).rsplit('.', 1)[0] + for suffix in self._impl_get_suffixes(option_bag): yield SynDynOptionDescription(self, subpath, suffix) diff --git a/tiramisu/option/masterslave.py b/tiramisu/option/masterslave.py index 9d3bc71..0afde8b 100644 --- a/tiramisu/option/masterslave.py +++ b/tiramisu/option/masterslave.py @@ -24,7 +24,7 @@ from itertools import chain from ..i18n import _ -from ..setting import groups, undefined +from ..setting import groups, undefined, OptionBag, ConfigBag from .optiondescription import OptionDescription from .option import Option from ..error import SlaveError, PropertiesOptionError @@ -104,35 +104,49 @@ class MasterSlaves(OptionDescription): def reset(self, values, - config_bag, + option_bag, _commit=True): + config_bag = ConfigBag(config=option_bag.config_bag.config, + _setting_properties=option_bag.config_bag._setting_properties, + force_permissive=option_bag.config_bag.force_permissive, + force_unrestraint=option_bag.config_bag.force_unrestraint, + _validate=False) for slave in self.getslaves(): - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = slave - sconfig_bag.validate = False slave_path = slave.impl_getpath(values._getcontext()) - values.reset(slave_path, - sconfig_bag, + soption_bag = OptionBag() + soption_bag.set_option(slave, + slave_path, + None, + config_bag) + values.reset(soption_bag, _commit=_commit) def pop(self, values, index, - config_bag, + option_bag, slaves=undefined): context = values._getcontext() if slaves is undefined: slaves = self.getslaves() + config_bag = ConfigBag(config=option_bag.config_bag.config, + _setting_properties=None, + force_permissive=option_bag.config_bag.force_permissive, + force_unrestraint=option_bag.config_bag.force_unrestraint, + _validate=False) for slave in slaves: slave_path = slave.impl_getpath(context) slavelen = values._p_.get_max_length(slave_path) - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = slave - if not values.is_default_owner(slave_path, - index, - config_bag, + soption_bag = OptionBag() + soption_bag.set_option(slave, + slave_path, + index, + config_bag) + # do not check force_default_on_freeze + soption_bag.properties = {} + if not values.is_default_owner(soption_bag, validate_meta=False): if slavelen > index: values._p_.resetvalue_index(slave_path, diff --git a/tiramisu/option/netmaskoption.py b/tiramisu/option/netmaskoption.py index c66fd25..fb3b123 100644 --- a/tiramisu/option/netmaskoption.py +++ b/tiramisu/option/netmaskoption.py @@ -95,9 +95,15 @@ class NetmaskOption(Option): if make_net and ip.prefixlen() != 32: val_ip = IP(val_ipnetwork) if ip.net() == val_ip: - msg = _('this is a network with netmask "{0}" ("{1}")') + if current_opt == opts[1]: + msg = _('this is a network with netmask "{0}" ("{1}")') + else: + msg = _('this is a network with {2} "{0}" ("{1}")') if ip.broadcast() == val_ip: - msg = _('this is a broadcast with netmask "{0}" ("{1}")') + if current_opt == opts[1]: + msg = _('this is a broadcast with netmask "{0}" ("{1}")') + else: + msg = _('this is a broadcast with {2} "{0}" ("{1}")') except ValueError: if not make_net: @@ -106,5 +112,10 @@ class NetmaskOption(Option): else: raise ValueError(_('with {2} "{0}" ("{1}")').format(val_ipnetwork, opts[1].impl_get_display_name(), typ)) if msg is not None: - raise ValueError(msg.format(val_netmask, - opts[1].impl_get_display_name())) + if current_opt == opts[1]: + raise ValueError(msg.format(val_netmask, + opts[1].impl_get_display_name())) + else: + raise ValueError(msg.format(val_ipnetwork, + opts[0].impl_get_display_name(), + typ)) diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 35a9d8d..03bff4a 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -25,7 +25,7 @@ import weakref from .baseoption import OnlyOption, submulti, STATIC_TUPLE from .symlinkoption import DynSymLinkOption from ..i18n import _ -from ..setting import log, undefined, debug +from ..setting import log, undefined, debug, OptionBag, ConfigBag from ..autolib import carry_out_calculation from ..error import (ConfigError, ValueWarning, PropertiesOptionError, display_list) @@ -145,9 +145,13 @@ class Option(OnlyOption): _setattr(self, '_default_multi', default_multi) if unique is not undefined: _setattr(self, '_unique', unique) + option_bag = OptionBag() + option_bag.set_option(self, + undefined, + None, + undefined) self.impl_validate(default, - is_multi=is_multi, - config_bag=undefined) + option_bag) if (is_multi and default != []) or \ (not is_multi and default is not None): if is_multi: @@ -180,34 +184,25 @@ class Option(OnlyOption): def impl_validate(self, value, - config_bag, + option_bag, context=undefined, - force_index=None, - current_opt=undefined, - is_multi=None, - check_error=True, - multi=None): + check_error=True): """ - :param value: the option's value - :param context: Config's context - :type context: :class:`tiramisu.config.Config` - :type validate: boolean - :param force_index: if multi, value has to be a list - not if force_index is not None - :type force_index: integer """ - if current_opt is undefined: - current_opt = self + if option_bag: + config_bag = option_bag.config_bag + force_index = option_bag.index + else: + config_bag = undefined + force_index = None if check_error and config_bag is not undefined and \ not config_bag.validate: # just to check propertieserror - self.valid_consistency(current_opt, + self.valid_consistency(option_bag, value, context, - force_index, - check_error, - config_bag) + check_error) return def _is_not_unique(value): @@ -233,13 +228,13 @@ class Option(OnlyOption): args.insert(0, ParamValue(val)) validator_params_ = Params(tuple(args), kwargs) # Raise ValueError if not valid - carry_out_calculation(current_opt, + carry_out_calculation(option_bag.ori_option, context=context, callback=validator, callback_params=validator_params_, index=_index, + option_bag=option_bag, orig_value=value, - config_bag=config_bag, is_validator=True) def do_validation(_value, @@ -251,13 +246,9 @@ class Option(OnlyOption): if _value is not None: if check_error: # option validation - if config_bag is undefined: - setting_properties = None - else: - setting_properties = config_bag.setting_properties self._validate(_value, - config_bag, - current_opt) + option_bag, + option_bag.ori_option) if ((check_error and not is_warnings_only) or (not check_error and is_warnings_only)): calculation_validator(_value, @@ -265,13 +256,13 @@ class Option(OnlyOption): self._second_level_validation(_value, is_warnings_only) - if is_multi is None: - is_multi = self.impl_is_multi() + #if is_multi is None: + # is_multi = self.impl_is_multi() is_warnings_only = getattr(self, '_warnings_only', False) try: val = value - if not is_multi: + if not self.impl_is_multi(): do_validation(val, None) elif force_index is not None: if self.impl_is_submulti(): @@ -282,14 +273,6 @@ class Option(OnlyOption): do_validation(val, force_index) else: - if multi is not None and self.impl_is_unique() and value in multi: - if not self.impl_is_submulti() and len(multi) - 1 >= force_index: - lst = list(multi) - lst.pop(force_index) - else: - lst = multi - if value in lst: - raise ValueError(_('this value is not uniq')) do_validation(val, force_index) elif not isinstance(value, list): @@ -309,17 +292,15 @@ class Option(OnlyOption): do_validation(val, idx) - self.valid_consistency(current_opt, + self.valid_consistency(option_bag, value, context, - force_index, - check_error, - config_bag) + check_error) except ValueError as err: if debug: # pragma: no cover log.debug('do_validation: value: {0}, index: {1}:' ' {2}'.format(val, - _index, + force_index, err), exc_info=True) if is_warnings_only: @@ -443,15 +424,16 @@ class Option(OnlyOption): all_cons_opts, params) #validate default value when add consistency - #FIXME validation! + option_bag = OptionBag() + option_bag.set_option(self, + undefined, + None, + undefined) self.impl_validate(self.impl_getdefault(), - undefined) + option_bag) self.impl_validate(self.impl_getdefault(), - undefined, + option_bag, check_error=False) - #FIXME - #if err: - # self._del_consistency() if func != '_cons_not_equal': #consistency could generate warnings or errors self._has_dependency = True @@ -465,37 +447,45 @@ class Option(OnlyOption): opt._add_dependency(self) def valid_consistency(self, - option, + option_bag, value, context, - index, - check_error, - config_bag): + check_error): if context is not undefined: descr = context.cfgimpl_get_description() # no consistency found at all if descr._cache_consistencies is None: return # get consistencies for this option - if isinstance(option, DynSymLinkOption): - consistencies = descr._cache_consistencies.get(option.impl_getopt()) + if isinstance(option_bag.option, DynSymLinkOption): + consistencies = descr._cache_consistencies.get(option_bag.option.impl_getopt()) else: - consistencies = descr._cache_consistencies.get(option) + consistencies = descr._cache_consistencies.get(option_bag.option) else: # is no context, get consistencies in option - consistencies = option.get_consistencies() - if consistencies is not None: + consistencies = option_bag.option.get_consistencies() + if consistencies: + if option_bag.config_bag is undefined: + cconfig_bag = undefined + elif option_bag.config_bag.force_permissive != True: + cconfig_bag = ConfigBag(config=option_bag.config_bag.config, + _setting_properties=option_bag.config_bag._setting_properties, + force_permissive=True, + force_unrestraint=option_bag.config_bag.force_unrestraint, + _validate=option_bag.config_bag._validate) + else: + cconfig_bag = option_bag.config_bag for cons_id, func, all_cons_opts, params in consistencies: warnings_only = params.get('warnings_only', False) if (warnings_only and not check_error) or (not warnings_only and check_error): transitive = params.get('transitive', True) #all_cons_opts[0] is the option where func is set - if isinstance(option, DynSymLinkOption): + if isinstance(option_bag.ori_option, DynSymLinkOption): opts = [] for opt in all_cons_opts: opts.append(DynSymLinkOption(opt(), - option._rootpath, - option._suffix)) + option_bag.ori_option._rootpath, + option_bag.ori_option._suffix)) wopt = opts[0] else: opts = all_cons_opts @@ -503,25 +493,23 @@ class Option(OnlyOption): wopt.launch_consistency(self, func, cons_id, - option, + option_bag, value, context, - index, opts, warnings_only, transitive, - config_bag) + cconfig_bag) def _get_consistency_value(self, - orig_option, + option_bag, current_option, - index, cons_id, context, config_bag, value, - transitive, func): + index = option_bag.index if func in ALLOWED_CONST_LIST: index = None index_ = None @@ -529,7 +517,7 @@ class Option(OnlyOption): index_ = None else: index_ = index - if orig_option == current_option: + if option_bag.ori_option == current_option: # orig_option is current option # we have already value, so use it return value @@ -537,14 +525,18 @@ class Option(OnlyOption): #if no context get default value return current_option.impl_getdefault() #otherwise calculate value - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = current_option - sconfig_bag.fromconsistency.append(cons_id) - sconfig_bag.force_permissive = True path = current_option.impl_getpath(context) + coption_bag = OptionBag() + coption_bag.set_option(current_option, + path, + index_, + config_bag) + cons_ids = option_bag.fromconsistency.copy() + cons_ids.append(cons_id) + coption_bag.fromconsistency = cons_ids + opt_value = context.getattr(path, - index_, - sconfig_bag) + coption_bag) if index_ is None and index is not None: if len(opt_value) <= index: opt_value = current_option.impl_getdefault_multi() @@ -556,10 +548,9 @@ class Option(OnlyOption): current_opt, func, cons_id, - option, + option_bag, value, context, - index, opts, warnings_only, transitive, @@ -583,9 +574,7 @@ class Option(OnlyOption): :param transitive: propertyerror is transitive :type transitive: `boolean` """ - #if context is not undefined: - # descr = context.cfgimpl_get_description() - if config_bag is not undefined and cons_id in config_bag.fromconsistency: + if cons_id in option_bag.fromconsistency: return all_cons_vals = [] all_cons_opts = [] @@ -595,31 +584,30 @@ class Option(OnlyOption): opt = opt() is_multi = False try: - opt_value = self._get_consistency_value(option, + opt_value = self._get_consistency_value(option_bag, opt, - index, cons_id, context, config_bag, value, - transitive, func) except PropertiesOptionError as err: if debug: # pragma: no cover log.debug('propertyerror in launch_consistency: {0}'.format(err)) if transitive: - err.set_orig_opt(option) + err.set_orig_opt(option_bag.option) raise err else: - if opt.impl_is_multi() and index is None and func not in ALLOWED_CONST_LIST: + if opt.impl_is_multi() and option_bag.index is None and \ + func not in ALLOWED_CONST_LIST: len_value = len(opt_value) if length is not None and length != len_value: if context is undefined: return - raise ValueError(_('unexpected length of "{}" in constency "{}", should be "{}"' - '').format(len(opt_value), - opt.impl_get_display_name(), - length)) + raise ValueError(_('unexpected length of "{}" in constency "{}", ' + 'should be "{}"').format(len(opt_value), + opt.impl_get_display_name(), + length)) else: length = len_value is_multi = True diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index f0f18c9..5f8d91e 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -22,7 +22,7 @@ from copy import copy from ..i18n import _ -from ..setting import ConfigBag, groups, undefined, owners +from ..setting import ConfigBag, OptionBag, groups, undefined, owners from .baseoption import BaseOption, OnlyOption from .option import ALLOWED_CONST_LIST, DynSymLinkOption from .syndynoptiondescription import SynDynOptionDescription @@ -173,11 +173,14 @@ class CacheOptionDescription(BaseOption): '"force_store_value" property').format( option.impl_get_display_name())) if not values._p_.hasvalue(subpath): - config_bag = ConfigBag(config=context, option=option) - config_bag.properties = frozenset() - value = values.getvalue(subpath, - None, - config_bag) + config_bag = ConfigBag(config=context) + option_bag = OptionBag() + option_bag.set_option(option, + subpath, + None, + config_bag) + option_bag.properties = frozenset() + value = values.getvalue(option_bag) value_setted = True values._p_.setvalue(subpath, value, @@ -224,13 +227,14 @@ class OptionDescriptionWalk(CacheOptionDescription): __slots__ = ('_children',) def build_dynoptions(self, - option, - config_bag): + option_bag): + option = option_bag.option dynopt = option.getsubdyn() rootpath = self.impl_get_path_by_opt(dynopt) ori_index = len(rootpath) + 1 - subpaths = [rootpath] + option.impl_getpath(config_bag.config)[ori_index:].split('.')[:-1] - for suffix in dynopt._impl_get_suffixes(config_bag): + subpaths = [rootpath] + option.impl_getpath( + option_bag.config_bag.config)[ori_index:].split('.')[:-1] + for suffix in dynopt._impl_get_suffixes(option_bag): subpath = '.'.join([subp + suffix for subp in subpaths]) if isinstance(option, OnlyOption): yield DynSymLinkOption(option, @@ -257,7 +261,12 @@ class OptionDescriptionWalk(CacheOptionDescription): name = option.impl_getname() if option.issubdyn(): if byname.startswith(name): - for doption in self.build_dynoptions(option, config_bag): + option_bag = OptionBag() + option_bag.set_option(option, + path, + None, + config_bag) + for doption in self.build_dynoptions(option_bag): if byname == doption.impl_getname(): dpath = doption.impl_getpath(config_bag.config) return (dpath, doption) @@ -319,9 +328,12 @@ class OptionDescriptionWalk(CacheOptionDescription): subpath = '' else: subpath = self.impl_getpath(config_bag.config) - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = child - for suffix in child._impl_get_suffixes(sconfig_bag): + option_bag = OptionBag() + option_bag.set_option(child, + subpath, + None, + config_bag) + for suffix in child._impl_get_suffixes(option_bag): yield SynDynOptionDescription(child, subpath, suffix) @@ -339,11 +351,16 @@ class OptionDescriptionWalk(CacheOptionDescription): subconfig, config_bag): for child in self._impl_st_getchildren(only_dyn=True): - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = child + #sconfig_bag = config_bag.copy('nooption') + #sconfig_bag.option = child cname = child.impl_getname() if name.startswith(cname): - for value in child._impl_get_suffixes(sconfig_bag): + option_bag = OptionBag() + option_bag.set_option(child, + subconfig.cfgimpl_get_path(), + None, + config_bag) + for value in child._impl_get_suffixes(option_bag): if name == cname + value: return SynDynOptionDescription(child, subconfig.cfgimpl_get_path(), diff --git a/tiramisu/option/symlinkoption.py b/tiramisu/option/symlinkoption.py index 10fd849..109b213 100644 --- a/tiramisu/option/symlinkoption.py +++ b/tiramisu/option/symlinkoption.py @@ -20,7 +20,7 @@ # ____________________________________________________________ from .baseoption import OnlyOption from ..i18n import _ -from ..setting import undefined +from ..setting import undefined, OptionBag class SymLinkOption(OnlyOption): @@ -104,22 +104,21 @@ class DynSymLinkOption(object): def impl_validate(self, value, - config_bag, + option_bag, context=undefined, - force_index=None, - current_opt=undefined, - is_multi=None, - check_error=True, - multi=None): - # add current_opt ! + check_error=True): + context = option_bag.config_bag.config + soption_bag = OptionBag() + soption_bag.set_option(self._opt, + self.impl_getpath(context), + option_bag.index, + option_bag.config_bag) + soption_bag.ori_option = option_bag.option + soption_bag.fromconsistency = option_bag.fromconsistency.copy() self._opt.impl_validate(value, - config_bag, - context, - force_index, - current_opt=self, - is_multi=is_multi, - check_error=check_error, - multi=multi) + soption_bag, + context=context, + check_error=check_error) def impl_is_dynsymlinkoption(self): return True diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 1d58224..60c912e 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -117,77 +117,99 @@ debug = False static_set = frozenset() -class ConfigBag(object): - __slots__ = ('default', - 'config', - 'option', - 'ori_option', - 'properties', - 'setting_properties', - 'force_permissive', - 'force_unrestraint', - 'trusted_cached_properties', - 'fromconsistency', - '_validator' +class OptionBag: + __slots__ = ('option', # current option + 'path', + 'index', + 'config_bag', + 'ori_option', # original option (for example useful for symlinkoption) + 'properties', # properties of current option + 'apply_requires', + 'fromconsistency' # history for consistency + ) + + def __init__(self): + self.option = None + self.fromconsistency = [] + + def set_option(self, + option, + path, + index, + config_bag): + if self.option != None: + raise Exception('hu?') + if path is None: + path = config_bag.config.cfgimpl_get_description().impl_get_path_by_opt(option) + self.path = path + self.index = index + self.option = option + self.config_bag = config_bag + + def __getattr__(self, key): + if key == 'properties': + settings = self.config_bag.config.cfgimpl_get_settings() + self.properties = settings.getproperties(self, apply_requires=self.apply_requires) + return self.properties + elif key == 'ori_option': + return self.option + elif key == 'apply_requires': + return True + raise KeyError('unknown key {} for OptionBag'.format(key)) + + +class ConfigBag: + __slots__ = ('config', # link to the current config (context) + '_setting_properties', # properties for current config + 'force_permissive', # force permissive + 'force_unrestraint', # do not validate properties + '_validate', # validate ) def __init__(self, config, **kwargs): - self.default = {'force_permissive': False, - 'force_unrestraint': False, - 'trusted_cached_properties': True, - } + self.force_permissive = False + self.force_unrestraint = False self.config = config - self._validator = True - self.fromconsistency = [] + self._validate = True for key, value in kwargs.items(): - if value != self.default.get(key): - setattr(self, key, value) + setattr(self, key, value) def __getattr__(self, key): if key == 'validate_properties': return not self.force_unrestraint if key == 'validate': - if self.setting_properties is not None: - return 'validator' in self.setting_properties - return self._validator + if self._validate and self._setting_properties is not None: + return 'validator' in self._setting_properties + return self._validate if key == 'setting_properties': if self.force_unrestraint: return None - self.setting_properties = self.config.cfgimpl_get_settings().get_context_properties() - return self.setting_properties + return self._setting_properties + if key == '_setting_properties': + self._setting_properties = self.config.cfgimpl_get_settings().get_context_properties() + if self._validate is False: + self._setting_properties = self._setting_properties - {'validator'} + return self._setting_properties if key not in self.__slots__: - raise KeyError('unknown key {}'.format(key)) - return self.default.get(key) + raise KeyError('unknown key {} for ConfigBag'.format(key)) + return None def __setattr__(self, key, value): if key == 'validate': - if self.setting_properties is not None: - if value is False: - self.setting_properties = frozenset(set(self.setting_properties) - {'validator'}) - else: - self.setting_properties = frozenset(set(self.setting_properties) | {'validator'}) - else: - self._validator = value + try: + del self._setting_properties + except AttributeError: + pass + self._validate = value else: super().__setattr__(key, value) - def delete(self, key): - try: - return self.__delattr__(key) - except AttributeError: - pass - - def copy(self, filters='all'): + def copy(self): kwargs = {} for key in self.__slots__: - if filters == 'nooption' and (key.startswith('option') or \ - key == 'properties'): - continue - if key == 'fromconsistency': + if key == 'fromconsistency' and self.fromconsistency != []: kwargs['fromconsistency'] = copy(self.fromconsistency) - elif key != 'default': - value = getattr(self, key) - if value != self.default.get(key): - kwargs[key] = value + else: + kwargs[key] = getattr(self, key) return ConfigBag(**kwargs) @@ -357,13 +379,14 @@ class Settings(object): return props def getproperties(self, - path, - index, - config_bag, + option_bag, apply_requires=True): """ """ - opt = config_bag.option + opt = option_bag.option + config_bag = option_bag.config_bag + path = option_bag.path + index = option_bag.index if opt.impl_is_symlinkoption(): opt = opt.impl_getopt() path = opt.impl_getpath(self._getcontext()) @@ -384,17 +407,11 @@ class Settings(object): props = self._p_.getproperties(path, opt.impl_getproperties()) else: - props = meta.cfgimpl_get_settings().getproperties(path, - index, - config_bag, + props = meta.cfgimpl_get_settings().getproperties(option_bag, apply_requires) if apply_requires: - props |= self.apply_requires(path, - opt.impl_getrequires(), - index, - False, - config_bag, - opt.impl_get_display_name()) + props |= self.apply_requires(option_bag, + False) props -= self.getpermissive(opt, path) if apply_requires: @@ -421,12 +438,8 @@ class Settings(object): return self._pp_.getpermissive(path) def apply_requires(self, - path, - current_requires, - index, - readable, - config_bag, - name): + option_bag, + readable): """carries out the jit (just in time) requirements between options a requirement is a tuple of this form that comes from the option's @@ -470,7 +483,7 @@ class Settings(object): :param path: the option's path in the config :type path: str """ - #current_requires = opt.impl_getrequires() + current_requires = option_bag.option.impl_getrequires() # filters the callbacks if readable: @@ -489,32 +502,38 @@ class Settings(object): breaked = False for option, expected in exps: reqpath = option.impl_getpath(context) - #FIXME c'est un peut tard ! - if reqpath.startswith(path + '.'): + #FIXME c'est un peu tard ! + if reqpath.startswith(option_bag.path + '.'): raise RequirementError(_("malformed requirements " "imbrication detected for option:" " '{0}' with requirement on: " - "'{1}'").format(path, reqpath)) + "'{1}'").format(option_bag.path, reqpath)) idx = None is_indexed = False if option.impl_is_master_slaves('slave'): - idx = index + idx = option_bag.index elif option.impl_is_multi(): is_indexed = True - sconfig_bag = config_bag.copy('nooption') - if config_bag.option == option: - sconfig_bag.setting_properties = None - sconfig_bag.force_unrestraint= False - sconfig_bag.validate = False - else: - sconfig_bag.force_permissive = True - sconfig_bag.option = option + config_bag = ConfigBag(config=option_bag.config_bag.config, + _setting_properties=option_bag.config_bag._setting_properties, + force_permissive=True, + force_unrestraint=option_bag.config_bag.force_unrestraint, + _validate=option_bag.config_bag._validate) + soption_bag = OptionBag() + soption_bag.set_option(option, + reqpath, + idx, + config_bag) + soption_bag.config_bag.force_permissive = True + if option_bag.option == option: + soption_bag.config_bag.force_unrestraint = True + soption_bag.config_bag.validate = False + soption_bag.apply_requires = False try: value = context.getattr(reqpath, - idx, - sconfig_bag) + soption_bag) if is_indexed: - value = value[index] + value = value[option_bag.index] except PropertiesOptionError as err: properties = err.proptype if not transitive: @@ -532,19 +551,15 @@ class Settings(object): prop_msg = _('properties') raise RequirementError(_('cannot access to option "{0}" because ' 'required option "{1}" has {2} {3}' - '').format(name, + '').format(option_bag.option.impl_get_display_name(), option.impl_get_display_name(), prop_msg, display_list(list(properties), add_quote=True))) # transitive action, add action if operator != 'and': if readable: - for msg in self.apply_requires(err._path, - err._requires, - err._index, - True, - err._config_bag, - err._name).values(): + for msg in self.apply_requires(err._option_bag, + True).values(): calc_properties.setdefault(action, []).extend(msg) else: calc_properties.add(action) @@ -587,23 +602,25 @@ class Settings(object): def setproperties(self, path, properties, - config_bag): + option_bag): """save properties for specified path (never save properties if same has option properties) """ # should have index !!! if self._getcontext().cfgimpl_get_meta() is not None: raise ConfigError(_('cannot change property with metaconfig')) - if path is not None and config_bag.option.impl_getrequires() is not None: - not_allowed_props = properties & getattr(config_bag.option, '_calc_properties', static_set) + if path is not None and option_bag.option.impl_getrequires() is not None: + not_allowed_props = properties & \ + getattr(option_bag.option, '_calc_properties', static_set) if not_allowed_props: - raise ValueError(_('cannot set property {} for option "{}" this property is calculated' - '').format(display_list(list(not_allowed_props), add_quote=True), - config_bag.option.impl_get_display_name())) - if config_bag is None: + raise ValueError(_('cannot set property {} for option "{}" this property is ' + 'calculated').format(display_list(list(not_allowed_props), + add_quote=True), + option_bag.option.impl_get_display_name())) + if option_bag is None: opt = None else: - opt = config_bag.option + opt = option_bag.option if opt and opt.impl_is_symlinkoption(): raise TypeError(_("can't assign property to the symlinkoption \"{}\"" "").format(opt.impl_get_display_name())) @@ -616,21 +633,20 @@ class Settings(object): self._p_.setproperties(path, properties) #values too because of slave values could have a PropertiesOptionError has value - self._getcontext().cfgimpl_reset_cache(opt, - path, - config_bag) + self._getcontext().cfgimpl_reset_cache(option_bag) + if option_bag is not None: + try: + del option_bag.properties + except AttributeError: + pass def set_context_permissive(self, permissive): self.setpermissive(None, - None, - None, permissive) def setpermissive(self, - opt, - path, - config_bag, + option_bag, permissives): """ enables us to put the permissives in the storage @@ -645,51 +661,55 @@ class Settings(object): raise ConfigError(_('cannot change permissive with metaconfig')) if not isinstance(permissives, frozenset): raise TypeError(_('permissive must be a frozenset')) - if opt and opt.impl_is_symlinkoption(): - raise TypeError(_("can't assign permissive to the symlinkoption \"{}\"" - "").format(opt.impl_get_display_name())) + if option_bag is not None: + opt = option_bag.option + if opt and opt.impl_is_symlinkoption(): + raise TypeError(_("can't assign permissive to the symlinkoption \"{}\"" + "").format(opt.impl_get_display_name())) + path = option_bag.path + else: + path = None forbidden_permissives = FORBIDDEN_SET_PERMISSIVES & permissives if forbidden_permissives: raise ConfigError(_('cannot add those permissives: {0}').format( ' '.join(forbidden_permissives))) self._pp_.setpermissive(path, permissives) - self._getcontext().cfgimpl_reset_cache(opt, - path, - config_bag) + if option_bag is not None: + self._getcontext().cfgimpl_reset_cache(option_bag) #____________________________________________________________ # reset methods def reset(self, - opt, - path, - config_bag, - all_properties=False): + option_bag): + # all_properties=False): if self._getcontext().cfgimpl_get_meta() is not None: raise ConfigError(_('cannot change property with metaconfig')) + if option_bag is None: + opt = None + else: + opt = option_bag.option if opt and opt.impl_is_symlinkoption(): raise TypeError(_("can't reset properties to the symlinkoption \"{}\"" "").format(opt.impl_get_display_name())) - if all_properties and (path or opt): - raise ValueError(_('opt and all_properties must not be set ' - 'together in reset')) - if all_properties: - self._p_.reset_all_properties() + #if all_properties and (path or opt): + # raise ValueError(_('opt and all_properties must not be set ' + # 'together in reset')) + #if all_properties: + # self._p_.reset_all_properties() else: - if opt is not None and path is None: - path = opt.impl_getpath(self._getcontext()) + if opt is not None: + path = option_bag.path + else: + path = None self._p_.delproperties(path) - self._getcontext().cfgimpl_reset_cache(opt, - path, - config_bag) + self._getcontext().cfgimpl_reset_cache(option_bag) #____________________________________________________________ # validate properties def validate_properties(self, - path, - index, - config_bag): + option_bag): """ validation upon the properties related to `opt` @@ -697,70 +717,56 @@ class Settings(object): :param force_permissive: behaves as if the permissive property was present """ - opt = config_bag.option - # calc properties - self_properties = config_bag.properties - if self_properties is None: - self_properties = self.getproperties(path, - index, - config_bag) - config_bag.properties = self_properties + self_properties = option_bag.properties + config_bag = option_bag.config_bag properties = self_properties & config_bag.setting_properties - {'frozen', 'mandatory', 'empty'} # remove permissive properties - if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and properties: + if (config_bag.force_permissive is True or \ + 'permissive' in config_bag.setting_properties) and properties: # remove global permissive if need properties -= self.get_context_permissive() # at this point an option should not remain in properties if properties != frozenset(): - raise PropertiesOptionError(path, - index, - config_bag, + raise PropertiesOptionError(option_bag, properties, self) def validate_mandatory(self, - path, - index, value, - config_bag): + option_bag): values = self._getcontext().cfgimpl_get_values() - opt = config_bag.option + opt = option_bag.option + config_bag = option_bag.config_bag is_mandatory = False if config_bag.setting_properties and 'mandatory' in config_bag.setting_properties: if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and \ 'mandatory' in self.get_context_permissive(): pass - elif 'mandatory' in config_bag.properties and values.isempty(opt, + elif 'mandatory' in option_bag.properties and values.isempty(opt, value, - index=index): + index=option_bag.index): is_mandatory = True - if 'empty' in config_bag.properties and values.isempty(opt, + if 'empty' in option_bag.properties and values.isempty(opt, value, force_allow_empty_list=True, - index=index): + index=option_bag.index): is_mandatory = True if is_mandatory: - raise PropertiesOptionError(path, - index, - config_bag, + raise PropertiesOptionError(option_bag, ['mandatory'], self) def validate_frozen(self, - path, - index, - config_bag): - if config_bag.setting_properties and \ - ('everything_frozen' in config_bag.setting_properties or - 'frozen' in config_bag.properties) and \ - not ((config_bag.force_permissive is True or - 'permissive' in config_bag.setting_properties) and + option_bag): + if option_bag.config_bag.setting_properties and \ + ('everything_frozen' in option_bag.config_bag.setting_properties or + 'frozen' in option_bag.properties) and \ + not ((option_bag.config_bag.force_permissive is True or + 'permissive' in option_bag.config_bag.setting_properties) and 'frozen' in self.get_context_permissive()): - raise PropertiesOptionError(path, - index, - config_bag, + raise PropertiesOptionError(option_bag, ['frozen'], self) return False diff --git a/tiramisu/storage/util.py b/tiramisu/storage/util.py index 63811d7..a769adf 100644 --- a/tiramisu/storage/util.py +++ b/tiramisu/storage/util.py @@ -44,13 +44,14 @@ class Cache(DictCache): _display_classname(self), id(self))) self._setcache(path, index, val, time()) - if DEBUG: - print('not setcache {} with index {} and value {} in {} ({})'.format(path, - index, - val, - _display_classname(self), - id(self))) - return + elif DEBUG: + print('not setcache {} with index {} and value {} and props {} and {} in {} ({})'.format(path, + index, + val, + props, + self_props, + _display_classname(self), + id(self))) def getcache(self, path, @@ -119,5 +120,5 @@ class Cache(DictCache): example: {'path1': {'index1': ('value1', 'time1')}, 'path2': {'index2': ('value2', 'time2', )}} """ if DEBUG: - print('get_chached', self._cache) + print('get_chached {} for {} ({})'.format(self._cache, _display_classname(self), id(self))) return self._get_cached() diff --git a/tiramisu/value.py b/tiramisu/value.py index fc0f12e..6fbc04f 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -17,7 +17,7 @@ # ____________________________________________________________ import weakref from .error import ConfigError, PropertiesOptionError -from .setting import owners, expires_time, undefined, forbidden_owners +from .setting import owners, expires_time, undefined, forbidden_owners, OptionBag, ConfigBag from .autolib import carry_out_calculation from .i18n import _ @@ -63,9 +63,7 @@ class Values(object): # get value def get_cached_value(self, - path, - index, - config_bag): + option_bag): """get value directly in cache if set otherwise calculated value and set it in cache @@ -81,40 +79,36 @@ class Values(object): :returns: value """ # try to retrive value in cache - setting_properties = config_bag.setting_properties - is_cached, value = self._p_.getcache(path, + setting_properties = option_bag.config_bag.setting_properties + is_cached, value = self._p_.getcache(option_bag.path, expires_time, - index, + option_bag.index, setting_properties, - config_bag.properties, + option_bag.properties, 'value') if not is_cached: # no cached value so get value - value = self.getvalue(path, - index, - config_bag) + value = self.getvalue(option_bag) # validate value context = self._getcontext() - opt = config_bag.option + opt = option_bag.option opt.impl_validate(value, + option_bag, context=context, - force_index=index, - check_error=True, - config_bag=config_bag) + check_error=True) if setting_properties and 'warnings' in setting_properties: opt.impl_validate(value, + option_bag, context=context, - force_index=index, - check_error=False, - config_bag=config_bag) + check_error=False) # store value in cache if not is_cached: - self._p_.setcache(path, - index, + self._p_.setcache(option_bag.path, + option_bag.index, value, setting_properties, - config_bag.properties) + option_bag.properties) if isinstance(value, list): # return a copy, so value cannot be modified return value.copy() @@ -122,9 +116,7 @@ class Values(object): return value def getvalue(self, - path, - index, - config_bag): + option_bag): """actually retrieves the value :param path: the path of the `Option` @@ -134,37 +126,22 @@ class Values(object): """ # get owner and value from store # index allowed only for slave - opt = config_bag.option - is_slave = opt.impl_is_master_slaves('slave') + index = option_bag.index + is_slave = option_bag.option.impl_is_master_slaves('slave') if index is None or not is_slave: _index = None else: _index = index - owner, value = self._p_.getowner(path, + owner, value = self._p_.getowner(option_bag.path, owners.default, index=_index, with_value=True) if owner != owners.default: - if config_bag.setting_properties is None: - # get property without apply requires - settings = self._getcontext().cfgimpl_get_settings() - self_properties = settings.getproperties(path, - index, - config_bag, - apply_requires=False) - else: - # if a value is store in storage, check if not frozen + force_default_on_freeze - # if frozen + force_default_on_freeze => force default value - self_properties = config_bag.properties - if self_properties is None: - settings = self._getcontext().cfgimpl_get_settings() - self_properties = settings.getproperties(path, - index, - config_bag) - config_bag.properties = self_properties - if not ('frozen' in self_properties and \ - 'force_default_on_freeze' in self_properties): + # if a value is store in storage, check if not frozen + force_default_on_freeze + # if frozen + force_default_on_freeze => force default value + if not ('frozen' in option_bag.properties and \ + 'force_default_on_freeze' in option_bag.properties): if index is not None and not is_slave: if len(value) > index: return value[index] @@ -172,14 +149,10 @@ class Values(object): #so return default value else: return value - return self.getdefaultvalue(path, - index, - config_bag) + return self.getdefaultvalue(option_bag) def getdefaultvalue(self, - path, - index, - config_bag): + option_bag): """get default value: - get meta config value or - get calculated value or @@ -192,36 +165,43 @@ class Values(object): :returns: default value """ context = self._getcontext() - opt = config_bag.option + config_bag = option_bag.config_bag + opt = option_bag.option + index = option_bag.index def _reset_cache(_value): - is_cache, cache_value = self._p_.getcache(path, + is_cache, cache_value = self._p_.getcache(option_bag.path, expires_time, index, config_bag.setting_properties, - config_bag.properties, + option_bag.properties, 'value') if is_cache and cache_value == _value: # calculation return same value as previous value, # so do not invalidate cache return # calculated value is a new value, so reset cache - context.cfgimpl_reset_cache(opt, - path, - config_bag) + context.cfgimpl_reset_cache(option_bag) if opt.impl_is_master_slaves('slave'): index_ = index else: index_ = None - if self._is_meta(path, - index_, - config_bag): + if option_bag.index != index_: + moption_bag = OptionBag() + moption_bag.set_option(opt, + option_bag.path, + index_, + config_bag) + moption_bag.fromconsistency = option_bag.fromconsistency.copy() + else: + moption_bag = option_bag + if self._is_meta(moption_bag): meta = context.cfgimpl_get_meta() # retrieved value from meta config try: - value = meta.getattr(path, - index, - config_bag) + # FIXME could have different property! + value = meta.getattr(moption_bag.path, + moption_bag) except PropertiesOptionError: # if properties error, return an other default value # unexpected error, should not happened @@ -237,7 +217,7 @@ class Values(object): callback=callback, callback_params=callback_params, index=index, - config_bag=config_bag) + option_bag=option_bag) if isinstance(value, list) and index is not None: # if value is a list and index is set if opt.impl_is_submulti() and (value == [] or not isinstance(value[0], list)): @@ -309,116 +289,94 @@ class Values(object): # set value def setvalue(self, - path, - index, value, - config_bag, + option_bag, _commit): context = self._getcontext() owner = context.cfgimpl_get_settings().getowner() - if config_bag.validate: - if index is not None or config_bag.option._has_consistencies(context): + if option_bag.config_bag.validate: + if option_bag.index is not None or option_bag.option._has_consistencies(context): # set value to a fake config when option has dependency # validation will be complet in this case (consistency, ...) tested_context = context._gen_fake_values() - sconfig_bag = config_bag.copy() - sconfig_bag.validate = False - tested_context.cfgimpl_get_values().setvalue(path, - index, - value, - sconfig_bag, - True) - sconfig_bag.validate = True - tested_context.getattr(path, - index, - sconfig_bag) + #sconfig_bag = config_bag.copy() + ori_validate = option_bag.config_bag.validate + if ori_validate is True: + option_bag.config_bag.validate = False + try: + tested_context.cfgimpl_get_values().setvalue(value, + option_bag, + True) + option_bag.config_bag.validate = True + tested_context.getattr(option_bag.path, + option_bag) + except Exception as exc: + if ori_validate is True: + option_bag.config_bag.validate = True + raise exc else: - self.setvalue_validation(path, - index, - value, - config_bag) + self.setvalue_validation(value, + option_bag) - self._setvalue(path, - index, + self._setvalue(option_bag, value, owner, - config_bag, commit=_commit) def setvalue_validation(self, - path, - index, value, - config_bag): + option_bag): context = self._getcontext() settings = context.cfgimpl_get_settings() # First validate properties with this value - self_properties = config_bag.properties - if self_properties is None: - self_properties = settings.getproperties(path, - index, - config_bag) - config_bag.properties = self_properties - opt = config_bag.option - settings.validate_frozen(path, - index, - config_bag) - settings.validate_mandatory(path, - index, - value, - config_bag) + opt = option_bag.option + settings.validate_frozen(option_bag) + settings.validate_mandatory(value, + option_bag) # Value must be valid for option opt.impl_validate(value, - config_bag, + option_bag, context, - check_error=True, - force_index=index) - if config_bag.setting_properties and 'warnings' in config_bag.setting_properties: + check_error=True) + if option_bag.config_bag.setting_properties and \ + 'warnings' in option_bag.config_bag.setting_properties: # No error found so emit warnings opt.impl_validate(value, - config_bag, + option_bag, context, - check_error=False, - force_index=index) + check_error=False) def _setvalue(self, - path, - index, + option_bag, value, owner, - config_bag, commit=True): - - self._getcontext().cfgimpl_reset_cache(config_bag.option, - path, - config_bag) + self._getcontext().cfgimpl_reset_cache(option_bag) if isinstance(value, list): # copy - value = list(value) - self._p_.setvalue(path, + value = value.copy() + self._p_.setvalue(option_bag.path, value, owner, - index, + option_bag.index, commit) def _is_meta(self, - path, - index, - config_bag, + option_bag, force_owner_is_default=False): - if not force_owner_is_default and self._p_.hasvalue(path, - index=index): + if not force_owner_is_default and self._p_.hasvalue(option_bag.path, + index=option_bag.index): # has already a value, so not meta return False context = self._getcontext() meta = context.cfgimpl_get_meta() if meta is None: return False - opt = config_bag.option + opt = option_bag.option if opt.impl_is_master_slaves('slave'): master = opt.impl_get_master_slaves().getmaster() masterp = master.impl_getpath(context) @@ -426,29 +384,23 @@ class Values(object): if self._p_.hasvalue(masterp, index=None): return False - return not meta.cfgimpl_get_values().is_default_owner(path, - index, - config_bag) + return not meta.cfgimpl_get_values().is_default_owner(option_bag) #______________________________________________________________________ # owner def is_default_owner(self, - path, - index, - config_bag, + option_bag, validate_meta=undefined): - return self._getowner(path, - index, - config_bag, - validate_meta=validate_meta, - only_default=True) == owners.default + return self.getowner(option_bag, + validate_meta=validate_meta, + only_default=True) == owners.default def getowner(self, - path, - index, - config_bag): + option_bag, + validate_meta=True, + only_default=False): """ retrieves the option's owner @@ -457,199 +409,144 @@ class Values(object): was present :returns: a `setting.owners.Owner` object """ - return self._getowner(path, - index, - config_bag) - - def _getowner(self, - path, - index, - config_bag, - validate_meta=True, - only_default=False): - """get owner of an option - """ context = self._getcontext() - opt = config_bag.option + opt = option_bag.option if opt.impl_is_symlinkoption(): - config_bag.ori_option = opt + option_bag.ori_option = opt opt = opt.impl_getopt() - config_bag.option = opt - path = opt.impl_getpath(context) - self_properties = config_bag.properties + option_bag.option = opt + option_bag.path = opt.impl_getpath(context) + self_properties = option_bag.properties settings = context.cfgimpl_get_settings() - if self_properties is None: - self_properties = settings.getproperties(path, - index, - config_bag) - config_bag.properties = self_properties - if config_bag.setting_properties is not None: - settings.validate_properties(path, - index, - config_bag) + if option_bag.config_bag.setting_properties is not None: + settings.validate_properties(option_bag) if 'frozen' in self_properties and 'force_default_on_freeze' in self_properties: return owners.default if only_default: - if self._p_.hasvalue(path, - index): + if self._p_.hasvalue(option_bag.path, + option_bag.index): owner = undefined else: owner = owners.default else: - owner = self._p_.getowner(path, + owner = self._p_.getowner(option_bag.path, owners.default, - index=index) + index=option_bag.index) if owner is owners.default and validate_meta is not False: meta = context.cfgimpl_get_meta() - if meta is not None and self._is_meta(path, - index, - config_bag): - owner = meta.cfgimpl_get_values()._getowner(path, - index, - config_bag, - only_default=only_default) + if meta is not None and self._is_meta(option_bag): + owner = meta.cfgimpl_get_values().getowner(option_bag, + only_default=only_default) return owner def setowner(self, - path, - index, owner, - config_bag): + option_bag): """ sets a owner to an option :param opt: the `option.Option` object :param owner: a valid owner, that is a `setting.owners.Owner` object """ - opt = config_bag.option + opt = option_bag.option if opt.impl_is_symlinkoption(): raise ConfigError(_("can't set owner for the symlinkoption \"{}\"" "").format(opt.impl_get_display_name())) if owner in forbidden_owners: raise ConfigError(_('set owner "{0}" is forbidden').format(str(owner))) - if not self._p_.hasvalue(path): + if not self._p_.hasvalue(option_bag.path): raise ConfigError(_('no value for {0} cannot change owner to {1}' - '').format(path, owner)) - self.setowner_validation(path, - index, - config_bag) - self._p_.setowner(path, owner, index=index) + '').format(option_bag.path, owner)) + self._getcontext().cfgimpl_get_settings().validate_frozen(option_bag) + self._p_.setowner(option_bag.path, + owner, + index=option_bag.index) #______________________________________________________________________ # reset def reset(self, - path, - config_bag, + option_bag, _commit=True): context = self._getcontext() setting = context.cfgimpl_get_settings() - hasvalue = self._p_.hasvalue(path) + hasvalue = self._p_.hasvalue(option_bag.path) - if hasvalue and config_bag.validate: + if hasvalue and option_bag.config_bag.validate: + ori_validate = option_bag.config_bag.validate + if ori_validate is True: + option_bag.config_bag.validate = False fake_context = context._gen_fake_values() fake_value = fake_context.cfgimpl_get_values() - sconfig_bag = config_bag.copy() - sconfig_bag.validate = False - fake_value.reset(path, - sconfig_bag) - value = fake_value.getdefaultvalue(path, - None, - config_bag) - fake_value.setvalue_validation(path, - None, - value, - config_bag) - opt = config_bag.option + fake_value.reset(option_bag) + if ori_validate is True: + option_bag.config_bag.validate = True + value = fake_value.getdefaultvalue(option_bag) + fake_value.setvalue_validation(value, + option_bag) + opt = option_bag.option if opt.impl_is_master_slaves('master'): opt.impl_get_master_slaves().reset(self, - config_bag, + option_bag, _commit=_commit) if hasvalue: - if 'force_store_value' in setting.getproperties(path, - None, - config_bag): - value = self.getdefaultvalue(path, - None, - config_bag) - self._setvalue(path, - None, + if 'force_store_value' in option_bag.properties: + value = self.getdefaultvalue(option_bag) + + self._setvalue(option_bag, value, owners.forced, - config_bag, commit=_commit) else: - self._p_.resetvalue(path, + self._p_.resetvalue(option_bag.path, _commit) - context.cfgimpl_reset_cache(config_bag.option, - path, - config_bag) + if not opt.impl_is_master_slaves('master'): + # if master, already reset behind + pass + context.cfgimpl_reset_cache(option_bag) def reset_slave(self, - path, - index, - config_bag): + option_bag): - if self._p_.hasvalue(path, index=index): + if self._p_.hasvalue(option_bag.path, index=option_bag.index): context = self._getcontext() - if config_bag.validate: + if option_bag.config_bag.validate: fake_context = context._gen_fake_values() fake_value = fake_context.cfgimpl_get_values() - sconfig_bag = config_bag.copy() - sconfig_bag.validate = False - fake_value.reset_slave(path, - index, - sconfig_bag) - value = fake_value.getdefaultvalue(path, - index, - config_bag) - fake_value.setvalue_validation(path, - index, - value, - config_bag) - self._p_.resetvalue_index(path, index) - context.cfgimpl_reset_cache(config_bag.option, - path, - config_bag) + ori_validate = option_bag.config_bag.validate + option_bag.config_bag.validate = False + try: + fake_value.reset_slave(option_bag) + value = fake_value.getdefaultvalue(option_bag) + fake_value.setvalue_validation(value, + option_bag) + option_bag.config_bag.validate = ori_validate + except Exception as err: + option_bag.config_bag.validate = ori_validate + raise err + self._p_.resetvalue_index(option_bag.path, option_bag.index) + context.cfgimpl_reset_cache(option_bag) def reset_master(self, - subconfig, - path, index, - config_bag): + option_bag, + subconfig): - current_value = self.get_cached_value(path, - None, - config_bag) + current_value = self.get_cached_value(option_bag) length = len(current_value) if index >= length: raise IndexError(_('index "{}" is higher than the length "{}" ' 'for option "{}"').format(index, length, - config_bag.option.impl_get_display_name())) + option_bag.option.impl_get_display_name())) current_value.pop(index) - self.setvalue(path, - None, - current_value, - config_bag, + self.setvalue(current_value, + option_bag, _commit=True) subconfig.cfgimpl_get_description().pop(self, index, - config_bag) - - def setowner_validation(self, - path, - index, - config_bag): - - context = self._getcontext() - settings = context.cfgimpl_get_settings() - # First validate properties with this value - self_properties = config_bag.properties - settings.validate_frozen(path, - index, - config_bag) + option_bag) #______________________________________________________________________ # information @@ -684,24 +581,26 @@ class Values(object): settings = context.cfgimpl_get_settings() # for option in config.cfgimpl_get_children(self.config_bag): for option in description.impl_getchildren(config_bag, context): - sconfig_bag = config_bag.copy('nooption') - sconfig_bag.option = option name = option.impl_getname() path = '.'.join(currpath + [name]) if option.impl_is_optiondescription(): - ori_setting_properties = sconfig_bag.setting_properties - sconfig_bag.setting_properties = od_setting_properties + ori_setting_properties = config_bag._setting_properties + config_bag._setting_properties = od_setting_properties try: + option_bag = OptionBag() + option_bag.set_option(option, + path, + None, + config_bag) subconfig = config.getattr(name, - None, - sconfig_bag) + option_bag) except PropertiesOptionError as err: pass else: - sconfig_bag.setting_properties = ori_setting_properties + config_bag._setting_properties = ori_setting_properties for path in self._mandatory_warnings(context, - sconfig_bag, + config_bag, option, currpath + [name], subconfig, @@ -711,26 +610,24 @@ class Values(object): # don't verifying symlink try: if not option.impl_is_master_slaves('slave'): - self_properties = settings.getproperties(path, - None, - sconfig_bag) - - sconfig_bag.properties = self_properties - if 'mandatory' in self_properties or 'empty' in self_properties: + option_bag = OptionBag() + option_bag.set_option(option, + path, + None, + config_bag) + if 'mandatory' in option_bag.properties or 'empty' in option_bag.properties: config.getattr(name, - None, - sconfig_bag) + option_bag) else: for index in range(config.cfgimpl_get_length()): - self_properties = settings.getproperties(path, - index, - sconfig_bag) - - sconfig_bag.properties = self_properties - if 'mandatory' in self_properties: + option_bag = OptionBag() + option_bag.set_option(option, + path, + index, + config_bag) + if 'mandatory' in option_bag.properties: config.getattr(name, - index, - sconfig_bag) + option_bag) except PropertiesOptionError as err: if err.proptype == ['mandatory']: yield path @@ -750,7 +647,8 @@ class Values(object): od_setting_properties = config_bag.setting_properties - {'mandatory', 'empty'} setting_properties = set(config_bag.setting_properties) - {'warnings'} setting_properties.update(['mandatory', 'empty']) - config_bag.setting_properties = frozenset(setting_properties) + config_bag = ConfigBag(config=config_bag.config) + config_bag._setting_properties = frozenset(setting_properties) config_bag.force_permissive = True descr = context.cfgimpl_get_description()