consistency is now simple validation
This commit is contained in:
@ -14,8 +14,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""Configuration management library written in python
|
||||
"""
|
||||
from .function import tiramisu_copy, calc_value, calc_value_property_help
|
||||
from .autolib import Calculation, Params, ParamOption, ParamSelfOption, ParamValue, ParamIndex, ParamContext
|
||||
from .function import calc_value, calc_value_property_help, valid_ip_netmask, \
|
||||
valid_network_netmask, valid_in_network, valid_broadcast, \
|
||||
valid_not_equal
|
||||
from .autolib import Calculation, Params, ParamOption, ParamSelfOption, ParamValue, \
|
||||
ParamIndex, ParamContext, ParamSuffix
|
||||
from .option import *
|
||||
from .error import APIError
|
||||
from .api import Config, MetaConfig, GroupConfig, MixConfig
|
||||
@ -32,6 +35,7 @@ allfuncs = ['Calculation',
|
||||
'ParamValue',
|
||||
'ParamIndex',
|
||||
'ParamContext',
|
||||
'ParamSuffix',
|
||||
'MetaConfig',
|
||||
'MixConfig',
|
||||
'GroupConfig',
|
||||
@ -44,9 +48,13 @@ allfuncs = ['Calculation',
|
||||
'Storage',
|
||||
'list_sessions',
|
||||
'delete_session',
|
||||
'tiramisu_copy',
|
||||
'calc_value',
|
||||
'calc_value_property_help']
|
||||
'calc_value_property_help',
|
||||
'valid_ip_netmask',
|
||||
'valid_network_netmask',
|
||||
'valid_in_network',
|
||||
'valid_broadcast',
|
||||
]
|
||||
allfuncs.extend(all_options)
|
||||
del(all_options)
|
||||
__all__ = tuple(allfuncs)
|
||||
|
@ -196,11 +196,6 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
|
||||
option = self._option_bag.option
|
||||
return option.impl_has_dependency(self_is_dep)
|
||||
|
||||
def requires(self):
|
||||
"""Get requires for an option"""
|
||||
option = self._option_bag.option
|
||||
return option.impl_getrequires()
|
||||
|
||||
def isoptiondescription(self):
|
||||
"""Test if option is an optiondescription"""
|
||||
option = self._option_bag.option
|
||||
@ -274,11 +269,6 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
|
||||
ret = value
|
||||
return ret
|
||||
|
||||
def consistencies(self):
|
||||
"""Get consistencies for an option (not for optiondescription)"""
|
||||
option = self._option_bag.option
|
||||
return option.get_consistencies()
|
||||
|
||||
def callbacks(self):
|
||||
"""Get callbacks for an option (not for optiondescription)"""
|
||||
option = self._option_bag.option
|
||||
|
@ -84,10 +84,14 @@ class ParamOption(Param):
|
||||
|
||||
|
||||
class ParamSelfOption(Param):
|
||||
__slots__ = ('todict',)
|
||||
__slots__ = ('todict', 'whole')
|
||||
def __init__(self,
|
||||
todict: bool=False) -> None:
|
||||
todict: bool=False,
|
||||
whole: bool=undefined) -> None:
|
||||
"""whole: send all value for a multi, not only indexed value"""
|
||||
self.todict = todict
|
||||
if whole is not undefined:
|
||||
self.whole = whole
|
||||
|
||||
|
||||
class ParamValue(Param):
|
||||
@ -104,15 +108,21 @@ class ParamIndex(Param):
|
||||
__slots__ = tuple()
|
||||
|
||||
|
||||
class ParamSuffix(Param):
|
||||
__slots__ = tuple()
|
||||
|
||||
|
||||
class Calculation:
|
||||
__slots__ = ('function',
|
||||
'params',
|
||||
'help_function',
|
||||
'has_index')
|
||||
'has_index',
|
||||
'warnings_only')
|
||||
def __init__(self,
|
||||
function: Callable,
|
||||
params: Params=Params(),
|
||||
help_function: Optional[Callable]=None):
|
||||
help_function: Optional[Callable]=None,
|
||||
warnings_only: bool=False):
|
||||
assert isinstance(function, Callable), _('first argument ({0}) must be a function').format(function)
|
||||
if help_function:
|
||||
assert isinstance(help_function, Callable), _('help_function ({0}) must be a function').format(help_function)
|
||||
@ -127,19 +137,22 @@ class Calculation:
|
||||
break
|
||||
else:
|
||||
self.has_index = False
|
||||
if warnings_only is True:
|
||||
self.warnings_only = warnings_only
|
||||
|
||||
def execute(self,
|
||||
option_bag: OptionBag,
|
||||
leadership_must_have_index: bool=False) -> Any:
|
||||
if leadership_must_have_index and not self.has_index:
|
||||
leadership_must_have_index = False
|
||||
leadership_must_have_index: bool=False,
|
||||
orig_value: Any=undefined,
|
||||
allow_raises=False) -> Any:
|
||||
return carry_out_calculation(option_bag.option,
|
||||
callback=self.function,
|
||||
callback_params=self.params,
|
||||
index=option_bag.index,
|
||||
config_bag=option_bag.config_bag,
|
||||
fromconsistency=option_bag.fromconsistency,
|
||||
leadership_must_have_index=leadership_must_have_index)
|
||||
leadership_must_have_index=leadership_must_have_index,
|
||||
orig_value=orig_value,
|
||||
allow_raises=allow_raises)
|
||||
|
||||
def help(self,
|
||||
option_bag: OptionBag,
|
||||
@ -147,14 +160,11 @@ class Calculation:
|
||||
if not self.help_function:
|
||||
return self.execute(option_bag,
|
||||
leadership_must_have_index=leadership_must_have_index)
|
||||
if leadership_must_have_index and not self.has_index:
|
||||
leadership_must_have_index = False
|
||||
return carry_out_calculation(option_bag.option,
|
||||
callback=self.help_function,
|
||||
callback_params=self.params,
|
||||
index=option_bag.index,
|
||||
config_bag=option_bag.config_bag,
|
||||
fromconsistency=option_bag.fromconsistency,
|
||||
leadership_must_have_index=leadership_must_have_index)
|
||||
|
||||
|
||||
@ -167,49 +177,58 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
|
||||
index: Optional[int],
|
||||
orig_value,
|
||||
config_bag: ConfigBag,
|
||||
fromconsistency: List,
|
||||
leadership_must_have_index: bool) -> Any:
|
||||
"""replace Param by true value"""
|
||||
if isinstance(callbk, ParamValue):
|
||||
return callbk.value
|
||||
if isinstance(callbk, ParamIndex):
|
||||
return index
|
||||
if config_bag is undefined:
|
||||
return undefined
|
||||
if isinstance(callbk, ParamContext):
|
||||
# Not an option, set full context
|
||||
return config_bag.context.duplicate(force_values=get_default_values_storages(),
|
||||
force_settings=get_default_settings_storages())
|
||||
if isinstance(callbk, ParamSelfOption):
|
||||
opt = option
|
||||
else:
|
||||
# it's ParamOption
|
||||
opt = callbk.option
|
||||
if opt.issubdyn():
|
||||
opt = opt.to_dynoption(option.rootpath,
|
||||
option.impl_getsuffix())
|
||||
path = opt.impl_getpath()
|
||||
is_follower = opt.impl_is_follower()
|
||||
if leadership_must_have_index and opt.impl_get_leadership() and index is None:
|
||||
raise Break()
|
||||
if index is not None and opt.impl_get_leadership() and \
|
||||
opt.impl_get_leadership().in_same_group(option):
|
||||
if opt == option:
|
||||
index_ = None
|
||||
with_index = False
|
||||
elif is_follower:
|
||||
index_ = index
|
||||
with_index = False
|
||||
else:
|
||||
index_ = None
|
||||
with_index = True
|
||||
else:
|
||||
index_ = None
|
||||
with_index = False
|
||||
if opt == option and orig_value is not undefined and \
|
||||
(not is_follower or index is None):
|
||||
value = orig_value
|
||||
else:
|
||||
def calc_index(callbk, index, same_leadership):
|
||||
if index is not None:
|
||||
if hasattr(callbk, 'whole'):
|
||||
whole = callbk.whole
|
||||
else:
|
||||
# if value is same_leadership, follower are isolate by default
|
||||
# otherwise option is a whole option
|
||||
whole = not same_leadership
|
||||
if not whole:
|
||||
return index
|
||||
return None
|
||||
|
||||
def calc_self(callbk, option, index, value, config_bag):
|
||||
# index must be apply only if follower
|
||||
is_follower = option.impl_is_follower()
|
||||
apply_index = calc_index(callbk, index, is_follower)
|
||||
if value is undefined or (apply_index is None and is_follower):
|
||||
if config_bag is undefined:
|
||||
return undefined
|
||||
path = option.impl_getpath()
|
||||
option_bag = get_option_bag(config_bag, option, path, apply_index)
|
||||
option_bag.config_bag.unrestraint()
|
||||
option_bag.config_bag.remove_validation()
|
||||
# if we are in properties calculation, cannot calculated properties
|
||||
option_bag.properties = config_bag.context.cfgimpl_get_settings().getproperties(option_bag,
|
||||
apply_requires=False)
|
||||
new_value = get_value(callbk, option_bag, path)
|
||||
if apply_index is None and is_follower:
|
||||
new_value[index] = value
|
||||
value = new_value
|
||||
elif apply_index is not None and not is_follower:
|
||||
value = value[apply_index]
|
||||
return value
|
||||
|
||||
def get_value(callbk, option_bag, path):
|
||||
try:
|
||||
# get value
|
||||
value = config_bag.context.getattr(path,
|
||||
option_bag)
|
||||
except PropertiesOptionError as err:
|
||||
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
|
||||
if callbk.notraisepropertyerror or callbk.raisepropertyerror:
|
||||
raise err
|
||||
raise ConfigError(_('unable to carry out a calculation for "{}"'
|
||||
', {}').format(option.impl_get_display_name(), err), err)
|
||||
except ValueError as err:
|
||||
raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option_bag.option.impl_get_display_name(), err))
|
||||
return value
|
||||
|
||||
def get_option_bag(config_bag, opt, path, index_):
|
||||
# don't validate if option is option that we tried to validate
|
||||
config_bag = config_bag.copy()
|
||||
config_bag.properties = config_bag.true_properties - {'warnings'}
|
||||
@ -220,29 +239,64 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
|
||||
path,
|
||||
index_,
|
||||
config_bag)
|
||||
if fromconsistency:
|
||||
option_bag.fromconsistency = fromconsistency.copy()
|
||||
if opt == option:
|
||||
option_bag.config_bag.unrestraint()
|
||||
option_bag.config_bag.remove_validation()
|
||||
# if we are in properties calculation, cannot calculated properties
|
||||
option_bag.properties = config_bag.context.cfgimpl_get_settings().getproperties(option_bag,
|
||||
apply_requires=False)
|
||||
try:
|
||||
# get value
|
||||
value = config_bag.context.getattr(path,
|
||||
option_bag)
|
||||
if with_index:
|
||||
value = value[index]
|
||||
except PropertiesOptionError as err:
|
||||
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
|
||||
if callbk.notraisepropertyerror or callbk.raisepropertyerror:
|
||||
raise err
|
||||
raise ConfigError(_('unable to carry out a calculation for "{}"'
|
||||
', {}').format(option.impl_get_display_name(), err), err)
|
||||
return option_bag
|
||||
|
||||
if isinstance(callbk, ParamValue):
|
||||
return callbk.value
|
||||
|
||||
if isinstance(callbk, ParamIndex):
|
||||
return index
|
||||
|
||||
if isinstance(callbk, ParamSuffix):
|
||||
if not option.issubdyn():
|
||||
raise ConfigError('option "{}" is not in a dynoptiondescription'.format(option.impl_get_display_name()))
|
||||
return option.impl_getsuffix()
|
||||
|
||||
if isinstance(callbk, ParamContext):
|
||||
if config_bag is undefined:
|
||||
return undefined
|
||||
# Not an option, set full context
|
||||
return config_bag.context.duplicate(force_values=get_default_values_storages(),
|
||||
force_settings=get_default_settings_storages())
|
||||
if isinstance(callbk, ParamSelfOption):
|
||||
if leadership_must_have_index and option.impl_get_leadership() and index is None:
|
||||
raise Break()
|
||||
value = calc_self(callbk, option, index, orig_value, config_bag)
|
||||
if not callbk.todict:
|
||||
return value
|
||||
return {'name': option.impl_get_display_name(),
|
||||
'value': value}
|
||||
|
||||
# it's ParamOption
|
||||
callbk_option = callbk.option
|
||||
if callbk_option.issubdyn():
|
||||
callbk_option = callbk_option.to_dynoption(option.rootpath,
|
||||
option.impl_getsuffix())
|
||||
if leadership_must_have_index and callbk_option.impl_get_leadership() and index is None:
|
||||
raise Break()
|
||||
if config_bag is undefined:
|
||||
return undefined
|
||||
if index is not None and callbk_option.impl_get_leadership() and \
|
||||
callbk_option.impl_get_leadership().in_same_group(option):
|
||||
if not callbk_option.impl_is_follower():
|
||||
# leader
|
||||
index_ = None
|
||||
with_index = True
|
||||
else:
|
||||
# follower
|
||||
index_ = index
|
||||
with_index = False
|
||||
else:
|
||||
index_ = None
|
||||
with_index = False
|
||||
path = callbk_option.impl_getpath()
|
||||
option_bag = get_option_bag(config_bag, callbk_option, path, index_)
|
||||
value = get_value(callbk, option_bag, path)
|
||||
if with_index:
|
||||
value = value[index]
|
||||
if not callbk.todict:
|
||||
return value
|
||||
return {'name': opt.impl_get_display_name(),
|
||||
return {'name': callbk_option.impl_get_display_name(),
|
||||
'value': value}
|
||||
|
||||
|
||||
@ -251,10 +305,9 @@ def carry_out_calculation(option,
|
||||
callback_params: Optional[Params],
|
||||
index: Optional[int],
|
||||
config_bag: Optional[ConfigBag],
|
||||
fromconsistency: List,
|
||||
orig_value=undefined,
|
||||
leadership_must_have_index: bool=False,
|
||||
is_validator: int=False):
|
||||
allow_raises: int=False):
|
||||
"""a function that carries out a calculation for an option's value
|
||||
|
||||
:param option: the option
|
||||
@ -265,109 +318,16 @@ def carry_out_calculation(option,
|
||||
:type callback_params: dict
|
||||
:param index: if an option is multi, only calculates the nth value
|
||||
:type index: int
|
||||
:param is_validator: to know if carry_out_calculation is used to validate a value
|
||||
:param allow_raises: to know if carry_out_calculation is used to validate a value
|
||||
|
||||
The callback_params is a dict. Key is used to build args (if key is '')
|
||||
and kwargs (otherwise). Values are tuple of:
|
||||
- values
|
||||
- tuple with option and boolean's force_permissive (True when don't raise
|
||||
if PropertiesOptionError)
|
||||
Values could have multiple values only when key is ''.
|
||||
|
||||
* if no callback_params:
|
||||
=> calculate(<function func at 0x2092320>, [], {})
|
||||
|
||||
* if callback_params={'': ('yes',)}
|
||||
=> calculate(<function func at 0x2092320>, ['yes'], {})
|
||||
|
||||
* if callback_params={'value': ('yes',)}
|
||||
=> calculate(<function func at 0x165b320>, [], {'value': 'yes'})
|
||||
|
||||
* if callback_params={'': ('yes', 'no')}
|
||||
=> calculate('yes', 'no')
|
||||
|
||||
* if callback_params={'value': ('yes', 'no')}
|
||||
=> ValueError()
|
||||
|
||||
* if callback_params={'': (['yes', 'no'],)}
|
||||
=> calculate(<function func at 0x176b320>, ['yes', 'no'], {})
|
||||
|
||||
* if callback_params={'value': ('yes', 'no')}
|
||||
=> raises ValueError()
|
||||
|
||||
* if callback_params={'': ((opt1, False),)}
|
||||
|
||||
- a simple option:
|
||||
opt1 == 11
|
||||
=> calculate(<function func at 0x1cea320>, [11], {})
|
||||
|
||||
- a multi option and not leadership
|
||||
opt1 == [1, 2, 3]
|
||||
=> calculate(<function func at 0x223c320>, [[1, 2, 3]], {})
|
||||
|
||||
- option is leader or follower of opt1:
|
||||
opt1 == [1, 2, 3]
|
||||
=> calculate(<function func at 0x223c320>, [1], {})
|
||||
=> calculate(<function func at 0x223c320>, [2], {})
|
||||
=> calculate(<function func at 0x223c320>, [3], {})
|
||||
|
||||
- opt is a leader or follower but not related to option:
|
||||
opt1 == [1, 2, 3]
|
||||
=> calculate(<function func at 0x11b0320>, [[1, 2, 3]], {})
|
||||
|
||||
* if callback_params={'value': ((opt1, False),)}
|
||||
|
||||
- a simple option:
|
||||
opt1 == 11
|
||||
=> calculate(<function func at 0x17ff320>, [], {'value': 11})
|
||||
|
||||
- a multi option:
|
||||
opt1 == [1, 2, 3]
|
||||
=> calculate(<function func at 0x1262320>, [], {'value': [1, 2, 3]})
|
||||
|
||||
* if callback_params={'': ((opt1, False), (opt2, False))}
|
||||
|
||||
- two single options
|
||||
opt1 = 11
|
||||
opt2 = 12
|
||||
=> calculate(<function func at 0x217a320>, [11, 12], {})
|
||||
|
||||
- a multi option with a simple option
|
||||
opt1 == [1, 2, 3]
|
||||
opt2 == 12
|
||||
=> calculate(<function func at 0x2153320>, [[1, 2, 3], 12], {})
|
||||
|
||||
- a multi option with an other multi option but with same length
|
||||
opt1 == [1, 2, 3]
|
||||
opt2 == [11, 12, 13]
|
||||
=> calculate(<function func at 0x1981320>, [[1, 2, 3], [11, 12, 13]], {})
|
||||
|
||||
- a multi option with an other multi option but with different length
|
||||
opt1 == [1, 2, 3]
|
||||
opt2 == [11, 12]
|
||||
=> calculate(<function func at 0x2384320>, [[1, 2, 3], [11, 12]], {})
|
||||
|
||||
- a multi option without value with a simple option
|
||||
opt1 == []
|
||||
opt2 == 11
|
||||
=> calculate(<function func at 0xb65320>, [[], 12], {})
|
||||
|
||||
* if callback_params={'value': ((opt1, False), (opt2, False))}
|
||||
=> raises ValueError()
|
||||
|
||||
If index is not undefined, 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.
|
||||
"""
|
||||
Values could have multiple values only when key is ''."""
|
||||
args = []
|
||||
kwargs = {}
|
||||
# if callback_params has a callback, launch several time calculate()
|
||||
if option.issubdyn():
|
||||
#FIXME why here? should be a ParamSuffix !
|
||||
kwargs['suffix'] = option.impl_getsuffix()
|
||||
if callback_params:
|
||||
for callbk in callback_params.args:
|
||||
try:
|
||||
@ -376,7 +336,6 @@ def carry_out_calculation(option,
|
||||
index,
|
||||
orig_value,
|
||||
config_bag,
|
||||
fromconsistency,
|
||||
leadership_must_have_index)
|
||||
if value is undefined:
|
||||
return undefined
|
||||
@ -395,7 +354,6 @@ def carry_out_calculation(option,
|
||||
index,
|
||||
orig_value,
|
||||
config_bag,
|
||||
fromconsistency,
|
||||
leadership_must_have_index)
|
||||
if value is undefined:
|
||||
return undefined
|
||||
@ -409,7 +367,7 @@ def carry_out_calculation(option,
|
||||
continue
|
||||
ret = calculate(option,
|
||||
callback,
|
||||
is_validator,
|
||||
allow_raises,
|
||||
args,
|
||||
kwargs)
|
||||
if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \
|
||||
@ -434,7 +392,7 @@ def carry_out_calculation(option,
|
||||
|
||||
def calculate(option,
|
||||
callback: Callable,
|
||||
is_validator: bool,
|
||||
allow_raises: bool,
|
||||
args,
|
||||
kwargs):
|
||||
"""wrapper that launches the 'callback'
|
||||
@ -447,7 +405,7 @@ def calculate(option,
|
||||
try:
|
||||
return callback(*args, **kwargs)
|
||||
except ValueError as err:
|
||||
if is_validator:
|
||||
if allow_raises:
|
||||
raise err
|
||||
error = err
|
||||
except Exception as err:
|
||||
|
@ -48,8 +48,7 @@ class SubConfig(object):
|
||||
descr,
|
||||
context,
|
||||
config_bag,
|
||||
subpath=None,
|
||||
fromconsistency=None):
|
||||
subpath=None):
|
||||
""" Configuration option management class
|
||||
|
||||
:param descr: describes the configuration schema
|
||||
@ -82,8 +81,6 @@ class SubConfig(object):
|
||||
full_leaderpath,
|
||||
None,
|
||||
cconfig_bag)
|
||||
if fromconsistency:
|
||||
moption_bag.fromconsistency = fromconsistency
|
||||
value = self.getattr(leaderpath,
|
||||
moption_bag)
|
||||
self._impl_length = len(value)
|
||||
@ -179,8 +176,7 @@ class SubConfig(object):
|
||||
|
||||
def cfgimpl_get_home_by_path(self,
|
||||
path,
|
||||
config_bag,
|
||||
fromconsistency=None):
|
||||
config_bag):
|
||||
""":returns: tuple (config, name)"""
|
||||
path = path.split('.')
|
||||
for step in path[:-1]:
|
||||
@ -193,8 +189,6 @@ class SubConfig(object):
|
||||
subpath,
|
||||
None,
|
||||
config_bag)
|
||||
if fromconsistency is not None:
|
||||
option_bag.fromconsistency = fromconsistency
|
||||
self = self.get_subconfig(option_bag)
|
||||
assert isinstance(self, SubConfig), _('unknown option {}').format(path[-1])
|
||||
return self, path[-1]
|
||||
@ -253,17 +247,11 @@ class SubConfig(object):
|
||||
|
||||
def get_subconfig(self,
|
||||
option_bag):
|
||||
if option_bag.fromconsistency:
|
||||
fromconsistency = option_bag.fromconsistency.copy()
|
||||
else:
|
||||
fromconsistency = None
|
||||
|
||||
self.cfgimpl_get_settings().validate_properties(option_bag)
|
||||
return SubConfig(option_bag.option,
|
||||
self._impl_context,
|
||||
option_bag.config_bag,
|
||||
option_bag.path,
|
||||
fromconsistency)
|
||||
option_bag.path)
|
||||
|
||||
def getattr(self,
|
||||
name,
|
||||
@ -276,13 +264,8 @@ class SubConfig(object):
|
||||
"""
|
||||
config_bag = option_bag.config_bag
|
||||
if '.' in name:
|
||||
if option_bag.fromconsistency:
|
||||
fromconsistency = option_bag.fromconsistency.copy()
|
||||
else:
|
||||
fromconsistency = None
|
||||
self, name = self.cfgimpl_get_home_by_path(name,
|
||||
config_bag,
|
||||
fromconsistency)
|
||||
config_bag)
|
||||
|
||||
option = option_bag.option
|
||||
if option.impl_is_symlinkoption():
|
||||
@ -309,8 +292,7 @@ class SubConfig(object):
|
||||
length,
|
||||
option_bag.index))
|
||||
if option.impl_is_follower() and option_bag.index is None:
|
||||
needs_re_verify_follower_properties = option_bag.option.impl_getrequires() or \
|
||||
self.cfgimpl_get_settings().has_properties_index(option_bag)
|
||||
needs_re_verify_follower_properties = self.cfgimpl_get_settings().has_properties_index(option_bag)
|
||||
value = []
|
||||
for idx in range(length):
|
||||
soption_bag = OptionBag()
|
||||
@ -318,7 +300,6 @@ class SubConfig(object):
|
||||
option_bag.path,
|
||||
idx,
|
||||
config_bag)
|
||||
soption_bag.fromconsistency = option_bag.fromconsistency.copy()
|
||||
try:
|
||||
value.append(self.getattr(name,
|
||||
soption_bag,
|
||||
@ -860,7 +841,6 @@ class KernelGroupConfig(_CommonConfig):
|
||||
err.proptype,
|
||||
err._settings,
|
||||
err._opt_type,
|
||||
err._requires,
|
||||
err._name,
|
||||
err._orig_opt))
|
||||
except (ValueError, LeadershipError, AttributeError) as err:
|
||||
@ -1069,7 +1049,6 @@ class KernelMixConfig(KernelGroupConfig):
|
||||
err.proptype,
|
||||
err._settings,
|
||||
err._opt_type,
|
||||
err._requires,
|
||||
err._name,
|
||||
err._orig_opt))
|
||||
except (ValueError, LeadershipError, AttributeError) as err:
|
||||
|
@ -58,12 +58,10 @@ class PropertiesOptionError(AttributeError):
|
||||
proptype,
|
||||
settings,
|
||||
opt_type=None,
|
||||
requires=None,
|
||||
name=None,
|
||||
orig_opt=None):
|
||||
if opt_type:
|
||||
self._opt_type = opt_type
|
||||
self._requires = requires
|
||||
self._name = name
|
||||
self._orig_opt = orig_opt
|
||||
else:
|
||||
@ -71,7 +69,6 @@ class PropertiesOptionError(AttributeError):
|
||||
self._opt_type = 'optiondescription'
|
||||
else:
|
||||
self._opt_type = 'option'
|
||||
self._requires = option_bag.option.impl_getrequires()
|
||||
self._name = option_bag.option.impl_get_display_name()
|
||||
self._orig_opt = None
|
||||
self._option_bag = option_bag
|
||||
@ -84,30 +81,21 @@ class PropertiesOptionError(AttributeError):
|
||||
self._orig_opt = opt
|
||||
|
||||
def __str__(self):
|
||||
#this part is a bit slow, so only execute when display
|
||||
# this part is a bit slow, so only execute when display
|
||||
if self.msg is not None:
|
||||
return self.msg
|
||||
if self._settings is None:
|
||||
return 'error'
|
||||
req = self._settings.apply_requires(self._option_bag,
|
||||
True)
|
||||
if req != {}:
|
||||
only_one = len(req) == 1
|
||||
msg = []
|
||||
for action, msg_ in req.items():
|
||||
msg.append('"{0}" ({1})'.format(action, display_list(msg_, add_quote=False)))
|
||||
msg = display_list(msg, add_quote=False)
|
||||
else:
|
||||
properties = list(self._settings.calc_raises_properties(self._option_bag,
|
||||
apply_requires=False))
|
||||
for property_ in self._settings.get_calculated_properties(self._option_bag):
|
||||
properties.append(property_.help(self._option_bag))
|
||||
properties = list(self._settings.calc_raises_properties(self._option_bag,
|
||||
apply_requires=False))
|
||||
for property_ in self._settings.get_calculated_properties(self._option_bag):
|
||||
properties.append(property_.help(self._option_bag))
|
||||
|
||||
if not properties:
|
||||
# if proptype == ['mandatory']
|
||||
properties = self.proptype
|
||||
only_one = len(properties) == 1
|
||||
msg = display_list(properties, add_quote=True)
|
||||
if not properties:
|
||||
# if proptype == ['mandatory']
|
||||
properties = self.proptype
|
||||
only_one = len(properties) == 1
|
||||
msg = display_list(properties, add_quote=True)
|
||||
if only_one:
|
||||
prop_msg = _('property')
|
||||
else:
|
||||
@ -125,7 +113,7 @@ class PropertiesOptionError(AttributeError):
|
||||
self._name,
|
||||
prop_msg,
|
||||
msg))
|
||||
del self._requires, self._opt_type, self._name
|
||||
del self._opt_type, self._name
|
||||
del self._settings, self._orig_opt
|
||||
return self.msg
|
||||
|
||||
@ -150,13 +138,6 @@ class ConflictError(Exception):
|
||||
|
||||
#____________________________________________________________
|
||||
# miscellaneous exceptions
|
||||
class RequirementError(Exception):
|
||||
"""a recursive loop occurs in the requirements tree
|
||||
requires
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class LeadershipError(Exception):
|
||||
"problem with a leadership's value length"
|
||||
pass
|
||||
|
@ -14,13 +14,144 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
from typing import Any, List, Optional
|
||||
from operator import add, mul, sub, truediv
|
||||
from ipaddress import ip_address, ip_interface, ip_network
|
||||
from .i18n import _
|
||||
from .setting import undefined
|
||||
from .error import display_list
|
||||
|
||||
|
||||
def tiramisu_copy(val): # pragma: no cover
|
||||
return val
|
||||
def valid_network_netmask(network: str,
|
||||
netmask: str):
|
||||
"""FIXME
|
||||
"""
|
||||
if isinstance(network, dict):
|
||||
network_value = network['value']
|
||||
network_display_name = '({})'.format(network['name'])
|
||||
else:
|
||||
network_value = network
|
||||
network_display_name = ''
|
||||
if None in [network_value, netmask]:
|
||||
return
|
||||
try:
|
||||
ip_network('{0}/{1}'.format(network_value, netmask))
|
||||
except ValueError:
|
||||
raise ValueError(_('network "{0}" {1}does not match with this netmask').format(network_value,
|
||||
network_display_name))
|
||||
|
||||
def valid_ip_netmask(ip: str,
|
||||
netmask: str):
|
||||
if isinstance(ip, dict):
|
||||
ip_value = ip['value']
|
||||
ip_display_name = '({})'.format(ip['name'])
|
||||
else:
|
||||
ip_value = ip
|
||||
ip_display_name = ''
|
||||
if None in [ip_value, netmask]:
|
||||
return
|
||||
ip_netmask = ip_interface('{0}/{1}'.format(ip_value, netmask))
|
||||
if ip_netmask.ip == ip_netmask.network.network_address:
|
||||
raise ValueError(_('IP \"{0}\" {1}with this netmask is in fact a network address').format(ip_value, ip_display_name))
|
||||
elif ip_netmask.ip == ip_netmask.network.broadcast_address:
|
||||
raise ValueError(_('IP \"{0}\" {1}with this netmask is in fact a broacast address').format(ip_value, ip_display_name))
|
||||
|
||||
|
||||
# FIXME CIDR ?
|
||||
def valid_broadcast(network: 'NetworkOption',
|
||||
netmask: 'NetmaskOption',
|
||||
broadcast: 'BroadcastOption'):
|
||||
if isinstance(network, dict):
|
||||
network_value = network['value']
|
||||
network_display_name = ' ({})'.format(network['name'])
|
||||
else:
|
||||
network_value = network
|
||||
network_display_name = ''
|
||||
if isinstance(netmask, dict):
|
||||
netmask_value = netmask['value']
|
||||
netmask_display_name = ' ({})'.format(netmask['name'])
|
||||
else:
|
||||
netmask_value = netmask
|
||||
netmask_display_name = ''
|
||||
if ip_network('{0}/{1}'.format(network, netmask)).broadcast_address != ip_address(broadcast):
|
||||
raise ValueError(_('broadcast invalid with network {0}{1} and netmask {2}{3}'
|
||||
'').format(network_value,
|
||||
network_display_name,
|
||||
netmask_value,
|
||||
netmask_display_name))
|
||||
|
||||
|
||||
def valid_in_network(ip,
|
||||
network,
|
||||
netmask=None):
|
||||
if isinstance(network, dict):
|
||||
network_value = network['value']
|
||||
network_display_name = ' ({})'.format(network['name'])
|
||||
else:
|
||||
network_value = network
|
||||
network_display_name = ''
|
||||
if netmask is None:
|
||||
network_obj = ip_network('{0}'.format(network_value))
|
||||
else:
|
||||
if isinstance(netmask, dict):
|
||||
netmask_value = netmask['value']
|
||||
netmask_display_name = ' ({})'.format(netmask['name'])
|
||||
else:
|
||||
netmask_value = netmask
|
||||
netmask_display_name = ''
|
||||
network_obj = ip_network('{0}/{1}'.format(network_value,
|
||||
netmask_value))
|
||||
if ip_interface(ip) not in network_obj:
|
||||
if netmask is None:
|
||||
msg = _('this IP is not in network {0}{1}').format(network_value,
|
||||
network_display_name)
|
||||
else:
|
||||
msg = _('this IP is not in network {0}{1} with netmask {2}{3}').format(network_value,
|
||||
network_display_name,
|
||||
netmask_value,
|
||||
netmask_display_name)
|
||||
raise ValueError(msg)
|
||||
|
||||
# test if ip is not network/broadcast IP
|
||||
ip_netmask = ip_interface('{0}/{1}'.format(ip, network_obj.netmask))
|
||||
if ip_netmask.ip == ip_netmask.network.network_address:
|
||||
if netmask is None:
|
||||
msg = _('this IP with the network {0}{1} is in fact a network address').format(network_value,
|
||||
network_display_name)
|
||||
else:
|
||||
msg = _('this IP with the netmask {0}{1} is in fact a network address').format(netmask_value,
|
||||
netmask_display_name)
|
||||
raise ValueError(msg)
|
||||
elif ip_netmask.ip == ip_netmask.network.broadcast_address:
|
||||
if netmask is None:
|
||||
msg = _('this IP with the network {0}{1} is in fact a broadcast address').format(network_value,
|
||||
network_display_name)
|
||||
else:
|
||||
msg = _('this IP with the netmask {0}{1} is in fact a broadcast address').format(netmask_value,
|
||||
netmask_display_name)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def valid_not_equal(*values):
|
||||
equal = set()
|
||||
for idx, val in enumerate(values[1:]):
|
||||
if isinstance(val, dict):
|
||||
if 'propertyerror' in val:
|
||||
continue
|
||||
tval = val['value']
|
||||
else:
|
||||
tval = val
|
||||
if values[0] == tval is not None:
|
||||
if isinstance(val, dict):
|
||||
if equal is True:
|
||||
equal = set()
|
||||
equal.add(val['name'])
|
||||
elif not equal:
|
||||
equal = True
|
||||
if equal:
|
||||
if equal is not True:
|
||||
msg = _('value is identical to {}').format(display_list(list(equal), add_quote=True))
|
||||
else:
|
||||
msg = _('value is identical')
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
class CalcValue:
|
||||
|
@ -51,14 +51,8 @@ class Base:
|
||||
__slots__ = ('_name',
|
||||
'_path',
|
||||
'_informations',
|
||||
#calcul
|
||||
'_subdyn',
|
||||
'_requires',
|
||||
'_properties',
|
||||
'_calc_properties',
|
||||
#
|
||||
'_consistencies',
|
||||
#other
|
||||
'_has_dependency',
|
||||
'_dependencies',
|
||||
'_has_calc_context',
|
||||
@ -68,19 +62,10 @@ class Base:
|
||||
def __init__(self,
|
||||
name: str,
|
||||
doc: str,
|
||||
requires=None,
|
||||
properties=None,
|
||||
is_multi: bool=False) -> None:
|
||||
if not valid_name(name):
|
||||
raise ValueError(_('"{0}" is an invalid name for an option').format(name))
|
||||
if requires is not None:
|
||||
calc_properties, requires = validate_requires_arg(self,
|
||||
is_multi,
|
||||
requires,
|
||||
name)
|
||||
else:
|
||||
calc_properties = frozenset()
|
||||
requires = undefined
|
||||
if properties is None:
|
||||
properties = frozenset()
|
||||
elif isinstance(properties, tuple):
|
||||
@ -92,153 +77,18 @@ class Base:
|
||||
assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},'
|
||||
' must be a frozenset').format(type(properties),
|
||||
name)
|
||||
self.validate_properties(name,
|
||||
calc_properties,
|
||||
properties)
|
||||
_setattr = object.__setattr__
|
||||
_setattr(self, '_name', name)
|
||||
_setattr(self, '_informations', {'doc': doc})
|
||||
if calc_properties is not undefined:
|
||||
_setattr(self, '_calc_properties', calc_properties)
|
||||
if requires is not undefined:
|
||||
_setattr(self, '_requires', requires)
|
||||
if properties:
|
||||
_setattr(self, '_properties', properties)
|
||||
|
||||
def validate_properties(self,
|
||||
name: str,
|
||||
calc_properties: FrozenSet[str],
|
||||
properties: FrozenSet[str]) -> None:
|
||||
set_forbidden_properties = calc_properties & properties
|
||||
if set_forbidden_properties != frozenset():
|
||||
raise ValueError(_('conflict: properties already set in requirement {0} for {1}'
|
||||
'').format(display_list(set_forbidden_properties, add_quote=True),
|
||||
name))
|
||||
assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},'
|
||||
' must be a frozenset').format(type(properties),
|
||||
name)
|
||||
for prop in properties:
|
||||
if not isinstance(prop, str):
|
||||
if not isinstance(prop, Calculation):
|
||||
raise ValueError(_('invalid property type {0} for {1}, must be a string or a Calculation').format(type(prop), name))
|
||||
params = prop.params
|
||||
for param in chain(params.args, params.kwargs.values()):
|
||||
for param in chain(prop.params.args, prop.params.kwargs.values()):
|
||||
if isinstance(param, ParamOption):
|
||||
param.option._add_dependency(self)
|
||||
|
||||
def _get_function_args(self,
|
||||
function: Callable) -> Tuple[Set[str], Set[str], bool, bool]:
|
||||
args = set()
|
||||
kwargs = set()
|
||||
positional = False
|
||||
keyword = False
|
||||
for param in signature(function).parameters.values():
|
||||
if param.kind == param.VAR_POSITIONAL:
|
||||
positional = True
|
||||
elif param.kind == param.VAR_KEYWORD:
|
||||
keyword = True
|
||||
elif param.default is param.empty:
|
||||
args.add(param.name)
|
||||
else:
|
||||
kwargs.add(param.name)
|
||||
return args, kwargs, positional, keyword
|
||||
|
||||
def _get_parameters_args(self,
|
||||
calculator_params: Optional[Params],
|
||||
add_value: bool) -> Tuple[Set[str], Set[str]]:
|
||||
args = set()
|
||||
kwargs = set()
|
||||
# add value as first argument
|
||||
if add_value:
|
||||
args.add('value')
|
||||
if self.impl_is_dynoptiondescription():
|
||||
kwargs.add('suffix')
|
||||
if calculator_params:
|
||||
for idx in range(len(calculator_params.args)):
|
||||
# construct an appropriate name
|
||||
args.add('param{}'.format(idx))
|
||||
for param in calculator_params.kwargs:
|
||||
kwargs.add(param)
|
||||
return args, kwargs
|
||||
|
||||
def _build_calculator_params(self,
|
||||
calculator: Callable,
|
||||
calculator_params: Optional[Params],
|
||||
type_: str,
|
||||
add_value: bool=False) -> Union[None, Params]:
|
||||
"""
|
||||
:add_value: add value as first argument for validator
|
||||
"""
|
||||
assert isinstance(calculator, Callable), _('{0} must be a function').format(type_)
|
||||
if calculator_params is not None:
|
||||
assert isinstance(calculator_params, Params), _('{0}_params must be a params'
|
||||
'').format(type_)
|
||||
for param in chain(calculator_params.args, calculator_params.kwargs.values()):
|
||||
if isinstance(param, ParamContext):
|
||||
self._has_calc_context = True
|
||||
elif isinstance(param, ParamOption):
|
||||
param.option._add_dependency(self)
|
||||
if type_ == 'validator':
|
||||
self._has_dependency = True
|
||||
is_multi = self.impl_is_optiondescription() or self.impl_is_multi()
|
||||
func_args, func_kwargs, func_positional, func_keyword = self._get_function_args(calculator)
|
||||
calculator_args, calculator_kwargs = self._get_parameters_args(calculator_params, add_value)
|
||||
# remove knowned kwargs
|
||||
common_kwargs = func_kwargs & calculator_kwargs
|
||||
func_kwargs -= common_kwargs
|
||||
calculator_kwargs -= common_kwargs
|
||||
# remove knowned calculator's kwargs in func's args
|
||||
common = func_args & calculator_kwargs
|
||||
func_args -= common
|
||||
calculator_kwargs -= common
|
||||
# remove unknown calculator's args in func's args
|
||||
for idx in range(min(len(calculator_args), len(func_args))):
|
||||
func_args.pop()
|
||||
calculator_args.pop()
|
||||
# remove unknown calculator's args in func's kwargs
|
||||
if is_multi:
|
||||
func_kwargs_left = func_kwargs - {'index', 'self'}
|
||||
else:
|
||||
func_kwargs_left = func_kwargs
|
||||
func_kwargs_pop = set()
|
||||
for idx in range(min(len(calculator_args), len(func_kwargs_left))):
|
||||
func_kwargs_pop.add(func_kwargs_left.pop())
|
||||
calculator_args.pop()
|
||||
func_kwargs -= func_kwargs_pop
|
||||
# func_positional or keyword is True, so assume all args or kwargs are satisfy
|
||||
if func_positional:
|
||||
calculator_args = set()
|
||||
if func_keyword:
|
||||
calculator_kwargs = set()
|
||||
if calculator_args or calculator_kwargs:
|
||||
# there is more args/kwargs than expected!
|
||||
raise ConfigError(_('cannot find those arguments "{}" in function "{}" for "{}"'
|
||||
'').format(display_list(list(calculator_args | calculator_kwargs)),
|
||||
calculator.__name__,
|
||||
self.impl_get_display_name()))
|
||||
has_index = False
|
||||
if is_multi and func_args and not self.impl_is_dynoptiondescription():
|
||||
if calculator_params is None:
|
||||
calculator_params = Params()
|
||||
params = list(calculator_params.args)
|
||||
if add_value:
|
||||
# only for validator
|
||||
params.append(ParamOption(self))
|
||||
func_args.pop()
|
||||
if func_args:
|
||||
has_index = True
|
||||
params.append(ParamIndex())
|
||||
func_args.pop()
|
||||
calculator_params.args = tuple(params)
|
||||
if func_args:
|
||||
raise ConfigError(_('missing those arguments "{}" in function "{}" for "{}"'
|
||||
'').format(display_list(list(func_args)),
|
||||
calculator.__name__,
|
||||
self.impl_get_display_name()))
|
||||
if not self.impl_is_dynoptiondescription() and is_multi and \
|
||||
not has_index and 'index' in func_kwargs:
|
||||
calculator_params.kwargs['index'] = ParamIndex()
|
||||
return calculator_params
|
||||
_setattr = object.__setattr__
|
||||
_setattr(self, '_name', name)
|
||||
_setattr(self, '_informations', {'doc': doc})
|
||||
if properties:
|
||||
_setattr(self, '_properties', properties)
|
||||
|
||||
def impl_has_dependency(self,
|
||||
self_is_dep: bool=True) -> bool:
|
||||
@ -323,9 +173,6 @@ class Base:
|
||||
def getsubdyn(self):
|
||||
return self._subdyn()
|
||||
|
||||
def impl_getrequires(self):
|
||||
return getattr(self, '_requires', STATIC_TUPLE)
|
||||
|
||||
def impl_get_callback(self):
|
||||
call = getattr(self, '_val_call', (None, None))[1]
|
||||
if call is None:
|
||||
@ -373,7 +220,6 @@ class Base:
|
||||
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
|
||||
" read-only").format(self.__class__.__name__,
|
||||
self,
|
||||
#self.impl_getname(),
|
||||
key))
|
||||
self._informations[key] = value
|
||||
|
||||
@ -445,181 +291,3 @@ class BaseOption(Base):
|
||||
|
||||
def impl_is_symlinkoption(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def validate_requires_arg(new_option: BaseOption,
|
||||
multi: bool,
|
||||
requires: List[dict],
|
||||
name: str) -> Tuple[FrozenSet, Tuple]:
|
||||
"""check malformed requirements
|
||||
and tranform dict to internal tuple
|
||||
|
||||
:param requires: have a look at the
|
||||
:meth:`tiramisu.setting.Settings.apply_requires` method to
|
||||
know more about
|
||||
the description of the requires dictionary
|
||||
"""
|
||||
def get_option(require):
|
||||
if 'option' in require:
|
||||
option = require['option']
|
||||
if option == 'self':
|
||||
option = new_option
|
||||
if __debug__:
|
||||
if not isinstance(option, BaseOption):
|
||||
raise ValueError(_('malformed requirements '
|
||||
'must be an option in option {0}').format(name))
|
||||
if not multi and option.impl_is_multi():
|
||||
raise ValueError(_('malformed requirements '
|
||||
'multi option must not set '
|
||||
'as requires of non multi option {0}').format(name))
|
||||
option._add_dependency(new_option)
|
||||
else:
|
||||
callback = require['callback']
|
||||
callback_params = new_option._build_calculator_params(callback,
|
||||
require.get('callback_params'),
|
||||
'callback')
|
||||
option = (callback, callback_params)
|
||||
return option
|
||||
|
||||
def _set_expected(action,
|
||||
inverse,
|
||||
transitive,
|
||||
same_action,
|
||||
option,
|
||||
expected,
|
||||
operator):
|
||||
if inverse not in ret_requires[action]:
|
||||
ret_requires[action][inverse] = ([(option, [expected])], action, inverse, transitive, same_action, operator)
|
||||
else:
|
||||
for exp in ret_requires[action][inverse][0]:
|
||||
if exp[0] == option:
|
||||
exp[1].append(expected)
|
||||
break
|
||||
else:
|
||||
ret_requires[action][inverse][0].append((option, [expected]))
|
||||
|
||||
def set_expected(require,
|
||||
ret_requires):
|
||||
expected = require['expected']
|
||||
inverse = get_inverse(require)
|
||||
transitive = get_transitive(require)
|
||||
same_action = get_sameaction(require)
|
||||
operator = get_operator(require)
|
||||
if isinstance(expected, list):
|
||||
for exp in expected:
|
||||
if __debug__ and set(exp.keys()) != {'option', 'value'}:
|
||||
raise ValueError(_('malformed requirements expected must have '
|
||||
'option and value for option {0}').format(name))
|
||||
option = get_option(exp)
|
||||
if __debug__:
|
||||
try:
|
||||
option._validate(exp['value'], undefined)
|
||||
except ValueError as err:
|
||||
raise ValueError(_('malformed requirements expected value '
|
||||
'must be valid for option {0}'
|
||||
': {1}').format(name, err))
|
||||
_set_expected(action,
|
||||
inverse,
|
||||
transitive,
|
||||
same_action,
|
||||
option,
|
||||
exp['value'],
|
||||
operator)
|
||||
else:
|
||||
option = get_option(require)
|
||||
if __debug__ and not isinstance(option, tuple) and expected is not None:
|
||||
try:
|
||||
option._validate(expected, undefined)
|
||||
except ValueError as err:
|
||||
raise ValueError(_('malformed requirements expected value '
|
||||
'must be valid for option {0}'
|
||||
': {1}').format(name, err))
|
||||
_set_expected(action,
|
||||
inverse,
|
||||
transitive,
|
||||
same_action,
|
||||
option,
|
||||
expected,
|
||||
operator)
|
||||
|
||||
def get_action(require):
|
||||
action = require['action']
|
||||
if action == 'force_store_value':
|
||||
raise ValueError(_("malformed requirements for option: {0}"
|
||||
" action cannot be force_store_value"
|
||||
).format(name))
|
||||
return action
|
||||
|
||||
def get_inverse(require):
|
||||
inverse = require.get('inverse', False)
|
||||
if inverse not in [True, False]:
|
||||
raise ValueError(_('malformed requirements for option: {0}'
|
||||
' inverse must be boolean'))
|
||||
return inverse
|
||||
|
||||
def get_transitive(require):
|
||||
transitive = require.get('transitive', True)
|
||||
if transitive not in [True, False]:
|
||||
raise ValueError(_('malformed requirements for option: {0}'
|
||||
' transitive must be boolean'))
|
||||
return transitive
|
||||
|
||||
def get_sameaction(require):
|
||||
same_action = require.get('same_action', True)
|
||||
if same_action not in [True, False]:
|
||||
raise ValueError(_('malformed requirements for option: {0}'
|
||||
' same_action must be boolean'))
|
||||
return same_action
|
||||
|
||||
def get_operator(require):
|
||||
operator = require.get('operator', 'or')
|
||||
if operator not in ['and', 'or']:
|
||||
raise ValueError(_('malformed requirements for option: "{0}"'
|
||||
' operator must be "or" or "and"').format(operator))
|
||||
return operator
|
||||
|
||||
|
||||
ret_requires = {}
|
||||
config_action = set()
|
||||
|
||||
# start parsing all requires given by user (has dict)
|
||||
# transforme it to a tuple
|
||||
for require in requires:
|
||||
if __debug__:
|
||||
if not isinstance(require, dict):
|
||||
raise ValueError(_("malformed requirements type for option:"
|
||||
" {0}, must be a dict").format(name))
|
||||
valid_keys = ('option', 'expected', 'action', 'inverse', 'transitive',
|
||||
'same_action', 'operator', 'callback', 'callback_params')
|
||||
unknown_keys = frozenset(require.keys()) - frozenset(valid_keys)
|
||||
if unknown_keys != frozenset():
|
||||
raise ValueError(_('malformed requirements for option: {0}'
|
||||
' unknown keys {1}, must only '
|
||||
'{2}').format(name,
|
||||
unknown_keys,
|
||||
valid_keys))
|
||||
# {'expected': ..., 'option': ..., 'action': ...}
|
||||
# {'expected': [{'option': ..., 'value': ...}, ...}], 'action': ...}
|
||||
# {'expected': ..., 'callback': ..., 'action': ...}
|
||||
if not 'expected' in require or not 'action' in require or \
|
||||
not (isinstance(require['expected'], list) or \
|
||||
'option' in require or \
|
||||
'callback' in require):
|
||||
raise ValueError(_("malformed requirements for option: {0}"
|
||||
" require must have option, expected and"
|
||||
" action keys").format(name))
|
||||
action = get_action(require)
|
||||
config_action.add(action)
|
||||
if action not in ret_requires:
|
||||
ret_requires[action] = {}
|
||||
set_expected(require, ret_requires)
|
||||
|
||||
# transform dict to tuple
|
||||
ret = []
|
||||
for requires in ret_requires.values():
|
||||
ret_action = []
|
||||
for require in requires.values():
|
||||
ret_action.append((tuple(require[0]), require[1],
|
||||
require[2], require[3], require[4], require[5]))
|
||||
ret.append(tuple(ret_action))
|
||||
return frozenset(config_action), tuple(ret)
|
||||
|
@ -46,22 +46,3 @@ class BroadcastOption(Option):
|
||||
ip_address(value)
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
|
||||
def _cons_broadcast(self,
|
||||
current_opt,
|
||||
opts,
|
||||
vals,
|
||||
warnings_only,
|
||||
context):
|
||||
if len(vals) != 3:
|
||||
raise ConfigError(_('invalid broadcast consistency, a network and a netmask are needed'))
|
||||
if None in vals:
|
||||
return
|
||||
broadcast, network, netmask = vals
|
||||
if ip_network('{0}/{1}'.format(network, netmask)).broadcast_address != ip_address(broadcast):
|
||||
raise ValueError(_('broadcast "{4}" invalid with network {0}/{1} ("{2}"/"{3}")'
|
||||
'').format(network,
|
||||
netmask,
|
||||
opts[1].impl_get_display_name(),
|
||||
opts[2].impl_get_display_name(),
|
||||
broadcast))
|
||||
|
@ -23,7 +23,7 @@ from types import FunctionType
|
||||
from ..setting import undefined
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from ..autolib import carry_out_calculation
|
||||
from ..autolib import carry_out_calculation, Calculation
|
||||
from ..error import ConfigError, display_list
|
||||
|
||||
|
||||
@ -41,83 +41,44 @@ class ChoiceOption(Option):
|
||||
doc,
|
||||
values,
|
||||
default=None,
|
||||
values_params=None,
|
||||
default_multi=None,
|
||||
requires=None,
|
||||
multi=False,
|
||||
callback=None,
|
||||
callback_params=None,
|
||||
validator=None,
|
||||
validator_params=None,
|
||||
validators=None,
|
||||
properties=None,
|
||||
warnings_only=False):
|
||||
|
||||
"""
|
||||
:param values: is a list of values the option can possibly take
|
||||
"""
|
||||
if isinstance(values, FunctionType):
|
||||
values_params = self._build_calculator_params(values,
|
||||
values_params,
|
||||
'values')
|
||||
if values_params:
|
||||
self._choice_values_params = values_params
|
||||
else:
|
||||
if values_params is not None:
|
||||
raise ValueError(_('values is not a function, so values_params must be None'))
|
||||
if not isinstance(values, tuple):
|
||||
raise TypeError(_('values must be a tuple or a function for {0}'
|
||||
).format(name))
|
||||
if not isinstance(values, (Calculation, tuple)):
|
||||
raise TypeError(_('values must be a tuple or a calculation for {0}'
|
||||
).format(name))
|
||||
self._choice_values = values
|
||||
super(ChoiceOption, self).__init__(name,
|
||||
doc,
|
||||
default=default,
|
||||
default_multi=default_multi,
|
||||
callback=callback,
|
||||
callback_params=callback_params,
|
||||
requires=requires,
|
||||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
validators=validators,
|
||||
properties=properties,
|
||||
warnings_only=warnings_only)
|
||||
|
||||
def get_callback(self):
|
||||
values = self._choice_values
|
||||
if isinstance(values, FunctionType):
|
||||
return (values, getattr(self, '_choice_values_params', {}))
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
def impl_get_values(self,
|
||||
option_bag,
|
||||
current_opt=undefined):
|
||||
if current_opt is undefined:
|
||||
current_opt = self
|
||||
values, values_params = self.get_callback()
|
||||
if values is not None:
|
||||
if option_bag is undefined:
|
||||
values = undefined
|
||||
else:
|
||||
values = carry_out_calculation(current_opt,
|
||||
callback=values,
|
||||
callback_params=values_params,
|
||||
index=None,
|
||||
config_bag=option_bag.config_bag,
|
||||
fromconsistency=[])
|
||||
if values is not undefined and not isinstance(values, list):
|
||||
raise ConfigError(_('calculated values for {0} is not a list'
|
||||
'').format(self.impl_getname()))
|
||||
option_bag):
|
||||
if isinstance(self._choice_values, Calculation):
|
||||
values = self._choice_values.execute(option_bag)
|
||||
if values is not undefined and not isinstance(values, list):
|
||||
raise ConfigError(_('calculated values for {0} is not a list'
|
||||
'').format(self.impl_getname()))
|
||||
else:
|
||||
values = self._choice_values
|
||||
return values
|
||||
|
||||
|
||||
def _validate(self,
|
||||
value,
|
||||
option_bag,
|
||||
current_opt=undefined):
|
||||
values = self.impl_get_values(option_bag,
|
||||
current_opt=current_opt)
|
||||
values = self.impl_get_values(option_bag)
|
||||
if values is not undefined and value not in values:
|
||||
if len(values) == 1:
|
||||
raise ValueError(_('only "{0}" is allowed'
|
||||
|
@ -42,12 +42,8 @@ class DomainnameOption(IPOption):
|
||||
doc,
|
||||
default=None,
|
||||
default_multi=None,
|
||||
requires=None,
|
||||
multi: bool=False,
|
||||
callback=None,
|
||||
callback_params=None,
|
||||
validator=None,
|
||||
validator_params=None,
|
||||
validators=None,
|
||||
properties=None,
|
||||
allow_ip: bool=False,
|
||||
cidr: bool=False,
|
||||
@ -94,12 +90,8 @@ class DomainnameOption(IPOption):
|
||||
doc,
|
||||
default=default,
|
||||
default_multi=default_multi,
|
||||
callback=callback,
|
||||
callback_params=callback_params,
|
||||
requires=requires,
|
||||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
validators=validators,
|
||||
properties=properties,
|
||||
warnings_only=warnings_only,
|
||||
cidr=cidr,
|
||||
|
@ -41,13 +41,11 @@ class DynOptionDescription(OptionDescription):
|
||||
doc: str,
|
||||
children: List[BaseOption],
|
||||
suffixes: Calculation,
|
||||
requires=None,
|
||||
properties=None) -> None:
|
||||
|
||||
super().__init__(name,
|
||||
doc,
|
||||
children,
|
||||
requires,
|
||||
properties)
|
||||
# check children + set relation to this dynoptiondescription
|
||||
for child in children:
|
||||
|
@ -18,15 +18,14 @@
|
||||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
from ipaddress import ip_address, ip_interface, ip_network
|
||||
from ipaddress import ip_address, ip_interface
|
||||
|
||||
from ..error import ConfigError
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from .stroption import StrOption
|
||||
from .netmaskoption import NetmaskOption
|
||||
from .networkoption import NetworkOption
|
||||
from ..function import valid_ip_netmask
|
||||
|
||||
|
||||
class IPOption(StrOption):
|
||||
@ -40,12 +39,8 @@ class IPOption(StrOption):
|
||||
doc,
|
||||
default=None,
|
||||
default_multi=None,
|
||||
requires=None,
|
||||
multi=False,
|
||||
callback=None,
|
||||
callback_params=None,
|
||||
validator=None,
|
||||
validator_params=None,
|
||||
validators=None,
|
||||
properties=None,
|
||||
private_only=False,
|
||||
allow_reserved=False,
|
||||
@ -63,12 +58,8 @@ class IPOption(StrOption):
|
||||
doc,
|
||||
default=default,
|
||||
default_multi=default_multi,
|
||||
callback=callback,
|
||||
callback_params=callback_params,
|
||||
requires=requires,
|
||||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
validators=validators,
|
||||
properties=properties,
|
||||
warnings_only=warnings_only,
|
||||
extra=extra)
|
||||
@ -98,9 +89,11 @@ class IPOption(StrOption):
|
||||
if not cidr:
|
||||
ip_address(value)
|
||||
else:
|
||||
ip_interface(value)
|
||||
ip = ip_interface(value)
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
if cidr:
|
||||
valid_ip_netmask(str(ip.ip), str(ip.netmask))
|
||||
|
||||
def _second_level_validation(self,
|
||||
value,
|
||||
@ -118,68 +111,3 @@ class IPOption(StrOption):
|
||||
else:
|
||||
msg = _("must be private IP")
|
||||
raise ValueError(msg)
|
||||
if '/' in value:
|
||||
net = NetmaskOption(self.impl_getname(),
|
||||
self.impl_get_display_name(),
|
||||
str(ip.netmask))
|
||||
net._cons_ip_netmask(self,
|
||||
(net, self),
|
||||
(str(ip.netmask), str(ip.ip)),
|
||||
warnings_only,
|
||||
None,
|
||||
True)
|
||||
|
||||
def _cons_in_network(self,
|
||||
current_opt,
|
||||
opts,
|
||||
vals,
|
||||
warnings_only,
|
||||
context):
|
||||
if len(opts) == 2 and isinstance(opts[0], IPOption) and \
|
||||
opts[0].impl_get_extra('_cidr') == False and \
|
||||
isinstance(opts[1], NetworkOption) and \
|
||||
opts[1].impl_get_extra('_cidr') == True:
|
||||
if None in vals:
|
||||
return
|
||||
ip, network = vals
|
||||
network_obj = ip_network(network)
|
||||
if ip_interface(ip) not in network_obj:
|
||||
msg = _('IP not in network "{0}" ("{1}")')
|
||||
raise ValueError(msg.format(network,
|
||||
opts[1].impl_get_display_name()))
|
||||
# test if ip is not network/broadcast IP
|
||||
netmask = NetmaskOption(self.impl_getname(),
|
||||
self.impl_get_display_name(),
|
||||
str(network_obj.netmask))
|
||||
netmask._cons_ip_netmask(self,
|
||||
(netmask, self),
|
||||
(str(network_obj.netmask), str(ip)),
|
||||
warnings_only,
|
||||
None,
|
||||
True)
|
||||
else:
|
||||
if len(vals) != 3 and context is undefined:
|
||||
raise ConfigError(_('ip_network needs an IP, a network and a netmask'))
|
||||
if len(vals) != 3 or None in vals:
|
||||
return
|
||||
ip, network, netmask = vals
|
||||
if ip_interface(ip) not in ip_network('{0}/{1}'.format(network,
|
||||
netmask)):
|
||||
if current_opt == opts[0]:
|
||||
msg = _('IP not in network "{2}"/"{4}" ("{3}"/"{5}")')
|
||||
elif current_opt == opts[1]:
|
||||
msg = _('the network doest not match with IP "{0}" ("{1}") and network "{4}" ("{5}")')
|
||||
else:
|
||||
msg = _('the netmask does not match with IP "{0}" ("{1}") and broadcast "{2}" ("{3}")')
|
||||
raise ValueError(msg.format(ip,
|
||||
opts[0].impl_get_display_name(),
|
||||
network,
|
||||
opts[1].impl_get_display_name(),
|
||||
netmask,
|
||||
opts[2].impl_get_display_name()))
|
||||
# test if ip is not network/broadcast IP
|
||||
opts[2]._cons_ip_netmask(current_opt,
|
||||
(opts[2], opts[0]),
|
||||
(netmask, ip),
|
||||
warnings_only,
|
||||
context)
|
||||
|
@ -25,13 +25,13 @@ from typing import List, Iterator, Optional, Any
|
||||
|
||||
|
||||
from ..i18n import _
|
||||
from ..setting import groups, undefined, OptionBag, Settings
|
||||
from ..setting import groups, undefined, OptionBag, Settings, ALLOWED_LEADER_PROPERTIES
|
||||
from ..value import Values
|
||||
from .optiondescription import OptionDescription
|
||||
from .syndynoptiondescription import SynDynLeadership
|
||||
from .baseoption import BaseOption
|
||||
from .option import Option
|
||||
from ..error import RequirementError
|
||||
from ..error import LeadershipError
|
||||
from ..autolib import Calculation, ParamOption
|
||||
|
||||
|
||||
@ -43,12 +43,10 @@ class Leadership(OptionDescription):
|
||||
name: str,
|
||||
doc: str,
|
||||
children: List[BaseOption],
|
||||
requires=None,
|
||||
properties=None) -> None:
|
||||
super().__init__(name,
|
||||
doc,
|
||||
children,
|
||||
requires=requires,
|
||||
properties=properties)
|
||||
self._group_type = groups.leadership
|
||||
followers = []
|
||||
@ -93,28 +91,9 @@ class Leadership(OptionDescription):
|
||||
raise ValueError(_("callback of leader's option shall "
|
||||
"not refered to a follower's ones"))
|
||||
|
||||
# leader should not have requires, only Leadership should have
|
||||
# so move requires to Leadership
|
||||
# if Leadership has requires too, cannot manage this move so raises
|
||||
leader_requires = getattr(leader, '_requires', None)
|
||||
if leader_requires:
|
||||
if __debug__ and self.impl_getrequires():
|
||||
raise RequirementError(_('leader {} have requirement, but Leadership {} too'
|
||||
'').format(leader.impl_getname(),
|
||||
self.impl_getname()))
|
||||
leader_calproperties = getattr(leader, '_requires', None)
|
||||
if leader_calproperties:
|
||||
setattr(self, '_requires', leader_requires)
|
||||
delattr(leader, '_requires')
|
||||
if __debug__:
|
||||
for requires_ in getattr(self, '_requires', ()):
|
||||
for require in requires_:
|
||||
for require_opt, values in require[0]:
|
||||
if not isinstance(require_opt, tuple) and require_opt.impl_is_multi() and require_opt.impl_get_leadership():
|
||||
raise ValueError(_('malformed requirements option "{0}" '
|
||||
'must not be in follower for "{1}"').format(
|
||||
require_opt.impl_getname(),
|
||||
self.impl_getname()))
|
||||
for prop in leader.impl_getproperties():
|
||||
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
|
||||
raise LeadershipError(_('leader cannot have "{}" property').format(prop))
|
||||
|
||||
def is_leader(self,
|
||||
opt: Option) -> bool:
|
||||
|
@ -49,58 +49,3 @@ class NetmaskOption(StrOption):
|
||||
ip_network('0.0.0.0/{0}'.format(value))
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
|
||||
def _cons_network_netmask(self,
|
||||
current_opt: Option,
|
||||
opts: List[Option],
|
||||
vals: List[str],
|
||||
warnings_only: bool,
|
||||
context: 'Config'):
|
||||
if context is undefined and len(vals) != 2:
|
||||
raise ConfigError(_('network_netmask needs a network and a netmask'))
|
||||
if None in vals or len(vals) != 2:
|
||||
return
|
||||
val_netmask, val_network = vals
|
||||
opt_netmask, opt_network = opts
|
||||
try:
|
||||
ip_network('{0}/{1}'.format(val_network, val_netmask))
|
||||
except ValueError:
|
||||
if current_opt == opt_network:
|
||||
raise ValueError(_('the netmask "{0}" ("{1}") does not match').format(val_netmask,
|
||||
opt_netmask.impl_get_display_name()))
|
||||
else:
|
||||
raise ValueError(_('the network "{0}" ("{1}") does not match').format(val_network,
|
||||
opt_network.impl_get_display_name()))
|
||||
|
||||
def _cons_ip_netmask(self,
|
||||
current_opt: Option,
|
||||
opts: List[Option],
|
||||
vals: List[str],
|
||||
warnings_only: bool,
|
||||
context: 'config',
|
||||
_cidr: bool=False):
|
||||
if context is undefined and len(vals) != 2:
|
||||
raise ConfigError(_('ip_netmask needs an IP and a netmask'))
|
||||
if None in vals or len(vals) != 2:
|
||||
return
|
||||
val_netmask, val_ip = vals
|
||||
opt_netmask, opt_ip = opts
|
||||
ip = ip_interface('{0}/{1}'.format(val_ip, val_netmask))
|
||||
if not _cidr and current_opt == opt_ip:
|
||||
if ip.ip == ip.network.network_address:
|
||||
raise ValueError( _('this is a network with netmask "{0}" ("{1}")'
|
||||
'').format(val_netmask,
|
||||
opt_netmask.impl_get_display_name()))
|
||||
elif ip.ip == ip.network.broadcast_address:
|
||||
raise ValueError(_('this is a broadcast with netmask "{0}" ("{1}")'
|
||||
'').format(val_netmask,
|
||||
opt_netmask.impl_get_display_name()))
|
||||
else:
|
||||
if ip.ip == ip.network.network_address:
|
||||
raise ValueError(_('IP "{0}" ("{1}") is the network'
|
||||
'').format(val_ip,
|
||||
opt_ip.impl_get_display_name()))
|
||||
elif ip.ip == ip.network.broadcast_address:
|
||||
raise ValueError(_('IP "{0}" ("{1}") is the broadcast'
|
||||
'').format(val_ip,
|
||||
opt_ip.impl_get_display_name()))
|
||||
|
@ -36,12 +36,8 @@ class NetworkOption(Option):
|
||||
doc,
|
||||
default=None,
|
||||
default_multi=None,
|
||||
requires=None,
|
||||
multi=False,
|
||||
callback=None,
|
||||
callback_params=None,
|
||||
validator=None,
|
||||
validator_params=None,
|
||||
validators=None,
|
||||
properties=None,
|
||||
warnings_only=False,
|
||||
cidr=False):
|
||||
@ -50,12 +46,8 @@ class NetworkOption(Option):
|
||||
doc,
|
||||
default=default,
|
||||
default_multi=default_multi,
|
||||
callback=callback,
|
||||
callback_params=callback_params,
|
||||
requires=requires,
|
||||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
validators=validators,
|
||||
properties=properties,
|
||||
warnings_only=warnings_only,
|
||||
extra=extra)
|
||||
|
@ -27,11 +27,11 @@ from itertools import chain
|
||||
from .baseoption import BaseOption, submulti, STATIC_TUPLE
|
||||
from ..i18n import _
|
||||
from ..setting import undefined, OptionBag, Undefined
|
||||
from ..autolib import Calculation, carry_out_calculation, Params, ParamValue, ParamContext, ParamOption
|
||||
from ..autolib import Calculation, Params, ParamValue, ParamContext, ParamOption
|
||||
from ..error import (ConfigError, ValueWarning, ValueErrorWarning, PropertiesOptionError,
|
||||
ValueOptionError, display_list)
|
||||
from .syndynoption import SynDynOption
|
||||
ALLOWED_CONST_LIST = ['_cons_not_equal']
|
||||
#ALLOWED_CONST_LIST = ['_cons_not_equal']
|
||||
|
||||
|
||||
class Option(BaseOption):
|
||||
@ -43,14 +43,14 @@ class Option(BaseOption):
|
||||
__slots__ = ('_extra',
|
||||
'_warnings_only',
|
||||
'_allow_empty_list',
|
||||
#multi
|
||||
# multi
|
||||
'_multi',
|
||||
'_unique',
|
||||
#value
|
||||
# value
|
||||
'_default',
|
||||
'_default_multi',
|
||||
#calcul
|
||||
'_val_call',
|
||||
#
|
||||
'_validators',
|
||||
#
|
||||
'_leadership',
|
||||
'_choice_values',
|
||||
@ -62,13 +62,9 @@ class Option(BaseOption):
|
||||
doc: str,
|
||||
default: Any=undefined,
|
||||
default_multi: Any=None,
|
||||
requires: List[Dict]=None,
|
||||
multi: bool=False,
|
||||
unique: bool=undefined,
|
||||
callback: Optional[Callable]=None,
|
||||
callback_params: Optional[Params]=None,
|
||||
validator: Optional[Callable]=None,
|
||||
validator_params: Optional[Params]=None,
|
||||
validators: Optional[List[Calculation]]=None,
|
||||
properties: Optional[List[str]]=None,
|
||||
warnings_only: bool=False,
|
||||
extra: Optional[Dict]=None,
|
||||
@ -99,19 +95,23 @@ class Option(BaseOption):
|
||||
default = []
|
||||
super().__init__(name,
|
||||
doc,
|
||||
requires=requires,
|
||||
properties=properties,
|
||||
is_multi=is_multi)
|
||||
if validator is not None:
|
||||
validator_params = self._build_calculator_params(validator,
|
||||
validator_params,
|
||||
'validator',
|
||||
add_value=True)
|
||||
if not validator_params:
|
||||
val_call = (validator,)
|
||||
else:
|
||||
val_call = (validator, validator_params)
|
||||
self._val_call = (val_call, None)
|
||||
if __debug__:
|
||||
if validators is not None:
|
||||
if not isinstance(validators, list):
|
||||
raise ValueError(_('validators must be a list of Calculation for "{}"').format(name))
|
||||
for validator in validators:
|
||||
if not isinstance(validator, Calculation):
|
||||
raise ValueError(_('validators must be a Calculation for "{}"').format(name))
|
||||
for param in chain(validator.params.args, validator.params.kwargs.values()):
|
||||
if isinstance(param, ParamContext):
|
||||
self._has_calc_context = True
|
||||
elif isinstance(param, ParamOption):
|
||||
param.option._add_dependency(self)
|
||||
self._has_dependency = True
|
||||
|
||||
self._validators = tuple(validators)
|
||||
if extra is not None and extra != {}:
|
||||
_setattr(self, '_extra', extra)
|
||||
if unique != undefined and not isinstance(unique, bool):
|
||||
@ -126,9 +126,14 @@ class Option(BaseOption):
|
||||
def test_multi_value(value):
|
||||
if isinstance(value, Calculation):
|
||||
return
|
||||
option_bag = OptionBag()
|
||||
option_bag.set_option(self,
|
||||
undefined,
|
||||
None,
|
||||
undefined)
|
||||
try:
|
||||
self._validate(value,
|
||||
undefined)
|
||||
option_bag)
|
||||
except ValueError as err:
|
||||
str_err = str(err)
|
||||
if not str_err:
|
||||
@ -168,9 +173,6 @@ class Option(BaseOption):
|
||||
default = tuple(default)
|
||||
_setattr(self, '_default', default)
|
||||
|
||||
self._impl_set_callback(callback,
|
||||
callback_params)
|
||||
|
||||
def value_dependencies(self,
|
||||
value: Any) -> Any:
|
||||
if isinstance(value, list):
|
||||
@ -250,16 +252,6 @@ class Option(BaseOption):
|
||||
#__________________________________________________________________________
|
||||
# validator
|
||||
|
||||
def impl_get_validator(self) -> Tuple[Callable, Params]:
|
||||
val = getattr(self, '_val_call', (None,))[0]
|
||||
if val is None:
|
||||
ret_val = (None, None)
|
||||
elif len(val) == 1:
|
||||
ret_val = (val[0], None)
|
||||
else:
|
||||
ret_val = val
|
||||
return ret_val
|
||||
|
||||
def impl_validate(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag,
|
||||
@ -272,13 +264,9 @@ class Option(BaseOption):
|
||||
|
||||
if check_error and config_bag is not undefined and \
|
||||
not 'validator' in config_bag.properties:
|
||||
# just to check propertieserror
|
||||
self.valid_consistency(option_bag,
|
||||
value,
|
||||
check_error,
|
||||
is_warnings_only)
|
||||
return
|
||||
|
||||
|
||||
def _is_not_unique(value):
|
||||
# if set(value) has not same length than value
|
||||
if check_error and self.impl_is_unique() and \
|
||||
@ -291,26 +279,33 @@ class Option(BaseOption):
|
||||
|
||||
def calculation_validator(val,
|
||||
_index):
|
||||
validator, validator_params = self.impl_get_validator()
|
||||
if validator is not None:
|
||||
#inject value in calculation
|
||||
if validator_params is None:
|
||||
args = []
|
||||
kwargs = None
|
||||
else:
|
||||
args = list(validator_params.args)
|
||||
kwargs = validator_params.kwargs
|
||||
args.insert(0, ParamValue(val))
|
||||
validator_params_ = Params(tuple(args), kwargs)
|
||||
# Raise ValueError if not valid
|
||||
carry_out_calculation(option_bag.ori_option,
|
||||
callback=validator,
|
||||
callback_params=validator_params_,
|
||||
index=_index,
|
||||
config_bag=option_bag.config_bag,
|
||||
fromconsistency=option_bag.fromconsistency,
|
||||
orig_value=value,
|
||||
is_validator=True)
|
||||
for validator in getattr(self, '_validators', []):
|
||||
calc_is_warnings_only = hasattr(validator, 'warnings_only') and validator.warnings_only
|
||||
if ((check_error and not calc_is_warnings_only) or
|
||||
(not check_error and calc_is_warnings_only)):
|
||||
try:
|
||||
kwargs = {'allow_raises': True}
|
||||
if _index is not None and option_bag.index == _index:
|
||||
soption_bag = option_bag
|
||||
else:
|
||||
soption_bag = option_bag.copy()
|
||||
soption_bag.index = _index
|
||||
kwargs['orig_value'] = value
|
||||
|
||||
validator.execute(soption_bag,
|
||||
leadership_must_have_index=True,
|
||||
**kwargs)
|
||||
except ValueError as err:
|
||||
if calc_is_warnings_only:
|
||||
warnings.warn_explicit(ValueWarning(val,
|
||||
self._display_name,
|
||||
self,
|
||||
'{0}'.format(err),
|
||||
_index),
|
||||
ValueWarning,
|
||||
self.__class__.__name__, 0)
|
||||
else:
|
||||
raise err
|
||||
|
||||
def do_validation(_value,
|
||||
_index):
|
||||
@ -328,8 +323,6 @@ class Option(BaseOption):
|
||||
if ((check_error and not is_warnings_only) or
|
||||
(not check_error and is_warnings_only)):
|
||||
try:
|
||||
calculation_validator(_value,
|
||||
_index)
|
||||
self._second_level_validation(_value,
|
||||
is_warnings_only)
|
||||
except ValueError as err:
|
||||
@ -343,6 +336,8 @@ class Option(BaseOption):
|
||||
self.__class__.__name__, 0)
|
||||
else:
|
||||
raise err
|
||||
calculation_validator(_value,
|
||||
_index)
|
||||
try:
|
||||
val = value
|
||||
err_index = force_index
|
||||
@ -379,12 +374,6 @@ class Option(BaseOption):
|
||||
for err_index, val in enumerate(value):
|
||||
do_validation(val,
|
||||
err_index)
|
||||
|
||||
if (not is_warnings_only or not check_error) and not isinstance(value, Calculation):
|
||||
self.valid_consistency(option_bag,
|
||||
value,
|
||||
check_error,
|
||||
is_warnings_only)
|
||||
except ValueError as err:
|
||||
if config_bag is undefined or \
|
||||
'demoting_error_warning' not in config_bag.properties:
|
||||
@ -419,11 +408,6 @@ class Option(BaseOption):
|
||||
warnings_only: bool) -> None:
|
||||
pass
|
||||
|
||||
#__________________________________________________________________________
|
||||
# leadership
|
||||
# def impl_is_leadership(self):
|
||||
# return self.impl_get_leadership() is not None
|
||||
|
||||
def impl_is_leader(self):
|
||||
leadership = self.impl_get_leadership()
|
||||
if leadership is None:
|
||||
@ -442,327 +426,6 @@ class Option(BaseOption):
|
||||
return leadership
|
||||
return leadership()
|
||||
|
||||
#____________________________________________________________
|
||||
# consistencies
|
||||
|
||||
def impl_add_consistency(self,
|
||||
func: str,
|
||||
*other_opts,
|
||||
**params) -> None:
|
||||
"""Add consistency means that value will be validate with other_opts
|
||||
option's values.
|
||||
|
||||
:param func: function's name
|
||||
:type func: `str`
|
||||
:param other_opts: options used to validate value
|
||||
:type other_opts: `list` of `tiramisu.option.Option`
|
||||
:param params: extra params (warnings_only and transitive are allowed)
|
||||
"""
|
||||
if self.impl_is_readonly():
|
||||
raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is"
|
||||
" read-only").format(
|
||||
self.__class__.__name__,
|
||||
self.impl_getname()))
|
||||
self._valid_consistencies(other_opts,
|
||||
func=func)
|
||||
func = '_cons_{0}'.format(func)
|
||||
if func not in dir(self):
|
||||
raise ConfigError(_('consistency {0} not available for this option').format(func))
|
||||
options = [weakref.ref(self)]
|
||||
for option in other_opts:
|
||||
options.append(weakref.ref(option))
|
||||
all_cons_opts = tuple(options)
|
||||
unknown_params = set(params.keys()) - set(['warnings_only', 'transitive'])
|
||||
if unknown_params != set():
|
||||
raise ValueError(_('unknown parameter {0} in consistency').format(unknown_params))
|
||||
self._add_consistency(func,
|
||||
all_cons_opts,
|
||||
params)
|
||||
#validate default value when add consistency
|
||||
option_bag = OptionBag()
|
||||
option_bag.set_option(self,
|
||||
undefined,
|
||||
None,
|
||||
undefined)
|
||||
default = self.impl_getdefault()
|
||||
if isinstance(default, tuple):
|
||||
default = list(default)
|
||||
self.impl_validate(default,
|
||||
option_bag)
|
||||
self.impl_validate(default,
|
||||
option_bag,
|
||||
check_error=False)
|
||||
if func != '_cons_not_equal':
|
||||
#consistency could generate warnings or errors
|
||||
self._has_dependency = True
|
||||
for wopt in all_cons_opts:
|
||||
opt = wopt()
|
||||
if func in ALLOWED_CONST_LIST:
|
||||
if getattr(opt, '_unique', undefined) == undefined:
|
||||
opt._unique = True
|
||||
if opt != self:
|
||||
self._add_dependency(opt)
|
||||
opt._add_dependency(self)
|
||||
|
||||
def _add_consistency(self,
|
||||
func: str,
|
||||
all_cons_opts: List[BaseOption],
|
||||
params: Dict) -> None:
|
||||
cons = (-1, func, all_cons_opts, params)
|
||||
consistencies = getattr(self, '_consistencies', None)
|
||||
if consistencies is None:
|
||||
self._consistencies = [cons]
|
||||
else:
|
||||
consistencies.append(cons)
|
||||
|
||||
def get_consistencies(self):
|
||||
return getattr(self, '_consistencies', STATIC_TUPLE)
|
||||
|
||||
def has_consistencies(self, context) -> bool:
|
||||
descr = context.cfgimpl_get_description()
|
||||
if getattr(descr, '_cache_consistencies', None) is None:
|
||||
return False
|
||||
return self in descr._cache_consistencies
|
||||
|
||||
def valid_consistency(self,
|
||||
option_bag: OptionBag,
|
||||
value: Any,
|
||||
check_error: bool,
|
||||
option_warnings_only: bool) -> None:
|
||||
if option_bag.config_bag is not undefined:
|
||||
descr = option_bag.config_bag.context.cfgimpl_get_description()
|
||||
# no consistency found at all
|
||||
if getattr(descr, '_cache_consistencies', None) is None:
|
||||
return
|
||||
# get consistencies for this option
|
||||
consistencies = descr._cache_consistencies.get(option_bag.option)
|
||||
else:
|
||||
# is no context, get consistencies in option
|
||||
consistencies = option_bag.option.get_consistencies()
|
||||
if consistencies:
|
||||
if option_bag.config_bag is undefined:
|
||||
coption_bag = option_bag.copy()
|
||||
else:
|
||||
cconfig_bag = option_bag.config_bag.copy()
|
||||
cconfig_bag.remove_warnings()
|
||||
cconfig_bag.set_permissive()
|
||||
coption_bag = option_bag.copy()
|
||||
coption_bag.config_bag = cconfig_bag
|
||||
if not option_bag.fromconsistency:
|
||||
fromconsistency_is_empty = True
|
||||
option_bag.fromconsistency = [cons_id for cons_id, f, a, p in consistencies]
|
||||
else:
|
||||
fromconsistency_is_empty = False
|
||||
for cons_id, func, all_cons_opts, params in consistencies:
|
||||
if not fromconsistency_is_empty and cons_id in option_bag.fromconsistency:
|
||||
continue
|
||||
warnings_only = option_warnings_only or params.get('warnings_only', False)
|
||||
if (warnings_only and not check_error) or (not warnings_only and check_error):
|
||||
transitive = params.get('transitive', True)
|
||||
#all_cons_opts[0] is the option where func is set
|
||||
if option_bag.ori_option.impl_is_dynsymlinkoption():
|
||||
opts = []
|
||||
for opt in all_cons_opts:
|
||||
opts.append(opt().to_dynoption(option_bag.ori_option.rootpath,
|
||||
option_bag.ori_option.suffix))
|
||||
wopt = opts[0]
|
||||
else:
|
||||
opts = all_cons_opts
|
||||
wopt = opts[0]()
|
||||
wopt.launch_consistency(self,
|
||||
func,
|
||||
cons_id,
|
||||
coption_bag,
|
||||
value,
|
||||
opts,
|
||||
warnings_only,
|
||||
transitive)
|
||||
if fromconsistency_is_empty:
|
||||
option_bag.fromconsistency = []
|
||||
|
||||
def _valid_consistencies(self,
|
||||
other_opts: List[BaseOption],
|
||||
init: bool=True,
|
||||
func: Optional[str]=None) -> None:
|
||||
if self.issubdyn():
|
||||
dynod = self.getsubdyn()
|
||||
else:
|
||||
dynod = None
|
||||
if self.impl_is_submulti():
|
||||
raise ConfigError(_('cannot add consistency with submulti option'))
|
||||
is_multi = self.impl_is_multi()
|
||||
for opt in other_opts:
|
||||
if isinstance(opt, weakref.ReferenceType):
|
||||
opt = opt()
|
||||
assert not opt.impl_is_submulti(), _('cannot add consistency with submulti option')
|
||||
assert isinstance(opt, Option), _('consistency must be set with an option, not {}').format(opt)
|
||||
if opt.issubdyn():
|
||||
if dynod is None:
|
||||
raise ConfigError(_('almost one option in consistency is '
|
||||
'in a dynoptiondescription but not all'))
|
||||
subod = opt.getsubdyn()
|
||||
if dynod != subod:
|
||||
raise ConfigError(_('option in consistency must be in same'
|
||||
' dynoptiondescription'))
|
||||
dynod = subod
|
||||
elif dynod is not None:
|
||||
raise ConfigError(_('almost one option in consistency is in a '
|
||||
'dynoptiondescription but not all'))
|
||||
if self is opt:
|
||||
raise ConfigError(_('cannot add consistency with itself'))
|
||||
if is_multi != opt.impl_is_multi():
|
||||
raise ConfigError(_('every options in consistency must be '
|
||||
'multi or none'))
|
||||
# FIXME
|
||||
if init and func != 'not_equal':
|
||||
opt._has_dependency = True
|
||||
|
||||
def launch_consistency(self,
|
||||
current_opt: BaseOption,
|
||||
func: Callable,
|
||||
cons_id: int,
|
||||
option_bag: OptionBag,
|
||||
value: Any,
|
||||
opts: List[BaseOption],
|
||||
warnings_only: bool,
|
||||
transitive: bool) -> None:
|
||||
"""Launch consistency now
|
||||
"""
|
||||
all_cons_vals = []
|
||||
all_cons_opts = []
|
||||
length = None
|
||||
for opt in opts:
|
||||
if isinstance(opt, weakref.ReferenceType):
|
||||
opt = opt()
|
||||
try:
|
||||
opt_value = self.get_consistency_value(option_bag,
|
||||
opt,
|
||||
cons_id,
|
||||
value,
|
||||
func)
|
||||
except PropertiesOptionError as err:
|
||||
if transitive:
|
||||
err.set_orig_opt(option_bag.option)
|
||||
raise err
|
||||
else:
|
||||
if opt.impl_is_multi() and option_bag.index is None and \
|
||||
func not in ALLOWED_CONST_LIST:
|
||||
len_value = len(opt_value)
|
||||
if length is not None and length != len_value:
|
||||
if option_bag.config_bag is undefined:
|
||||
return
|
||||
raise ValueError(_('unexpected length of "{}" in constency "{}", '
|
||||
'should be "{}"').format(len(opt_value),
|
||||
opt.impl_get_display_name(),
|
||||
length)) # pragma: no cover
|
||||
length = len_value
|
||||
if isinstance(opt_value, list) and func in ALLOWED_CONST_LIST:
|
||||
for value_ in opt_value:
|
||||
all_cons_vals.append(value_)
|
||||
all_cons_opts.append(opt)
|
||||
else:
|
||||
all_cons_vals.append(opt_value)
|
||||
all_cons_opts.append(opt)
|
||||
if option_bag.config_bag is not undefined and \
|
||||
not 'validator' in option_bag.config_bag.properties:
|
||||
return
|
||||
all_values = []
|
||||
if length is None:
|
||||
all_values = [all_cons_vals]
|
||||
elif length:
|
||||
all_values = zip(*all_cons_vals)
|
||||
try:
|
||||
context = option_bag.config_bag if option_bag.config_bag is undefined else option_bag.config_bag.context
|
||||
for values in all_values:
|
||||
getattr(self, func)(current_opt,
|
||||
all_cons_opts,
|
||||
values,
|
||||
warnings_only,
|
||||
context)
|
||||
except ValueError as err:
|
||||
if warnings_only:
|
||||
warnings.warn_explicit(ValueWarning(value,
|
||||
self._display_name,
|
||||
current_opt,
|
||||
"{}".format(err),
|
||||
option_bag.index),
|
||||
ValueWarning,
|
||||
self.__class__.__name__, 0)
|
||||
else:
|
||||
raise err
|
||||
|
||||
def get_consistency_value(self,
|
||||
option_bag: OptionBag,
|
||||
current_option: BaseOption,
|
||||
cons_id: int,
|
||||
value: Any,
|
||||
func: str) -> Any:
|
||||
if option_bag.ori_option == current_option:
|
||||
# orig_option is current option
|
||||
# we have already value, so use it
|
||||
return value
|
||||
if option_bag.config_bag is undefined:
|
||||
#if no context get default value
|
||||
return current_option.impl_getdefault()
|
||||
if func in ALLOWED_CONST_LIST:
|
||||
index = None
|
||||
index_ = None
|
||||
elif current_option.impl_is_leader():
|
||||
index = option_bag.index
|
||||
index_ = None
|
||||
else:
|
||||
index = option_bag.index
|
||||
index_ = index
|
||||
#otherwise calculate value
|
||||
path = current_option.impl_getpath()
|
||||
coption_bag = OptionBag()
|
||||
coption_bag.set_option(current_option,
|
||||
path,
|
||||
index_,
|
||||
option_bag.config_bag)
|
||||
fromconsistency = option_bag.fromconsistency.copy()
|
||||
fromconsistency.append(cons_id)
|
||||
coption_bag.fromconsistency = fromconsistency
|
||||
current_value = option_bag.config_bag.context.getattr(path,
|
||||
coption_bag)
|
||||
if index_ is None and index is not None:
|
||||
#if self is a follower and current_option is a leader and func not in ALLOWED_CONST_LIST
|
||||
#return only the value of the leader for isolate follower
|
||||
current_value = current_value[index]
|
||||
return current_value
|
||||
|
||||
def _cons_not_equal(self,
|
||||
current_opt: BaseOption,
|
||||
opts: List[BaseOption],
|
||||
vals: List[Any],
|
||||
warnings_only: bool,
|
||||
context) -> None:
|
||||
equal = []
|
||||
is_current = False
|
||||
for idx_inf, val_inf in enumerate(vals):
|
||||
for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
|
||||
if val_inf == val_sup is not None:
|
||||
for opt_ in [opts[idx_inf], opts[idx_inf + idx_sup + 1]]:
|
||||
if opt_ == current_opt:
|
||||
is_current = True
|
||||
elif opt_ not in equal:
|
||||
equal.append(opt_)
|
||||
if equal:
|
||||
if is_current:
|
||||
if warnings_only:
|
||||
msg = _('should be different from the value of {}')
|
||||
else:
|
||||
msg = _('must be different from the value of {}')
|
||||
else:
|
||||
if warnings_only:
|
||||
msg = _('value for {} should be different')
|
||||
else:
|
||||
msg = _('value for {} must be different')
|
||||
equal_name = []
|
||||
for opt in equal:
|
||||
equal_name.append(opt.impl_get_display_name())
|
||||
raise ValueError(msg.format(display_list(list(equal_name), add_quote=True)))
|
||||
|
||||
def to_dynoption(self,
|
||||
rootpath: str,
|
||||
suffix: str) -> SynDynOption:
|
||||
|
@ -25,14 +25,12 @@ from typing import Optional, Iterator, Union, List
|
||||
from ..i18n import _
|
||||
from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined
|
||||
from .baseoption import BaseOption
|
||||
from .option import ALLOWED_CONST_LIST
|
||||
from .syndynoptiondescription import SynDynOptionDescription, SynDynLeadership
|
||||
from ..error import ConfigError, ConflictError
|
||||
|
||||
|
||||
class CacheOptionDescription(BaseOption):
|
||||
__slots__ = ('_cache_consistencies',
|
||||
'_cache_force_store_values')
|
||||
__slots__ = ('_cache_force_store_values',)
|
||||
|
||||
def impl_already_build_caches(self) -> bool:
|
||||
return self.impl_is_readonly()
|
||||
@ -101,60 +99,10 @@ class CacheOptionDescription(BaseOption):
|
||||
'"force_metaconfig_on_freeze" '
|
||||
'property without "frozen"'
|
||||
'').format(option.impl_get_display_name()))
|
||||
for cons_id, func, all_cons_opts, params in option.get_consistencies():
|
||||
option._valid_consistencies(all_cons_opts[1:], init=False)
|
||||
if func not in ALLOWED_CONST_LIST and is_multi:
|
||||
if __debug__ and not option.impl_get_leadership():
|
||||
raise ConfigError(_('malformed consistency option "{0}" '
|
||||
'must be in same leadership').format(
|
||||
option.impl_getname()))
|
||||
leadership = option.impl_get_leadership()
|
||||
for weak_opt in all_cons_opts:
|
||||
opt = weak_opt()
|
||||
if __debug__ and func not in ALLOWED_CONST_LIST and is_multi:
|
||||
if not opt.impl_get_leadership():
|
||||
raise ConfigError(_('malformed consistency option "{0}" '
|
||||
'must not be a multi for "{1}"').format(
|
||||
option.impl_getname(), opt.impl_getname()))
|
||||
elif leadership != opt.impl_get_leadership():
|
||||
raise ConfigError(_('malformed consistency option "{0}" '
|
||||
'must be in same leadership as "{1}"').format(
|
||||
option.impl_getname(), opt.impl_getname()))
|
||||
_consistencies.setdefault(weak_opt,
|
||||
[]).append((_consistencies_id,
|
||||
func,
|
||||
all_cons_opts,
|
||||
params))
|
||||
_consistencies_id += 1
|
||||
# if context is set to callback, must be reset each time a value change
|
||||
if hasattr(option, '_has_calc_context'):
|
||||
self._add_dependency(option)
|
||||
|
||||
if __debug__:
|
||||
is_follower = None
|
||||
if is_multi:
|
||||
all_requires = option.impl_getrequires()
|
||||
for requires in all_requires:
|
||||
for require in requires:
|
||||
#if option in require is a multi:
|
||||
# * option in require must be a leader or a follower
|
||||
# * current option must be a follower (and only a follower)
|
||||
# * option in require and current option must be in same leadership
|
||||
for require_opt, values in require[0]:
|
||||
if not isinstance(require_opt, tuple) and require_opt.impl_is_multi():
|
||||
if is_follower is None:
|
||||
is_follower = option.impl_is_follower()
|
||||
if is_follower:
|
||||
leadership = option.impl_get_leadership()
|
||||
if is_follower and require_opt.impl_get_leadership():
|
||||
if leadership != require_opt.impl_get_leadership():
|
||||
raise ValueError(_('malformed requirements option "{0}" '
|
||||
'must be in same leadership for "{1}"').format(
|
||||
require_opt.impl_getname(), option.impl_getname()))
|
||||
else:
|
||||
raise ValueError(_('malformed requirements option "{0}" '
|
||||
'must not be a multi for "{1}"').format(
|
||||
require_opt.impl_getname(), option.impl_getname()))
|
||||
if option.impl_is_readonly():
|
||||
raise ConflictError(_('duplicate option: {0}').format(option))
|
||||
if not self.impl_is_readonly() and display_name:
|
||||
@ -162,15 +110,6 @@ class CacheOptionDescription(BaseOption):
|
||||
option._path = subpath
|
||||
option._set_readonly()
|
||||
if init:
|
||||
if _consistencies != {}:
|
||||
self._cache_consistencies = {}
|
||||
for weak_opt, cons in _consistencies.items():
|
||||
opt = weak_opt()
|
||||
if __debug__ and opt not in cache_option:
|
||||
raise ConfigError(_('consistency with option {0} '
|
||||
'which is not in Config').format(
|
||||
opt.impl_getname()))
|
||||
self._cache_consistencies[opt] = tuple(cons)
|
||||
self._cache_force_store_values = force_store_values
|
||||
self._path = self._name
|
||||
self._set_readonly()
|
||||
@ -275,7 +214,6 @@ class OptionDescription(OptionDescriptionWalk):
|
||||
name: str,
|
||||
doc: str,
|
||||
children: List[BaseOption],
|
||||
requires=None,
|
||||
properties=None) -> None:
|
||||
"""
|
||||
:param children: a list of options (including optiondescriptions)
|
||||
@ -285,7 +223,6 @@ class OptionDescription(OptionDescriptionWalk):
|
||||
'must be a list').format(name)
|
||||
super().__init__(name,
|
||||
doc=doc,
|
||||
requires=requires,
|
||||
properties=properties)
|
||||
child_names = []
|
||||
if __debug__:
|
||||
|
@ -48,12 +48,8 @@ class PortOption(StrOption):
|
||||
doc,
|
||||
default=None,
|
||||
default_multi=None,
|
||||
requires=None,
|
||||
multi=False,
|
||||
callback=None,
|
||||
callback_params=None,
|
||||
validator=None,
|
||||
validator_params=None,
|
||||
validators=None,
|
||||
properties=None,
|
||||
allow_range=False,
|
||||
allow_zero=False,
|
||||
@ -89,12 +85,8 @@ class PortOption(StrOption):
|
||||
doc,
|
||||
default=default,
|
||||
default_multi=default_multi,
|
||||
callback=callback,
|
||||
callback_params=callback_params,
|
||||
requires=requires,
|
||||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
validators=validators,
|
||||
properties=properties,
|
||||
warnings_only=warnings_only,
|
||||
extra=extra)
|
||||
|
@ -58,6 +58,3 @@ class SymLinkOption(BaseOption):
|
||||
|
||||
def impl_getopt(self) -> BaseOption:
|
||||
return self._opt
|
||||
|
||||
def get_consistencies(self) -> tuple:
|
||||
return ()
|
||||
|
@ -64,24 +64,11 @@ class SynDynOption:
|
||||
def impl_getpath(self) -> str:
|
||||
return self.rootpath + '.' + self.impl_getname()
|
||||
|
||||
def impl_validate(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag,
|
||||
check_error: bool=True) -> None:
|
||||
soption_bag = OptionBag()
|
||||
soption_bag.set_option(self.opt,
|
||||
self.impl_getpath(),
|
||||
option_bag.index,
|
||||
option_bag.config_bag)
|
||||
soption_bag.ori_option = option_bag.option
|
||||
soption_bag.fromconsistency = option_bag.fromconsistency.copy()
|
||||
self.opt.impl_validate(value,
|
||||
soption_bag,
|
||||
check_error=check_error)
|
||||
|
||||
def impl_is_dynsymlinkoption(self) -> bool:
|
||||
return True
|
||||
|
||||
def impl_get_leadership(self):
|
||||
return self.opt.impl_get_leadership().to_dynoption(self.rootpath,
|
||||
self.suffix)
|
||||
leadership = self.opt.impl_get_leadership()
|
||||
if leadership:
|
||||
return leadership.to_dynoption(self.rootpath,
|
||||
self.suffix)
|
||||
|
@ -15,8 +15,7 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ____________________________________________________________
|
||||
from .error import (RequirementError, PropertiesOptionError,
|
||||
ConstError, ConfigError, display_list)
|
||||
from .error import PropertiesOptionError, ConstError, ConfigError, LeadershipError, display_list
|
||||
from .i18n import _
|
||||
|
||||
|
||||
@ -109,7 +108,12 @@ FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value'])
|
||||
FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze',
|
||||
'force_metaconfig_on_freeze',
|
||||
'force_store_value'])
|
||||
|
||||
ALLOWED_LEADER_PROPERTIES = frozenset(['empty',
|
||||
'force_store_value',
|
||||
'mandatory',
|
||||
'force_default_on_freeze',
|
||||
'force_metaconfig_on_freeze',
|
||||
'frozen'])
|
||||
|
||||
static_set = frozenset()
|
||||
|
||||
@ -123,12 +127,10 @@ class OptionBag:
|
||||
'properties', # properties of current option
|
||||
'properties_setted',
|
||||
'apply_requires', # apply requires or not for this option
|
||||
'fromconsistency' # history for consistency
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.option = None
|
||||
self.fromconsistency = []
|
||||
|
||||
def set_option(self,
|
||||
option,
|
||||
@ -235,9 +237,6 @@ class ConfigBag:
|
||||
return
|
||||
raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover
|
||||
|
||||
# def __setattr__(self, key, value):
|
||||
# super().__setattr__(key, value)
|
||||
|
||||
def copy(self):
|
||||
kwargs = {}
|
||||
for key in self.__slots__:
|
||||
@ -447,24 +446,19 @@ class Settings(object):
|
||||
if isinstance(prop, str):
|
||||
props.add(prop)
|
||||
elif apply_requires:
|
||||
new_props = prop.execute(option_bag,
|
||||
leadership_must_have_index=True)
|
||||
if not new_props:
|
||||
new_prop = prop.execute(option_bag,
|
||||
leadership_must_have_index=True)
|
||||
if not new_prop:
|
||||
continue
|
||||
elif not isinstance(new_props, str):
|
||||
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_props),
|
||||
elif not isinstance(new_prop, str):
|
||||
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_prop),
|
||||
option_bag.option.impl_getname(),
|
||||
prop.function.__name__))
|
||||
props.add(new_props)
|
||||
# else:
|
||||
# props.update(new_props)
|
||||
if apply_requires:
|
||||
props |= self.apply_requires(option_bag,
|
||||
False,
|
||||
search_properties=search_properties)
|
||||
if not option.impl_is_optiondescription() and option.impl_is_leader() and new_prop not in ALLOWED_LEADER_PROPERTIES:
|
||||
raise LeadershipError(_('leader cannot have "{}" property').format(new_prop))
|
||||
props.add(new_prop)
|
||||
props -= self.getpermissives(option,
|
||||
path)
|
||||
#if apply_requires and config_bag.properties == config_bag.true_properties:
|
||||
if apply_requires and not config_bag.is_unrestraint:
|
||||
cache.setcache(path,
|
||||
index,
|
||||
@ -508,195 +502,6 @@ class Settings(object):
|
||||
path = opt.impl_getpath()
|
||||
return self._pp_.getpermissives(path)
|
||||
|
||||
def apply_requires(self,
|
||||
option_bag,
|
||||
readable,
|
||||
search_properties=None):
|
||||
"""carries out the jit (just in time) requirements between options
|
||||
|
||||
a requirement is a tuple of this form that comes from the option's
|
||||
requirements validation::
|
||||
|
||||
(option, expected, action, inverse, transitive, same_action)
|
||||
|
||||
let's have a look at all the tuple's items:
|
||||
|
||||
- **option** is the target option's
|
||||
|
||||
- **expected** is the target option's value that is going to trigger
|
||||
an action
|
||||
|
||||
- **action** is the (property) action to be accomplished if the target
|
||||
option happens to have the expected value
|
||||
|
||||
- if **inverse** is `True` and if the target option's value does not
|
||||
apply, then the property action must be removed from the option's
|
||||
properties list (wich means that the property is inverted)
|
||||
|
||||
- **transitive**: but what happens if the target option cannot be
|
||||
accessed ? We don't kown the target option's value. Actually if some
|
||||
property in the target option is not present in the permissive, the
|
||||
target option's value cannot be accessed. In this case, the
|
||||
**action** have to be applied to the option. (the **action** property
|
||||
is then added to the option).
|
||||
|
||||
- **same_action**: actually, if **same_action** is `True`, the
|
||||
transitivity is not accomplished. The transitivity is accomplished
|
||||
only if the target option **has the same property** that the demanded
|
||||
action. If the target option's value is not accessible because of
|
||||
another reason, because of a property of another type, then an
|
||||
exception :exc:`~error.RequirementError` is raised.
|
||||
|
||||
And at last, if no target option matches the expected values, the
|
||||
action will not add to the option's properties list.
|
||||
|
||||
:param opt: the option on wich the requirement occurs
|
||||
:type opt: `option.Option()`
|
||||
:param path: the option's path in the config
|
||||
:type path: str
|
||||
"""
|
||||
current_requires = option_bag.option.impl_getrequires()
|
||||
|
||||
# filters the callbacks
|
||||
if readable:
|
||||
calc_properties = {}
|
||||
else:
|
||||
calc_properties = set()
|
||||
|
||||
if not current_requires:
|
||||
return calc_properties
|
||||
|
||||
context = option_bag.config_bag.context
|
||||
all_properties = None
|
||||
for requires in current_requires:
|
||||
for require in requires:
|
||||
exps, action, inverse, transitive, same_action, operator = require
|
||||
#if search_properties and action not in search_properties:
|
||||
# continue
|
||||
breaked = False
|
||||
for option, expected in exps:
|
||||
if not isinstance(option, tuple):
|
||||
if option.issubdyn():
|
||||
option = option.to_dynoption(option_bag.option.rootpath,
|
||||
option_bag.option.impl_getsuffix())
|
||||
reqpath = option.impl_getpath()
|
||||
if __debug__ and reqpath.startswith(option_bag.path + '.'):
|
||||
# FIXME too later!
|
||||
raise RequirementError(_("malformed requirements "
|
||||
"imbrication detected for option:"
|
||||
" '{0}' with requirement on: "
|
||||
"'{1}'").format(option_bag.path, reqpath))
|
||||
idx = None
|
||||
is_indexed = False
|
||||
if option.impl_is_follower():
|
||||
idx = option_bag.index
|
||||
if idx is None:
|
||||
continue
|
||||
elif option.impl_is_leader() and option_bag.index is None:
|
||||
continue
|
||||
elif option.impl_is_multi() and option_bag.index is not None:
|
||||
is_indexed = True
|
||||
config_bag = option_bag.config_bag.copy()
|
||||
soption_bag = OptionBag()
|
||||
soption_bag.set_option(option,
|
||||
reqpath,
|
||||
idx,
|
||||
config_bag)
|
||||
if option_bag.option == option:
|
||||
soption_bag.config_bag.unrestraint()
|
||||
soption_bag.config_bag.remove_validation()
|
||||
soption_bag.apply_requires = False
|
||||
else:
|
||||
soption_bag.config_bag.properties = soption_bag.config_bag.true_properties
|
||||
soption_bag.config_bag.set_permissive()
|
||||
else:
|
||||
if not option_bag.option.impl_is_optiondescription() and option_bag.option.impl_is_follower():
|
||||
idx = option_bag.index
|
||||
if idx is None:
|
||||
continue
|
||||
is_indexed = False
|
||||
try:
|
||||
if not isinstance(option, tuple):
|
||||
value = context.getattr(reqpath,
|
||||
soption_bag)
|
||||
else:
|
||||
value = context.cfgimpl_get_values().carry_out_calculation(option_bag,
|
||||
option[0],
|
||||
option[1])
|
||||
except (PropertiesOptionError, ConfigError) as err:
|
||||
if isinstance(err, ConfigError):
|
||||
if not isinstance(err.ori_err, PropertiesOptionError):
|
||||
raise err
|
||||
err = err.ori_err
|
||||
properties = err.proptype
|
||||
# if not transitive, properties must be verify in current requires
|
||||
# otherwise if same_action, property must be in properties
|
||||
# otherwise add property in returned properties (if operator is 'and')
|
||||
if not transitive:
|
||||
if all_properties is None:
|
||||
all_properties = []
|
||||
for requires_ in current_requires:
|
||||
for require_ in requires_:
|
||||
all_properties.append(require_[1])
|
||||
if not set(properties) - set(all_properties):
|
||||
continue
|
||||
if same_action and action not in properties:
|
||||
if len(properties) == 1:
|
||||
prop_msg = _('property')
|
||||
else:
|
||||
prop_msg = _('properties')
|
||||
err = RequirementError(_('cannot access to option "{0}" because '
|
||||
'required option "{1}" has {2} {3}'
|
||||
'').format(option_bag.option.impl_get_display_name(),
|
||||
option.impl_get_display_name(),
|
||||
prop_msg,
|
||||
display_list(list(properties), add_quote=True)))
|
||||
err.proptype = properties
|
||||
raise err
|
||||
# transitive action, add action
|
||||
if operator != 'and':
|
||||
if readable:
|
||||
for msg in self.apply_requires(err._option_bag,
|
||||
True).values():
|
||||
calc_properties.setdefault(action, []).extend(msg)
|
||||
else:
|
||||
calc_properties.add(action)
|
||||
breaked = True
|
||||
break
|
||||
else:
|
||||
if is_indexed:
|
||||
value = value[option_bag.index]
|
||||
if (not inverse and value in expected or
|
||||
inverse and value not in expected):
|
||||
if operator != 'and':
|
||||
if readable:
|
||||
display_value = display_list(expected, 'or', add_quote=True)
|
||||
if isinstance(option, tuple):
|
||||
if not inverse:
|
||||
msg = _('the calculated value is {0}').format(display_value)
|
||||
else:
|
||||
msg = _('the calculated value is not {0}').format(display_value)
|
||||
else:
|
||||
name = option.impl_get_display_name()
|
||||
if not inverse:
|
||||
msg = _('the value of "{0}" is {1}').format(name, display_value)
|
||||
else:
|
||||
msg = _('the value of "{0}" is not {1}').format(name, display_value)
|
||||
calc_properties.setdefault(action, []).append(msg)
|
||||
else:
|
||||
calc_properties.add(action)
|
||||
breaked = True
|
||||
break
|
||||
elif operator == 'and':
|
||||
break
|
||||
else:
|
||||
if operator == 'and':
|
||||
calc_properties.add(action)
|
||||
continue
|
||||
if breaked:
|
||||
break
|
||||
return calc_properties
|
||||
|
||||
#____________________________________________________________
|
||||
# set methods
|
||||
def set_context_properties(self,
|
||||
@ -715,23 +520,21 @@ class Settings(object):
|
||||
(never save properties if same has option properties)
|
||||
"""
|
||||
opt = option_bag.option
|
||||
if opt.impl_getrequires() is not None:
|
||||
not_allowed_props = properties & \
|
||||
getattr(opt, '_calc_properties', static_set)
|
||||
if not_allowed_props:
|
||||
raise ValueError(_('cannot set property {} for option "{}" this property is '
|
||||
'calculated').format(display_list(list(not_allowed_props),
|
||||
add_quote=True),
|
||||
opt.impl_get_display_name()))
|
||||
if opt.impl_is_symlinkoption():
|
||||
raise TypeError(_("can't assign property to the symlinkoption \"{}\""
|
||||
"").format(opt.impl_get_display_name()))
|
||||
if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \
|
||||
'frozen' not in properties and \
|
||||
opt.impl_is_leader():
|
||||
raise ConfigError(_('a leader ({0}) cannot have '
|
||||
'"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
|
||||
'').format(opt.impl_get_display_name()))
|
||||
if not opt.impl_is_optiondescription() and opt.impl_is_leader():
|
||||
not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
|
||||
if not_allowed_properties:
|
||||
if len(not_allowed_properties) == 1:
|
||||
raise LeadershipError(_('leader cannot have "{}" property').format(list(not_allowed_properties)[0]))
|
||||
else:
|
||||
raise LeadershipError(_('leader cannot have {} properties').format(display_list(list(not_allowed_properties), add_quote=True)))
|
||||
if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \
|
||||
'frozen' not in properties:
|
||||
raise LeadershipError(_('a leader ({0}) cannot have '
|
||||
'"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
|
||||
'').format(opt.impl_get_display_name()))
|
||||
self._p_.setproperties(path,
|
||||
properties)
|
||||
# values too because of follower values could have a PropertiesOptionError has value
|
||||
|
@ -67,7 +67,7 @@ class Callbacks(object):
|
||||
raise ValueError(_('context is not supported from now for {}').format(path))
|
||||
if isinstance(callback_param, ParamOption):
|
||||
has_option = True
|
||||
if callback.__name__ != 'tiramisu_copy' or 'expire' in childapi.option.properties():
|
||||
if 'expire' in childapi.option.properties():
|
||||
self.tiramisu_web.set_remotable(callback_param.option.impl_getpath(), form)
|
||||
if not has_option and form.get(path, {}).get('remote', False) == False:
|
||||
if 'expire' in childapi.option.properties():
|
||||
@ -79,14 +79,15 @@ class Callbacks(object):
|
||||
form.setdefault(path, {})['clearable'] = True
|
||||
|
||||
def manage_callbacks(self, form):
|
||||
for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
|
||||
if callback_params is not None:
|
||||
for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
|
||||
if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy':
|
||||
opt_path = callback_param.option.impl_getpath()
|
||||
if form.get(opt_path, {}).get('remote') is not True:
|
||||
form.setdefault(opt_path, {})
|
||||
form[opt_path].setdefault('copy', []).append(path)
|
||||
pass
|
||||
#for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
|
||||
# if callback_params is not None:
|
||||
# for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
|
||||
# if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy':
|
||||
# opt_path = callback_param.option.impl_getpath()
|
||||
# if form.get(opt_path, {}).get('remote') is not True:
|
||||
# form.setdefault(opt_path, {})
|
||||
# form[opt_path].setdefault('copy', []).append(path)
|
||||
|
||||
def process(self,
|
||||
form):
|
||||
|
@ -17,7 +17,7 @@
|
||||
# ____________________________________________________________
|
||||
import weakref
|
||||
from typing import Optional, Any, Callable
|
||||
from .error import ConfigError, PropertiesOptionError, RequirementError
|
||||
from .error import ConfigError, PropertiesOptionError
|
||||
from .setting import owners, undefined, forbidden_owners, OptionBag, ConfigBag
|
||||
from .autolib import Calculation, carry_out_calculation, Params
|
||||
from .i18n import _
|
||||
@ -78,7 +78,7 @@ class Values(object):
|
||||
# store value in cache
|
||||
properties = option_bag.config_bag.properties
|
||||
validator = 'validator' in properties and 'demoting_error_warning' not in properties
|
||||
if not option_bag.fromconsistency and (not is_cached or validator):
|
||||
if not is_cached or validator:
|
||||
cache.setcache(option_bag.path,
|
||||
option_bag.index,
|
||||
value,
|
||||
@ -258,8 +258,7 @@ class Values(object):
|
||||
callback=callback,
|
||||
callback_params=callback_params,
|
||||
index=option_bag.index,
|
||||
config_bag=option_bag.config_bag,
|
||||
fromconsistency=option_bag.fromconsistency)
|
||||
config_bag=option_bag.config_bag)
|
||||
def isempty(self,
|
||||
opt,
|
||||
value,
|
||||
@ -289,24 +288,8 @@ class Values(object):
|
||||
context = option_bag.config_bag.context
|
||||
owner = self.get_context_owner()
|
||||
if 'validator' in option_bag.config_bag.properties:
|
||||
if option_bag.index is not None or option_bag.option.has_consistencies(context):
|
||||
# set value to a fake config when option has dependency
|
||||
# validation will be complet in this case (consistency, ...)
|
||||
tested_context = context._gen_fake_values()
|
||||
config_bag = option_bag.config_bag.copy()
|
||||
config_bag.unrestraint()
|
||||
config_bag.context = tested_context
|
||||
soption_bag = option_bag.copy()
|
||||
soption_bag.config_bag = config_bag
|
||||
tested_context.cfgimpl_get_values().setvalue(value,
|
||||
soption_bag,
|
||||
True)
|
||||
soption_bag.config_bag.properties = option_bag.config_bag.properties
|
||||
tested_context.getattr(soption_bag.path,
|
||||
soption_bag)
|
||||
else:
|
||||
self.setvalue_validation(value,
|
||||
option_bag)
|
||||
self.setvalue_validation(value,
|
||||
option_bag)
|
||||
|
||||
self._setvalue(option_bag,
|
||||
value,
|
||||
@ -622,7 +605,7 @@ class Values(object):
|
||||
except PropertiesOptionError as err:
|
||||
if err.proptype == ['mandatory']:
|
||||
yield path
|
||||
except (RequirementError, ConfigError):
|
||||
except ConfigError:
|
||||
pass
|
||||
|
||||
def mandatory_warnings(self,
|
||||
|
Reference in New Issue
Block a user