refactoring values

This commit is contained in:
gwen 2013-02-21 17:07:00 +01:00
parent d058e2946b
commit e6d5d349c8
6 changed files with 111 additions and 107 deletions

View File

@ -89,6 +89,14 @@ def test_groups_with_master():
interface1.set_group_type(groups.master) interface1.set_group_type(groups.master)
assert interface1.get_group_type() == groups.master assert interface1.get_group_type() == groups.master
def test_groups_with_master_in_config():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.set_group_type(groups.master)
cfg = Config(interface1)
assert interface1.get_group_type() == groups.master
def test_allowed_groups(): def test_allowed_groups():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
@ -108,7 +116,7 @@ def test_sub_group_in_master_group():
invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0]) invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
raises(ConfigError, "invalid_group.set_group_type(groups.master)") raises(ConfigError, "invalid_group.set_group_type(groups.master)")
def test_group_has_always_multis(): def test_group_always_has_multis():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau") netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
group = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) group = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"pretty small and local configuration management tool" "pretty small and local configuration management tool"
# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -27,7 +27,7 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
from tiramisu.option import (OptionDescription, Option, SymLinkOption, from tiramisu.option import (OptionDescription, Option, SymLinkOption,
apply_requires) apply_requires)
from tiramisu.setting import groups, owners, Setting from tiramisu.setting import groups, owners, Setting
from tiramisu.value import OptionValues, Multi from tiramisu.value import OptionValues
# ____________________________________________________________ # ____________________________________________________________
class Config(object): class Config(object):
@ -92,32 +92,23 @@ class Config(object):
- settles various default values for options - settles various default values for options
""" """
self._validate_duplicates(self._cfgimpl_descr._children) self._validate_duplicates(self._cfgimpl_descr._children)
#max len for a master/slave group if self._cfgimpl_descr.group_type == groups.master:
max_len_child = 0 mastername = self._cfgimpl_descr._name
masteropt = getattr(self._cfgimpl_descr, mastername)
self._cfgimpl_values.master_groups[masteropt] = []
for child in self._cfgimpl_descr._children: for child in self._cfgimpl_descr._children:
if isinstance(child, OptionDescription): if isinstance(child, OptionDescription):
self._validate_duplicates(child._children) self._validate_duplicates(child._children)
self._cfgimpl_subconfigs[child] = Config(child, parent=self, self._cfgimpl_subconfigs[child] = Config(child, parent=self,
context=self._cfgimpl_context) context=self._cfgimpl_context)
if (self._cfgimpl_descr.group_type == groups.master and
child != masteropt):
self._cfgimpl_values.master_groups[child] = []
self._cfgimpl_values.master_groups[masteropt].append(child)
# def cfgimpl_update(self): if self._cfgimpl_descr.group_type == groups.master:
# """dynamically adds `Option()` or `OptionDescription()` print self._cfgimpl_values.master_groups
# """
# # FIXME this is an update for new options in the schema only
# # see the update_child() method of the descr object
# for child in self._cfgimpl_descr._children:
# if isinstance(child, Option):
# if child._name not in self._cfgimpl_values:
# if child.is_multi():
# self._cfgimpl_values[child._name] = Multi(
# copy(child.getdefault()), config=self, opt=child)
# else:
# self._cfgimpl_values[child._name] = copy(child.getdefault())
# 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)
# ____________________________________________________________ # ____________________________________________________________
# attribute methods # attribute methods
def __setattr__(self, name, value): def __setattr__(self, name, value):
@ -130,8 +121,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.setoption(name, value)
self._cfgimpl_context._cfgimpl_settings.getowner())
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"
@ -156,15 +146,15 @@ class Config(object):
def __getattr__(self, name): def __getattr__(self, name):
return self._getattr(name) return self._getattr(name)
def fill_multi(self, opt, result, use_default_multi=False, default_multi=None): # def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
"""fills a multi option with default and calculated values # """fills a multi option with default and calculated values
""" # """
# FIXME C'EST ENCORE DU N'IMPORTE QUOI # # FIXME C'EST ENCORE DU N'IMPORTE QUOI
if not isinstance(result, list): # if not isinstance(result, list):
_result = [result] # _result = [result]
else: # else:
_result = result # _result = result
return Multi(_result, self._cfgimpl_context, opt) # return Multi(_result, self._cfgimpl_context, opt)
def _getattr(self, name, permissive=False): def _getattr(self, name, permissive=False):
""" """
@ -227,33 +217,9 @@ class Config(object):
def setoption(self, name, value, who=None): def setoption(self, name, value, who=None):
"""effectively modifies the value of an Option() """effectively modifies the value of an Option()
(typically called by the __setattr__) (typically called by the __setattr__)
:param who: an object that lives in `setting.owners`
""" """
child = getattr(self._cfgimpl_descr, name) child = getattr(self._cfgimpl_descr, name)
if type(child) != SymLinkOption: child.setoption(self, value)
if who == None:
who = self._cfgimpl_context._cfgimpl_settings.owner
if child.is_multi():
if not isinstance(who, owners.DefaultOwner):
if type(value) != Multi:
if type(value) == list:
value = Multi(value, self._cfgimpl_context, child)
else:
raise ConfigError("invalid value for option:"
" {0} that is set to multi".format(name))
else:
value = self.fill_multi(child, child.getdefault(),
use_default_multi=True,
default_multi=child.getdefault_multi())
if not isinstance(who, owners.Owner):
raise TypeError("invalid owner [{0}] for option: {1}".format(
str(who), name))
child.setoption(self, value)
child.setowner(self, who)
else:
homeconfig = self._cfgimpl_get_toplevel()
child.setoption(homeconfig, value)
def set(self, **kwargs): def set(self, **kwargs):
""" """
@ -276,8 +242,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, homeconfig.setoption(name, value)
self._cfgimpl_context._cfgimpl_settings.getowner())
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, ))

