never same calculated properties has properties (only in cache)

This commit is contained in:
Emmanuel Garette 2013-08-22 22:46:02 +02:00
parent 3a6296f7e0
commit 04aa4e6bf1
3 changed files with 63 additions and 49 deletions

View File

@ -429,3 +429,13 @@ def test_requires_multi_disabled_inverse_2():
except PropertiesOptionError, err: except PropertiesOptionError, err:
props = err.proptype props = err.proptype
assert props == ['disabled'] 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")')

View File

@ -97,10 +97,9 @@ class Option(BaseInformation):
Reminder: an Option object is **not** a container for the value Reminder: an Option object is **not** a container for the value
""" """
__slots__ = ('_name', '_requires', '_multi', '_validator', __slots__ = ('_name', '_requires', '_multi', '_validator',
'_default_multi', '_default_multi', '_default', '_properties', '_callback',
'_default', '_multitype', '_master_slaves', '_consistencies', '_empty',
'_properties', '_callback', '_multitype', '_calc_properties')
'_master_slaves', '_consistencies', '_empty')
_empty = '' _empty = ''
def __init__(self, name, doc, default=None, default_multi=None, def __init__(self, name, doc, default=None, default_multi=None,
@ -130,8 +129,8 @@ class Option(BaseInformation):
self._name = name self._name = name
self._impl_informations = {} self._impl_informations = {}
self.impl_set_information('doc', doc) self.impl_set_information('doc', doc)
requires = validate_requires_arg(requires, self._name) self._calc_properties, self._requires = validate_requires_arg(
self._requires = requires requires, self._name)
self._multi = multi self._multi = multi
self._consistencies = None self._consistencies = None
if validator is not None: if validator is not None:
@ -714,7 +713,8 @@ class OptionDescription(BaseInformation):
The `OptionsDescription` objects lives in the `tiramisu.config.Config`. The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
""" """
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type', __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): def __init__(self, name, doc, children, requires=None, properties=None):
""" """
@ -738,8 +738,7 @@ class OptionDescription(BaseInformation):
'{0}').format(child)) '{0}').format(child))
old = child old = child
self._children = (tuple(child_names), tuple(children)) self._children = (tuple(child_names), tuple(children))
requires = validate_requires_arg(requires, self._name) self._calc_properties, self._requires = validate_requires_arg(requires, self._name)
self._requires = requires
self._cache_paths = None self._cache_paths = None
self._consistencies = None self._consistencies = None
if properties is None: if properties is None:
@ -933,7 +932,7 @@ def validate_requires_arg(requires, name):
the description of the requires dictionnary the description of the requires dictionnary
""" """
if requires is None: if requires is None:
return None return None, None
ret_requires = {} ret_requires = {}
config_action = {} config_action = {}
@ -999,7 +998,6 @@ def validate_requires_arg(requires, name):
inverse, transitive, same_action) inverse, transitive, same_action)
else: else:
ret_requires[action][option][1].append(expected) ret_requires[action][option][1].append(expected)
ret = [] ret = []
for opt_requires in ret_requires.values(): for opt_requires in ret_requires.values():
ret_action = [] ret_action = []
@ -1011,4 +1009,4 @@ def validate_requires_arg(requires, name):
require[5]) require[5])
ret_action.append(req) ret_action.append(req)
ret.append(tuple(ret_action)) ret.append(tuple(ret_action))
return tuple(ret) return tuple(config_action.keys()), tuple(ret)

View File

