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:
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")')

View File

@ -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)

View File

@ -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)