View File

@ -23,3 +23,5 @@ class MandatoryError(Exception):
pass pass
class NoValueReturned(Exception): class NoValueReturned(Exception):
pass pass
class OptionValueError(Exception):
pass

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"option types and option description for the configuration management" "option types and option description for the configuration management"
# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -27,7 +27,6 @@ from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
PropertiesOptionError) PropertiesOptionError)
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
from tiramisu.value import Multi
requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')] requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')]
@ -217,7 +216,7 @@ class Option(HiddenBaseType, DisabledBaseType):
def reset(self, config): def reset(self, config):
"""resets the default value and owner """resets the default value and owner
""" """
config.setoption(self._name, self.getdefault(), owners.default) config._cfgimpl_context._cfgimpl_values.reset(self)
def is_default_owner(self, config): def is_default_owner(self, config):
""" """
@ -241,9 +240,9 @@ class Option(HiddenBaseType, DisabledBaseType):
# so '' is considered as being None # so '' is considered as being None
if not self.is_multi() and value == '': if not self.is_multi() and value == '':
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], # value = Multi([{'': None}.get(i, i) for i in value],
config._cfgimpl_context, self) # config._cfgimpl_context, self)
if config._cfgimpl_context._cfgimpl_settings.is_mandatory() \ if config._cfgimpl_context._cfgimpl_settings.is_mandatory() \
and ((self.is_multi() and value == []) or \ and ((self.is_multi() and value == []) or \
(not self.is_multi() and value is None)): (not self.is_multi() and value is None)):
@ -261,10 +260,6 @@ class Option(HiddenBaseType, DisabledBaseType):
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)
# if type(config._cfgimpl_context._cfgimpl_values[self]) == Multi:
# config._cfgimpl_context._cfgimpl_values.previous_values[self] = list(config._cfgimpl_context._cfgimpl_values[self])
# else:
# config._cfgimpl_context._cfgimpl_values.previous_values[self] = config._cfgimpl_context._cfgimpl_values[self]
config._cfgimpl_context._cfgimpl_values[self] = value config._cfgimpl_context._cfgimpl_values[self] = value
def getkey(self, value): def getkey(self, value):
@ -341,7 +336,7 @@ class SymLinkOption(object):
self.opt = opt self.opt = opt
def setoption(self, config, value): def setoption(self, config, value):
setattr(config, self.path, value) setattr(config._cfgimpl_get_toplevel(), self.path, value)
def __getattr__(self, name): def __getattr__(self, name):
if name in ('_name', 'path', 'opt', 'setoption'): if name in ('_name', 'path', 'opt', 'setoption'):

