Merge branch 'master' into orm
authorEmmanuel Garette <egarette@cadoles.com>
Tue, 4 Feb 2014 20:48:20 +0000 (21:48 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Tue, 4 Feb 2014 20:54:30 +0000 (21:54 +0100)
Conflicts:
tiramisu/config.py
tiramisu/option.py

test/test_config_api.py
test/test_metaconfig.py
test/test_option_owner.py
test/test_requires.py
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/value.py

index 36c5e39..f76895a 100644 (file)
@@ -4,7 +4,8 @@ from py.test import raises
 
 from tiramisu.config import Config
 from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \
-    BoolOption, FilenameOption, OptionDescription
+    BoolOption, FilenameOption, UnicodeOption, SymLinkOption, IPOption, \
+    PortOption, OptionDescription
 
 
 def make_description():
@@ -150,6 +151,7 @@ def test_find_in_config():
     # not OptionDescription
     raises(AttributeError, "conf.find_first(byname='gc')")
     raises(AttributeError, "conf.gc.find_first(byname='gc2')")
+    raises(ValueError, "conf.find(byname='bool', type_='unknown')")
 
 
 def test_find_multi():
@@ -208,3 +210,18 @@ def test_iter_all_prop():
     config = Config(descr)
     config.read_only()
     assert list(config.iter_all()) == [('string2', 'string2')]
+
+
+def test_invalid_option():
+    raises(TypeError, "ChoiceOption('a', '', [1, 2])")
+    raises(TypeError, "ChoiceOption('a', '', 1)")
+    raises(TypeError, "ChoiceOption('a', '', (1,), open_values='string')")
+    raises(ValueError, "ChoiceOption('a', '', (1,), 3)")
+    raises(ValueError, "FloatOption('a', '', 'string')")
+    raises(ValueError, "UnicodeOption('a', '', 1)")
+    raises(ValueError, "SymLinkOption('a', 'string')")
+    raises(ValueError, "IPOption('a', '', 1)")
+    raises(ValueError, "IPOption('a', '', 'string')")
+    raises(ValueError, "PortOption('a', '', 'string')")
+    raises(ValueError, "PortOption('a', '', '11:12:13', allow_range=True)")
+    raises(ValueError, "PortOption('a', '', 11111111111111111111)")
index 9409d85..a8fc395 100644 (file)
@@ -5,7 +5,7 @@ from py.test import raises
 from tiramisu.setting import owners
 from tiramisu.config import Config, GroupConfig, MetaConfig
 from tiramisu.option import IntOption, OptionDescription
-from tiramisu.error import ConfigError
+from tiramisu.error import ConfigError, PropertiesOptionError
 
 owners.addowner('meta')
 
@@ -15,10 +15,14 @@ def make_description():
     i2 = IntOption('i2', '', default=1)
     i3 = IntOption('i3', '')
     i4 = IntOption('i4', '', default=2)
-    od1 = OptionDescription('od1', '', [i1, i2, i3, i4])
+    i5 = IntOption('i5', '', default=[2], multi=True)
+    i6 = IntOption('i6', '', properties=('disabled',))
+    od1 = OptionDescription('od1', '', [i1, i2, i3, i4, i5, i6])
     od2 = OptionDescription('od2', '', [od1])
     conf1 = Config(od2)
     conf2 = Config(od2)
+    conf1.read_write()
+    conf2.read_write()
     meta = MetaConfig([conf1, conf2])
     meta.cfgimpl_get_settings().setowner(owners.meta)
     return meta
@@ -29,7 +33,7 @@ def make_description():
 #FIXME serialization
 def test_none():
     meta = make_description()
-    conf1, conf2 = meta._impl_children
+    conf1, conf2 = meta.cfgimpl_get_children()
     assert conf1.od1.i3 is conf2.od1.i3 is None
     assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default
     meta.od1.i3 = 3
@@ -58,7 +62,7 @@ def test_none():
 
 def test_default():
     meta = make_description()
-    conf1, conf2 = meta._impl_children
+    conf1, conf2 = meta.cfgimpl_get_children()
     assert conf1.od1.i2 == conf2.od1.i2 == 1
     assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
     meta.od1.i2 = 3
@@ -87,7 +91,7 @@ def test_default():
 
 def test_contexts():
     meta = make_description()
-    conf1, conf2 = meta._impl_children
+    conf1, conf2 = meta.cfgimpl_get_children()
     assert conf1.od1.i2 == conf2.od1.i2 == 1
     assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
     meta.setattrs('od1.i2', 6)
@@ -101,14 +105,15 @@ def test_find():
     i2 = meta.unwrap_from_path('od1.i2')
     assert [i2] == meta.find(byname='i2')
     assert i2 == meta.find_first(byname='i2')
-    assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None, 'od1.i2': 1}
+    assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None,
+                                'od1.i2': 1, 'od1.i5': [2], 'od1.i6': None}
 
 
 def test_meta_meta():
     meta1 = make_description()
     meta2 = MetaConfig([meta1])
     meta2.cfgimpl_get_settings().setowner(owners.meta)
