diff --git a/test/new_api/test_config.py b/test/new_api/test_config.py index fa27f34..059f537 100644 --- a/test/new_api/test_config.py +++ b/test/new_api/test_config.py @@ -83,7 +83,7 @@ def test_base_config_force_permissive(): config = Config(descr) api = getapi(config) api.property.read_write() - api.permissive.set(('hidden',)) + api.permissive.set(frozenset(['hidden'])) raises(PropertiesOptionError, "api.option('boolop').value.get()") assert api.forcepermissive.option('boolop').value.get() is True diff --git a/test/new_api/test_config_api.py b/test/new_api/test_config_api.py index 1ffa915..54e466d 100644 --- a/test/new_api/test_config_api.py +++ b/test/new_api/test_config_api.py @@ -90,7 +90,7 @@ def test_make_dict(): config = Config(descr) api = getapi(config) api.property.read_write() - api.permissive.set(('hidden',)) + api.permissive.set(frozenset(['hidden'])) d = api.option.make_dict() assert d == {"s1.a": False, "int": 42} api.option('int').value.set(43) @@ -162,7 +162,7 @@ def test_find_in_config(): conf = Config(descr) api = getapi(conf) api.property.read_only() - api.permissive.set(('hidden',)) + api.permissive.set(frozenset(['hidden'])) ret = api.option.find('dummy') assert len(ret) == 1 _is_same_opt(ret[0], api.option.get('gc.dummy')) diff --git a/test/new_api/test_mandatory.py b/test/new_api/test_mandatory.py index 8cc1350..d164095 100644 --- a/test/new_api/test_mandatory.py +++ b/test/new_api/test_mandatory.py @@ -363,7 +363,7 @@ def test_mandatory_warnings_hidden(): api = getapi(Config(descr)) api.option('str').value.set('') api.property.read_write() - api.permissive.set(('hidden',)) + api.permissive.set(frozenset(['hidden'])) api.option('str').value.get() assert set(api.value.mandatory_warnings()) == {'str', 'str1', 'unicode2', 'str3'} api.option('str').property.add('hidden') diff --git a/test/new_api/test_option_calculation.py b/test/new_api/test_option_calculation.py index 453e37d..325abd2 100644 --- a/test/new_api/test_option_calculation.py +++ b/test/new_api/test_option_calculation.py @@ -633,7 +633,7 @@ def test_callback_master_and_slaves_master4(): api = getapi(Config(maconfig)) api.property.read_write() api.property.add('expert') - api.permissive.set(['expert']) + api.permissive.set(frozenset(['expert'])) assert list(api.value.mandatory_warnings()) == [] @@ -998,7 +998,7 @@ def test_callback_two_disabled2(): maconfig = OptionDescription('rootconfig', '', [od1, od2]) api = getapi(Config(maconfig)) api.property.read_write() - api.permissive.set(['hidden']) + api.permissive.set(frozenset(['hidden'])) raises(PropertiesOptionError, "api.option('od2.opt2').value.get()") assert api.forcepermissive.option('od2.opt2').owner.isdefault() diff --git a/test/new_api/test_option_consistency.py b/test/new_api/test_option_consistency.py index 0657292..afdcce0 100644 --- a/test/new_api/test_option_consistency.py +++ b/test/new_api/test_option_consistency.py @@ -703,7 +703,7 @@ def test_consistency_permissive(): a.impl_add_consistency('not_equal', b) api = getapi(Config(od)) api.property.read_write() - api.permissive.set(('hidden',)) + api.permissive.set(frozenset(['hidden'])) api.option('a').value.set(1) diff --git a/test/new_api/test_option_owner.py b/test/new_api/test_option_owner.py index 9412c43..9eafe20 100644 --- a/test/new_api/test_option_owner.py +++ b/test/new_api/test_option_owner.py @@ -50,7 +50,7 @@ def test_hidden_owner(): #raises(PropertiesOptionError, "api.forcepermissive.option('dummy').owner.get()") #raises(PropertiesOptionError, "api.option('dummy').owner.isdefault()") #raises(PropertiesOptionError, "api.forcepermissive.option('dummy').owner.isdefault()") - api.permissive.set(('hidden',)) + api.permissive.set(frozenset(['hidden'])) api.forcepermissive.option('dummy').value.get() api.forcepermissive.option('dummy').owner.isdefault() diff --git a/test/new_api/test_option_setting.py b/test/new_api/test_option_setting.py index 5290dbf..eb25abf 100644 --- a/test/new_api/test_option_setting.py +++ b/test/new_api/test_option_setting.py @@ -542,7 +542,7 @@ def test_pprint(): except Exception as error: err = error - assert str(err) == msg_error.format('option', 'string', properties, display_list(['disabled', 'hidden'])) + assert str(err) == msg_error.format('option', 'string', properties, display_list(['disabled', 'hidden'], add_quote=True)) err = None try: @@ -550,4 +550,4 @@ def test_pprint(): except Exception as error: err = error - assert str(err) == msg_error.format('option', 'string3', prop, 'hidden') + assert str(err) == msg_error.format('option', 'string3', prop, '"hidden"') diff --git a/test/new_api/test_option_with_special_name.py b/test/new_api/test_option_with_special_name.py new file mode 100644 index 0000000..c99a815 --- /dev/null +++ b/test/new_api/test_option_with_special_name.py @@ -0,0 +1,59 @@ +#this test is much more to test that **it's there** and answers attribute access +from .autopath import do_autopath +do_autopath() + +from py.test import raises + +from tiramisu import BoolOption, OptionDescription, ChoiceOption,\ + IntOption, FloatOption, StrOption, Config, getapi + + +def make_description(): + gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') + gcdummy = BoolOption('dummy', 'dummy', default=False) + gcdummy2 = BoolOption('hide', 'dummy', default=True) + 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) + wantframework_option = BoolOption('wantframework', 'Test requires', + default=False) + + gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption, gcdummy2]) + descr = OptionDescription('tiram', '', [gcgroup, booloption, objspaceoption, + wantref_option, stroption, + wantframework_option, + intoption, boolop]) + return descr + + +def test_root_config_answers_ok(): + "if you hide the root config, the options in this namespace behave normally" + gcdummy = BoolOption('dummy', 'dummy', default=False) + boolop = BoolOption('boolop', 'Test boolean option op', default=True) + descr = OptionDescription('tiramisu', '', [gcdummy, boolop]) + api = getapi(Config(descr)) + #settings = cfg.cfgimpl_get_settings() + #settings.append('hidden') + + assert api.option('dummy').value.get() is False + assert api.option('boolop').value.get() is True + + +def test_optname_shall_not_start_with_numbers(): + raises(ValueError, "gcdummy = BoolOption('123dummy', 'dummy', default=False)") + raises(ValueError, "descr = OptionDescription('123tiramisu', '', [])") + + +def test_option_has_an_api_name(): + raises(ValueError, "BoolOption('cfgimpl_get_settings', 'dummy', default=False)") + raises(ValueError, "BoolOption('unwrap_from_path', 'dummy', default=False)") + raises(ValueError, "BoolOption('impl_getdoc', 'dummy', default=False)") + raises(ValueError, "BoolOption('_unvalid', 'dummy', default=False)") + raises(ValueError, "BoolOption('6unvalid', 'dummy', default=False)") + BoolOption('unvalid6', 'dummy', default=False) + BoolOption('unvalid_', 'dummy', default=False) diff --git a/test/new_api/test_permissive.py b/test/new_api/test_permissive.py new file mode 100644 index 0000000..742f6e9 --- /dev/null +++ b/test/new_api/test_permissive.py @@ -0,0 +1,258 @@ +# coding: utf-8 +from .autopath import do_autopath +do_autopath() + +from py.test import raises + +from tiramisu import IntOption, UnicodeOption, OptionDescription, Config, getapi +from tiramisu.error import PropertiesOptionError + + +def make_description(): + u1 = IntOption('u1', '', properties=('frozen', 'mandatory', 'disabled', )) + u2 = IntOption('u2', '', properties=('frozen', 'mandatory', 'disabled', )) + return OptionDescription('od1', '', [u1, u2]) + + +def test_permissive(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_write() + api.property.read_write() + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + api.permissive.set(frozenset(['disabled'])) + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + api.property.add('permissive') + api.option('u1').value.get() + api.property.pop('permissive') + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + +def test_permissive_mandatory(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_only() + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset(['disabled']) + api.permissive.set(frozenset(['mandatory', 'disabled'])) + api.property.add('permissive') + api.option('u1').value.get() + api.property.pop('permissive') + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset(['disabled']) + + +def test_permissive_frozen(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_write() + api.permissive.set(frozenset(['frozen', 'disabled'])) + try: + api.option('u1').value.set(1) + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset(['disabled']) + api.property.add('permissive') + api.option('u1').value.set(1) + assert api.option('u1').value.get() == 1 + api.property.pop('permissive') + try: + api.option('u1').value.set(1) + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset(['disabled']) + + +def test_invalid_permissive(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_write() + raises(TypeError, "api.permissive.set(['frozen', 'disabled'])") + + +def test_permissive_option(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_write() + + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + api.option('u1').permissive.set(frozenset(['disabled'])) + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset() + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + api.property.add('permissive') + api.option('u1').value.get() + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + api.property.pop('permissive') + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset() + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + +def test_permissive_option_cache(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_write() + + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + api.option('u1').permissive.set(frozenset(['disabled'])) + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset() + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + api.property.add('permissive') + api.option('u1').value.get() + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + api.property.pop('permissive') + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset() + props = frozenset() + try: + api.option('u2').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == {'disabled'} + + +def test_permissive_option_mandatory(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_only() + props = frozenset() + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset(['disabled']) + api.option('u1').permissive.set(frozenset(['mandatory', 'disabled'])) + api.property.add('permissive') + api.option('u1').value.get() + api.property.pop('permissive') + try: + api.option('u1').value.get() + except PropertiesOptionError as err: + props = err.proptype + assert props == frozenset(['disabled']) + + +def test_permissive_option_frozen(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_write() + api.option('u1').permissive.set(frozenset(['frozen', 'disabled'])) + api.option('u1').value.set(1) + assert api.option('u1').value.get() == 1 + api.property.add('permissive') + assert api.option('u1').value.get() == 1 + api.property.pop('permissive') + assert api.option('u1').value.get() == 1 + + +def test_invalid_option_permissive(): + descr = make_description() + api = getapi(Config(descr)) + api.property.read_write() + raises(TypeError, "api.option('u1').permissive.set(['frozen', 'disabled'])") + + +def test_remove_option_permissive(): + var1 = UnicodeOption('var1', '', u'value', properties=('hidden',)) + od1 = OptionDescription('od1', '', [var1]) + rootod = OptionDescription('rootod', '', [od1]) + api = getapi(Config(rootod)) + api.property.read_write() + raises(PropertiesOptionError, "api.option('od1.var1').value.get()") + api.option('od1.var1').permissive.set(frozenset(['hidden'])) + assert api.option('od1.var1').value.get() == 'value' + api.option('od1.var1').permissive.set(frozenset()) + raises(PropertiesOptionError, "api.option('od1.var1').value.get()") diff --git a/tiramisu/api.py b/tiramisu/api.py index 8045fe9..6aec4ed 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -326,11 +326,15 @@ class TiramisuOptionPermissive(CommonTiramisuOption): slave_need_index = False def __init__(self, + name, path, index, + subconfig, config_bag): - super(TiramisuOptionPermissive, self).__init__(path, + super(TiramisuOptionPermissive, self).__init__(name, + path, index, + subconfig, config_bag) self.settings = config_bag.config.cfgimpl_get_settings() @@ -340,16 +344,16 @@ class TiramisuOptionPermissive(CommonTiramisuOption): if TIRAMISU_VERSION == 2: args = [self.setting_properties, self.path] else: - args = [self._opt, self.path] + args = [self.get_option(), self.path] return self.settings.getpermissive(*args) @count - def set(self, permissive): + def set(self, permissives): if TIRAMISU_VERSION == 2: - permissive = tuple(permissive) - self.settings.setpermissive(opt=self._opt, + permissive = tuple(permissives) + self.settings.setpermissive(opt=self.get_option(), path=self.path, - permissive=permissive) + permissives=permissives) @count def reset(self, path): @@ -575,14 +579,14 @@ class TiramisuContextProperty(TiramisuContext): def add(self, prop): props = self.get() props.add(prop) - self.set(props) + self.set(frozenset(props)) self.config_bag.setting_properties = self.config_bag.config.cfgimpl_get_settings().get_context_properties() @count def pop(self, prop): props = self.get() props.remove(prop) - self.set(props) + self.set(frozenset(props)) self.config_bag.setting_properties = self.config_bag.config.cfgimpl_get_settings().get_context_properties() @count @@ -599,7 +603,7 @@ class TiramisuContextProperty(TiramisuContext): @count def set(self, props): - self.config_bag.config.cfgimpl_get_settings().set_context_properties(frozenset(props)) + self.config_bag.config.cfgimpl_get_settings().set_context_properties(props) self.config_bag.setting_properties = self.config_bag.config.cfgimpl_get_settings().get_context_properties() def reset(self): @@ -610,8 +614,6 @@ class TiramisuContextPermissive(TiramisuContext): @count def set(self, permissives): - if TIRAMISU_VERSION != 2: - permissives = frozenset(permissives) self.config_bag.config.cfgimpl_get_settings().set_context_permissive(permissives) diff --git a/tiramisu/error.py b/tiramisu/error.py index db784c4..8321940 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -82,14 +82,14 @@ class PropertiesOptionError(AttributeError): msg = [] for action, msg_ in req.items(): msg.append('"{0}" ({1})'.format(action, display_list(msg_))) + msg = display_list(msg) else: only_one = len(self.proptype) == 1 - msg = list(self.proptype) + msg = display_list(list(self.proptype), add_quote=True) if only_one: prop_msg = _('property') else: prop_msg = _('properties') - msg = display_list(msg) if self._orig_opt: return str(_('cannot access to {0} "{1}" because "{2}" has {3} {4}' '').format(self._type, diff --git a/tiramisu/setting.py b/tiramisu/setting.py index bd180db..6bb928e 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -107,7 +107,7 @@ rw_remove = set(['permissive', 'everything_frozen', 'mandatory', 'empty']) FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value']) -forbidden_set_permissives = frozenset(['frozen', 'force_default_on_freeze']) +FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze']) log = getLogger('tiramisu') @@ -372,10 +372,6 @@ class Settings(object): index, False, config_bag) - #if requires != set([]): - # props = copy(props) - # props |= requires - props -= self.getpermissive(opt, path) if config_bag.setting_properties is not None and \ @@ -625,7 +621,7 @@ class Settings(object): if opt and opt.impl_is_symlinkoption(): raise TypeError(_("can't assign permissive to the SymLinkOption \"{}\"" "").format(opt.impl_get_display_name())) - forbidden_permissives = forbidden_set_permissives & permissives + forbidden_permissives = FORBIDDEN_SET_PERMISSIVES & permissives if forbidden_permissives: raise ConfigError(_('cannot add those permissives: {0}').format( ' '.join(forbidden_permissives))) @@ -688,10 +684,9 @@ class Settings(object): # remove permissive properties - if config_bag.force_permissive is True and properties: + if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and properties: # remove global permissive if need properties -= self.get_context_permissive() - # at this point an option should not remain in properties if properties != frozenset(): datas = {'path': path, @@ -711,14 +706,21 @@ class Settings(object): config_bag): values = self._getcontext().cfgimpl_get_values() opt = config_bag.option - if config_bag.setting_properties and 'mandatory' in config_bag.setting_properties and \ - ('mandatory' in config_bag.properties and values.isempty(opt, - value, - index=index) or \ - 'empty' in config_bag.properties and values.isempty(opt, + is_mandatory = False + if config_bag.setting_properties and 'mandatory' in config_bag.setting_properties: + if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and \ + 'mandatory' in self.get_context_permissive(): + pass + elif 'mandatory' in config_bag.properties and values.isempty(opt, + value, + index=index): + is_mandatory = True + if 'empty' in config_bag.properties and values.isempty(opt, value, force_allow_empty_list=True, - index=index)): + index=index): + is_mandatory = True + if is_mandatory: datas = {'path': path, 'config_bag': config_bag, 'index': index, @@ -731,8 +733,14 @@ class Settings(object): def validate_frozen(self, config_bag): - return 'everything_frozen' in config_bag.setting_properties or \ - 'frozen' in config_bag.properties + if config_bag.setting_properties and \ + ('everything_frozen' in config_bag.setting_properties or + 'frozen' in config_bag.properties) and \ + not ((config_bag.force_permissive is True or + 'permissive' in config_bag.setting_properties) and + 'frozen' in self.get_context_permissive()): + return True + return False #____________________________________________________________ # read only/read write @@ -742,7 +750,7 @@ class Settings(object): props = self._p_.getproperties(None, default_properties) modified = False - if remove & props != set([]): + if remove & props: props = props - remove modified = True if append & props != append: