diff --git a/ChangeLog b/ChangeLog index 070a857..fc4aec4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Mon Mar 7 16:10:30 2016 +0200 Emmanuel Garette + * force_store_value is now used directly when configuration is loaded + Sun Nov 29 23:01:28 2015 +0200 Emmanuel Garette * requires could be apply to a slave and properties could be different diff --git a/test/test_freeze.py b/test/test_freeze.py index 4e1ffaf..67fe5a3 100644 --- a/test/test_freeze.py +++ b/test/test_freeze.py @@ -3,11 +3,13 @@ from autopath import do_autopath do_autopath() -from tiramisu.setting import owners +from py.test import raises + +from tiramisu.setting import owners, groups from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ StrOption, OptionDescription from tiramisu.config import Config -from tiramisu.error import PropertiesOptionError +from tiramisu.error import PropertiesOptionError, ConfigError #____________________________________________________________ @@ -38,6 +40,18 @@ def make_description_freeze(): return descr +def return_val(): + return 1 + + +def return_val2(value): + return value + + +def return_val3(context, value): + return value + + def test_freeze_whole_config(): descr = make_description_freeze() conf = Config(descr) @@ -155,9 +169,17 @@ def test_freeze_get_multi(): def test_force_store_value(): descr = make_description_freeze() conf = Config(descr) - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.wantref - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', False)} + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('forced', False), + 'wantref2': ('forced', False), + 'wantref3': ('forced', [False])} + conf.wantref = True + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', True), + 'wantref2': ('forced', False), + 'wantref3': ('forced', [False])} + del(conf.wantref) + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('forced', False), + 'wantref2': ('forced', False), + 'wantref3': ('forced', [False])} def test_force_store_value_no_requirement(): @@ -169,87 +191,47 @@ def test_force_store_value_no_requirement(): pass -def test_force_store_value_ro(): - descr = make_description_freeze() +def test_force_store_value_masterslaves_slave(): + b = IntOption('int', 'Test int option', multi=True) + c = StrOption('str', 'Test string option', multi=True, properties=('force_store_value',)) + descr = OptionDescription("int", "", [b, c]) + descr.impl_set_group_type(groups.master) + raises(ConfigError, "conf = Config(descr)") + + +def test_force_store_value_masterslaves(): + b = IntOption('int', 'Test int option', multi=True, properties=('force_store_value',)) + c = StrOption('str', 'Test string option', multi=True) + descr = OptionDescription("int", "", [b, c]) + descr.impl_set_group_type(groups.master) conf = Config(descr) - conf.read_only() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.wantref - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', False)} + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', [])} -def test_force_store_value_hidden(): - descr = make_description_freeze() +def test_force_store_value_callback(): + b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val) + descr = OptionDescription("int", "", [b]) conf = Config(descr) - conf.cfgimpl_get_settings().setpermissive(('hidden',)) - conf.read_write() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.getattr('wantref2', force_permissive=True) - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref2': ('user', False)} + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 1)} -def test_force_store_value_owner(): - descr = make_description_freeze() +def test_force_store_value_callback_params(): + b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val2, callback_params={'value': (2,)}) + descr = OptionDescription("int", "", [b]) conf = Config(descr) - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.getowner(conf.unwrap_from_path('wantref')) - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', False)} + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)} -def test_force_store_value_owner_ro(): - descr = make_description_freeze() +def test_force_store_value_callback_params_2(): + b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val3, callback_params={'': ((None,),), 'value': (2,)}) + descr = OptionDescription("int", "", [b]) conf = Config(descr) - conf.read_only() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.getowner(conf.unwrap_from_path('wantref')) - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', False)} + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)} -def test_force_store_value_owner_hidden(): - descr = make_description_freeze() +def test_force_store_value_callback_params_with_opt(): + a = IntOption('val1', "", 2) + b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val2, callback_params={'value': ((a, False),)}) + descr = OptionDescription("int", "", [a, b]) conf = Config(descr) - conf.cfgimpl_get_settings().setpermissive(('hidden',)) - conf.read_write() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.getowner(conf.unwrap_from_path('wantref2'), force_permissive=True) - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref2': ('user', False)} - - -def test_force_store_value_modified(): - descr = make_description_freeze() - conf = Config(descr) - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.cfgimpl_get_values().get_modified_values() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == { - 'wantref': ('user', False), 'wantref2': ('user', False), - 'wantref3': ('user', [False])} - - -def test_force_store_value_modified_ro(): - descr = make_description_freeze() - conf = Config(descr) - conf.read_only() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.cfgimpl_get_values().get_modified_values() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == { - 'wantref': ('user', False), 'wantref2': ('user', False), - 'wantref3': ('user', [False])} - - -def test_force_store_value_modified_hidden(): - descr = make_description_freeze() - conf = Config(descr) - conf.cfgimpl_get_settings().setpermissive(('hidden',)) - conf.read_write() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} - conf.cfgimpl_get_values().get_modified_values() - assert conf.cfgimpl_get_values()._p_.get_modified_values() == { - 'wantref': ('user', False), 'wantref2': ('user', False), - 'wantref3': ('user', [False])} - - -def test_force_store_value_multi(): - descr = make_description_freeze() - conf = Config(descr) - conf.read_write() - assert conf.getowner(conf.unwrap_from_path('wantref3')) == 'user' + assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)} diff --git a/tiramisu/config.py b/tiramisu/config.py index 2293e42..a09102b 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -540,8 +540,8 @@ class _CommonConfig(SubConfig): def _impl_build_all_caches(self): descr = self.cfgimpl_get_description() if not descr.impl_already_build_caches(): - descr.impl_build_cache() descr.impl_build_cache_option() + descr.impl_build_cache(self) def read_only(self): "read only is a global config's setting, see `settings.py`" @@ -685,10 +685,10 @@ class Config(_CommonConfig): self._impl_settings = Settings(self, settings) self._impl_values = Values(self, values) super(Config, self).__init__(descr, weakref.ref(self)) - self._impl_build_all_caches() self._impl_meta = None #undocumented option used only in test script self._impl_test = False + self._impl_build_all_caches() self._impl_name = name def cfgimpl_reset_cache(self, diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 0c083df..08ece6a 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -304,7 +304,7 @@ class BaseOption(Base): "frozen" (which has noting to do with the high level "freeze" propertie or "read_only" property) """ - if name not in ('_option', '_is_build_cache') and \ + if name != '_option' and \ not isinstance(value, tuple) and \ not name.startswith('_state') and \ not name == '_sa_instance_state': diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index 4c78d9a..dc5172b 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -23,7 +23,7 @@ import re from ..i18n import _ -from ..setting import groups, undefined # , log +from ..setting import groups, undefined, owners # , log from .baseoption import BaseOption, SymLinkOption from . import MasterSlaves from ..error import ConfigError, ConflictError @@ -92,26 +92,35 @@ class OptionDescription(BaseOption, StorageOptionDescription): """ return _impl_getpaths(self, include_groups, _currpath) - def impl_build_cache(self, _consistencies=None, cache_option=None): + def impl_build_cache(self, config, path='', _consistencies=None, + cache_option=None, force_store_values=None): """validate duplicate option and set option has readonly option """ if cache_option is None: init = True _consistencies = {} cache_option = [] + force_store_values = [] else: init = False for option in self._impl_getchildren(dyn=False): #FIXME specifique id for sqlalchemy? #FIXME avec sqlalchemy ca marche le multi parent ? (dans des configs différentes) cache_option.append(option._get_id()) + if path == '': + subpath = option.impl_getname() + else: + subpath = path + '.' + option.impl_getname() if isinstance(option, OptionDescription): option._set_readonly(False) - option.impl_build_cache(_consistencies, cache_option) + option.impl_build_cache(config, subpath, _consistencies, + cache_option, force_store_values) #cannot set multi option as OptionDescription requires else: option._set_readonly(True) is_multi = option.impl_is_multi() + if 'force_store_value' in option.impl_getproperties(): + force_store_values.append((subpath, option)) for func, all_cons_opts, params in option._get_consistencies(): option._valid_consistencies(all_cons_opts[1:]) if is_multi: @@ -175,6 +184,21 @@ class OptionDescription(BaseOption, StorageOptionDescription): opt.impl_getname())) self._cache_consistencies[opt] = tuple(cons) self._set_readonly(False) + for subpath, option in force_store_values: + value = config.cfgimpl_get_values()._get_cached_value(option, + path=subpath, + validate=False, + trusted_cached_properties=False, + validate_properties=True) + if option.impl_is_master_slaves('slave'): + # problem with index + raise ConfigError(_('a slave ({0}) cannot have ' + 'force_store_value property').format(subpath)) + if option._is_subdyn(): + raise ConfigError(_('a dynoption ({0}) cannot have ' + 'force_store_value property').format(subpath)) + config._impl_values._p_.setvalue(subpath, value, + owners.forced, None) # ____________________________________________________________ def impl_set_group_type(self, group_type): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 94765a3..0eb6cf0 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -214,6 +214,7 @@ def populate_owners(): """ setattr(owners, 'default', owners.DefaultOwner('default')) setattr(owners, 'user', owners.Owner('user')) + setattr(owners, 'forced', owners.Owner('forced')) def addowner(name): """ diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index ef4ad19..68989f9 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -38,6 +38,9 @@ class Values(Cache): """ values = [] vidx = None + if index is None: +# raise Exception('arf') + pass def _setvalue_info(nb, idx, value, vidx): lst = list(self._values[nb]) diff --git a/tiramisu/value.py b/tiramisu/value.py index 9740a44..e7dc6bb 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -103,9 +103,8 @@ class Values(object): value = opt.impl_getdefault_multi() return value - def _getvalue(self, opt, path, is_default, self_properties, - index=undefined, submulti_index=undefined, with_meta=True, - masterlen=undefined, returns_raise=False): + def _getvalue(self, opt, path, self_properties, index, submulti_index, + with_meta, masterlen): """actually retrieves the value :param opt: the `option.Option()` object @@ -114,6 +113,11 @@ class Values(object): force_default = 'frozen' in self_properties and \ 'force_default_on_freeze' in self_properties # not default value + is_default = self._is_default_owner(opt, path, + validate_properties=False, + validate_meta=False, + self_properties=self_properties, + index=index) if not is_default and not force_default: if opt.impl_is_master_slaves('slave'): return self._p_.getvalue(path, index) @@ -126,7 +130,8 @@ class Values(object): #so return default value else: return value - return self._getdefaultvalue(opt, path, with_meta, index, submulti_index, returns_raise) + return self._getdefaultvalue(opt, path, with_meta, index, + submulti_index, True) def get_modified_values(self): context = self._getcontext() @@ -172,7 +177,15 @@ class Values(object): if opt.impl_is_master_slaves('master'): opt.impl_get_master_slaves().reset(opt, self, _setting_properties) if hasvalue: - self._p_.resetvalue(path) + if 'force_store_value' in setting._getproperties(opt=opt, + path=path, + setting_properties=_setting_properties, + read_write=False, + apply_requires=False): + value = self._getdefaultvalue(opt, path, True, undefined, undefined, False) + self._setvalue(opt, path, value, force_owner=owners.forced) + else: + self._p_.resetvalue(path) context.cfgimpl_reset_cache() def _isempty(self, opt, value, force_allow_empty_list=False, index=None): @@ -300,17 +313,9 @@ class Values(object): setting_properties = setting._getproperties(read_write=False) if self_properties is undefined: self_properties = setting._getproperties(opt, path, read_write=False, index=index) - is_default = self._is_default_owner(opt, path, - validate_properties=False, - validate_meta=False, - self_properties=self_properties, - index=index) config_error = None - value = self._getvalue(opt, path, is_default, self_properties, - index=index, submulti_index=submulti_index, - with_meta=with_meta, - masterlen=masterlen, - returns_raise=True) + value = self._getvalue(opt, path, self_properties, index, submulti_index, + with_meta, masterlen) if isinstance(value, Exception): if isinstance(value, ConfigError): # For calculating properties, we need value (ie for mandatory @@ -351,13 +356,6 @@ class Values(object): config_error = err value = None - if is_default and 'force_store_value' in self_properties: - if isinstance(value, Multi): - item = list(value) - else: - item = value - self.setitem(opt, item, path, check_frozen=False, - force_permissive=force_permissive) if validate_properties: if config_error is not None: # should not raise PropertiesOptionError if option is @@ -407,10 +405,13 @@ class Values(object): raise err self._setvalue(opt, path, value) - def _setvalue(self, opt, path, value): + def _setvalue(self, opt, path, value, force_owner=undefined): context = self._getcontext() context.cfgimpl_reset_cache() - owner = context.cfgimpl_get_settings().getowner() + if force_owner is undefined: + owner = context.cfgimpl_get_settings().getowner() + else: + owner = force_owner # in storage, value must not be a multi if isinstance(value, Multi): value = list(value)