View File

@ -95,6 +95,26 @@ def populate_owners():
# names are in the module now # names are in the module now
populate_owners() populate_owners()
class MultiTypeModule(_const):
class MultiType(str):
pass
class DefaultMultiType(MultiType):
pass
class MasterMultiType(MultiType):
pass
class SlaveMultiType(MultiType):
pass
multitypes = MultiTypeModule()
def populate_multitypes():
setattr(multitypes, 'default', multitypes.DefaultMultiType('default'))
setattr(multitypes, 'master', multitypes.MasterMultiType('master'))
setattr(multitypes, 'slave', multitypes.SlaveMultiType('slave'))
populate_multitypes()
#____________________________________________________________ #____________________________________________________________
class Setting(): class Setting():
"``Config()``'s configuration options" "``Config()``'s configuration options"

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"takes care of the option's values and multi values" "takes care of the option's values and multi values"
# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -25,10 +25,16 @@ from tiramisu.setting import owners
class OptionValues(object): class OptionValues(object):
def __init__(self, context): def __init__(self, context):
"""
Initializes the values's dict.
:param context: the context is the home config's values and properties
"""
self.owners = {} self.owners = {}
"Config's root indeed is in charge of the `Option()`'s values" "Config's root indeed is in charge of the `Option()`'s values"
self.values = {} self.values = {}
self.previous_values = {} self.previous_values = {}
self.master_groups = {}
self.context = context self.context = context
def _get_value(self, opt): def _get_value(self, opt):
@ -42,24 +48,42 @@ class OptionValues(object):
return opt.getdefault() return opt.getdefault()
return self.values[opt] return self.values[opt]
def _is_empty(self, opt): def reset(self, opt):
if opt in self.values:
self.set_previous_value(opt)
del(self.values[opt])
self.setowner(opt, owners.default)
def set_previous_value(self, opt):
if opt in self.values:
old_value = self.values[opt]
else:
old_value = None
if type(old_value) == Multi:
self.previous_values[opt] = list(old_value)
else:
self.previous_values[opt] = old_value
def _is_empty(self, opt, value=None):
"convenience method to know if an option is empty" "convenience method to know if an option is empty"
if value is not None:
return False
if (not opt.is_multi() and self._get_value(opt) == None) or \ if (not opt.is_multi() and self._get_value(opt) == None) or \
(opt.is_multi() and (self._get_value(opt) == [] or \ (opt.is_multi() and (self._get_value(opt) == [] or \
None in self._get_value(opt))): None in self._get_value(opt))):
return True return True
return False return False
def _test_mandatory(self, opt): def _test_mandatory(self, opt, value=None):
# mandatory options # mandatory options
mandatory = self.context._cfgimpl_settings.mandatory mandatory = self.context._cfgimpl_settings.mandatory
if opt.is_mandatory() and mandatory: if opt.is_mandatory() and mandatory:
if self._is_empty(opt) and \ if self._is_empty(opt, value) and \
opt.is_empty_by_default(): opt.is_empty_by_default():
raise MandatoryError("option: {0} is mandatory " raise MandatoryError("option: {0} is mandatory "
"and shall have a value".format(opt._name)) "and shall have a value".format(opt._name))
def fill_multi(self, opt, result, use_default_multi=False, default_multi=None): def fill_multi(self, opt, result):
"""fills a multi option with default and calculated values """fills a multi option with default and calculated values
""" """
value = self._get_value(opt) value = self._get_value(opt)
@ -71,6 +95,7 @@ class OptionValues(object):
def __getitem__(self, opt): def __getitem__(self, opt):
# options with callbacks # options with callbacks
value = self._get_value(opt)
if opt.has_callback(): if opt.has_callback():
if (not opt.is_frozen() or \ if (not opt.is_frozen() or \
not opt.is_forced_on_freeze()) and \ not opt.is_forced_on_freeze()) and \
@ -83,68 +108,57 @@ class OptionValues(object):
pass pass
else: else:
if opt.is_multi(): if opt.is_multi():
#FIXME revoir les multis value = fill_multi(opt, result)
_result = fill_multi(opt, result)
else: else:
# this result **shall not** be a list # this result **shall not** be a list
if isinstance(result, list): if isinstance(result, list):
raise ConfigError('invalid calculated value returned' raise ConfigError('invalid calculated value returned '
' for option {0} : shall not be a list'.format(name)) 'for option {0} : shall not be a list'.format(name))
_result = result value = result
if _result != None and not opt.validate(_result, if value != None and not opt.validate(value,
self.context._cfgimpl_settings.validator): self.context._cfgimpl_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.values[opt] = _result
self.owners[opt] = owners.default
# frozen and force default # frozen and force default
if not opt.has_callback() and opt.is_forced_on_freeze(): if not opt.has_callback() and opt.is_forced_on_freeze():
value = opt.getdefault() value = opt.getdefault()
if opt.is_multi(): if opt.is_multi():
#FIXME le fill_multi value = self.fill_multi(opt, value)
value = self.fill_multi(opt, value, self._test_mandatory(opt, value)
use_default_multi=True,
default_multi=opt.getdefault_multi())
self.values[opt] = value
self.owners[opt] = owners.default
self._test_mandatory(opt)
value = self._get_value(opt)
return value return value
def __setitem__(self, opt, value): def __setitem__(self, opt, value):
if opt in self.values: self.set_previous_value(opt)
old_value = self.values[opt]
else:
old_value = None
if type(old_value) == Multi:
self.previous_values[opt] = list(value)
else:
self.previous_values[opt] = value
self.values[opt] = value self.values[opt] = value
self.setowner(opt, self.context._cfgimpl_settings.getowner())
def __contains__(self, opt): def __contains__(self, opt):
return opt in self.values return opt in self.values
#____________________________________________________________ #____________________________________________________________
def setowner(self, opt, owner): def setowner(self, opt, owner):
pass if isinstance(owner, owners.Owner):
self.owners[opt] = owner
else:
raise OptionValueError("Bad owner: " + str(owner))
def getowner(self, opt): def getowner(self, opt):
return self.owners.get(opt, owners.default) return self.owners.get(opt, owners.default)
# ____________________________________________________________ # ____________________________________________________________
# multi types # multi types
class Multi(list): class Multi(list):
"""multi options values container """multi options values container
that support item notation for the values of multi options""" that support item notation for the values of multi options"""
def __init__(self, lst, context, opt): def __init__(self, lst, context, opt, multitype=settings.multitypes.default):
""" """
:param lst: the Multi wraps a list value :param lst: the Multi wraps a list value
:param context: the context has the settings and the values :param context: the home config that has the settings and the values
:param opt: the option object that have this Multi value :param opt: the option object that have this Multi value
""" """
self.settings = context._cfgimpl_settings self.settings = context._cfgimpl_settings
self.opt = opt self.opt = opt
self.values = context._cfgimpl_values self.values = context._cfgimpl_values
self.multitype = multitype
super(Multi, self).__init__(lst) super(Multi, self).__init__(lst)
def __setitem__(self, key, value): def __setitem__(self, key, value):