-    conf1, conf2 = meta1._impl_children
+    conf1, conf2 = meta1.cfgimpl_get_children()
     assert conf1.od1.i2 == conf2.od1.i2 == 1
     assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
     meta2.od1.i2 = 3
@@ -142,15 +147,21 @@ def test_meta_meta_set():
     meta1 = make_description()
     meta2 = MetaConfig([meta1])
     meta2.cfgimpl_get_settings().setowner(owners.meta)
-    conf1, conf2 = meta1._impl_children
+    conf1, conf2 = meta1.cfgimpl_get_children()
     meta2.setattrs('od1.i1', 7)
+    #PropertiesOptionError
+    meta2.setattrs('od1.i6', 7)
     assert conf1.od1.i1 == conf2.od1.i1 == 7
     assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user
     assert [conf1, conf2] == meta2.find_firsts(byname='i1', byvalue=7)
     conf1.od1.i1 = 8
+    assert [conf1, conf2] == meta2.find_firsts(byname='i1')
     assert [conf2] == meta2.find_firsts(byname='i1', byvalue=7)
     assert [conf1] == meta2.find_firsts(byname='i1', byvalue=8)
+    assert [conf1, conf2] == meta2.find_firsts(byname='i5', byvalue=2)
     raises(AttributeError, "meta2.find_firsts(byname='i1', byvalue=10)")
+    raises(AttributeError, "meta2.find_firsts(byname='not', byvalue=10)")
+    raises(AttributeError, "meta2.find_firsts(byname='i6')")
 
 
 def test_not_meta():
@@ -159,9 +170,10 @@ def test_not_meta():
     od2 = OptionDescription('od2', '', [od1])
     conf1 = Config(od2)
     conf2 = Config(od2)
+    raises(ValueError, "GroupConfig(conf1)")
     meta = GroupConfig([conf1, conf2])
     raises(ConfigError, 'meta.od1.i1')
-    conf1, conf2 = meta._impl_children
+    conf1, conf2 = meta.cfgimpl_get_children()
     meta.setattrs('od1.i1', 7)
     assert conf1.od1.i1 == conf2.od1.i1 == 7
     assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user
index b758e91..d4c3f6b 100644 (file)
@@ -36,6 +36,7 @@ def test_default_owner():
     cfg = Config(descr)
     assert cfg.dummy is False
     assert cfg.getowner(gcdummy) == 'default'
+    raises(TypeError, "cfg.getowner('gcdummy')")
     cfg.dummy = True
     assert cfg.getowner(gcdummy) == owners.user
 
index 4cf9372..ff9c2a6 100644 (file)
@@ -24,6 +24,28 @@ def test_requires():
     except PropertiesOptionError as err:
         props = err.proptype
     assert props == ['disabled']
+    c.activate_service = True
+    c.ip_address_service
+
+
+def test_requires_with_requires():
+    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()
+    c.cfgimpl_get_settings()[b].append('test')
+    c.ip_address_service
+    c.activate_service = False
+    props = []
+    try:
+        c.ip_address_service
+    except PropertiesOptionError as err:
+        props = err.proptype
+    assert props == ['disabled']
+    c.activate_service = True
+    c.ip_address_service
 
 
 def test_requires_invalid():
index d28aa22..f05d900 100644 (file)
@@ -434,12 +434,9 @@ class SubConfig(object):
 
     def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten):
         if isinstance(opt, OptionDescription):
