cannot set properties if those properties are in requirement
authorEmmanuel Garette <egarette@cadoles.com>
Mon, 2 Sep 2013 17:47:00 +0000 (19:47 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Mon, 2 Sep 2013 17:47:00 +0000 (19:47 +0200)
test/test_requires.py
test/test_slots.py
tiramisu/option.py

index 3d386d7..ba6cf3f 100644 (file)
@@ -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'}])")
index f99484f..1f2aee6 100644 (file)
@@ -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:
index ff50ea2..9a2dd5d 100644 (file)
@@ -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