diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index f333ea2..eb1a852 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 tiramisu.setting import settings def make_description(): gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') @@ -60,14 +61,6 @@ def test_identical_paths(): descr = make_description_duplicates() raises(ConflictConfigError, "cfg = Config(descr)") -#def test_identical_for_names(): -# """if there is something that -# have the same name, an exection is raised -# """ -# descr = make_description_duplicates() -# raises(ConflictConfigError, "cfg = Config(descr)") - - def make_description2(): gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) @@ -97,8 +90,7 @@ def make_description2(): intoption, boolop]) return descr -# FIXME: XXX would you mind putting the multi validations anywhere else -# than in the requires !!! +# FIXME: il faudra tester les validations sur les multis #def test_multi_constraints(): # "a multi in a constraint has to have the same length" # descr = make_description2() @@ -213,3 +205,47 @@ def test_disabled_with_group(): cfg.int = 1 raises(PropertiesOptionError, "cfg.gc.name") assert gcgroup._is_disabled() +#____________________________________________________________ + +def make_description_callback(): + gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') + gcdummy = BoolOption('dummy', 'dummy', callback="toto") + 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']) + wantframework_option = BoolOption('wantframework', 'Test requires', + default=False, + requires=['boolop']) + gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) + descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption, + wantref_option, stroption, + wantframework_option, + intoption, boolop]) + return descr + +def test_has_callback(): + descr = make_description_callback() + # here the owner is 'default' + config = Config(descr) + config.bool = False + # because dummy has a callback + dummy = config.unwrap_from_path('gc.dummy') + settings.freeze() + dummy.freeze() + raises(TypeError, "config.gc.dummy = True") + +def test_freeze_and_has_callback_with_setoption(): + descr = make_description_callback() + config = Config(descr) + config.bool = False + settings.freeze() + dummy = config.unwrap_from_path('gc.dummy') + dummy.freeze() + raises(TypeError, "config.gc.setoption('dummy', True, 'gen_config')") +#____________________________________________________________ diff --git a/test/test_option_owner.py b/test/test_option_owner.py index a3ae1b5..8131c84 100644 --- a/test/test_option_owner.py +++ b/test/test_option_owner.py @@ -3,31 +3,9 @@ import autopath from py.test import raises from tiramisu.config import * from tiramisu.option import * -from tiramisu.setting import settings +from tiramisu.setting import settings, owners def make_description(): - gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') - gcdummy = BoolOption('dummy', 'dummy', callback="toto") - 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']) - wantframework_option = BoolOption('wantframework', 'Test requires', - default=False, - requires=['boolop']) - gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) - descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption, - wantref_option, stroption, - wantframework_option, - intoption, boolop]) - return descr - -def make_description2(): gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) objspaceoption = ChoiceOption('objspace', 'Object space', @@ -37,35 +15,36 @@ def make_description2(): 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']) + wantref_option = BoolOption('wantref', 'Test requires', default=False) wantframework_option = BoolOption('wantframework', 'Test requires', - default=False, - requires=['boolop']) + default=False) + gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) - descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption, + descr = OptionDescription('tiram', '', [gcgroup, booloption, objspaceoption, wantref_option, stroption, wantframework_option, intoption, boolop]) return descr -def test_has_callback(): - descr = make_description() - # here the owner is 'default' - config = Config(descr) - config.bool = False - # because dummy has a callback - dummy = config.unwrap_from_path('gc.dummy') - settings.freeze() - dummy.freeze() - raises(TypeError, "config.gc.dummy = True") +def test_default_owner(): + gcdummy = BoolOption('dummy', 'dummy', default=False) + descr = OptionDescription('tiramisu', '', [gcdummy]) + cfg = Config(descr) + assert cfg.dummy == False + dm = cfg.unwrap_from_path('dummy') + assert dm.getowner(cfg) == 'default' + dm.setowner(cfg, owners.user) + assert dm.getowner(cfg) == owners.user -#____________________________________________________________ -def test_freeze_and_has_callback_with_setoption(): - descr = make_description() - config = Config(descr) - config.bool = False - settings.freeze() - dummy = config.unwrap_from_path('gc.dummy') - dummy.freeze() - raises(TypeError, "config.gc.setoption('dummy', True, 'gen_config')") +def test_owner_is_not_a_string(): + gcdummy = BoolOption('dummy', 'dummy', default=False) + descr = OptionDescription('tiramisu', '', [gcdummy]) + cfg = Config(descr) + assert cfg.dummy == False + dm = cfg.unwrap_from_path('dummy') + assert dm.getowner(cfg) == owners.default + assert dm.getowner(cfg) == 'default' + assert isinstance(dm.getowner(cfg), owners.Owner) + dm.setowner(cfg, owners.user) + + assert dm.getowner(cfg) == 'user' diff --git a/test/test_option_setting.py b/test/test_option_setting.py index eb5fa44..2997178 100644 --- a/test/test_option_setting.py +++ b/test/test_option_setting.py @@ -51,10 +51,10 @@ def test_reset(): config = Config(descr) config.string = "foo" assert config.string == "foo" - assert config._cfgimpl_value_owners['string'] == 'user' + assert config._cfgimpl_value_owners['string'] == owners.user config.unwrap_from_path("string").reset(config) assert config.string == 'string' - assert config._cfgimpl_value_owners['string'] == 'default' + assert config._cfgimpl_value_owners['string'] == owners.default def test_reset_with_multi(): s = StrOption("string", "", default=["string"], default_multi="string" , multi=True) @@ -250,7 +250,7 @@ def test_setoption_from_option(): booloption = BoolOption('bool', 'Test boolean option', default=True) descr = OptionDescription('descr', '', [booloption]) cfg = Config(descr) - booloption.setoption(cfg, False, 'owner') + booloption.setoption(cfg, False) assert cfg.bool == False #____________________________________________________________ def test_dwim_set(): @@ -348,14 +348,11 @@ def test_accepts_multiple_changes_from_option(): config.string = "egg" assert s.getdefault() == "string" assert config.string == "egg" - s.setoption(config, 'blah', "default") + s.setoption(config, 'blah') assert s.getdefault() == "string" assert config.string == "blah" - s.setoption(config, 'bol', "user") + s.setoption(config, 'bol') assert config.string == 'bol' -# config.override({'string': "blurp"}) -# assert config.string == 'blurp' -# assert s.getdefault() == 'blurp' def test_allow_multiple_changes_from_config(): """ @@ -367,28 +364,13 @@ def test_allow_multiple_changes_from_config(): suboption = OptionDescription("bip", "", [s2]) descr = OptionDescription("options", "", [s, suboption]) config = Config(descr) - config.setoption("string", 'blah', "user") - config.setoption("string", "oh", "user") + config.setoption("string", 'blah', owners.user) + config.setoption("string", "oh", owners.user) assert config.string == "oh" config.set(string2= 'blah') assert config.bip.string2 == 'blah' # ____________________________________________________________ - -#def test_overrides_are_defaults(): -# descr = OptionDescription("test", "", [ -# BoolOption("b1", "", default=False), -# BoolOption("b2", "", default=False), -# ]) -# # overrides here -# config = Config(descr) -# config.b2 = True -# assert config.b2 -# # test with a require -# config.b1 = True -# assert config.b2 - - # ____________________________________________________________ - # accessing a value by the get method +# accessing a value by the get method def test_access_by_get(): descr = make_description() cfg = Config(descr) diff --git a/test/test_parsing_group.py b/test/test_parsing_group.py index d9c2e00..10c7a93 100644 --- a/test/test_parsing_group.py +++ b/test/test_parsing_group.py @@ -57,7 +57,13 @@ def test_get_group_type(): descr = make_description() config = Config(descr) grp = config.unwrap_from_path('creole.general') - assert grp.get_group_type() == "family" + assert grp.get_group_type() == groups.family + assert grp.get_group_type() == 'family' + assert isinstance(grp.get_group_type(), groups.GroupName) + grp.set_group_type(groups.default) + assert isinstance(grp.get_group_type(), groups.DefaultGroupName) + assert grp.get_group_type() == groups.default + assert grp.get_group_type() == 'default' def test_iter_on_groups(): descr = make_description() diff --git a/tiramisu/config.py b/tiramisu/config.py index 1b69006..fbfdb69 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -26,7 +26,7 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError, MandatoryError, MethodCallError, NoValueReturned) from tiramisu.option import (OptionDescription, Option, SymLinkOption, Multi, apply_requires) -from tiramisu.setting import settings, groups +from tiramisu.setting import settings, groups, owners # ____________________________________________________________ class Config(object): @@ -81,7 +81,7 @@ class Config(object): childdef = child.getdefault() self._cfgimpl_values[child._name] = childdef self._cfgimpl_previous_values[child._name] = childdef - child.setowner(self, 'default') + child.setowner(self, owners.default) elif isinstance(child, OptionDescription): self._validate_duplicates(child._children) self._cfgimpl_values[child._name] = Config(child, parent=self) @@ -100,7 +100,7 @@ class Config(object): copy(child.getdefault()), config=self, opt=child) else: self._cfgimpl_values[child._name] = copy(child.getdefault()) - child.setowner(self, 'default') + child.setowner(self, owners.default) elif isinstance(child, OptionDescription): if child._name not in self._cfgimpl_values: self._cfgimpl_values[child._name] = Config(child, parent=self) @@ -117,7 +117,7 @@ class Config(object): return setattr(homeconfig, name, value) if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption: self._validate(name, getattr(self._cfgimpl_descr, name)) - self.setoption(name, value, settings.owner) + self.setoption(name, value, owners.user) def _validate(self, name, opt_or_descr, permissive=False): "validation for the setattr and the getattr" @@ -263,7 +263,7 @@ class Config(object): raise ConfigError('invalid calculated value returned' ' for option {0}'.format(name)) self._cfgimpl_values[name] = _result - opt_or_descr.setowner(self, 'default') + opt_or_descr.setowner(self, owners.default) # frozen and force default if not opt_or_descr.has_callback() and opt_or_descr.is_forced_on_freeze(): value = opt_or_descr.getdefault() @@ -272,7 +272,7 @@ class Config(object): use_default_multi=True, default_multi=opt_or_descr.getdefault_multi()) self._cfgimpl_values[name] = value - opt_or_descr.setowner(self, 'default') + opt_or_descr.setowner(self, owners.default) self._test_mandatory(name, opt_or_descr) value = self._cfgimpl_values[name] self._valid_len(name, value) @@ -307,17 +307,14 @@ class Config(object): def setoption(self, name, value, who=None): """effectively modifies the value of an Option() (typically called by the __setattr__) - - :param who: is an owner's name - who is **not necessarily** a owner, because it cannot be a list - :type who: string + :param who : an object that lives in `setting.owners` """ child = getattr(self._cfgimpl_descr, name) if type(child) != SymLinkOption: if who == None: who = settings.owner if child.is_multi(): - if who != 'default': + if not isinstance(who, owners.DefaultOwner): if type(value) != Multi: if type(value) == list: value = Multi(value, self, child) @@ -329,11 +326,15 @@ class Config(object): use_default_multi=True, default_multi=child.getdefault_multi()) self._valid_len(name, value) - child.setoption(self, value, who) + if not isinstance(who, owners.Owner): + raise TypeError("invalid owner [{0}] for option: {1}".format( + str(who), name)) + print "ssdfsdfsdfsdfsdf", type(child) + child.setoption(self, value) child.setowner(self, who) else: homeconfig = self._cfgimpl_get_toplevel() - child.setoption(homeconfig, value, who) + child.setoption(homeconfig, value) def set(self, **kwargs): """ @@ -355,7 +356,7 @@ class Config(object): pass except Exception, e: raise e # HiddenOptionError or DisabledOptionError - homeconfig.setoption(name, value, settings.owner) + homeconfig.setoption(name, value, owners.user) elif len(candidates) > 1: raise AmbigousOptionError( 'more than one option that ends with %s' % (key, )) @@ -457,8 +458,8 @@ class Config(object): """iteration on groups objects only. All groups are returned if `group_type` is `None`, otherwise the groups can be filtered by categories (families, or whatever). - :param group_type: if defined, is an instance of `settings.GroupName` - or `settings.MasterGroupName` that lives in + :param group_type: if defined, is an instance of `groups.GroupName` + or `groups.MasterGroupName` that lives in `settings.groups` """ diff --git a/tiramisu/option.py b/tiramisu/option.py index b5ca031..7f78717 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -26,7 +26,7 @@ from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError, RequiresError, RequirementRecursionError, MandatoryError, PropertiesOptionError) from tiramisu.autolib import carry_out_calculation -from tiramisu.setting import settings, groups +from tiramisu.setting import settings, groups, owners requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')] @@ -65,7 +65,7 @@ class Multi(list): super(Multi, self).__init__(lst) def __setitem__(self, key, value): - return self._setvalue(value, key, who=settings.owner) + return self._setvalue(value, key, who=owners.user) def append(self, value, add_master=True): """the list value can be updated (appened) @@ -307,13 +307,13 @@ class Option(HiddenBaseType, DisabledBaseType): """ :param config: *must* be only the **parent** config (not the toplevel config) - :param owner: is a **real** owner, that is a name or a list - which is allowable here + :param owner: is a **real** owner, that is an object + that lives in setting.owners """ name = self._name - if not type(owner) == str: - raise ConfigError("invalid type for owner option: {0}".format( - name)) + if not isinstance(owner, owners.Owner): + raise ConfigError("invalid type owner for option: {0}".format( + str(name))) config._cfgimpl_value_owners[name] = owner def getowner(self, config): @@ -323,7 +323,7 @@ class Option(HiddenBaseType, DisabledBaseType): def reset(self, config): """resets the default value and owner """ - config.setoption(self._name, self.getdefault(), 'default') + config.setoption(self._name, self.getdefault(), owners.default) def is_default_owner(self, config): """ @@ -333,11 +333,10 @@ class Option(HiddenBaseType, DisabledBaseType): """ return self.getowner(config) == 'default' - def setoption(self, config, value, who): + def setoption(self, config, value): """changes the option's value with the value_owner's who :param config: the parent config is necessary here to store the value - :param who : is **not necessarily** a owner because it cannot be a list - :type who: string """ + """ name = self._name rootconfig = config._cfgimpl_get_toplevel() if not self.validate(value, settings.validator): @@ -446,7 +445,7 @@ class SymLinkOption(object): self.path = path self.opt = opt - def setoption(self, config, value, who): + def setoption(self, config, value): setattr(config, self.path, value) def __getattr__(self, name): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 927fae3..39e84f9 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -42,6 +42,10 @@ class GroupModule(_const): *normal* means : groups that are not master """ pass + class DefaultGroupName(GroupName): + """groups that are default (typically 'default')""" + pass + class MasterGroupName(GroupName): """allowed normal group (OptionDescription) names *master* means : groups that have the 'master' attribute set @@ -54,16 +58,44 @@ def populate_groups(): "populates the available groups in the appropriate namespaces" _available_group_names = ('default', 'family', 'group') _available_groups_with_a_master = ('group', ) + _available_default_groups = ('default', ) # populates normal or master groups for grp in _available_group_names: if grp in _available_groups_with_a_master: setattr(groups, grp, groups.MasterGroupName(grp)) + elif grp in _available_default_groups: + setattr(groups, grp, groups.DefaultGroupName(grp)) else: setattr(groups, grp, groups.GroupName(grp)) # names are in the module now populate_groups() # ____________________________________________________________ +class OwnerModule(_const): + """emulates a module to manage unique owner names. + owners are living in `Config._cfgimpl_value_owners` + """ + class Owner(str): + """allowed owner names + """ + pass + class DefaultOwner(Owner): + """groups that are default (typically 'default')""" + pass +# setting.owners (emulates a module) +owners = OwnerModule() + +def populate_owners(): + """populates the available owners in the appropriate namespaces + + - 'user' is the generic is the generic owner. + - 'default' is the config owner after init time + """ + setattr(owners, 'default', owners.DefaultOwner('default')) + setattr(owners,'user', owners.Owner('user')) +# names are in the module now +populate_owners() +#____________________________________________________________ class Setting(): "``Config()``'s configuration options" # properties attribute: the name of a property enables this property @@ -75,8 +107,6 @@ class Setting(): frozen = True # enables validation function for options if set validator = False - # generic owner. 'default' is the general config owner after init time - owner = 'user' # ____________________________________________________________ # properties methods def has_properties(self):