From a067d2cdd9f27a153f1a994567bbcad4f907b91d Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 4 Feb 2014 21:14:30 +0100 Subject: [PATCH 1/2] add some tests --- test/test_config_api.py | 19 ++++++++++++++++++- test/test_metaconfig.py | 30 +++++++++++++++++++++--------- test/test_option_owner.py | 1 + tiramisu/config.py | 33 ++++++++++++--------------------- tiramisu/option.py | 30 +++++++++++++++++++----------- tiramisu/value.py | 2 ++ 6 files changed, 73 insertions(+), 42 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index 75920eb..f5bc2c1 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -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)") diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index 9409d85..a8fc395 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -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 diff --git a/test/test_option_owner.py b/test/test_option_owner.py index b758e91..d4c3f6b 100644 --- a/test/test_option_owner.py +++ b/test/test_option_owner.py @@ -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 diff --git a/tiramisu/config.py b/tiramisu/config.py index e3ac94f..c9fb992 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -296,12 +296,9 @@ class SubConfig(object): :return: find list or an exception if nothing has been found """ def _filter_by_name(): - try: - if byname is None or path == byname or \ - path.endswith('.' + byname): - return True - except IndexError: - pass + if byname is None or path == byname or \ + path.endswith('.' + byname): + return True return False def _filter_by_value(): @@ -452,22 +449,16 @@ 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._name) - if flatten: - name = opt._name - else: - name = '.'.join(_currpath + [opt._name]) - pathsvalues.append((name, value)) - except PropertiesOptionError: - pass # this just a hidden or disabled option + value = self._getattr(opt._name) + if flatten: + name = opt._name + else: + name = '.'.join(_currpath + [opt._name]) + pathsvalues.append((name, value)) def cfgimpl_get_path(self): descr = self.cfgimpl_get_description() diff --git a/tiramisu/option.py b/tiramisu/option.py index ad99416..409d6eb 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -662,7 +662,7 @@ class ChoiceOption(Option): return self._open_values def _validate(self, value): - if not self._open_values and not value in self._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._values)) @@ -782,9 +782,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)) @@ -856,18 +860,22 @@ class PortOption(Option): if self._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._min_value <= int(val) <= self._max_value: - raise ValueError('invalid port, must be an between {0} and {1}' - ''.format(self._min_value, self._max_value)) + try: + if not self._min_value <= int(val) <= self._max_value: + raise ValueError(_('invalid port, must be an between {0} ' + 'and {1}').format(self._min_value, + self._max_value)) + except ValueError: + raise ValueError(_('invalid port')) class NetworkOption(Option): diff --git a/tiramisu/value.py b/tiramisu/value.py index 863f2a1..ed70f90 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -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(): From c52b2f84f42dbfab7c9ab8ee8459d271b45d9593 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 4 Feb 2014 21:40:07 +0100 Subject: [PATCH 2/2] if option with requires has a property, calculated properties are store in storage --- test/test_requires.py | 22 ++++++++++++++++++++++ tiramisu/setting.py | 10 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/test/test_requires.py b/test/test_requires.py index 4cf9372..ff9c2a6 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -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(): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 101b551..38cf7b5 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -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: @@ -446,8 +446,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