diff --git a/test/test_requires.py b/test/test_requires.py index 3d386d7..ba6cf3f 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -112,6 +112,40 @@ def test_multiple_requires_cumulative(): c.ip_address_service +def test_multiple_requires_cumulative_inverse(): + a = StrOption('activate_service', '') + b = IPOption('ip_address_service', '', + requires=[{'option': a, 'expected': 'yes', 'action': 'disabled', 'inverse': True}, + {'option': a, 'expected': 'yes', 'action': 'hidden', 'inverse': True}]) + od = OptionDescription('service', '', [a, b]) + c = Config(od) + c.read_write() + props = [] + try: + c.ip_address_service + except PropertiesOptionError as err: + props = err.proptype + assert set(props) == set(['hidden', 'disabled']) + c.activate_service = 'yes' + c.ip_address_service + + c.activate_service = 'ok' + props = [] + try: + c.ip_address_service + except PropertiesOptionError as err: + props = err.proptype + assert set(props) == set(['hidden', 'disabled']) + + c.activate_service = 'no' + props = [] + try: + c.ip_address_service + except PropertiesOptionError as err: + props = err.proptype + assert set(props) == set(['hidden', 'disabled']) + + def test_multiple_requires_inverse(): a = StrOption('activate_service', '') b = IPOption('ip_address_service', '', @@ -500,3 +534,9 @@ def test_set_item(): c = Config(od) c.read_write() raises(ValueError, 'c.cfgimpl_get_settings()[a] = ("test",)') + + +def test_properties_conflict(): + a = BoolOption('activate_service', '', True) + raises(ValueError, "IPOption('ip_address_service', '', properties=('disabled',), requires=[{'option': a, 'expected': False, 'action': 'disabled'}])") + raises(ValueError, "od1 = OptionDescription('service', '', [a], properties=('disabled',), requires=[{'option': a, 'expected': False, 'action': 'disabled'}])") diff --git a/test/test_slots.py b/test/test_slots.py index f99484f..1f2aee6 100644 --- a/test/test_slots.py +++ b/test/test_slots.py @@ -107,7 +107,7 @@ def test_slots_option_readonly_name(): def test_slots_description(): - # __slots__ for OptionDescription must be complete + # __slots__ for OptionDescription should be complete for __getattr__ slots = set() for subclass in OptionDescription.__mro__: if subclass is not object: diff --git a/tiramisu/option.py b/tiramisu/option.py index ff50ea2..9a2dd5d 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -292,6 +292,12 @@ class Option(BaseOption): ' must be a tuple').format( type(properties), self._name)) + if self._calc_properties is not None and properties is not tuple(): + set_forbidden_properties = set(properties) & self._calc_properties + if set_forbidden_properties != frozenset(): + raise ValueError('conflict: properties already set in ' + 'requirement {0}'.format( + list(set_forbidden_properties))) self._properties = properties # 'hidden', 'disabled'... def _launch_consistency(self, func, opt, vals, context, index, opt_): @@ -804,7 +810,8 @@ class OptionDescription(BaseOption): __slots__ = ('_name', '_requires', '_cache_paths', '_group_type', '_state_group_type', '_properties', '_children', '_consistencies', '_calc_properties', '__weakref__', - '_readonly', '_impl_informations') + '_readonly', '_impl_informations', '_state_requires', + '_state_consistencies') _opt_type = 'optiondescription' def __init__(self, name, doc, children, requires=None, properties=None): @@ -838,6 +845,12 @@ class OptionDescription(BaseOption): raise TypeError(_('invalid properties type {0} for {1},' ' must be a tuple').format(type(properties), self._name)) + if self._calc_properties is not None and properties is not tuple(): + set_forbidden_properties = set(properties) & self._calc_properties + if set_forbidden_properties != frozenset(): + raise ValueError('conflict: properties already set in ' + 'requirement {0}'.format( + list(set_forbidden_properties))) self._properties = properties # 'hidden', 'disabled'... # the group_type is useful for filtering OptionDescriptions in a config self._group_type = groups.default