From 04aa4e6bf1281309bdc41255356c14aecaf4b5ee Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 22 Aug 2013 22:46:02 +0200 Subject: [PATCH] never same calculated properties has properties (only in cache) --- test/test_requires.py | 10 ++++++ tiramisu/option.py | 22 ++++++------ tiramisu/setting.py | 80 +++++++++++++++++++++++-------------------- 3 files changed, 63 insertions(+), 49 deletions(-) diff --git a/test/test_requires.py b/test/test_requires.py index 9fa3837..9376364 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -429,3 +429,13 @@ def test_requires_multi_disabled_inverse_2(): except PropertiesOptionError, err: props = err.proptype 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() + raises(ValueError, 'c.cfgimpl_get_settings()[b].append("disabled")') diff --git a/tiramisu/option.py b/tiramisu/option.py index d95998a..1a36272 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -97,10 +97,9 @@ class Option(BaseInformation): Reminder: an Option object is **not** a container for the value """ __slots__ = ('_name', '_requires', '_multi', '_validator', - '_default_multi', - '_default', - '_properties', '_callback', '_multitype', - '_master_slaves', '_consistencies', '_empty') + '_default_multi', '_default', '_properties', '_callback', + '_multitype', '_master_slaves', '_consistencies', '_empty', + '_calc_properties') _empty = '' def __init__(self, name, doc, default=None, default_multi=None, @@ -130,8 +129,8 @@ class Option(BaseInformation): self._name = name self._impl_informations = {} self.impl_set_information('doc', doc) - requires = validate_requires_arg(requires, self._name) - self._requires = requires + self._calc_properties, self._requires = validate_requires_arg( + requires, self._name) self._multi = multi self._consistencies = None if validator is not None: @@ -714,7 +713,8 @@ class OptionDescription(BaseInformation): The `OptionsDescription` objects lives in the `tiramisu.config.Config`. """ __slots__ = ('_name', '_requires', '_cache_paths', '_group_type', - '_properties', '_children', '_consistencies') + '_properties', '_children', '_consistencies', + '_calc_properties') def __init__(self, name, doc, children, requires=None, properties=None): """ @@ -738,8 +738,7 @@ class OptionDescription(BaseInformation): '{0}').format(child)) old = child self._children = (tuple(child_names), tuple(children)) - requires = validate_requires_arg(requires, self._name) - self._requires = requires + self._calc_properties, self._requires = validate_requires_arg(requires, self._name) self._cache_paths = None self._consistencies = None if properties is None: @@ -933,7 +932,7 @@ def validate_requires_arg(requires, name): the description of the requires dictionnary """ if requires is None: - return None + return None, None ret_requires = {} config_action = {} @@ -999,7 +998,6 @@ def validate_requires_arg(requires, name): inverse, transitive, same_action) else: ret_requires[action][option][1].append(expected) - ret = [] for opt_requires in ret_requires.values(): ret_action = [] @@ -1011,4 +1009,4 @@ def validate_requires_arg(requires, name): require[5]) ret_action.append(req) ret.append(tuple(ret_action)) - return tuple(ret) + return tuple(config_action.keys()), tuple(ret) diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 13176de..0b7551c 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -161,6 +161,11 @@ class Property(object): self._properties = prop def append(self, propname): + if self._opt is not None and self._opt._calc_properties is not None \ + and propname in self._opt._calc_properties: + raise ValueError(_('cannot append {0} property for option {1}: ' + 'this property is calculated').format( + propname, self._opt._name)) self._properties.add(propname) self._setting._setproperties(self._properties, self._opt, self._path) @@ -250,9 +255,9 @@ class Settings(object): is_cached, props = self._p_.getcache('property', path, ntime) if is_cached: return props - if is_apply_req: - self.apply_requires(opt, path) props = self._p_.getproperties(path, opt._properties) + if is_apply_req: + props |= self.apply_requires(opt, path) if 'expire' in self: if ntime is None: ntime = time() @@ -261,11 +266,16 @@ class Settings(object): def append(self, propname): "puts property propname in the Config's properties attribute" - Property(self, self._getproperties()).append(propname) + props = self._p_.getproperties(None, default_properties) + props.add(propname) + self._setproperties(props, None, None) def remove(self, propname): "deletes property propname in the Config's properties attribute" - Property(self, self._getproperties()).remove(propname) + props = self._p_.getproperties(None, default_properties) + if propname in props: + props.remove(propname) + self._setproperties(props, None, None) def _setproperties(self, properties, opt, path): """save properties for specified opt @@ -274,6 +284,8 @@ class Settings(object): if opt is None: self._p_.setproperties(None, properties) else: + if opt._calc_properties is not None: + properties -= opt._calc_properties if set(opt._properties) == properties: self._p_.reset_properties(path) else: @@ -378,56 +390,53 @@ class Settings(object): def apply_requires(self, opt, path): """carries out the jit (just in time) requirements between options - - a requirement is a tuple of this form that comes from the option's + + a requirement is a tuple of this form that comes from the option's requirements validation:: (option, expected, action, inverse, transitive, same_action) let's have a look at all the tuple's items: - + - **option** is the target option's name or path - + - **expected** is the target option's value that is going to trigger an action - - - **action** is the (property) action to be accomplished if the target option + + - **action** is the (property) action to be accomplished if the target option happens to have the expected value - - - if **inverse** is `True` and if the target option's value does not - apply, then the property action must be removed from the option's + + - if **inverse** is `True` and if the target option's value does not + apply, then the property action must be removed from the option's properties list (wich means that the property is inverted) - - - **transitive**: but what happens if the target option cannot be - accessed ? We don't kown the target option's value. Actually if some - property in the target option is not present in the permissive, the - target option's value cannot be accessed. In this case, the - **action** have to be applied to the option. (the **action** property + + - **transitive**: but what happens if the target option cannot be + accessed ? We don't kown the target option's value. Actually if some + property in the target option is not present in the permissive, the + target option's value cannot be accessed. In this case, the + **action** have to be applied to the option. (the **action** property is then added to the option). - - - **same_action**: actually, if **same_action** is `True`, the - transitivity is not accomplished. The transitivity is accomplished - only if the target option **has the same property** that the demanded - action. If the target option's value is not accessible because of - another reason, because of a property of another type, then an + + - **same_action**: actually, if **same_action** is `True`, the + transitivity is not accomplished. The transitivity is accomplished + only if the target option **has the same property** that the demanded + action. If the target option's value is not accessible because of + another reason, because of a property of another type, then an exception :exc:`~error.RequirementError` is raised. - And at last, if no target option matches the expected values, the + And at last, if no target option matches the expected values, the action must be removed from the option's properties list. - + :param opt: the option on wich the requirement occurs :type opt: `option.Option()` :param path: the option's path in the config :type path: str """ if opt._requires is None: - return + return frozenset() # filters the callbacks - setting = Property(self, - self._getproperties(opt, path, False), - opt, path=path) + calc_properties = set() for requires in opt._requires: - matches = False for require in requires: option, expected, action, inverse, \ transitive, same_action = require @@ -460,13 +469,10 @@ class Settings(object): if (not inverse and value in expected or inverse and value not in expected): - matches = True - setting.append(action) + calc_properties.add(action) # the calculation cannot be carried out break - # no requirement has been triggered, then just reverse the action - if not matches: - setting.remove(action) + return calc_properties def _get_opt_path(self, opt): return self.context.cfgimpl_get_description().impl_get_path_by_opt(opt)