settings are in a separate object

This commit is contained in:
gwen 2012-11-19 10:45:03 +01:00
parent 5969eaa2d6
commit 86f9096937
6 changed files with 143 additions and 164 deletions

View file

@ -3,6 +3,7 @@ import autopath
from py.test import raises from py.test import raises
from tiramisu.config import * from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
from tiramisu.setting import settings
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
@ -48,25 +49,6 @@ def make_description2():
intoption, boolop]) intoption, boolop])
return descr 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(): def test_has_callback():
descr = make_description() descr = make_description()
# here the owner is 'default' # here the owner is 'default'
@ -74,7 +56,7 @@ def test_has_callback():
config.bool = False config.bool = False
# because dummy has a callback # because dummy has a callback
dummy = config.unwrap_from_path('gc.dummy') dummy = config.unwrap_from_path('gc.dummy')
config.cfgimpl_freeze() settings.freeze()
dummy.freeze() dummy.freeze()
raises(TypeError, "config.gc.dummy = True") raises(TypeError, "config.gc.dummy = True")
@ -83,12 +65,7 @@ def test_freeze_and_has_callback_with_setoption():
descr = make_description() descr = make_description()
config = Config(descr) config = Config(descr)
config.bool = False config.bool = False
config.cfgimpl_freeze() settings.freeze()
dummy = config.unwrap_from_path('gc.dummy') dummy = config.unwrap_from_path('gc.dummy')
dummy.freeze() dummy.freeze()
raises(TypeError, "config.gc.setoption('dummy', True, 'gen_config')") 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})")

View file

@ -5,6 +5,7 @@ from py.test import raises
from tiramisu.config import * from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
from tiramisu.setting import settings
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
@ -74,7 +75,7 @@ def test_frozen_value():
s = StrOption("string", "", default="string") s = StrOption("string", "", default="string")
descr = OptionDescription("options", "", [s]) descr = OptionDescription("options", "", [s])
config = Config(descr) config = Config(descr)
config.cfgimpl_freeze() settings.freeze()
s.freeze() s.freeze()
raises(TypeError, 'config.string = "egg"') raises(TypeError, 'config.string = "egg"')
@ -82,7 +83,7 @@ def test_freeze():
"freeze a whole configuration object" "freeze a whole configuration object"
descr = make_description() descr = make_description()
conf = Config(descr) conf = Config(descr)
conf.cfgimpl_freeze() settings.freeze()
name = conf.unwrap_from_path("gc.name") name = conf.unwrap_from_path("gc.name")
name.freeze() name.freeze()
raises(TypeError, "conf.gc.name = 'framework'") raises(TypeError, "conf.gc.name = 'framework'")
@ -133,4 +134,3 @@ def test_with_many_subgroups():
assert name == "booltwo" assert name == "booltwo"
option = getattr(homeconfig._cfgimpl_descr, name) option = getattr(homeconfig._cfgimpl_descr, name)
assert option._is_hidden() assert option._is_hidden()

View file

@ -4,6 +4,7 @@ from py.test import raises
from tiramisu.config import * from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
from tiramisu.setting import settings
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') 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) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
descr = OptionDescription('tiramisu', '', [gcdummy, boolop]) descr = OptionDescription('tiramisu', '', [gcdummy, boolop])
cfg = Config(descr) cfg = Config(descr)
cfg.cfgimpl_enable_property('hiddend') #cfgimpl_hide() settings.enable_property('hiddend') #cfgimpl_hide()
assert cfg.dummy == False assert cfg.dummy == False
assert cfg.boolop == True assert cfg.boolop == True

View file

