diff --git a/test/test_option_owner.py b/test/test_option_owner.py index 6a4703b..a3ae1b5 100644 --- a/test/test_option_owner.py +++ b/test/test_option_owner.py @@ -3,6 +3,7 @@ import autopath 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') @@ -48,25 +49,6 @@ def make_description2(): intoption, boolop]) return descr -#def test_override_are_default_owner(): -# "config.override() implies that the owner is 'default' again" -# descr = make_description2() -# config = Config(descr) -# config.bool = False -# # default -# assert config.gc._cfgimpl_value_owners['dummy'] == 'default' -# # user -# config.gc.dummy = True -# assert config.gc._cfgimpl_value_owners['dummy'] == 'user' -# assert config._cfgimpl_values['gc']._cfgimpl_value_owners['dummy'] == 'user' -# #Options have an available default setting and can give it back -# assert config._cfgimpl_descr._children[0]._children[1].getdefault() == False -# config.override({'gc.dummy':True}) -# assert config.gc._cfgimpl_value_owners['dummy'] == 'default' -# # user again -# config.gc.dummy = False -# assert config.gc._cfgimpl_value_owners['dummy'] == 'user' - def test_has_callback(): descr = make_description() # here the owner is 'default' @@ -74,7 +56,7 @@ def test_has_callback(): config.bool = False # because dummy has a callback dummy = config.unwrap_from_path('gc.dummy') - config.cfgimpl_freeze() + settings.freeze() dummy.freeze() raises(TypeError, "config.gc.dummy = True") @@ -83,12 +65,7 @@ def test_freeze_and_has_callback_with_setoption(): descr = make_description() config = Config(descr) config.bool = False - config.cfgimpl_freeze() + settings.freeze() dummy = config.unwrap_from_path('gc.dummy') dummy.freeze() raises(TypeError, "config.gc.setoption('dummy', True, 'gen_config')") - -#def test_cannot_override(): -# descr = make_description() -# config = Config(descr, bool=False) -# raises(TypeError, "config.override({'gc.dummy': True})") diff --git a/test/test_option_type.py b/test/test_option_type.py index d19920f..bba0845 100644 --- a/test/test_option_type.py +++ b/test/test_option_type.py @@ -1,10 +1,11 @@ # coding: utf-8 -"frozen and hidden values" +"frozen and hidden values" import autopath 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') @@ -23,12 +24,12 @@ def make_description(): wantframework_option = BoolOption('wantframework', 'Test requires', default=False, requires=[('gc.name', 'framework')]) - + # ____________________________________________________________ booloptiontwo = BoolOption('booltwo', 'Test boolean option two', default=False) subgroup = OptionDescription('subgroup', '', [booloptiontwo]) # ____________________________________________________________ - + gcgroup = OptionDescription('gc', '', [subgroup, gcoption, gcdummy, floatoption]) descr = OptionDescription('trs', '', [gcgroup, booloption, objspaceoption, wantref_option, stroption, @@ -52,7 +53,7 @@ def make_description_freeze(): wantframework_option = BoolOption('wantframework', 'Test requires', default=False, requires=['boolop']) - + gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption, wantref_option, stroption, @@ -74,7 +75,7 @@ def test_frozen_value(): s = StrOption("string", "", default="string") descr = OptionDescription("options", "", [s]) config = Config(descr) - config.cfgimpl_freeze() + settings.freeze() s.freeze() raises(TypeError, 'config.string = "egg"') @@ -82,7 +83,7 @@ def test_freeze(): "freeze a whole configuration object" descr = make_description() conf = Config(descr) - conf.cfgimpl_freeze() + settings.freeze() name = conf.unwrap_from_path("gc.name") name.freeze() raises(TypeError, "conf.gc.name = 'framework'") @@ -115,7 +116,7 @@ def test_group_is_hidden(): assert config.gc.float == 2.3 #dummy est en hide raises(PropertiesOptionError, "config.gc.dummy == False") - + def test_global_show(): descr = make_description() config = Config(descr) @@ -133,4 +134,3 @@ def test_with_many_subgroups(): assert name == "booltwo" option = getattr(homeconfig._cfgimpl_descr, name) assert option._is_hidden() - diff --git a/test/test_option_with_special_name.py b/test/test_option_with_special_name.py index 0d6b538..03c2557 100644 --- a/test/test_option_with_special_name.py +++ b/test/test_option_with_special_name.py @@ -4,6 +4,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') @@ -39,6 +40,6 @@ def test_root_config_answers_ok(): boolop = BoolOption('boolop', 'Test boolean option op', default=True) descr = OptionDescription('tiramisu', '', [gcdummy, boolop]) cfg = Config(descr) - cfg.cfgimpl_enable_property('hiddend') #cfgimpl_hide() + settings.enable_property('hiddend') #cfgimpl_hide() assert cfg.dummy == False assert cfg.boolop == True diff --git a/tiramisu/config.py b/tiramisu/config.py index 0b8dbbe..ac33645 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -26,23 +26,11 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError, MandatoryError, MethodCallError, NoValueReturned) from tiramisu.option import (OptionDescription, Option, SymLinkOption, group_types, Multi, apply_requires) - -# ______________________________________________________________________ -# generic owner. 'default' is the general config owner after init time -default_owner = 'user' +from tiramisu.setting import settings # ____________________________________________________________ class Config(object): "main configuration management entry" - #properties attribute: the name of a property enables this property - _cfgimpl_properties = ['hidden', 'disabled'] - _cfgimpl_permissive = [] - #mandatory means: a mandatory option has to have a value that is not None - _cfgimpl_mandatory = True - _cfgimpl_frozen = True - #enables validation function for options if set - _cfgimpl_validator = False - _cfgimpl_owner = default_owner _cfgimpl_toplevel = None def __init__(self, descr, parent=None): @@ -62,7 +50,6 @@ class Config(object): self._cfgimpl_warnings = [] self._cfgimpl_toplevel = self._cfgimpl_get_toplevel() '`freeze()` allows us to carry out this calculation again if necessary' - self._cfgimpl_frozen = self._cfgimpl_toplevel._cfgimpl_frozen self._cfgimpl_build() def _validate_duplicates(self, children): @@ -100,11 +87,6 @@ class Config(object): self._cfgimpl_values[child._name] = Config(child, parent=self) # self.override(overrides) - def cfgimpl_set_permissive(self, permissive): - if not isinstance(permissive, list): - raise TypeError('permissive must be a list') - self._cfgimpl_permissive = permissive - def cfgimpl_update(self): """dynamically adds `Option()` or `OptionDescription()` """ @@ -123,38 +105,6 @@ class Config(object): if child._name not in self._cfgimpl_values: self._cfgimpl_values[child._name] = Config(child, parent=self) - def cfgimpl_set_owner(self, owner): - ":param owner: sets the default value for owner at the Config level" - self._cfgimpl_owner = owner - for child in self._cfgimpl_descr._children: - if isinstance(child, OptionDescription): - self._cfgimpl_values[child._name].cfgimpl_set_owner(owner) - # ____________________________________________________________ - # properties methods - def _cfgimpl_has_properties(self): - "has properties means the Config's properties attribute is not empty" - return bool(len(self._cfgimpl_properties)) - - def _cfgimpl_has_property(self, propname): - """has property propname in the Config's properties attribute - :param property: string wich is the name of the property""" - return propname in self._cfgimpl_properties - - def cfgimpl_enable_property(self, propname): - "puts property propname in the Config's properties attribute" - if self._cfgimpl_parent != None: - raise MethodCallError("this method root_hide() shall not be" - "used with non-root Config() object") - if propname not in self._cfgimpl_properties: - self._cfgimpl_properties.append(propname) - - def cfgimpl_disable_property(self, propname): - "deletes property propname in the Config's properties attribute" - if self._cfgimpl_parent != None: - raise MethodCallError("this method root_hide() shall not be" - "used with non-root Config() object") - if self._cfgimpl_has_property(propname): - self._cfgimpl_properties.remove(propname) # ____________________________________________________________ # attribute methods def __setattr__(self, name, value): @@ -167,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, self._cfgimpl_owner) + self.setoption(name, value, settings.owner) def _validate(self, name, opt_or_descr, permissive=False): "validation for the setattr and the getattr" @@ -177,10 +127,10 @@ class Config(object): raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr))) properties = copy(opt_or_descr.properties) for proper in copy(properties): - if not self._cfgimpl_toplevel._cfgimpl_has_property(proper): + if not settings.has_property(proper): properties.remove(proper) if permissive: - for perm in self._cfgimpl_toplevel._cfgimpl_permissive: + for perm in settings.permissive: if perm in properties: properties.remove(perm) if properties != []: @@ -199,8 +149,7 @@ class Config(object): def _test_mandatory(self, path, opt): # mandatory options - homeconfig = self._cfgimpl_get_toplevel() - mandatory = homeconfig._cfgimpl_mandatory + mandatory = settings.mandatory if opt.is_mandatory() and mandatory: if self._is_empty(opt) and \ opt.is_empty_by_default(): @@ -215,9 +164,9 @@ class Config(object): attribute notation mechanism for accessing the value of an option :param name: attribute name :param permissive: permissive doesn't raise some property error - (see ``_cfgimpl_permissive``) + (see ``settings.permissive``) :return: option's value if name is an option name, OptionDescription - otherwise + otherwise """ # attribute access by passing a path, # for instance getattr(self, "creole.general.family.adresse_ip_eth0") @@ -250,9 +199,9 @@ class Config(object): return value else: return value - rootconfig = self._cfgimpl_get_toplevel() try: - result = opt_or_descr.getcallback_value(rootconfig) + result = opt_or_descr.getcallback_value( + self._cfgimpl_get_toplevel()) except NoValueReturned, err: pass else: @@ -267,7 +216,7 @@ class Config(object): ' for option {0} : shall not be a list'.format(name)) _result = result if _result != None and not opt_or_descr.validate(_result, - rootconfig._cfgimpl_validator): + settings.validator): raise ConfigError('invalid calculated value returned' ' for option {0}'.format(name)) self._cfgimpl_values[name] = _result @@ -327,7 +276,7 @@ class Config(object): child = getattr(self._cfgimpl_descr, name) if type(child) != SymLinkOption: if who == None: - who = self._cfgimpl_owner + who = settings.owner if child.is_multi(): if type(value) != Multi: if type(value) == list: @@ -361,7 +310,7 @@ class Config(object): pass except Exception, e: raise e # HiddenOptionError or DisabledOptionError - homeconfig.setoption(name, value, self._cfgimpl_owner) + homeconfig.setoption(name, value, settings.owner) elif len(candidates) > 1: raise AmbigousOptionError( 'more than one option that ends with %s' % (key, )) @@ -433,64 +382,6 @@ class Config(object): "Config implements its own warning pile" return self._cfgimpl_get_toplevel()._cfgimpl_warnings # ____________________________________________________________ - # Config()'s status - def cfgimpl_freeze(self): - "cannot modify the frozen `Option`'s" - rootconfig = self._cfgimpl_get_toplevel() - rootconfig._cfgimpl_frozen = True - self._cfgimpl_frozen = True - - def cfgimpl_unfreeze(self): - "can modify the Options that are frozen" - rootconfig = self._cfgimpl_get_toplevel() - rootconfig._cfgimpl_frozen = False - self._cfgimpl_frozen = False - - def is_frozen(self): - "freeze flag at Config level" - rootconfig = self._cfgimpl_get_toplevel() - return rootconfig._cfgimpl_frozen - - def cfgimpl_read_only(self): - "convenience method to freeze, hidde and disable" - self.cfgimpl_freeze() - rootconfig = self._cfgimpl_get_toplevel() - rootconfig.cfgimpl_disable_property('hidden') - rootconfig.cfgimpl_enable_property('disabled') - rootconfig._cfgimpl_mandatory = True - rootconfig._cfgimpl_validator = True - - def cfgimpl_read_write(self): - "convenience method to freeze, hidde and disable" - self.cfgimpl_freeze() - rootconfig = self._cfgimpl_get_toplevel() - rootconfig.cfgimpl_enable_property('hidden') - rootconfig.cfgimpl_enable_property('disabled') - rootconfig._cfgimpl_mandatory = False - - def cfgimpl_non_mandatory(self): - """mandatory at the Config level means that the Config raises an error - if a mandatory option is found""" - if self._cfgimpl_parent != None: - raise MethodCallError("this method root_mandatory shall" - " not be used with non-root Confit() object") - rootconfig = self._cfgimpl_get_toplevel() - rootconfig._cfgimpl_mandatory = False - - def cfgimpl_mandatory(self): - """mandatory at the Config level means that the Config raises an error - if a mandatory option is found""" - if self._cfgimpl_parent != None: - raise MethodCallError("this method root_mandatory shall" - " not be used with non-root Confit() object") - rootconfig = self._cfgimpl_get_toplevel() - rootconfig._cfgimpl_mandatory = True - - def is_mandatory(self): - "all mandatory Options shall have a value" - rootconfig = self._cfgimpl_get_toplevel() - return rootconfig._cfgimpl_mandatory - # ____________________________________________________________ def getkey(self): return self._cfgimpl_descr.getkey(self) @@ -685,8 +576,8 @@ def mandatory_warnings(config): :returns: generator of mandatory Option's path """ - mandatory = config._cfgimpl_get_toplevel()._cfgimpl_mandatory - config._cfgimpl_get_toplevel()._cfgimpl_mandatory = True + mandatory = settings.mandatory + settings.mandatory = True for path in config._cfgimpl_descr.getpaths(include_groups=True): try: value = config._getattr(path, permissive=True) @@ -694,4 +585,4 @@ def mandatory_warnings(config): yield path except PropertiesOptionError: pass - config._cfgimpl_get_toplevel()._cfgimpl_mandatory = mandatory + settings.mandatory = mandatory diff --git a/tiramisu/option.py b/tiramisu/option.py index 5b865fc..259825b 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -26,6 +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 requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')] @@ -63,7 +64,7 @@ class Multi(list): def setoption(self, value, key=None, who=None): if who is None: - who = self.config._cfgimpl_owner + who = settings.owner if value != None: if not self.child._validate(value): raise ConfigError("invalid value {0} " @@ -78,7 +79,7 @@ class Multi(list): return ret def pop(self, key): - self.child.setowner(self.config, self.config._cfgimpl_owner) + self.child.setowner(self.config, settings.owner) super(Multi, self).pop(key) # ____________________________________________________________ # @@ -257,7 +258,7 @@ class Option(HiddenBaseType, DisabledBaseType): :type who: string """ name = self._name rootconfig = config._cfgimpl_get_toplevel() - if not self.validate(value, rootconfig._cfgimpl_validator): + if not self.validate(value, settings.validator): raise ConfigError('invalid value %s for option %s' % (value, name)) if self.is_mandatory(): # value shall not be '' for a mandatory option @@ -266,14 +267,14 @@ class Option(HiddenBaseType, DisabledBaseType): value = None if self.is_multi() and '' in value: value = Multi([{'': None}.get(i, i) for i in value], config, self) - if config.is_mandatory() and ((self.is_multi() and value == []) or \ + if settings.is_mandatory() and ((self.is_multi() and value == []) or \ (not self.is_multi() and value is None)): raise MandatoryError('cannot change the value to %s for ' 'option %s' % (value, name)) if name not in config._cfgimpl_values: raise AttributeError('unknown option %s' % (name)) - if config.is_frozen() and self.is_frozen(): + if settings.is_frozen() and self.is_frozen(): raise TypeError('cannot change the value to %s for ' 'option %s this option is frozen' % (str(value), name)) apply_requires(self, config) diff --git a/tiramisu/setting.py b/tiramisu/setting.py new file mode 100644 index 0000000..b8275f0 --- /dev/null +++ b/tiramisu/setting.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +"sets the options of the configuration objects Config object itself" +# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ + +class Setting(): + "``Config()``'s configuration options" + # properties attribute: the name of a property enables this property + properties = ['hidden', 'disabled'] + # overrides the validations in the acces of the option values + permissive = [] + # a mandatory option must have a value that is not None + mandatory = True + 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): + "has properties means the Config's properties attribute is not empty" + return bool(len(self.properties)) + + def has_property(self, propname): + """has property propname in the Config's properties attribute + :param property: string wich is the name of the property""" + return propname in self.properties + + def enable_property(self, propname): + "puts property propname in the Config's properties attribute" + if propname not in self.properties: + self.properties.append(propname) + + def disable_property(self, propname): + "deletes property propname in the Config's properties attribute" + if self.has_property(propname): + self.properties.remove(propname) + + def set_permissive(self, permissive): + if not isinstance(permissive, list): + raise TypeError('permissive must be a list') + self.permissive = permissive + + def read_only(self): + "convenience method to freeze, hidde and disable" + self.freeze() + self.disable_property('hidden') + self.enable_property('disabled') + self.mandatory = True + self.validator = True + + def read_write(self): + "convenience method to freeze, hidde and disable" + self.freeze() + self.enable_property('hidden') + self.enable_property('disabled') + self.mandatory = False + + def non_mandatory(self): + """mandatory at the Config level means that the Config raises an error + if a mandatory option is found""" + self.mandatory = False + + def mandatory(self): + """mandatory at the Config level means that the Config raises an error + if a mandatory option is found""" + self.mandatory = True + + def is_mandatory(self): + "all mandatory Options shall have a value" + return self.mandatory + + def freeze(self): + "cannot modify the frozen `Option`'s" + self.frozen = True + + def unfreeze(self): + "can modify the Options that are frozen" + self.frozen = False + + def is_frozen(self): + "freeze flag at Config level" + return self.frozen + + def set_owner(self, owner): + ":param owner: sets the default value for owner at the Config level" + self.owner = owner + +# Setting is actually a singleton +settings = Setting()