force_store_value is now used directly when configuration is loaded

This commit is contained in:
Emmanuel Garette 2016-03-07 16:13:41 +01:00
parent 51d14f30a4
commit da89c1aa58
8 changed files with 120 additions and 106 deletions

View File

@ -1,3 +1,6 @@
Mon Mar 7 16:10:30 2016 +0200 Emmanuel Garette <egarette@cadoles.com>
* force_store_value is now used directly when configuration is loaded
Sun Nov 29 23:01:28 2015 +0200 Emmanuel Garette <egarette@cadoles.com> Sun Nov 29 23:01:28 2015 +0200 Emmanuel Garette <egarette@cadoles.com>
* requires could be apply to a slave and properties could be different * requires could be apply to a slave and properties could be different

View File

@ -3,11 +3,13 @@
from autopath import do_autopath from autopath import do_autopath
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, \ from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
StrOption, OptionDescription StrOption, OptionDescription
from tiramisu.config import Config 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 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(): def test_freeze_whole_config():
descr = make_description_freeze() descr = make_description_freeze()
conf = Config(descr) conf = Config(descr)
@ -155,9 +169,17 @@ def test_freeze_get_multi():
def test_force_store_value(): def test_force_store_value():
descr = make_description_freeze() descr = make_description_freeze()
conf = Config(descr) conf = Config(descr)
assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('forced', False),
conf.wantref 'wantref2': ('forced', False),
assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', 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(): def test_force_store_value_no_requirement():
@ -169,87 +191,47 @@ def test_force_store_value_no_requirement():
pass pass
def test_force_store_value_ro(): def test_force_store_value_masterslaves_slave():
descr = make_description_freeze() 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 = Config(descr)
conf.read_only() assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', [])}
assert conf.cfgimpl_get_values()._p_.get_modified_values() == {}
conf.wantref
assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', False)}
def test_force_store_value_hidden(): def test_force_store_value_callback():
descr = make_description_freeze() b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val)
descr = OptionDescription("int", "", [b])
conf = Config(descr) conf = Config(descr)
conf.cfgimpl_get_settings().setpermissive(('hidden',)) assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 1)}
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)}
def test_force_store_value_owner(): def test_force_store_value_callback_params():
descr = make_description_freeze() b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val2, callback_params={'value': (2,)})
descr = OptionDescription("int", "", [b])
conf = Config(descr) conf = Config(descr)
assert conf.cfgimpl_get_values()._p_.get_modified_values() == {} assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)}
conf.getowner(conf.unwrap_from_path('wantref'))
assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref': ('user', False)}
def test_force_store_value_owner_ro(): def test_force_store_value_callback_params_2():
descr = make_description_freeze() 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 = Config(descr)
conf.read_only() assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)}
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)}
def test_force_store_value_owner_hidden(): def test_force_store_value_callback_params_with_opt():
descr = make_description_freeze() 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 = Config(descr)
conf.cfgimpl_get_settings().setpermissive(('hidden',)) assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)}
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'

View File