@ -26,23 +26,11 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
MandatoryError, MethodCallError, NoValueReturned) MandatoryError, MethodCallError, NoValueReturned)
from tiramisu.option import (OptionDescription, Option, SymLinkOption, from tiramisu.option import (OptionDescription, Option, SymLinkOption,
group_types, Multi, apply_requires) group_types, Multi, apply_requires)
from tiramisu.setting import settings
# ______________________________________________________________________
# generic owner. 'default' is the general config owner after init time
default_owner = 'user'
# ____________________________________________________________ # ____________________________________________________________
class Config(object): class Config(object):
"main configuration management entry" "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 _cfgimpl_toplevel = None
def __init__(self, descr, parent=None): def __init__(self, descr, parent=None):
@ -62,7 +50,6 @@ class Config(object):
self._cfgimpl_warnings = [] self._cfgimpl_warnings = []
self._cfgimpl_toplevel = self._cfgimpl_get_toplevel() self._cfgimpl_toplevel = self._cfgimpl_get_toplevel()
'`freeze()` allows us to carry out this calculation again if necessary' '`freeze()` allows us to carry out this calculation again if necessary'
self._cfgimpl_frozen = self._cfgimpl_toplevel._cfgimpl_frozen
self._cfgimpl_build() self._cfgimpl_build()
def _validate_duplicates(self, children): def _validate_duplicates(self, children):
@ -100,11 +87,6 @@ class Config(object):
self._cfgimpl_values[child._name] = Config(child, parent=self) self._cfgimpl_values[child._name] = Config(child, parent=self)
# self.override(overrides) # 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): def cfgimpl_update(self):
"""dynamically adds `Option()` or `OptionDescription()` """dynamically adds `Option()` or `OptionDescription()`
""" """
@ -123,38 +105,6 @@ class Config(object):
if child._name not in self._cfgimpl_values: if child._name not in self._cfgimpl_values:
self._cfgimpl_values[child._name] = Config(child, parent=self) 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 # attribute methods
def __setattr__(self, name, value): def __setattr__(self, name, value):
@ -167,7 +117,7 @@ class Config(object):
return setattr(homeconfig, name, value) return setattr(homeconfig, name, value)
if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption: if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
self._validate(name, getattr(self._cfgimpl_descr, name)) 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): def _validate(self, name, opt_or_descr, permissive=False):
"validation for the setattr and the getattr" "validation for the setattr and the getattr"
@ -177,10 +127,10 @@ class Config(object):
raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr))) raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr)))
properties = copy(opt_or_descr.properties) properties = copy(opt_or_descr.properties)
for proper in copy(properties): for proper in copy(properties):
if not self._cfgimpl_toplevel._cfgimpl_has_property(proper): if not settings.has_property(proper):
properties.remove(proper) properties.remove(proper)
if permissive: if permissive:
for perm in self._cfgimpl_toplevel._cfgimpl_permissive: for perm in settings.permissive:
if perm in properties: if perm in properties:
properties.remove(perm) properties.remove(perm)
if properties != []: if properties != []:
@ -199,8 +149,7 @@ class Config(object):
def _test_mandatory(self, path, opt): def _test_mandatory(self, path, opt):
# mandatory options # mandatory options
homeconfig = self._cfgimpl_get_toplevel() mandatory = settings.mandatory
mandatory = homeconfig._cfgimpl_mandatory
if opt.is_mandatory() and mandatory: if opt.is_mandatory() and mandatory:
if self._is_empty(opt) and \ if self._is_empty(opt) and \
opt.is_empty_by_default(): opt.is_empty_by_default():
@ -215,7 +164,7 @@ class Config(object):
attribute notation mechanism for accessing the value of an option attribute notation mechanism for accessing the value of an option
:param name: attribute name :param name: attribute name
:param permissive: permissive doesn't raise some property error :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 :return: option's value if name is an option name, OptionDescription
otherwise otherwise
""" """
@ -250,9 +199,9 @@ class Config(object):
return value return value
else: else:
return value return value
rootconfig = self._cfgimpl_get_toplevel()
try: try:
result = opt_or_descr.getcallback_value(rootconfig) result = opt_or_descr.getcallback_value(
self._cfgimpl_get_toplevel())
except NoValueReturned, err: except NoValueReturned, err:
pass pass
else: else:
@ -267,7 +216,7 @@ class Config(object):
' for option {0} : shall not be a list'.format(name)) ' for option {0} : shall not be a list'.format(name))
_result = result _result = result
if _result != None and not opt_or_descr.validate(_result, if _result != None and not opt_or_descr.validate(_result,
rootconfig._cfgimpl_validator): settings.validator):
raise ConfigError('invalid calculated value returned' raise ConfigError('invalid calculated value returned'
' for option {0}'.format(name)) ' for option {0}'.format(name))
self._cfgimpl_values[name] = _result self._cfgimpl_values[name] = _result
@ -327,7 +276,7 @@ class Config(object):
child = getattr(self._cfgimpl_descr, name) child = getattr(self._cfgimpl_descr, name)
if type(child) != SymLinkOption: if type(child) != SymLinkOption:
if who == None: if who == None:
who = self._cfgimpl_owner who = settings.owner
if child.is_multi(): if child.is_multi():
if type(value) != Multi: if type(value) != Multi:
if type(value) == list: if type(value) == list:
@ -361,7 +310,7 @@ class Config(object):
pass pass
except Exception, e: except Exception, e:
raise e # HiddenOptionError or DisabledOptionError raise e # HiddenOptionError or DisabledOptionError
homeconfig.setoption(name, value, self._cfgimpl_owner) homeconfig.setoption(name, value, settings.owner)
elif len(candidates) > 1: elif len(candidates) > 1:
raise AmbigousOptionError( raise AmbigousOptionError(
'more than one option that ends with %s' % (key, )) 'more than one option that ends with %s' % (key, ))
@ -433,64 +382,6 @@ class Config(object):
"Config implements its own warning pile" "Config implements its own warning pile"
return self._cfgimpl_get_toplevel()._cfgimpl_warnings 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): def getkey(self):
return self._cfgimpl_descr.getkey(self) return self._cfgimpl_descr.getkey(self)
@ -685,8 +576,8 @@ def mandatory_warnings(config):
:returns: generator of mandatory Option's path :returns: generator of mandatory Option's path
""" """
mandatory = config._cfgimpl_get_toplevel()._cfgimpl_mandatory mandatory = settings.mandatory
config._cfgimpl_get_toplevel()._cfgimpl_mandatory = True settings.mandatory = True
for path in config._cfgimpl_descr.getpaths(include_groups=True): for path in config._cfgimpl_descr.getpaths(include_groups=True):
try: try:
value = config._getattr(path, permissive=True) value = config._getattr(path, permissive=True)
@ -694,4 +585,4 @@ def mandatory_warnings(config):
yield path yield path
except PropertiesOptionError: except PropertiesOptionError:
pass pass
config._cfgimpl_get_toplevel()._cfgimpl_mandatory = mandatory settings.mandatory = mandatory

