From 3170237c8e1e7848f48c57c0103beb6c9a6caaa0 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 17 Apr 2013 21:33:34 +0200 Subject: [PATCH] properties validation not in setting and now launch when modify multi --- test/test_freeze.py | 138 ++++++++++++++++++++++++++++++++ test/test_mandatory.py | 22 +++++ test/test_option_consistency.py | 5 +- test/test_option_type.py | 92 ++++----------------- tiramisu/config.py | 89 +++++--------------- tiramisu/error.py | 3 +- tiramisu/setting.py | 35 ++++++++ tiramisu/value.py | 25 +++++- 8 files changed, 258 insertions(+), 151 deletions(-) create mode 100644 test/test_freeze.py diff --git a/test/test_freeze.py b/test/test_freeze.py new file mode 100644 index 0000000..d4f6d65 --- /dev/null +++ b/test/test_freeze.py @@ -0,0 +1,138 @@ +# coding: utf-8 +"frozen and hidden values" +import autopath + +from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ + StrOption, OptionDescription +from tiramisu.config import Config +from tiramisu.error import PropertiesOptionError + + +#____________________________________________________________ +#freeze +def make_description_freeze(): + gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') + gcdummy = BoolOption('dummy', 'dummy', default=False) + objspaceoption = ChoiceOption('objspace', 'Object space', + ('std', 'thunk'), 'std') + booloption = BoolOption('bool', 'Test boolean option', default=True) + intoption = IntOption('int', 'Test int option', default=0) + floatoption = FloatOption('float', 'Test float option', default=2.3) + stroption = StrOption('str', 'Test string option', default="abc") + boolop = BoolOption('boolop', 'Test boolean option op', default=[True], multi=True) + wantref_option = BoolOption('wantref', 'Test requires', default=False, + requires=(('boolop', True, 'hidden'),)) + wantframework_option = BoolOption('wantframework', 'Test requires', + default=False, + requires=(('boolop', True, 'hidden'),)) + + gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) + descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption, + wantref_option, stroption, + wantframework_option, + intoption, boolop]) + return descr + + +def test_freeze_whole_config(): + descr = make_description_freeze() + conf = Config(descr) + setting = conf.cfgimpl_get_settings() + setting.read_write() + setting.enable_property('everything_frozen') + assert conf.gc.dummy is False + prop = [] + try: + conf.gc.dummy = True + except PropertiesOptionError, err: + prop = err.proptype + assert 'frozen' in prop + setting.disable_property('everything_frozen') + conf.gc.dummy = True + assert conf.gc.dummy is True + + +def test_freeze_one_option(): + "freeze an option " + descr = make_description_freeze() + conf = Config(descr) + setting = conf.cfgimpl_get_settings() + setting.read_write() + #freeze only one option + dummy = conf.unwrap_from_path('gc.dummy') + setting.add_property('frozen', dummy) + assert conf.gc.dummy is False + prop = [] + try: + conf.gc.dummy = True + except PropertiesOptionError, err: + prop = err.proptype + assert 'frozen' in prop + + +def test_frozen_value(): + "setattr a frozen value at the config level" + s = StrOption("string", "", default="string") + descr = OptionDescription("options", "", [s]) + config = Config(descr) + setting = config.cfgimpl_get_settings() + setting.read_write() + setting.enable_property('frozen') + setting.add_property('frozen', s) + prop = [] + try: + config.string = "egg" + except PropertiesOptionError, err: + prop = err.proptype + assert 'frozen' in prop + + +def test_freeze(): + "freeze a whole configuration object" + descr = make_description_freeze() + conf = Config(descr) + setting = conf.cfgimpl_get_settings() + setting.read_write() + setting.enable_property('frozen') + name = conf.unwrap_from_path("gc.name") + setting.add_property('frozen', name) + prop = [] + try: + conf.gc.name = 'framework' + except PropertiesOptionError, err: + prop = err.proptype + assert 'frozen' in prop + + +def test_freeze_multi(): + descr = make_description_freeze() + conf = Config(descr) + setting = conf.cfgimpl_get_settings() + setting.read_write() + setting.enable_property('frozen') + obj = conf.unwrap_from_path('boolop') + setting.add_property('frozen', obj) + prop = [] + try: + conf.boolop = [True] + except PropertiesOptionError, err: + prop = err.proptype + assert 'frozen' in prop + + +def test_freeze_get_multi(): + descr = make_description_freeze() + conf = Config(descr) + setting = conf.cfgimpl_get_settings() + setting.read_write() + setting.enable_property('frozen') + valmulti = conf.boolop + valmulti.append(False) + obj = conf.unwrap_from_path('boolop') + setting.add_property('frozen', obj) + prop = [] + try: + valmulti.append(False) + except PropertiesOptionError, err: + prop = err.proptype + assert 'frozen' in prop diff --git a/test/test_mandatory.py b/test/test_mandatory.py index c598cd3..6172c2a 100644 --- a/test/test_mandatory.py +++ b/test/test_mandatory.py @@ -150,6 +150,15 @@ def test_mandatory_multi_empty(): assert 'mandatory' in prop +def test_mandatory_multi_append(): + descr = make_description() + config = Config(descr) + setting = config.cfgimpl_get_settings() + config.str3 = ['yes'] + setting.read_write() + config.str3.append(None) + + def test_mandatory_disabled(): descr = make_description() config = Config(descr) @@ -212,3 +221,16 @@ def test_mandatory_warnings_disabled(): assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3'] setting.add_property('disabled', descr.str) assert list(mandatory_warnings(config)) == ['str1', 'str2', 'str3'] + + +def test_mandatory_warnings_frozen(): + descr = make_description() + config = Config(descr) + config.str = '' + setting = config.cfgimpl_get_settings() + setting.read_write() + config.str + assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3'] + setting.add_property('frozen', descr.str) + setting.read_only() + assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3'] diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 4d3b8f5..735724f 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -3,6 +3,7 @@ from py.test import raises from tiramisu.config import * from tiramisu.option import * +from error import ConfigError def make_description(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') @@ -242,7 +243,7 @@ def test_has_callback(): dummy = config.unwrap_from_path('gc.dummy') setting.enable_property('freeze') setting.add_property('frozen', dummy) - raises(ConfigError, "config.gc.dummy = True") + raises(PropertiesOptionError, "config.gc.dummy = True") def test_freeze_and_has_callback_with_setoption(): descr = make_description_callback() @@ -253,5 +254,5 @@ def test_freeze_and_has_callback_with_setoption(): config.cfgimpl_get_settings().enable_property('freeze') dummy = config.unwrap_from_path('gc.dummy') config.cfgimpl_get_settings().add_property('frozen', dummy) - raises(ConfigError, "config.gc.setoption('dummy', descr.gc.dummy, True)") + raises(PropertiesOptionError, "config.gc.setoption('dummy', descr.gc.dummy, True)") #____________________________________________________________ diff --git a/test/test_option_type.py b/test/test_option_type.py index 3e66e7c..12a777d 100644 --- a/test/test_option_type.py +++ b/test/test_option_type.py @@ -3,21 +3,24 @@ import autopath from py.test import raises -from tiramisu.config import * -from tiramisu.option import * +from tiramisu.config import Config +from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ + StrOption, OptionDescription +from tiramisu.error import PropertiesOptionError + def make_description(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False, properties=(('hidden'),)) objspaceoption = ChoiceOption('objspace', 'Object space', - ('std', 'thunk'), 'std') + ('std', 'thunk'), 'std') booloption = BoolOption('bool', 'Test boolean option', default=True) intoption = IntOption('int', 'Test int option', default=0) floatoption = FloatOption('float', 'Test float option', default=2.3) stroption = StrOption('str', 'Test string option', default="abc") wantref_option = BoolOption('wantref', 'Test requires', default=False, - requires=(('gc.name', 'ref', 'hidden'),)) + requires=(('gc.name', 'ref', 'hidden'),)) wantframework_option = BoolOption('wantframework', 'Test requires', default=False, requires=(('gc.name', 'framework', 'hidden'),)) @@ -29,75 +32,12 @@ def make_description(): gcgroup = OptionDescription('gc', '', [subgroup, gcoption, gcdummy, floatoption]) descr = OptionDescription('trs', '', [gcgroup, booloption, objspaceoption, - wantref_option, stroption, - wantframework_option, - intoption]) - return descr -#____________________________________________________________ -#freeze -def make_description_freeze(): - gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') - gcdummy = BoolOption('dummy', 'dummy', default=False) - objspaceoption = ChoiceOption('objspace', 'Object space', - ('std', 'thunk'), 'std') - booloption = BoolOption('bool', 'Test boolean option', default=True) - intoption = IntOption('int', 'Test int option', default=0) - floatoption = FloatOption('float', 'Test float option', default=2.3) - stroption = StrOption('str', 'Test string option', default="abc") - boolop = BoolOption('boolop', 'Test boolean option op', default=True) - wantref_option = BoolOption('wantref', 'Test requires', default=False, - requires=(('boolop', True, 'hidden'),)) - wantframework_option = BoolOption('wantframework', 'Test requires', - default=False, - requires=(('boolop', True, 'hidden'),)) - - gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) - descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption, - wantref_option, stroption, - wantframework_option, - intoption, boolop]) + wantref_option, stroption, + wantframework_option, + intoption]) return descr -def test_freeze_whole_config(): - descr = make_description_freeze() - conf = Config(descr) - settings = conf.cfgimpl_get_settings() - settings.enable_property('everything_frozen') - assert conf.gc.dummy == False - raises(ConfigError, "conf.gc.dummy = True") - settings.disable_property('everything_frozen') - conf.gc.dummy = True - assert conf.gc.dummy == True -def test_freeze_one_option(): - "freeze an option " - descr = make_description_freeze() - conf = Config(descr) - setting = conf.cfgimpl_get_settings() - setting.read_write() - #freeze only one option - dummy = conf.unwrap_from_path('gc.dummy') - conf.gc.cfgimpl_get_settings().add_property('frozen', dummy) - assert conf.gc.dummy == False - raises(ConfigError, "conf.gc.dummy = True") - -def test_frozen_value(): - "setattr a frozen value at the config level" - s = StrOption("string", "", default="string") - descr = OptionDescription("options", "", [s]) - config = Config(descr) - settings = config.cfgimpl_get_settings().enable_property('frozen') - config.cfgimpl_get_settings().add_property('frozen', s) - raises(ConfigError, 'config.string = "egg"') - -def test_freeze(): - "freeze a whole configuration object" - descr = make_description() - conf = Config(descr) - settings = conf.cfgimpl_get_settings().enable_property('frozen') - name = conf.unwrap_from_path("gc.name") - conf.cfgimpl_get_settings().add_property('frozen', name) - raises(ConfigError, "conf.gc.name = 'framework'") # ____________________________________________________________ def test_is_hidden(): descr = make_description() @@ -110,9 +50,7 @@ def test_is_hidden(): raises(PropertiesOptionError, "config.gc.dummy == False") # getattr raises(PropertiesOptionError, "config.gc.dummy") - # I want to access to this option anyway - opt = config.unwrap_from_path("gc.dummy") - assert config.cfgimpl_get_values()[opt] == False + def test_group_is_hidden(): descr = make_description() @@ -120,7 +58,7 @@ def test_group_is_hidden(): setting = config.cfgimpl_get_settings() setting.read_write() gc = config.unwrap_from_path('gc') - dummy = config.unwrap_from_path('gc.dummy') + config.unwrap_from_path('gc.dummy') config.cfgimpl_get_settings().add_property('hidden', gc) raises(PropertiesOptionError, "config.gc.dummy") assert config.cfgimpl_get_settings().has_property('hidden', gc) @@ -132,6 +70,7 @@ def test_group_is_hidden(): #dummy est en hide raises(PropertiesOptionError, "config.gc.dummy == False") + def test_global_show(): descr = make_description() config = Config(descr) @@ -142,15 +81,16 @@ def test_global_show(): assert config.cfgimpl_get_settings().has_property('hidden', dummy) raises(PropertiesOptionError, "config.gc.dummy == False") + def test_with_many_subgroups(): descr = make_description() config = Config(descr) booltwo = config.unwrap_from_path('gc.subgroup.booltwo') assert not config.cfgimpl_get_settings().has_property('hidden', booltwo) - assert config.gc.subgroup.booltwo == False + assert config.gc.subgroup.booltwo is False config.cfgimpl_get_settings().add_property('hidden', booltwo) path = 'gc.subgroup.booltwo' homeconfig, name = config.cfgimpl_get_home_by_path(path) assert name == "booltwo" - option = getattr(homeconfig._cfgimpl_descr, name) + getattr(homeconfig._cfgimpl_descr, name) assert config.cfgimpl_get_settings().has_property('hidden', booltwo) diff --git a/tiramisu/config.py b/tiramisu/config.py index 717fc49..f29ac00 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -21,8 +21,7 @@ # the whole pypy projet is under MIT licence # ____________________________________________________________ #from inspect import getmembers, ismethod -from tiramisu.error import (PropertiesOptionError, ConfigError, - AmbigousOptionError) +from tiramisu.error import PropertiesOptionError, AmbigousOptionError from tiramisu.option import OptionDescription, Option, SymLinkOption from tiramisu.setting import groups, Setting, apply_requires from tiramisu.value import Values @@ -79,49 +78,10 @@ class SubConfig(object): return homeconfig.__setattr__(name, value) child = getattr(self._cfgimpl_descr, name) if type(child) != SymLinkOption: - self._validate(name, getattr(self._cfgimpl_descr, name), value, - force_permissive=force_permissive) - self.setoption(name, child, value) + self.setoption(name, child, value, force_permissive) else: child.setoption(self.cfgimpl_get_context(), value) - def _validate_descr(self, name, opt_or_descr, force_permissive=False, is_raise=True): - if not isinstance(opt_or_descr, Option) and \ - not isinstance(opt_or_descr, OptionDescription): - raise TypeError(_('unexpected object: {0}').format(repr(opt_or_descr))) - properties = set(self.cfgimpl_get_settings().get_properties(opt_or_descr)) - #remove this properties, those properties are validate in value/setting - properties = properties - set(['mandatory', 'frozen']) - set_properties = set(self.cfgimpl_get_settings().get_properties()) - properties = properties & set_properties - if force_permissive is True or self.cfgimpl_get_settings().has_property('permissive', is_apply_req=False): - properties = properties - set(self.cfgimpl_get_settings().get_permissive()) - properties = properties - set(self.cfgimpl_get_settings().get_permissive(opt_or_descr)) - properties = list(properties) - if is_raise: - if properties != []: - raise PropertiesOptionError(_("trying to access" - " to an option named: {0} with properties" - " {1}").format(name, str(properties)), - properties) - else: - return properties - - def _validate(self, name, opt_or_descr, value, force_permissive=False, - force_properties=None): - "validation for the setattr and the getattr" - properties = self._validate_descr(name, opt_or_descr, - force_permissive=force_permissive, - is_raise=False) - if self.cfgimpl_get_context().cfgimpl_get_values().is_mandatory_err( - opt_or_descr, value, force_properties=force_properties): - properties.append('mandatory') - if properties != []: - raise PropertiesOptionError(_("trying to access" - " to an option named: {0} with properties" - " {1}").format(name, str(properties)), - properties) - def __getattr__(self, name): return self._getattr(name) @@ -142,14 +102,23 @@ class SubConfig(object): return homeconfig._getattr(name, force_permissive=force_permissive, force_properties=force_properties, validate=validate) + # special attributes + if name.startswith('_cfgimpl_'): + # if it were in __dict__ it would have been found already + object.__getattr__(self, name) opt_or_descr = getattr(self._cfgimpl_descr, name) # symlink options - if type(opt_or_descr) == SymLinkOption: + if isinstance(opt_or_descr, SymLinkOption): rootconfig = self.cfgimpl_get_context() path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt) - return rootconfig._getattr(path, validate=validate) - if isinstance(opt_or_descr, OptionDescription): - self._validate_descr(name, opt_or_descr, force_permissive=force_permissive) + return rootconfig._getattr(path, validate=validate, + force_properties=force_properties, + force_permissive=force_permissive) + elif isinstance(opt_or_descr, OptionDescription): + self.cfgimpl_get_settings().validate_properties(opt_or_descr, + True, False, + force_permissive=force_permissive, + force_properties=force_properties) children = self.cfgimpl_get_description()._children if opt_or_descr not in children[1]: raise AttributeError(_("{0} with name {1} object has " @@ -157,36 +126,22 @@ class SubConfig(object): opt_or_descr._name, name)) return SubConfig(opt_or_descr, self._cfgimpl_context) - # special attributes - if name.startswith('_cfgimpl_'): - # if it were in __dict__ it would have been found already - object.__getattr__(self, name) - value = self.cfgimpl_get_values()._getitem(opt_or_descr, - validate=validate) - self._validate(name, opt_or_descr, value, - force_permissive=force_permissive, - force_properties=force_properties) - return value + else: + value = self.cfgimpl_get_values()._getitem(opt_or_descr, + validate=validate, + force_properties=force_properties, + force_permissive=force_permissive) + return value - def setoption(self, name, child, value): + def setoption(self, name, child, value, force_permissive=False): """effectively modifies the value of an Option() (typically called by the __setattr__) """ - setting = self.cfgimpl_get_settings() #needed ? apply_requires(child, self) - #needed to ? if child not in self._cfgimpl_descr._children[1]: raise AttributeError(_('unknown option {0}').format(name)) - if setting.has_property('everything_frozen'): - raise ConfigError(_("cannot set a value to the option {0} if the whole " - "config has been frozen").format(name)) - - if setting.has_property('frozen') and setting.has_property('frozen', - child, is_apply_req=False): - raise ConfigError(_('cannot change the value to {0} for ' - 'option {1} this option is frozen').format(str(value), name)) self.cfgimpl_get_values()[child] = value def cfgimpl_get_home_by_path(self, path, force_permissive=False, force_properties=None): diff --git a/tiramisu/error.py b/tiramisu/error.py index 43083c9..fc31491 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -33,8 +33,7 @@ class AmbigousOptionError(StandardError): class ConfigError(StandardError): - """if modify frozen config - or try to change owner for an option without value + """try to change owner for an option without value or if error in calculation""" pass diff --git a/tiramisu/setting.py b/tiramisu/setting.py index dffc4ef..5af26f9 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -211,6 +211,41 @@ class Setting(object): self.set_properties(properties, opt) #____________________________________________________________ + def validate_properties(self, opt_or_descr, is_descr, is_write, + value=None, force_permissive=False, + force_properties=None): + properties = set(self.get_properties(opt_or_descr)) + #remove this properties, those properties are validate in after + properties = properties - set(['mandatory', 'frozen']) + set_properties = self.get_properties() + if force_properties is not None: + set_properties.extend(force_properties) + set_properties = set(set_properties) + properties = properties & set_properties + if force_permissive is True or self.has_property('permissive', is_apply_req=False): + properties = properties - set(self.get_permissive()) + properties = properties - set(self.get_permissive(opt_or_descr)) + properties = list(properties) + raise_text = _("trying to access" + " to an option named: {0} with properties" + " {1}") + if not is_descr: + if self.context.cfgimpl_get_values().is_mandatory_err(opt_or_descr, + value, + force_properties=force_properties): + properties.append('mandatory') + if is_write and (self.has_property('everything_frozen') or ( + self.has_property('frozen') and + self.has_property('frozen', opt_or_descr, + is_apply_req=False))): + properties.append('frozen') + raise_text = _('cannot change the value to {0} for ' + 'option {1} this option is frozen') + if properties != []: + raise PropertiesOptionError(raise_text.format(opt_or_descr._name, + str(properties)), + properties) + def get_permissive(self, opt=None): return self.permissives.get(opt, []) diff --git a/tiramisu/value.py b/tiramisu/value.py index fb3ddba..c1d8477 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -107,10 +107,11 @@ class Values(object): def __getitem__(self, opt): return self._getitem(opt) - def _getitem(self, opt, validate=True): + def _getitem(self, opt, validate=True, force_permissive=False, + force_properties=None): # options with callbacks - value = self._get_value(opt) setting = self.context.cfgimpl_get_settings() + value = self._get_value(opt) is_frozen = setting.has_property('frozen', opt, False) if opt.has_callback(): #if value is set and : @@ -137,11 +138,23 @@ class Values(object): if self.is_default_owner(opt) and \ setting.has_property('force_store_value', opt, False): self.setitem(opt, value, validate=validate) + setting.validate_properties(opt, False, False, value=value, + force_permissive=force_permissive, + force_properties=force_properties) return value def __setitem__(self, opt, value): + #valid config + #FIXME: + force_permissive = False + force_properties = None + setting = self.context.cfgimpl_get_settings() + setting.validate_properties(opt, False, True, + value=value, force_permissive=force_permissive, + force_properties=force_properties) + #valid opt if not opt.validate(value, self.context, - self.context.cfgimpl_get_settings().has_property('validator')): + setting.has_property('validator')): raise ValueError(_('invalid value {}' ' for option {}').format(value, opt._name)) if opt.is_multi(): @@ -227,7 +240,11 @@ class Multi(list): " which is a slave").format(self.opt._name)) elif self.opt.get_multitype() == multitypes.master: for slave in self.opt.get_master_slaves(): - self.context.cfgimpl_get_values()[slave].append(slave.getdefault_multi(), force=True) + self.context.cfgimpl_get_values()[slave].append( + slave.getdefault_multi(), force=True) + self.context.cfgimpl_get_settings().validate_properties(self.opt, + False, True, + value=value) self._validate(value) self.context.cfgimpl_get_values().setitem(self.opt, self) super(Multi, self).append(value)