-            try:
-                pathsvalues += getattr(self, path).make_dict(flatten,
-                                                             _currpath +
-                                                             path.split('.'))
-            except PropertiesOptionError:
-                pass  # this just a hidden or disabled option
+            pathsvalues += getattr(self, path).make_dict(flatten,
+                                                         _currpath +
+                                                         path.split('.'))
         else:
             try:
                 value = self._getattr(opt.impl_getname())
index 24d1b05..66f2a38 100644 (file)
@@ -715,7 +715,7 @@ class ChoiceOption(Option):
         return self._choice_open_values
 
     def _validate(self, value):
-        if not self._choice_open_values and not value in self._choice_values:
+        if not self.impl_is_openvalues() and not value in self.impl_get_values():
             raise ValueError(_('value {0} is not permitted, '
                                'only {1} is allowed'
                                '').format(value, self._choice_values))
@@ -832,9 +832,13 @@ class IPOption(Option):
     def _validate(self, value):
         # sometimes an ip term starts with a zero
         # but this does not fit in some case, for example bind does not like it
-        for val in value.split('.'):
-            if val.startswith("0") and len(val) > 1:
-                raise ValueError(_('invalid IP'))
+        try:
+            for val in value.split('.'):
+                if val.startswith("0") and len(val) > 1:
+                    raise ValueError(_('invalid IP'))
+        except AttributeError:
+            #if integer for example
+            raise ValueError(_('invalid IP'))
         # 'standard' validation
         try:
             IP('{0}/32'.format(value))
@@ -905,18 +909,23 @@ class PortOption(Option):
         if self._extra['_allow_range'] and ":" in str(value):
             value = str(value).split(':')
             if len(value) != 2:
-                raise ValueError('invalid part, range must have two values '
-                                 'only')
+                raise ValueError(_('invalid part, range must have two values '
+                                 'only'))
             if not value[0] < value[1]:
-                raise ValueError('invalid port, first port in range must be'
-                                 ' smaller than the second one')
+                raise ValueError(_('invalid port, first port in range must be'
+                                 ' smaller than the second one'))
         else:
             value = [value]
 
         for val in value:
-            if not self._extra['_min_value'] <= int(val) <= self._extra['_max_value']:
-                raise ValueError('invalid port, must be an between {0} and {1}'
-                                 ''.format(self._extra['_min_value'], self._extra['_max_value']))
+            try:
+                if not self._extra['_min_value'] <= int(val) <= self._extra['_max_value']:
+                    raise ValueError('invalid port, must be an between {0} '
+                                     'and {1}'.format(
+                                         self._extra['_min_value'],
+                                         self._extra['_max_value']))
+            except ValueError:
+                raise ValueError(_('invalid port'))
 
 
 class NetworkOption(Option):
index 9e9926a..fc4241d 100644 (file)
@@ -372,7 +372,7 @@ class Settings(object):
 
     def _getproperties(self, opt=None, path=None, is_apply_req=True):
         if opt is None:
-            props = self._p_.getproperties(path, default_properties)
+            props = copy(self._p_.getproperties(path, default_properties))
         else:
             if path is None:
                 raise ValueError(_('if opt is not None, path should not be'
@@ -383,8 +383,8 @@ class Settings(object):
                     ntime = int(time())
                 is_cached, props = self._p_.getcache(path, ntime)
                 if is_cached:
-                    return props
-            props = self._p_.getproperties(path, opt._properties)
+                    return copy(props)
+            props = copy(self._p_.getproperties(path, opt._properties))
             if is_apply_req:
                 props |= self.apply_requires(opt, path)
             if 'cache' in self:
@@ -447,8 +447,8 @@ class Settings(object):
                          (typically with the `frozen` property)
         """
         # opt properties
-        properties = copy(self._getproperties(opt_or_descr, path))
-        self_properties = copy(self._getproperties())
+        properties = self._getproperties(opt_or_descr, path)
+        self_properties = self._getproperties()
         # remove opt permissive
         # permissive affect option's permission with or without permissive
         # global property
index b65ced4..a2e640c 100644 (file)
@@ -66,6 +66,8 @@ class Values(object):
         meta = self._getcontext().cfgimpl_get_meta()
         if meta is not None:
             value = meta.cfgimpl_get_values()[opt]
+            if isinstance(value, Multi):
+                value = list(value)
         else:
             value = opt.impl_getdefault()
         if opt.impl_is_multi():