refactore carry_out_calculation + test + documentation

This commit is contained in:
2013-09-19 21:38:46 +02:00
parent 30ff0fb72b
commit 90ae9aa70d
5 changed files with 397 additions and 88 deletions

View File

@ -23,11 +23,9 @@ from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.i18n import _
# ____________________________________________________________
def carry_out_calculation(name,
config,
callback,
callback_params,
index=None):
def carry_out_calculation(name, config, callback, callback_params,
index=None, max_len=None):
"""a function that carries out a calculation for an option's value
:param name: the option name (`opt._name`)
@ -40,36 +38,104 @@ def carry_out_calculation(name,
:type callback_params: dict
:param index: if an option is multi, only calculates the nth value
:type index: int
:param max_len: max length for a multi
:type max_len: int
* if no callback_params:
=> calculate()
* if callback_params={'': ('yes',)}
=> calculate('yes')
* if callback_params={'value': ('yes',)}
=> calculate(value='yes')
* if callback_params={'': ('yes', 'no')}
=> calculate('yes', 'no')
* if callback_params={'value': ('yes', 'no')}
=> ValueError()
* if callback_params={'': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(11)
- a multi option:
opt1 == [1, 2, 3]
=> calculate(1)
=> calculate(2)
=> calculate(3)
* if callback_params={'value': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(value=11)
- a multi option:
opt1 == [1, 2, 3]
=> calculate(value=1)
=> calculate(value=2)
=> calculate(value=3)
* if callback_params={'': ((opt1, False), (opt2, False))}
- a multi option with a simple option
opt1 == [1, 2, 3]
opt2 == 11
=> calculate(1, 11)
=> calculate(2, 11)
=> calculate(3, 11)
- a multi option with an other multi option but with same length
opt1 == [1, 2, 3]
opt2 == [11, 12, 13]
callback_params={'': ((opt1, False), (opt2, False))}
=> calculate(1, 11)
=> calculate(2, 12)
=> calculate(3, 13)
- a multi option with an other multi option but with different length
opt1 == [1, 2, 3]
opt2 == [11, 12]
callback_params={'': ((opt1, False), (opt2, False))}
=> ConfigError()
* if callback_params={'value': ((opt1, False), (opt2, False))}
=> ConfigError()
If index is not None, return a value, otherwise return:
* a list if one parameters have multi option
* a value otherwise
If calculate return list, this list is extend to return value.
"""
#callback, callback_params = option.getcallback()
#if callback_params is None:
# callback_params = {}
tcparams = {}
one_is_multi = False
len_multi = 0
for key, values in callback_params.items():
for value in values:
if type(value) == tuple:
path, check_disabled = value
if config is None:
if check_disabled:
continue
raise ConfigError(_('no config specified but needed'))
for key, callbacks in callback_params.items():
for callbk in callbacks:
if isinstance(callbk, tuple):
option, force_permissive = callbk
# get value
try:
opt_value = config._getattr(path, force_permissive=True)
opt = config.unwrap_from_path(path, force_permissive=True)
path = config.cfgimpl_get_description().impl_get_path_by_opt(option)
value = config._getattr(path, force_permissive=True)
except PropertiesOptionError as err:
if check_disabled:
if force_permissive:
continue
raise ConfigError(_('unable to carry out a calculation, '
'option {0} has properties: {1} for: '
'{2}').format(path, err.proptype,
'{2}').format(option._name, err.proptype,
name))
is_multi = opt.impl_is_multi()
is_multi = option.impl_is_multi()
if is_multi:
if opt_value is not None:
len_value = len(opt_value)
if value is not None:
len_value = len(value)
if len_multi != 0 and len_multi != len_value:
raise ConfigError(_('unable to carry out a '
'calculation, option value with'
@ -77,16 +143,23 @@ def carry_out_calculation(name,
'length for: {0}').format(name))
len_multi = len_value
one_is_multi = True
tcparams.setdefault(key, []).append((opt_value, is_multi))
tcparams.setdefault(key, []).append((value, is_multi))
else:
tcparams.setdefault(key, []).append((value, False))
tcparams.setdefault(key, []).append((callbk, False))
if one_is_multi:
ret = []
if index:
range_ = [index]
if index < len_multi:
range_ = [index]
else:
range_ = []
ret = None
else:
range_ = range(len_multi)
if max_len and max_len < len_multi:
range_ = range(max_len)
else:
range_ = range(len_multi)
for incr in range_:
tcp = {}
params = []
@ -97,15 +170,9 @@ def carry_out_calculation(name,
if key == '':
params.append(value[incr])
else:
if len(value) > incr:
tcp[key] = value[incr]
else:
tcp[key] = ''
tcp[key] = value[incr]
else:
if key == '':
params.append(value)
else:
tcp[key] = value
params.append(value)
calc = calculate(name, callback, params, tcp)
if index:
ret = calc
@ -114,7 +181,6 @@ def carry_out_calculation(name,
ret.extend(calc)
else:
ret.append(calc)
return ret
else:
tcp = {}
@ -130,7 +196,6 @@ def carry_out_calculation(name,
def calculate(name, callback, params, tcparams):
# FIXME we don't need the option's name down there.
"""wrapper that launches the 'callback'
:param callback: callback name

View File

@ -26,7 +26,7 @@ from copy import copy, deepcopy
from types import FunctionType
from IPy import IP
from tiramisu.error import ConflictError
from tiramisu.error import ConflictError, ConfigError
from tiramisu.setting import groups, multitypes
from tiramisu.i18n import _
from tiramisu.autolib import carry_out_calculation
@ -97,8 +97,8 @@ class BaseOption(object):
"frozen" (which has noting to do with the high level "freeze"
propertie or "read_only" property)
"""
if not name.startswith('_state') and \
name not in ('_cache_paths', '_consistencies'):
if not name.startswith('_state') and name not in ('_cache_paths',
'_consistencies'):
is_readonly = False
# never change _name
if name == '_name':
@ -327,7 +327,7 @@ class Option(BaseOption):
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
callback_params=None, validator=None, validator_params=None,
properties=None):
"""
:param name: the option's name
@ -344,18 +344,15 @@ class Option(BaseOption):
:param callback_params: the callback's parameter
:param validator: the name of a function which stands for a custom
validation of the value
:param validator_args: the validator's parameters
:param validator_params: the validator's parameters
:param properties: tuple of default properties
"""
super(Option, self).__init__(name, doc, requires, properties)
self._multi = multi
if validator is not None:
if type(validator) != FunctionType:
raise TypeError(_("validator must be a function"))
if validator_args is None:
validator_args = {}
self._validator = (validator, validator_args)
validate_callback(validator, validator_params, 'validator')
self._validator = (validator, validator_params)
else:
self._validator = None
if not self._multi and default_multi is not None:
@ -377,11 +374,7 @@ class Option(BaseOption):
"no callback defined"
" yet for option {0}").format(name))
if callback is not None:
if type(callback) != FunctionType:
raise ValueError('callback must be a function')
if callback_params is not None and \
not isinstance(callback_params, dict):
raise ValueError('callback_params must be a dict')
validate_callback(callback, callback_params, 'callback')
self._callback = (callback, callback_params)
else:
self._callback = None
@ -448,11 +441,23 @@ class Option(BaseOption):
def val_validator(val):
if self._validator is not None:
callback_params = deepcopy(self._validator[1])
callback_params.setdefault('', []).insert(0, val)
return carry_out_calculation(self._name, config=context,
callback=self._validator[0],
callback_params=callback_params)
if self._validator[1] is not None:
validator_params = deepcopy(self._validator[1])
if '' in validator_params:
lst = list(validator_params[''])
lst.insert(0, val)
validator_params[''] = tuple(lst)
else:
validator_params[''] = (val,)
else:
validator_params = {'': (val,)}
ret = carry_out_calculation(self._name, config=context,
callback=self._validator[0],
callback_params=validator_params)
if ret not in [False, True]:
raise ConfigError(_('validator should return a boolean, '
'not {0}').format(ret))
return ret
else:
return True
@ -566,7 +571,7 @@ class ChoiceOption(Option):
def __init__(self, name, doc, values, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, open_values=False, validator=None,
validator_args=None, properties=()):
validator_params=None, properties=()):
"""
:param values: is a list of values the option can possibly take
"""
@ -584,7 +589,7 @@ class ChoiceOption(Option):
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
validator_params=validator_params,
properties=properties)
def impl_get_values(self):
@ -702,7 +707,7 @@ class IPOption(Option):
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
callback_params=None, validator=None, validator_params=None,
properties=None, only_private=False):
self._only_private = only_private
super(IPOption, self).__init__(name, doc, default=default,
@ -712,7 +717,7 @@ class IPOption(Option):
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
validator_params=validator_params,
properties=properties)
def _validate(self, value):
@ -738,7 +743,7 @@ class PortOption(Option):
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
callback_params=None, validator=None, validator_params=None,
properties=None, allow_range=False, allow_zero=False,
allow_wellknown=True, allow_registred=True,
allow_private=False):
@ -772,7 +777,7 @@ class PortOption(Option):
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
validator_params=validator_params,
properties=properties)
def _validate(self, value):
@ -857,7 +862,7 @@ class DomainnameOption(Option):
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
callback_params=None, validator=None, validator_params=None,
properties=None, allow_ip=False, type_='domainname'):
#netbios: for MS domain
#hostname: to identify the device
@ -876,7 +881,7 @@ class DomainnameOption(Option):
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
validator_params=validator_params,
properties=properties)
def _validate(self, value):
@ -1252,3 +1257,33 @@ def validate_requires_arg(requires, name):
require[3], require[4], require[5]))
ret.append(tuple(ret_action))
return frozenset(config_action.keys()), tuple(ret)
def validate_callback(callback, callback_params, type_):
if type(callback) != FunctionType:
raise ValueError(_('{0} should be a function').format(type_))
if callback_params is not None:
if not isinstance(callback_params, dict):
raise ValueError(_('{0}_params should be a dict').format(type_))
for key, callbacks in callback_params.items():
if key != '' and len(callbacks) != 1:
raise ValueError(_('{0}_params with key {1} should not have '
'length different to 1').format(type_,
key))
if not isinstance(callbacks, tuple):
raise ValueError(_('{0}_params should be tuple for key "{1}"'
).format(type_, key))
for callbk in callbacks:
if isinstance(callbk, tuple):
option, force_permissive = callbk
if type_ == 'validator' and not force_permissive:
raise ValueError(_('validator not support tuple'))
if not isinstance(option, Option) and not \
isinstance(option, SymLinkOption):
raise ValueError(_('{0}_params should have an option '
'not a {0} for first argument'
).format(type_, type(option)))
if force_permissive not in [True, False]:
raise ValueError(_('{0}_params should have a boolean'
'not a {0} for second argument'
).format(type_, type(force_permissive)))

