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)
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():
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)
@ -108,7 +116,7 @@ def test_sub_group_in_master_group():
invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
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)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
group = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"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
# 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,
apply_requires)
from tiramisu.setting import groups, owners, Setting
from tiramisu.value import OptionValues, Multi
from tiramisu.value import OptionValues
# ____________________________________________________________
class Config(object):
@ -92,32 +92,23 @@ class Config(object):
- settles various default values for options
"""
self._validate_duplicates(self._cfgimpl_descr._children)
#max len for a master/slave group
max_len_child = 0
if self._cfgimpl_descr.group_type == groups.master:
mastername = self._cfgimpl_descr._name
masteropt = getattr(self._cfgimpl_descr, mastername)
self._cfgimpl_values.master_groups[masteropt] = []
for child in self._cfgimpl_descr._children:
if isinstance(child, OptionDescription):
self._validate_duplicates(child._children)
self._cfgimpl_subconfigs[child] = Config(child, parent=self,
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):
# """dynamically adds `Option()` or `OptionDescription()`
# """
# # 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)
if self._cfgimpl_descr.group_type == groups.master:
print self._cfgimpl_values.master_groups
# ____________________________________________________________
# attribute methods
def __setattr__(self, name, value):
@ -130,8 +121,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_context._cfgimpl_settings.getowner())
self.setoption(name, value)
def _validate(self, name, opt_or_descr, permissive=False):
"validation for the setattr and the getattr"
@ -156,15 +146,15 @@ class Config(object):
def __getattr__(self, name):
return self._getattr(name)
def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
"""fills a multi option with default and calculated values
"""
# FIXME C'EST ENCORE DU N'IMPORTE QUOI
if not isinstance(result, list):
_result = [result]
else:
_result = result
return Multi(_result, self._cfgimpl_context, opt)
# def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
# """fills a multi option with default and calculated values
# """
# # FIXME C'EST ENCORE DU N'IMPORTE QUOI
# if not isinstance(result, list):
# _result = [result]
# else:
# _result = result
# return Multi(_result, self._cfgimpl_context, opt)
def _getattr(self, name, permissive=False):
"""
@ -227,40 +217,16 @@ class Config(object):
def setoption(self, name, value, who=None):
"""effectively modifies the value of an Option()
(typically called by the __setattr__)
:param who: an object that lives in `setting.owners`
"""
child = getattr(self._cfgimpl_descr, name)
if type(child) != SymLinkOption:
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)
child.setoption(self, value)
def set(self, **kwargs):
"""
do what I mean"-interface to option setting. Searches all paths
starting from that config for matches of the optional arguments
and sets the found option if the match is not ambiguous.
:param kwargs: dict of name strings to values.
"""
all_paths = [p.split(".") for p in self.getpaths(allpaths=True)]
@ -276,8 +242,7 @@ class Config(object):
pass
except Exception, e:
raise e # HiddenOptionError or DisabledOptionError
homeconfig.setoption(name, value,
self._cfgimpl_context._cfgimpl_settings.getowner())
homeconfig.setoption(name, value)
elif len(candidates) > 1:
raise AmbigousOptionError(
'more than one option that ends with %s' % (key, ))
@ -380,7 +345,7 @@ 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 `groups.GroupType`
or `groups.MasterGroupType` that lives in
`setting.groups`

View file

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

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"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
# 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)
from tiramisu.autolib import carry_out_calculation
from tiramisu.setting import groups, owners
from tiramisu.value import Multi
requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')]
@ -217,7 +216,7 @@ class Option(HiddenBaseType, DisabledBaseType):
def reset(self, config):
"""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):
"""
@ -241,9 +240,9 @@ class Option(HiddenBaseType, DisabledBaseType):
# so '' is considered as being None
if not self.is_multi() and value == '':
value = None
if self.is_multi() and '' in value:
value = Multi([{'': None}.get(i, i) for i in value],
config._cfgimpl_context, self)
# if self.is_multi() and '' in value:
# value = Multi([{'': None}.get(i, i) for i in value],
# config._cfgimpl_context, self)
if config._cfgimpl_context._cfgimpl_settings.is_mandatory() \
and ((self.is_multi() and value == []) or \
(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 '
'option %s this option is frozen' % (str(value), name))
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
def getkey(self, value):
@ -341,7 +336,7 @@ class SymLinkOption(object):
self.opt = opt
def setoption(self, config, value):
setattr(config, self.path, value)
setattr(config._cfgimpl_get_toplevel(), self.path, value)
def __getattr__(self, name):
if name in ('_name', 'path', 'opt', 'setoption'):

View file

@ -95,6 +95,26 @@ def populate_owners():
# names are in the module now
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():
"``Config()``'s configuration options"

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"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
# 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):
def __init__(self, context):
"""
Initializes the values's dict.
:param context: the context is the home config's values and properties
"""
self.owners = {}
"Config's root indeed is in charge of the `Option()`'s values"
self.values = {}
self.previous_values = {}
self.master_groups = {}
self.context = context
def _get_value(self, opt):
@ -42,24 +48,42 @@ class OptionValues(object):
return opt.getdefault()
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"
if value is not None:
return False
if (not opt.is_multi() and self._get_value(opt) == None) or \
(opt.is_multi() and (self._get_value(opt) == [] or \
None in self._get_value(opt))):
return True
return False
def _test_mandatory(self, opt):
def _test_mandatory(self, opt, value=None):
# mandatory options
mandatory = self.context._cfgimpl_settings.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():
raise MandatoryError("option: {0} is mandatory "
"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
"""
value = self._get_value(opt)
@ -71,6 +95,7 @@ class OptionValues(object):
def __getitem__(self, opt):
# options with callbacks
value = self._get_value(opt)
if opt.has_callback():
if (not opt.is_frozen() or \
not opt.is_forced_on_freeze()) and \
@ -83,68 +108,57 @@ class OptionValues(object):
pass
else:
if opt.is_multi():
#FIXME revoir les multis
_result = fill_multi(opt, result)
value = fill_multi(opt, result)
else:
# this result **shall not** be a list
if isinstance(result, list):
raise ConfigError('invalid calculated value returned'
' for option {0} : shall not be a list'.format(name))
_result = result
if _result != None and not opt.validate(_result,
raise ConfigError('invalid calculated value returned '
'for option {0} : shall not be a list'.format(name))
value = result
if value != None and not opt.validate(value,
self.context._cfgimpl_settings.validator):
raise ConfigError('invalid calculated value returned'
' for option {0}'.format(name))
self.values[opt] = _result
self.owners[opt] = owners.default
# frozen and force default
if not opt.has_callback() and opt.is_forced_on_freeze():
value = opt.getdefault()
if opt.is_multi():
#FIXME le fill_multi
value = self.fill_multi(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)
value = self.fill_multi(opt, value)
self._test_mandatory(opt, value)
return value
def __setitem__(self, opt, value):
if opt in self.values:
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.set_previous_value(opt)
self.values[opt] = value
self.setowner(opt, self.context._cfgimpl_settings.getowner())
def __contains__(self, opt):
return opt in self.values
#____________________________________________________________
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):
return self.owners.get(opt, owners.default)
# ____________________________________________________________
# multi types
class Multi(list):
"""multi options values container
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 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
"""
self.settings = context._cfgimpl_settings
self.opt = opt
self.values = context._cfgimpl_values
self.multitype = multitype
super(Multi, self).__init__(lst)
def __setitem__(self, key, value):