diff --git a/test/test_config.py b/test/test_config.py index e9c470f..064ec8b 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -85,7 +85,7 @@ def test_base_config_and_groups(): assert nm.impl_getname() == 'name' gc = config.unwrap_from_path('gc') assert gc.impl_getname() == 'gc' - #nm = config.unwrap_fromimpl_getname()('name') + #nm = config.unwrap_from_name('name') #assert nm.impl_getname() == 'name' diff --git a/test/test_freeze.py b/test/test_freeze.py index 67fe2f4..cda1406 100644 --- a/test/test_freeze.py +++ b/test/test_freeze.py @@ -146,10 +146,10 @@ def test_force_store_value(): assert conf.getowner(conf.unwrap_from_path('wantref')) == 'user' -#def test_force_store_value_ro(): -# descr = make_description_freeze() -# conf = Config(descr) -# conf.read_only() -# assert conf.getowner(conf.unwrap_from_path('wantref')) == 'default' -# conf.wantref -# assert conf.getowner(conf.unwrap_from_path('wantref')) == 'user' +def test_force_store_value_ro(): + descr = make_description_freeze() + conf = Config(descr) + conf.read_only() + assert conf.getowner(conf.unwrap_from_path('wantref')) == 'default' + conf.wantref + assert conf.getowner(conf.unwrap_from_path('wantref')) == 'user' diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 5e85530..f7040e4 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -102,8 +102,7 @@ def test_consistency_after_config(): od2 = OptionDescription('od2', '', [b]) od = OptionDescription('root', '', [od1, od2]) Config(od) - #FIXME a cause du read_only - #raises(AttributeError, "a.impl_add_consistency('not_equal', b)") + raises(AttributeError, "a.impl_add_consistency('not_equal', b)") def test_consistency_not_equal_symlink(): diff --git a/test/test_requires.py b/test/test_requires.py index 4f35cfd..4cf9372 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -503,19 +503,19 @@ def test_requires_multi_disabled_inverse_2(): assert props == ['disabled'] -#def test_requires_requirement_append(): -# a = BoolOption('activate_service', '', True) -# b = IPOption('ip_address_service', '', -# requires=[{'option': a, 'expected': False, 'action': 'disabled'}]) -# od = OptionDescription('service', '', [a, b]) -# c = Config(od) -# c.read_write() -# str(c.cfgimpl_get_settings()) -# str(c.cfgimpl_get_settings()[b]) -# raises(ValueError, 'c.cfgimpl_get_settings()[b].append("disabled")') -# c.activate_service = False -# # disabled is now set, test to remove disabled before store in storage -# c.cfgimpl_get_settings()[b].append("test") +def test_requires_requirement_append(): + a = BoolOption('activate_service', '', True) + b = IPOption('ip_address_service', '', + requires=[{'option': a, 'expected': False, 'action': 'disabled'}]) + od = OptionDescription('service', '', [a, b]) + c = Config(od) + c.read_write() + str(c.cfgimpl_get_settings()) + str(c.cfgimpl_get_settings()[b]) + raises(ValueError, 'c.cfgimpl_get_settings()[b].append("disabled")') + c.activate_service = False + # disabled is now set, test to remove disabled before store in storage + c.cfgimpl_get_settings()[b].append("test") def test_requires_different_inverse(): diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 726d53f..ef5bce5 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -29,8 +29,7 @@ def carry_out_calculation(option, config, callback, callback_params, index=None, max_len=None): """a function that carries out a calculation for an option's value - :param name: the option name (`opt.impl_getname()`) - :param name: the option + :param option: the option :param config: the context config in order to have the whole options available :param callback: the name of the callback function diff --git a/tiramisu/option.py b/tiramisu/option.py index 7aa6ad5..e793ee5 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -22,11 +22,10 @@ # ____________________________________________________________ import re import sys -from copy import copy # , deepcopy +from copy import copy from types import FunctionType from IPy import IP import warnings -#from pickle import loads, dumps from tiramisu.error import ConfigError, ConflictError, ValueWarning from tiramisu.setting import groups, multitypes @@ -335,43 +334,45 @@ class Option(BaseOption): warnings_only, choice_values, choice_open_values) - #def __setattr__(self, name, value): - # """set once and only once some attributes in the option, - # like `_name`. `_name` cannot be changed one the option and - # pushed in the :class:`tiramisu.option.OptionDescription`. + def impl_is_readonly(self): + try: + if self._readonly is True: + return True + except AttributeError: + pass + return False - # if the attribute `_readonly` is set to `True`, the option is - # "frozen" (which has noting to do with the high level "freeze" - # propertie or "read_only" property) - # """ - # #FIXME ne devrait pas pouvoir redefinir _option - # if not name == '_option': - # is_readonly = False - # # never change _name - # if name == '_name': - # try: - # self._name - # #so _name is already set - # is_readonly = True - # except: - # pass - # elif name != '_readonly': - # try: - # if self._readonly is True: - # is_readonly = True - # except AttributeError: - # self._readonly = False - # if is_readonly: - # raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" - # " read-only").format( - # self.__class__.__name__, - # self._name, - # name)) - # object.__setattr__(self, name, value) + def __setattr__(self, name, value): + """set once and only once some attributes in the option, + like `_name`. `_name` cannot be changed one the option and + pushed in the :class:`tiramisu.option.OptionDescription`. - #def impl_getproperties(self): - # for prop in self._properties: - # yield(prop.name) + if the attribute `_readonly` is set to `True`, the option is + "frozen" (which has noting to do with the high level "freeze" + propertie or "read_only" property) + """ + #FIXME ne devrait pas pouvoir redefinir _option + #FIXME c'est une merde pour sqlachemy surement un cache ... + if not name == '_option' and not isinstance(value, tuple): + is_readonly = False + # never change _name + if name == '_name': + try: + if self._name is not None: + #so _name is already set + is_readonly = True + #FIXME je n'aime pas ce except ... + except: + pass + elif name != '_readonly': + is_readonly = self.impl_is_readonly() + if is_readonly: + raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" + " read-only").format( + self.__class__.__name__, + self._name, + name)) + super(Option, self).__setattr__(name, value) def impl_getrequires(self): return self._requires @@ -443,8 +444,6 @@ class Option(BaseOption): if self._validator is not None: if self._validator_params is not None: validator_params = {} - #FIXME toujours utile ce deepcopy ? - #validator_params = deepcopy(self._validator_params) for val_param, values in self._validator_params.items(): validator_params[val_param] = values #FIXME ... ca sert à quoi ... @@ -550,6 +549,11 @@ class Option(BaseOption): :param other_opts: options used to validate value :type other_opts: `list` of `tiramisu.option.Option` """ + if self.impl_is_readonly(): + raise AttributeError(_("'{0}' ({1}) cannont add consistency, option is" + " read-only").format( + self.__class__.__name__, + self._name)) for opt in other_opts: if not isinstance(opt, Option): raise ConfigError(_('consistency should be set with an option')) @@ -1010,7 +1014,7 @@ class DomainnameOption(Option): domainname: fqdn: with tld, not supported yet """ - #__slots__ = ('_dom_type', '_allow_ip', '_allow_without_dot', '_domain_re') + #__slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re') def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, @@ -1167,7 +1171,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): self._cache_paths = None self._cache_consistencies = None # the group_type is useful for filtering OptionDescriptions in a config - self._optiondescription_group_type = groups.default + self._group_type = groups.default #def impl_getproperties(self): # #FIXME @@ -1317,12 +1321,12 @@ class OptionDescription(BaseOption, StorageOptionDescription): :param group_type: an instance of `GroupType` or `MasterGroupType` that lives in `setting.groups` """ - if self._optiondescription_group_type != groups.default: + if self._group_type != groups.default: raise TypeError(_('cannot change group_type if already set ' - '(old {0}, new {1})').format(self._optiondescription_group_type, + '(old {0}, new {1})').format(self._group_type, group_type)) if isinstance(group_type, groups.GroupType): - self._optiondescription_group_type = group_type + self._group_type = group_type if isinstance(group_type, groups.MasterGroupType): #if master (same name has group) is set #for collect all slaves @@ -1367,7 +1371,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): ' not allowed').format(group_type)) def impl_get_group_type(self): - return getattr(groups, self._optiondescription_group_type) + return getattr(groups, self._group_type) def _valid_consistency(self, option, value, context, index): if self._cache_consistencies is None: @@ -1396,7 +1400,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): self.impl_build_cache() descr = self super(OptionDescription, self)._impl_getstate(descr) - self._state_group_type = str(self._optiondescription_group_type) + self._state_group_type = str(self._group_type) for option in self.impl_getchildren(): option._impl_getstate(descr) @@ -1426,7 +1430,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): self._cache_consistencies = None self.impl_build_cache(force_no_consistencies=True) descr = self - self._optiondescription_group_type = getattr(groups, self._state_group_type) + self._group_type = getattr(groups, self._state_group_type) del(self._state_group_type) super(OptionDescription, self)._impl_setstate(descr) for option in self.impl_getchildren(): @@ -1450,7 +1454,7 @@ def validate_requires_arg(requires, name): the description of the requires dictionary """ if requires is None: - return None + return None, None ret_requires = {} config_action = {} diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 4d7da51..e483041 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -268,7 +268,7 @@ class Property(object): :type propname: string """ if self._opt is not None and self._opt.impl_getrequires() is not None \ - and propname in self._opt.impl_getrequires(): + and propname in self._opt._calc_properties: raise ValueError(_('cannot append {0} property for option {1}: ' 'this property is calculated').format( propname, self._opt.impl_getname())) diff --git a/tiramisu/storage/sqlalchemy/option.py b/tiramisu/storage/sqlalchemy/option.py index dc0bc7d..82339d1 100644 --- a/tiramisu/storage/sqlalchemy/option.py +++ b/tiramisu/storage/sqlalchemy/option.py @@ -275,7 +275,7 @@ class _Base(SqlAlchemyBase): 'polymorphic_on': _type } #FIXME devrait etre une table - _optiondescription_group_type = Column(String) + _group_type = Column(String) def __init__(self): self.commit()