View File

@ -124,7 +124,7 @@ class Values(object):
return True
return False
def _getcallback_value(self, opt, index=None):
def _getcallback_value(self, opt, index=None, max_len=None):
"""
retrieves a value for the options that have a callback
@ -139,7 +139,7 @@ class Values(object):
return carry_out_calculation(opt._name, config=self.context(),
callback=callback,
callback_params=callback_params,
index=index)
index=index, max_len=max_len)
def __getitem__(self, opt):
"enables us to use the pythonic dictionary-like access to values"
@ -190,6 +190,7 @@ class Values(object):
if opt.impl_has_callback() and (
self._is_default_owner(path) or
(is_frozen and 'force_default_on_freeze' in setting[opt])):
lenmaster = None
no_value_slave = False
if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.slave):
@ -202,7 +203,7 @@ class Values(object):
if not no_value_slave:
try:
value = self._getcallback_value(opt)
value = self._getcallback_value(opt, max_len=lenmaster)
except ConfigError as config_error:
value = None
# should not raise PropertiesOptionError if option is
@ -389,20 +390,27 @@ class Multi(list):
def _valid_slave(self, value):
#if slave, had values until master's one
values = self.context().cfgimpl_get_values()
masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt(
self.opt.impl_get_master_slaves())
mastervalue = getattr(self.context(), masterp)
masterlen = len(mastervalue)
valuelen = len(value)
if valuelen > masterlen or (valuelen < masterlen and
not self.context().cfgimpl_get_values(
)._is_default_owner(self.path)):
not values._is_default_owner(self.path)):
raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format(
self.opt._name, masterp))
elif valuelen < masterlen:
for num in range(0, masterlen - valuelen):
value.append(self.opt.impl_getdefault_multi())
if self.opt.impl_has_callback():
# if callback add a value, but this value will not change
# anymore automaticly (because this value has owner)
index = value.__len__()
value.append(values._getcallback_value(self.opt,
index=index))
else:
value.append(self.opt.impl_getdefault_multi())
#else: same len so do nothing
return value
@ -420,8 +428,17 @@ class Multi(list):
self.opt._name, slave._name))
elif len(value_slave) < masterlen:
for num in range(0, masterlen - len(value_slave)):
value_slave.append(slave.impl_getdefault_multi(),
force=True)
if slave.impl_has_callback():
# if callback add a value, but this value will not
# change anymore automaticly (because this value
# has owner)
index = value_slave.__len__()
value_slave.append(
values._getcallback_value(slave, index=index),
force=True)
else:
value_slave.append(slave.impl_getdefault_multi(),
force=True)
def __setitem__(self, key, value):
self._validate(value)