View file

@ -26,6 +26,7 @@ from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
RequiresError, RequirementRecursionError, MandatoryError, RequiresError, RequirementRecursionError, MandatoryError,
PropertiesOptionError) PropertiesOptionError)
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
from tiramisu.setting import settings
requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')] requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')]
@ -63,7 +64,7 @@ class Multi(list):
def setoption(self, value, key=None, who=None): def setoption(self, value, key=None, who=None):
if who is None: if who is None:
who = self.config._cfgimpl_owner who = settings.owner
if value != None: if value != None:
if not self.child._validate(value): if not self.child._validate(value):
raise ConfigError("invalid value {0} " raise ConfigError("invalid value {0} "
@ -78,7 +79,7 @@ class Multi(list):
return ret return ret
def pop(self, key): 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) super(Multi, self).pop(key)
# ____________________________________________________________ # ____________________________________________________________
# #
@ -257,7 +258,7 @@ class Option(HiddenBaseType, DisabledBaseType):
:type who: string """ :type who: string """
name = self._name name = self._name
rootconfig = config._cfgimpl_get_toplevel() 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)) raise ConfigError('invalid value %s for option %s' % (value, name))
if self.is_mandatory(): if self.is_mandatory():
# value shall not be '' for a mandatory option # value shall not be '' for a mandatory option
@ -266,14 +267,14 @@ class Option(HiddenBaseType, DisabledBaseType):
value = None value = None
if self.is_multi() and '' in value: if self.is_multi() and '' in value:
value = Multi([{'': None}.get(i, i) for i in value], config, self) 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)): (not self.is_multi() and value is None)):
raise MandatoryError('cannot change the value to %s for ' raise MandatoryError('cannot change the value to %s for '
'option %s' % (value, name)) 'option %s' % (value, name))
if name not in config._cfgimpl_values: if name not in config._cfgimpl_values:
raise AttributeError('unknown option %s' % (name)) 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 ' raise TypeError('cannot change the value to %s for '
'option %s this option is frozen' % (str(value), name)) 'option %s this option is frozen' % (str(value), name))
apply_requires(self, config) apply_requires(self, config)

109
tiramisu/setting.py Normal file
View file

@ -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()