@ -161,6 +161,11 @@ class Property(object):
self._properties = prop self._properties = prop
def append(self, propname): 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._properties.add(propname)
self._setting._setproperties(self._properties, self._opt, self._path) 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) is_cached, props = self._p_.getcache('property', path, ntime)
if is_cached: if is_cached:
return props return props
if is_apply_req:
self.apply_requires(opt, path)
props = self._p_.getproperties(path, opt._properties) props = self._p_.getproperties(path, opt._properties)
if is_apply_req:
props |= self.apply_requires(opt, path)
if 'expire' in self: if 'expire' in self:
if ntime is None: if ntime is None:
ntime = time() ntime = time()
@ -261,11 +266,16 @@ class Settings(object):
def append(self, propname): def append(self, propname):
"puts property propname in the Config's properties attribute" "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): def remove(self, propname):
"deletes property propname in the Config's properties attribute" "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): def _setproperties(self, properties, opt, path):
"""save properties for specified opt """save properties for specified opt
@ -274,6 +284,8 @@ class Settings(object):
if opt is None: if opt is None:
self._p_.setproperties(None, properties) self._p_.setproperties(None, properties)
else: else:
if opt._calc_properties is not None:
properties -= opt._calc_properties
if set(opt._properties) == properties: if set(opt._properties) == properties:
self._p_.reset_properties(path) self._p_.reset_properties(path)
else: else:
@ -378,56 +390,53 @@ class Settings(object):
def apply_requires(self, opt, path): def apply_requires(self, opt, path):
"""carries out the jit (just in time) requirements between options """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:: requirements validation::
(option, expected, action, inverse, transitive, same_action) (option, expected, action, inverse, transitive, same_action)
let's have a look at all the tuple's items: let's have a look at all the tuple's items:
- **option** is the target option's name or path - **option** is the target option's name or path
- **expected** is the target option's value that is going to trigger an action - **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 happens to have the expected value
- if **inverse** is `True` and if the target option's value does not - 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 apply, then the property action must be removed from the option's
properties list (wich means that the property is inverted) properties list (wich means that the property is inverted)
- **transitive**: but what happens if the target option cannot be - **transitive**: but what happens if the target option cannot be
accessed ? We don't kown the target option's value. Actually if some 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 property in the target option is not present in the permissive, the
target option's value cannot be accessed. In this case, the target option's value cannot be accessed. In this case, the
**action** have to be applied to the option. (the **action** property **action** have to be applied to the option. (the **action** property
is then added to the option). is then added to the option).
- **same_action**: actually, if **same_action** is `True`, the - **same_action**: actually, if **same_action** is `True`, the
transitivity is not accomplished. The transitivity is accomplished transitivity is not accomplished. The transitivity is accomplished
only if the target option **has the same property** that the demanded only if the target option **has the same property** that the demanded
action. If the target option's value is not accessible because of action. If the target option's value is not accessible because of
another reason, because of a property of another type, then an another reason, because of a property of another type, then an
exception :exc:`~error.RequirementError` is raised. 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. action must be removed from the option's properties list.
:param opt: the option on wich the requirement occurs :param opt: the option on wich the requirement occurs
:type opt: `option.Option()` :type opt: `option.Option()`
:param path: the option's path in the config :param path: the option's path in the config
:type path: str :type path: str
""" """
if opt._requires is None: if opt._requires is None:
return return frozenset()
# filters the callbacks # filters the callbacks
setting = Property(self, calc_properties = set()
self._getproperties(opt, path, False),
opt, path=path)
for requires in opt._requires: for requires in opt._requires:
matches = False
for require in requires: for require in requires:
option, expected, action, inverse, \ option, expected, action, inverse, \
transitive, same_action = require transitive, same_action = require
@ -460,13 +469,10 @@ class Settings(object):
if (not inverse and if (not inverse and
value in expected or value in expected or
inverse and value not in expected): inverse and value not in expected):
matches = True calc_properties.add(action)
setting.append(action)
# the calculation cannot be carried out # the calculation cannot be carried out
break break
# no requirement has been triggered, then just reverse the action return calc_properties
if not matches:
setting.remove(action)
def _get_opt_path(self, opt): def _get_opt_path(self, opt):
return self.context.cfgimpl_get_description().impl_get_path_by_opt(opt) return self.context.cfgimpl_get_description().impl_get_path_by_opt(opt)