force_store_value is now used directly when configuration is loaded
authorEmmanuel Garette <egarette@cadoles.com>
Mon, 7 Mar 2016 15:13:41 +0000 (16:13 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Mon, 7 Mar 2016 15:13:41 +0000 (16:13 +0100)
ChangeLog
test/test_freeze.py
tiramisu/config.py
tiramisu/option/baseoption.py
tiramisu/option/optiondescription.py
tiramisu/setting.py
tiramisu/storage/dictionary/value.py
tiramisu/value.py

index 070a857..fc4aec4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
        * requires could be apply to a slave and properties could be different
 
index 4e1ffaf..67fe5a3 100644 (file)
@@ -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()
-    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)}
-
-
-def test_force_store_value_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.getattr('wantref2', force_permissive=True)
-    assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'wantref2': ('user', False)}
-
-
-def test_force_store_value_owner():
-    descr = make_description_freeze()
-    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)}
+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_owner_ro():
-    descr = make_description_freeze()
+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.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', [])}
 
 
-def test_force_store_value_owner_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.getowner(conf.unwrap_from_path('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_modified():
-    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.cfgimpl_get_values().get_modified_values()
-    assert conf.cfgimpl_get_values()._p_.get_modified_values() == {
-        'wantref': ('user', False), 'wantref2': ('user', False),
-        'wantref3': ('user', [False])}
+    assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)}
 
 
-def test_force_store_value_modified_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.cfgimpl_get_values().get_modified_values()
-    assert conf.cfgimpl_get_values()._p_.get_modified_values() == {
-        'wantref': ('user', False), 'wantref2': ('user', False),
-        'wantref3': ('user', [False])}
+    assert conf.cfgimpl_get_values()._p_.get_modified_values() == {'int': ('forced', 2)}
 
 
-def test_force_store_value_modified_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.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)}
index 2293e42..a09102b 100644 (file)
@@ -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,
index 0c083df..08ece6a 100644 (file)
@@ -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':
index 4c78d9a..dc5172b 100644 (file)
@@ -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):
index 94765a3..0eb6cf0 100644 (file)
@@ -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):
         """
index ef4ad19..68989f9 100644 (file)
@@ -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])
index 9740a44..e7dc6bb 100644 (file)
@@ -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)