@ -540,8 +540,8 @@ class _CommonConfig(SubConfig):
def _impl_build_all_caches(self): def _impl_build_all_caches(self):
descr = self.cfgimpl_get_description() descr = self.cfgimpl_get_description()
if not descr.impl_already_build_caches(): if not descr.impl_already_build_caches():
descr.impl_build_cache()
descr.impl_build_cache_option() descr.impl_build_cache_option()
descr.impl_build_cache(self)
def read_only(self): def read_only(self):
"read only is a global config's setting, see `settings.py`" "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_settings = Settings(self, settings)
self._impl_values = Values(self, values) self._impl_values = Values(self, values)
super(Config, self).__init__(descr, weakref.ref(self)) super(Config, self).__init__(descr, weakref.ref(self))
self._impl_build_all_caches()
self._impl_meta = None self._impl_meta = None
#undocumented option used only in test script #undocumented option used only in test script
self._impl_test = False self._impl_test = False
self._impl_build_all_caches()
self._impl_name = name self._impl_name = name
def cfgimpl_reset_cache(self, def cfgimpl_reset_cache(self,

View File

@ -304,7 +304,7 @@ class BaseOption(Base):
"frozen" (which has noting to do with the high level "freeze" "frozen" (which has noting to do with the high level "freeze"
propertie or "read_only" property) propertie or "read_only" property)
""" """
if name not in ('_option', '_is_build_cache') and \ if name != '_option' and \
not isinstance(value, tuple) and \ not isinstance(value, tuple) and \
not name.startswith('_state') and \ not name.startswith('_state') and \
not name == '_sa_instance_state': not name == '_sa_instance_state':

View File

@ -23,7 +23,7 @@ import re
from ..i18n import _ from ..i18n import _
from ..setting import groups, undefined # , log from ..setting import groups, undefined, owners # , log
from .baseoption import BaseOption, SymLinkOption from .baseoption import BaseOption, SymLinkOption
from . import MasterSlaves from . import MasterSlaves
from ..error import ConfigError, ConflictError from ..error import ConfigError, ConflictError
@ -92,26 +92,35 @@ class OptionDescription(BaseOption, StorageOptionDescription):
""" """
return _impl_getpaths(self, include_groups, _currpath) 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 """validate duplicate option and set option has readonly option
""" """
if cache_option is None: if cache_option is None:
init = True init = True
_consistencies = {} _consistencies = {}
cache_option = [] cache_option = []
force_store_values = []
else: else:
init = False init = False
for option in self._impl_getchildren(dyn=False): for option in self._impl_getchildren(dyn=False):
#FIXME specifique id for sqlalchemy? #FIXME specifique id for sqlalchemy?
#FIXME avec sqlalchemy ca marche le multi parent ? (dans des configs différentes) #FIXME avec sqlalchemy ca marche le multi parent ? (dans des configs différentes)
cache_option.append(option._get_id()) cache_option.append(option._get_id())
if path == '':
subpath = option.impl_getname()
else:
subpath = path + '.' + option.impl_getname()
if isinstance(option, OptionDescription): if isinstance(option, OptionDescription):
option._set_readonly(False) 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 #cannot set multi option as OptionDescription requires
else: else:
option._set_readonly(True) option._set_readonly(True)
is_multi = option.impl_is_multi() 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(): for func, all_cons_opts, params in option._get_consistencies():
option._valid_consistencies(all_cons_opts[1:]) option._valid_consistencies(all_cons_opts[1:])
if is_multi: if is_multi:
@ -175,6 +184,21 @@ class OptionDescription(BaseOption, StorageOptionDescription):
opt.impl_getname())) opt.impl_getname()))
self._cache_consistencies[opt] = tuple(cons) self._cache_consistencies[opt] = tuple(cons)
self._set_readonly(False) 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): def impl_set_group_type(self, group_type):

View File

@ -214,6 +214,7 @@ def populate_owners():
""" """
setattr(owners, 'default', owners.DefaultOwner('default')) setattr(owners, 'default', owners.DefaultOwner('default'))
setattr(owners, 'user', owners.Owner('user')) setattr(owners, 'user', owners.Owner('user'))
setattr(owners, 'forced', owners.Owner('forced'))
def addowner(name): def addowner(name):
""" """

View File

@ -38,6 +38,9 @@ class Values(Cache):
""" """
values = [] values = []
vidx = None vidx = None
if index is None:
# raise Exception('arf')
pass
def _setvalue_info(nb, idx, value, vidx): def _setvalue_info(nb, idx, value, vidx):
lst = list(self._values[nb]) lst = list(self._values[nb])

View File

@ -103,9 +103,8 @@ class Values(object):
value = opt.impl_getdefault_multi() value = opt.impl_getdefault_multi()
return value return value
def _getvalue(self, opt, path, is_default, self_properties, def _getvalue(self, opt, path, self_properties, index, submulti_index,
index=undefined, submulti_index=undefined, with_meta=True, with_meta, masterlen):
masterlen=undefined, returns_raise=False):
"""actually retrieves the value """actually retrieves the value
:param opt: the `option.Option()` object :param opt: the `option.Option()` object
@ -114,6 +113,11 @@ class Values(object):
force_default = 'frozen' in self_properties and \ force_default = 'frozen' in self_properties and \
'force_default_on_freeze' in self_properties 'force_default_on_freeze' in self_properties
# not default value # 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 not is_default and not force_default:
if opt.impl_is_master_slaves('slave'): if opt.impl_is_master_slaves('slave'):
return self._p_.getvalue(path, index) return self._p_.getvalue(path, index)
@ -126,7 +130,8 @@ class Values(object):
#so return default value #so return default value
else: else:
return value 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): def get_modified_values(self):
context = self._getcontext() context = self._getcontext()
@ -172,7 +177,15 @@ class Values(object):
if opt.impl_is_master_slaves('master'): if opt.impl_is_master_slaves('master'):
opt.impl_get_master_slaves().reset(opt, self, _setting_properties) opt.impl_get_master_slaves().reset(opt, self, _setting_properties)
if hasvalue: 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() context.cfgimpl_reset_cache()
def _isempty(self, opt, value, force_allow_empty_list=False, index=None): 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) setting_properties = setting._getproperties(read_write=False)
if self_properties is undefined: if self_properties is undefined:
self_properties = setting._getproperties(opt, path, read_write=False, index=index) 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 config_error = None
value = self._getvalue(opt, path, is_default, self_properties, value = self._getvalue(opt, path, self_properties, index, submulti_index,
index=index, submulti_index=submulti_index, with_meta, masterlen)
with_meta=with_meta,
masterlen=masterlen,
returns_raise=True)
if isinstance(value, Exception): if isinstance(value, Exception):
if isinstance(value, ConfigError): if isinstance(value, ConfigError):
# For calculating properties, we need value (ie for mandatory # For calculating properties, we need value (ie for mandatory
@ -351,13 +356,6 @@ class Values(object):
config_error = err config_error = err
value = None 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 validate_properties:
if config_error is not None: if config_error is not None:
# should not raise PropertiesOptionError if option is # should not raise PropertiesOptionError if option is
@ -407,10 +405,13 @@ class Values(object):
raise err raise err
self._setvalue(opt, path, value) self._setvalue(opt, path, value)
def _setvalue(self, opt, path, value): def _setvalue(self, opt, path, value, force_owner=undefined):
context = self._getcontext() context = self._getcontext()
context.cfgimpl_reset_cache() 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 # in storage, value must not be a multi
if isinstance(value, Multi): if isinstance(value, Multi):
value = list(value) value = list(value)