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,40 +217,16 @@ 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):
""" """
do what I mean"-interface to option setting. Searches all paths do what I mean"-interface to option setting. Searches all paths
starting from that config for matches of the optional arguments starting from that config for matches of the optional arguments
and sets the found option if the match is not ambiguous. and sets the found option if the match is not ambiguous.
:param kwargs: dict of name strings to values. :param kwargs: dict of name strings to values.
""" """
all_paths = [p.split(".") for p in self.getpaths(allpaths=True)] all_paths = [p.split(".") for p in self.getpaths(allpaths=True)]
@ -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, ))
@ -380,7 +345,7 @@ class Config(object):
"""iteration on groups objects only. """iteration on groups objects only.
All groups are returned if `group_type` is `None`, otherwise the groups All groups are returned if `group_type` is `None`, otherwise the groups
can be filtered by categories (families, or whatever). can be filtered by categories (families, or whatever).
:param group_type: if defined, is an instance of `groups.GroupType` :param group_type: if defined, is an instance of `groups.GroupType`
or `groups.MasterGroupType` that lives in or `groups.MasterGroupType` that lives in
`setting.groups` `setting.groups`

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):