tiramisu/option.py:
separate _consistencies (for Option) and _cache_consistencies (for OptionDescription) _launch_consistency need index for multi's option _cons_not_equal support multi options tiramisu/value.py: Multi._validate support consistency
This commit is contained in:
parent
482dfec7f2
commit
70f684e70c
|
@ -5,6 +5,7 @@ from tiramisu.setting import owners, groups
|
||||||
from tiramisu.config import Config
|
from tiramisu.config import Config
|
||||||
from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
|
from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
|
||||||
BroadcastOption, SymLinkOption, OptionDescription
|
BroadcastOption, SymLinkOption, OptionDescription
|
||||||
|
from tiramisu.error import ConfigError
|
||||||
|
|
||||||
|
|
||||||
def test_consistency_not_equal():
|
def test_consistency_not_equal():
|
||||||
|
@ -22,6 +23,60 @@ def test_consistency_not_equal():
|
||||||
c.b = 2
|
c.b = 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency_not_equal_many_opts():
|
||||||
|
a = IntOption('a', '')
|
||||||
|
b = IntOption('b', '')
|
||||||
|
c = IntOption('c', '')
|
||||||
|
d = IntOption('d', '')
|
||||||
|
e = IntOption('e', '')
|
||||||
|
f = IntOption('f', '')
|
||||||
|
od = OptionDescription('od', '', [a, b, c, d, e, f])
|
||||||
|
a.impl_add_consistency('not_equal', b, c, d, e, f)
|
||||||
|
c = Config(od)
|
||||||
|
assert c.a is None
|
||||||
|
assert c.b is None
|
||||||
|
#
|
||||||
|
c.a = 1
|
||||||
|
del(c.a)
|
||||||
|
#
|
||||||
|
c.a = 1
|
||||||
|
raises(ValueError, "c.b = 1")
|
||||||
|
#
|
||||||
|
c.b = 2
|
||||||
|
raises(ValueError, "c.f = 2")
|
||||||
|
raises(ValueError, "c.f = 1")
|
||||||
|
#
|
||||||
|
c.d = 3
|
||||||
|
raises(ValueError, "c.f = 3")
|
||||||
|
raises(ValueError, "c.a = 3")
|
||||||
|
raises(ValueError, "c.c = 3")
|
||||||
|
raises(ValueError, "c.e = 3")
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency_not_in_config():
|
||||||
|
a = IntOption('a', '')
|
||||||
|
b = IntOption('b', '')
|
||||||
|
a.impl_add_consistency('not_equal', b)
|
||||||
|
od1 = OptionDescription('od1', '', [a])
|
||||||
|
od2 = OptionDescription('od2', '', [b])
|
||||||
|
od = OptionDescription('root', '', [od1])
|
||||||
|
raises(ConfigError, "Config(od)")
|
||||||
|
od = OptionDescription('root', '', [od1, od2])
|
||||||
|
Config(od)
|
||||||
|
#with subconfig
|
||||||
|
raises(ConfigError, "Config(od.od1)")
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency_afer_config():
|
||||||
|
a = IntOption('a', '')
|
||||||
|
b = IntOption('b', '')
|
||||||
|
od1 = OptionDescription('od1', '', [a])
|
||||||
|
od2 = OptionDescription('od2', '', [b])
|
||||||
|
od = OptionDescription('root', '', [od1, od2])
|
||||||
|
Config(od)
|
||||||
|
raises(AttributeError, "a.impl_add_consistency('not_equal', b)")
|
||||||
|
|
||||||
|
|
||||||
def test_consistency_not_equal_symlink():
|
def test_consistency_not_equal_symlink():
|
||||||
a = IntOption('a', '')
|
a = IntOption('a', '')
|
||||||
b = IntOption('b', '')
|
b = IntOption('b', '')
|
||||||
|
@ -29,7 +84,7 @@ def test_consistency_not_equal_symlink():
|
||||||
od = OptionDescription('od', '', [a, b, c])
|
od = OptionDescription('od', '', [a, b, c])
|
||||||
a.impl_add_consistency('not_equal', b)
|
a.impl_add_consistency('not_equal', b)
|
||||||
c = Config(od)
|
c = Config(od)
|
||||||
assert set(od._consistencies.keys()) == set([a, b])
|
assert set(od._cache_consistencies.keys()) == set([a, b])
|
||||||
|
|
||||||
|
|
||||||
def test_consistency_not_equal_multi():
|
def test_consistency_not_equal_multi():
|
||||||
|
@ -53,6 +108,14 @@ def test_consistency_default():
|
||||||
raises(ValueError, "a.impl_add_consistency('not_equal', b)")
|
raises(ValueError, "a.impl_add_consistency('not_equal', b)")
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency_default_multi():
|
||||||
|
a = IntOption('a', '', [2, 1], multi=True)
|
||||||
|
b = IntOption('b', '', [1, 1], multi=True)
|
||||||
|
c = IntOption('c', '', [1, 2], multi=True)
|
||||||
|
raises(ValueError, "a.impl_add_consistency('not_equal', b)")
|
||||||
|
a.impl_add_consistency('not_equal', c)
|
||||||
|
|
||||||
|
|
||||||
def test_consistency_default_diff():
|
def test_consistency_default_diff():
|
||||||
a = IntOption('a', '', 3)
|
a = IntOption('a', '', 3)
|
||||||
b = IntOption('b', '', 1)
|
b = IntOption('b', '', 1)
|
||||||
|
@ -99,7 +162,7 @@ def test_consistency_ip_netmask_error_multi():
|
||||||
a = IPOption('a', '', multi=True)
|
a = IPOption('a', '', multi=True)
|
||||||
b = NetmaskOption('b', '')
|
b = NetmaskOption('b', '')
|
||||||
od = OptionDescription('od', '', [a, b])
|
od = OptionDescription('od', '', [a, b])
|
||||||
raises(ValueError, "b.impl_add_consistency('ip_netmask', a)")
|
raises(ConfigError, "b.impl_add_consistency('ip_netmask', a)")
|
||||||
|
|
||||||
|
|
||||||
def test_consistency_ip_netmask_multi():
|
def test_consistency_ip_netmask_multi():
|
||||||
|
@ -170,11 +233,42 @@ def test_consistency_broadcast():
|
||||||
b.impl_add_consistency('network_netmask', a)
|
b.impl_add_consistency('network_netmask', a)
|
||||||
c.impl_add_consistency('broadcast', a, b)
|
c.impl_add_consistency('broadcast', a, b)
|
||||||
c = Config(od)
|
c = Config(od)
|
||||||
|
#first, test network_netmask
|
||||||
|
c.a = ['192.168.1.128']
|
||||||
|
raises(ValueError, "c.b = ['255.255.255.0']")
|
||||||
|
#
|
||||||
c.a = ['192.168.1.0']
|
c.a = ['192.168.1.0']
|
||||||
c.b = ['255.255.255.0']
|
c.b = ['255.255.255.0']
|
||||||
c.c = ['192.168.1.255']
|
c.c = ['192.168.1.255']
|
||||||
raises(ValueError, "c.a = ['192.168.1.1']")
|
raises(ValueError, "c.a = ['192.168.1.1']")
|
||||||
|
#
|
||||||
c.a = ['192.168.1.0', '192.168.2.128']
|
c.a = ['192.168.1.0', '192.168.2.128']
|
||||||
c.b = ['255.255.255.0', '255.255.255.128']
|
c.b = ['255.255.255.0', '255.255.255.128']
|
||||||
c.c = ['192.168.1.255', '192.168.2.255']
|
c.c = ['192.168.1.255', '192.168.2.255']
|
||||||
raises(ValueError, "c.c[1] = '192.168.2.128'")
|
raises(ValueError, "c.c[1] = '192.168.2.128'")
|
||||||
|
c.c[1] = '192.168.2.255'
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency_broadcast_default():
|
||||||
|
a = NetworkOption('a', '', '192.168.1.0')
|
||||||
|
b = NetmaskOption('b', '', '255.255.255.128')
|
||||||
|
c = BroadcastOption('c', '', '192.168.2.127')
|
||||||
|
d = BroadcastOption('d', '', '192.168.1.127')
|
||||||
|
od = OptionDescription('a', '', [a, b, c])
|
||||||
|
raises(ValueError, "c.impl_add_consistency('broadcast', a, b)")
|
||||||
|
od2 = OptionDescription('a', '', [a, b, d])
|
||||||
|
d.impl_add_consistency('broadcast', a, b)
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency_not_all():
|
||||||
|
#_cache_consistencies is not None by not options has consistencies
|
||||||
|
a = NetworkOption('a', '', multi=True)
|
||||||
|
b = NetmaskOption('b', '', multi=True)
|
||||||
|
c = BroadcastOption('c', '', multi=True)
|
||||||
|
od = OptionDescription('a', '', [a, b, c])
|
||||||
|
od.impl_set_group_type(groups.master)
|
||||||
|
b.impl_add_consistency('network_netmask', a)
|
||||||
|
c = Config(od)
|
||||||
|
c.a = ['192.168.1.0']
|
||||||
|
c.b = ['255.255.255.0']
|
||||||
|
c.c = ['192.168.1.255']
|
||||||
|
|
|
@ -40,7 +40,7 @@ def _diff_opt(opt1, opt2):
|
||||||
if diff2 != set():
|
if diff2 != set():
|
||||||
raise Exception('more attribute in opt2 {0}'.format(list(diff2)))
|
raise Exception('more attribute in opt2 {0}'.format(list(diff2)))
|
||||||
for attr in attr1:
|
for attr in attr1:
|
||||||
if attr in ['_cache_paths']:
|
if attr in ['_cache_paths', '_cache_consistencies']:
|
||||||
continue
|
continue
|
||||||
err1 = False
|
err1 = False
|
||||||
err2 = False
|
err2 = False
|
||||||
|
|
|
@ -61,9 +61,8 @@ class BaseOption(object):
|
||||||
__setattr__ method
|
__setattr__ method
|
||||||
"""
|
"""
|
||||||
__slots__ = ('_name', '_requires', '_properties', '_readonly',
|
__slots__ = ('_name', '_requires', '_properties', '_readonly',
|
||||||
'_consistencies', '_calc_properties', '_impl_informations',
|
'_calc_properties', '_impl_informations',
|
||||||
'_state_consistencies', '_state_readonly', '_state_requires',
|
'_state_readonly', '_state_requires', '_stated')
|
||||||
'_stated')
|
|
||||||
|
|
||||||
def __init__(self, name, doc, requires, properties):
|
def __init__(self, name, doc, requires, properties):
|
||||||
if not valid_name(name):
|
if not valid_name(name):
|
||||||
|
@ -73,7 +72,6 @@ class BaseOption(object):
|
||||||
self.impl_set_information('doc', doc)
|
self.impl_set_information('doc', doc)
|
||||||
self._calc_properties, self._requires = validate_requires_arg(
|
self._calc_properties, self._requires = validate_requires_arg(
|
||||||
requires, self._name)
|
requires, self._name)
|
||||||
self._consistencies = None
|
|
||||||
if properties is None:
|
if properties is None:
|
||||||
properties = tuple()
|
properties = tuple()
|
||||||
if not isinstance(properties, tuple):
|
if not isinstance(properties, tuple):
|
||||||
|
@ -98,8 +96,7 @@ class BaseOption(object):
|
||||||
"frozen" (which has noting to do with the high level "freeze"
|
"frozen" (which has noting to do with the high level "freeze"
|
||||||
propertie or "read_only" property)
|
propertie or "read_only" property)
|
||||||
"""
|
"""
|
||||||
if not name.startswith('_state') and name not in ('_cache_paths',
|
if not name.startswith('_state') and not name.startswith('_cache'):
|
||||||
'_consistencies'):
|
|
||||||
is_readonly = False
|
is_readonly = False
|
||||||
# never change _name
|
# never change _name
|
||||||
if name == '_name':
|
if name == '_name':
|
||||||
|
@ -109,15 +106,12 @@ class BaseOption(object):
|
||||||
is_readonly = True
|
is_readonly = True
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
elif name != '_readonly':
|
||||||
try:
|
try:
|
||||||
if self._readonly is True:
|
if self._readonly is True:
|
||||||
if value is True:
|
|
||||||
# already readonly and try to re set readonly
|
|
||||||
# don't raise, just exit
|
|
||||||
return
|
|
||||||
is_readonly = True
|
is_readonly = True
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
self._readonly = False
|
||||||
if is_readonly:
|
if is_readonly:
|
||||||
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
|
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
|
||||||
" read-only").format(
|
" read-only").format(
|
||||||
|
@ -149,57 +143,6 @@ class BaseOption(object):
|
||||||
raise ValueError(_("information's item not found: {0}").format(
|
raise ValueError(_("information's item not found: {0}").format(
|
||||||
key))
|
key))
|
||||||
|
|
||||||
# serialize/unserialize
|
|
||||||
def _impl_convert_consistencies(self, descr, load=False):
|
|
||||||
"""during serialization process, many things have to be done.
|
|
||||||
one of them is the localisation of the options.
|
|
||||||
The paths are set once for all.
|
|
||||||
|
|
||||||
:type descr: :class:`tiramisu.option.OptionDescription`
|
|
||||||
:param load: `True` if we are at the init of the option description
|
|
||||||
:type load: bool
|
|
||||||
"""
|
|
||||||
if not load and self._consistencies is None:
|
|
||||||
self._state_consistencies = None
|
|
||||||
elif load and self._state_consistencies is None:
|
|
||||||
self._consistencies = None
|
|
||||||
del(self._state_consistencies)
|
|
||||||
else:
|
|
||||||
if load:
|
|
||||||
consistencies = self._state_consistencies
|
|
||||||
else:
|
|
||||||
consistencies = self._consistencies
|
|
||||||
if isinstance(consistencies, list):
|
|
||||||
new_value = []
|
|
||||||
for consistency in consistencies:
|
|
||||||
values = []
|
|
||||||
for obj in consistency[1]:
|
|
||||||
if load:
|
|
||||||
values.append(descr.impl_get_opt_by_path(obj))
|
|
||||||
else:
|
|
||||||
values.append(descr.impl_get_path_by_opt(obj))
|
|
||||||
new_value.append((consistency[0], tuple(values)))
|
|
||||||
|
|
||||||
else:
|
|
||||||
new_value = {}
|
|
||||||
for key, _consistencies in consistencies.items():
|
|
||||||
new_value[key] = []
|
|
||||||
for key_cons, _cons in _consistencies:
|
|
||||||
_list_cons = []
|
|
||||||
for _con in _cons:
|
|
||||||
if load:
|
|
||||||
_list_cons.append(
|
|
||||||
descr.impl_get_opt_by_path(_con))
|
|
||||||
else:
|
|
||||||
_list_cons.append(
|
|
||||||
descr.impl_get_path_by_opt(_con))
|
|
||||||
new_value[key].append((key_cons, tuple(_list_cons)))
|
|
||||||
if load:
|
|
||||||
del(self._state_consistencies)
|
|
||||||
self._consistencies = new_value
|
|
||||||
else:
|
|
||||||
self._state_consistencies = new_value
|
|
||||||
|
|
||||||
def _impl_convert_requires(self, descr, load=False):
|
def _impl_convert_requires(self, descr, load=False):
|
||||||
"""export of the requires during the serialization process
|
"""export of the requires during the serialization process
|
||||||
|
|
||||||
|
@ -245,10 +188,7 @@ class BaseOption(object):
|
||||||
for func in dir(self):
|
for func in dir(self):
|
||||||
if func.startswith('_impl_convert_'):
|
if func.startswith('_impl_convert_'):
|
||||||
getattr(self, func)(descr)
|
getattr(self, func)(descr)
|
||||||
try:
|
|
||||||
self._state_readonly = self._readonly
|
self._state_readonly = self._readonly
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __getstate__(self, stated=True):
|
def __getstate__(self, stated=True):
|
||||||
"""special method to enable the serialization with pickle
|
"""special method to enable the serialization with pickle
|
||||||
|
@ -268,7 +208,8 @@ class BaseOption(object):
|
||||||
for subclass in self.__class__.__mro__:
|
for subclass in self.__class__.__mro__:
|
||||||
if subclass is not object:
|
if subclass is not object:
|
||||||
slots.update(subclass.__slots__)
|
slots.update(subclass.__slots__)
|
||||||
slots -= frozenset(['_cache_paths', '__weakref__'])
|
slots -= frozenset(['_cache_paths', '_cache_consistencies',
|
||||||
|
'__weakref__'])
|
||||||
states = {}
|
states = {}
|
||||||
for slot in slots:
|
for slot in slots:
|
||||||
# remove variable if save variable converted
|
# remove variable if save variable converted
|
||||||
|
@ -327,7 +268,8 @@ class Option(BaseOption):
|
||||||
"""
|
"""
|
||||||
__slots__ = ('_multi', '_validator', '_default_multi', '_default',
|
__slots__ = ('_multi', '_validator', '_default_multi', '_default',
|
||||||
'_state_callback', '_callback', '_multitype',
|
'_state_callback', '_callback', '_multitype',
|
||||||
'_warnings_only', '_master_slaves', '__weakref__')
|
'_consistencies', '_warnings_only', '_master_slaves',
|
||||||
|
'_state_consistencies', '__weakref__')
|
||||||
_empty = ''
|
_empty = ''
|
||||||
|
|
||||||
def __init__(self, name, doc, default=None, default_multi=None,
|
def __init__(self, name, doc, default=None, default_multi=None,
|
||||||
|
@ -393,66 +335,58 @@ class Option(BaseOption):
|
||||||
self._warnings_only = warnings_only
|
self._warnings_only = warnings_only
|
||||||
self.impl_validate(default)
|
self.impl_validate(default)
|
||||||
self._default = default
|
self._default = default
|
||||||
|
self._consistencies = None
|
||||||
|
|
||||||
def _launch_consistency(self, func, right_opt, right_val, context, index,
|
def _launch_consistency(self, func, option, value, context, index,
|
||||||
left_opts):
|
all_cons_opts):
|
||||||
|
"""Launch consistency now
|
||||||
|
|
||||||
|
:param func: function name, this name should start with _cons_
|
||||||
|
:type func: `str`
|
||||||
|
:param option: option that value is changing
|
||||||
|
:type option: `tiramisu.option.Option`
|
||||||
|
:param value: new value of this option
|
||||||
|
:param context: Config's context, if None, check default value instead
|
||||||
|
:type context: `tiramisu.config.Config`
|
||||||
|
:param index: only for multi option, consistency should be launch for
|
||||||
|
specified index
|
||||||
|
:type index: `int`
|
||||||
|
:param all_cons_opts: all options concerne by this consistency
|
||||||
|
:type all_cons_opts: `list` of `tiramisu.option.Option`
|
||||||
|
"""
|
||||||
if context is not None:
|
if context is not None:
|
||||||
descr = context.cfgimpl_get_description()
|
descr = context.cfgimpl_get_description()
|
||||||
#right_opt is also in left_opts
|
#option is also in all_cons_opts
|
||||||
if right_opt not in left_opts:
|
if option not in all_cons_opts:
|
||||||
raise ConfigError(_('right_opt not in left_opts'))
|
raise ConfigError(_('option not in all_cons_opts'))
|
||||||
|
|
||||||
left_vals = []
|
all_cons_vals = []
|
||||||
for opt in left_opts:
|
for opt in all_cons_opts:
|
||||||
if right_opt == opt:
|
#get value
|
||||||
value = right_val
|
if option == opt:
|
||||||
|
opt_value = value
|
||||||
else:
|
else:
|
||||||
|
#if context, calculate value, otherwise get default value
|
||||||
if context is not None:
|
if context is not None:
|
||||||
path = descr.impl_get_path_by_opt(opt)
|
opt_value = context._getattr(
|
||||||
value = context._getattr(path, validate=False)
|
descr.impl_get_path_by_opt(opt), validate=False)
|
||||||
else:
|
else:
|
||||||
value = opt.impl_getdefault()
|
opt_value = opt.impl_getdefault()
|
||||||
if index is None:
|
|
||||||
#could be multi or not
|
#append value
|
||||||
left_vals.append(value)
|
if not self.impl_is_multi() or option == opt:
|
||||||
|
all_cons_vals.append(opt_value)
|
||||||
else:
|
else:
|
||||||
#value is not already set, could be higher
|
#value is not already set, could be higher index
|
||||||
try:
|
try:
|
||||||
if right_opt == opt:
|
all_cons_vals.append(opt_value[index])
|
||||||
val = value
|
|
||||||
else:
|
|
||||||
val = value[index]
|
|
||||||
if val is None:
|
|
||||||
#no value so no consistencies
|
|
||||||
return
|
|
||||||
left_vals.append(val)
|
|
||||||
except IndexError:
|
except IndexError:
|
||||||
#so return if no value
|
#so return if no value
|
||||||
return
|
return
|
||||||
|
getattr(self, func)(all_cons_opts, all_cons_vals)
|
||||||
if self.impl_is_multi():
|
|
||||||
if index is None:
|
|
||||||
for idx, right_v in enumerate(right_val):
|
|
||||||
try:
|
|
||||||
left_v = []
|
|
||||||
for left_val in left_vals:
|
|
||||||
left_v.append(left_val[idx])
|
|
||||||
if None in left_v:
|
|
||||||
continue
|
|
||||||
except IndexError:
|
|
||||||
continue
|
|
||||||
getattr(self, func)(left_opts, left_v)
|
|
||||||
else:
|
|
||||||
if None in left_vals:
|
|
||||||
return
|
|
||||||
getattr(self, func)(left_opts, left_vals)
|
|
||||||
else:
|
|
||||||
if None in left_vals:
|
|
||||||
return
|
|
||||||
getattr(self, func)(left_opts, left_vals)
|
|
||||||
|
|
||||||
def impl_validate(self, value, context=None, validate=True,
|
def impl_validate(self, value, context=None, validate=True,
|
||||||
force_no_multi=False):
|
force_index=None):
|
||||||
"""
|
"""
|
||||||
:param value: the option's value
|
:param value: the option's value
|
||||||
:param context: Config's context
|
:param context: Config's context
|
||||||
|
@ -509,12 +443,11 @@ class Option(BaseOption):
|
||||||
if context is not None:
|
if context is not None:
|
||||||
descr = context.cfgimpl_get_description()
|
descr = context.cfgimpl_get_description()
|
||||||
|
|
||||||
if not self._multi or force_no_multi:
|
if not self._multi or force_index is not None:
|
||||||
do_validation(value)
|
do_validation(value, force_index)
|
||||||
else:
|
else:
|
||||||
if not isinstance(value, list):
|
if not isinstance(value, list):
|
||||||
raise ValueError(_("invalid value {0} for option {1} "
|
raise ValueError(_("which must be a list").format(value,
|
||||||
"which must be a list").format(value,
|
|
||||||
self._name))
|
self._name))
|
||||||
for index, val in enumerate(value):
|
for index, val in enumerate(value):
|
||||||
do_validation(val, index)
|
do_validation(val, index)
|
||||||
|
@ -561,31 +494,45 @@ class Option(BaseOption):
|
||||||
def impl_is_multi(self):
|
def impl_is_multi(self):
|
||||||
return self._multi
|
return self._multi
|
||||||
|
|
||||||
def impl_add_consistency(self, func, *left_opts):
|
def impl_add_consistency(self, func, *other_opts):
|
||||||
|
"""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`
|
||||||
|
"""
|
||||||
if self._consistencies is None:
|
if self._consistencies is None:
|
||||||
self._consistencies = []
|
self._consistencies = []
|
||||||
for opt in left_opts:
|
for opt in other_opts:
|
||||||
if not isinstance(opt, Option):
|
if not isinstance(opt, Option):
|
||||||
raise ValueError(_('consistency should be set with an option'))
|
raise ConfigError(_('consistency should be set with an option'))
|
||||||
if self is opt:
|
if self is opt:
|
||||||
raise ValueError(_('cannot add consistency with itself'))
|
raise ConfigError(_('cannot add consistency with itself'))
|
||||||
if self.impl_is_multi() != opt.impl_is_multi():
|
if self.impl_is_multi() != opt.impl_is_multi():
|
||||||
raise ValueError(_('options in consistency should be multi in '
|
raise ConfigError(_('every options in consistency should be '
|
||||||
'two sides'))
|
'multi or none'))
|
||||||
func = '_cons_{0}'.format(func)
|
func = '_cons_{0}'.format(func)
|
||||||
opts = tuple([self] + list(left_opts))
|
all_cons_opts = tuple([self] + list(other_opts))
|
||||||
self._launch_consistency(func, self, self.impl_getdefault(), None,
|
value = self.impl_getdefault()
|
||||||
None, opts)
|
if value is not None:
|
||||||
self._consistencies.append((func, opts))
|
if self.impl_is_multi():
|
||||||
|
for idx, val in enumerate(value):
|
||||||
|
self._launch_consistency(func, self, val, None,
|
||||||
|
idx, all_cons_opts)
|
||||||
|
else:
|
||||||
|
self._launch_consistency(func, self, value, None,
|
||||||
|
None, all_cons_opts)
|
||||||
|
self._consistencies.append((func, all_cons_opts))
|
||||||
self.impl_validate(self.impl_getdefault())
|
self.impl_validate(self.impl_getdefault())
|
||||||
|
|
||||||
def _cons_not_equal(self, opts, vals):
|
def _cons_not_equal(self, opts, vals):
|
||||||
if len(opts) != 2:
|
for idx_inf, val_inf in enumerate(vals):
|
||||||
raise ConfigError(_('invalid len for opts'))
|
for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
|
||||||
if vals[0] == vals[1]:
|
if val_inf == val_sup is not None:
|
||||||
raise ValueError(_("invalid value {0} for option {1} "
|
raise ValueError(_("same value for {0} and {1}").format(
|
||||||
"must be different as {2} option"
|
opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name))
|
||||||
"").format(vals[0], self._name, opts[1]._name))
|
|
||||||
|
|
||||||
def _impl_convert_callbacks(self, descr, load=False):
|
def _impl_convert_callbacks(self, descr, load=False):
|
||||||
if not load and self._callback is None:
|
if not load and self._callback is None:
|
||||||
|
@ -621,6 +568,57 @@ class Option(BaseOption):
|
||||||
else:
|
else:
|
||||||
self._state_callback = (callback, cllbck_prms)
|
self._state_callback = (callback, cllbck_prms)
|
||||||
|
|
||||||
|
# serialize/unserialize
|
||||||
|
def _impl_convert_consistencies(self, descr, load=False):
|
||||||
|
"""during serialization process, many things have to be done.
|
||||||
|
one of them is the localisation of the options.
|
||||||
|
The paths are set once for all.
|
||||||
|
|
||||||
|
:type descr: :class:`tiramisu.option.OptionDescription`
|
||||||
|
:param load: `True` if we are at the init of the option description
|
||||||
|
:type load: bool
|
||||||
|
"""
|
||||||
|
if not load and self._consistencies is None:
|
||||||
|
self._state_consistencies = None
|
||||||
|
elif load and self._state_consistencies is None:
|
||||||
|
self._consistencies = None
|
||||||
|
del(self._state_consistencies)
|
||||||
|
else:
|
||||||
|
if load:
|
||||||
|
consistencies = self._state_consistencies
|
||||||
|
else:
|
||||||
|
consistencies = self._consistencies
|
||||||
|
if isinstance(consistencies, list):
|
||||||
|
new_value = []
|
||||||
|
for consistency in consistencies:
|
||||||
|
values = []
|
||||||
|
for obj in consistency[1]:
|
||||||
|
if load:
|
||||||
|
values.append(descr.impl_get_opt_by_path(obj))
|
||||||
|
else:
|
||||||
|
values.append(descr.impl_get_path_by_opt(obj))
|
||||||
|
new_value.append((consistency[0], tuple(values)))
|
||||||
|
|
||||||
|
else:
|
||||||
|
new_value = {}
|
||||||
|
for key, _consistencies in consistencies.items():
|
||||||
|
new_value[key] = []
|
||||||
|
for key_cons, _cons in _consistencies:
|
||||||
|
_list_cons = []
|
||||||
|
for _con in _cons:
|
||||||
|
if load:
|
||||||
|
_list_cons.append(
|
||||||
|
descr.impl_get_opt_by_path(_con))
|
||||||
|
else:
|
||||||
|
_list_cons.append(
|
||||||
|
descr.impl_get_path_by_opt(_con))
|
||||||
|
new_value[key].append((key_cons, tuple(_list_cons)))
|
||||||
|
if load:
|
||||||
|
del(self._state_consistencies)
|
||||||
|
self._consistencies = new_value
|
||||||
|
else:
|
||||||
|
self._state_consistencies = new_value
|
||||||
|
|
||||||
def _second_level_validation(self, value):
|
def _second_level_validation(self, value):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -734,7 +732,7 @@ class SymLinkOption(BaseOption):
|
||||||
__slots__ = ('_name', '_opt', '_state_opt')
|
__slots__ = ('_name', '_opt', '_state_opt')
|
||||||
_opt_type = 'symlink'
|
_opt_type = 'symlink'
|
||||||
#not return _opt consistencies
|
#not return _opt consistencies
|
||||||
_consistencies = {}
|
_consistencies = None
|
||||||
|
|
||||||
def __init__(self, name, opt):
|
def __init__(self, name, opt):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
@ -760,12 +758,6 @@ class SymLinkOption(BaseOption):
|
||||||
del(self._state_opt)
|
del(self._state_opt)
|
||||||
super(SymLinkOption, self)._impl_setstate(descr)
|
super(SymLinkOption, self)._impl_setstate(descr)
|
||||||
|
|
||||||
def _impl_convert_consistencies(self, descr, load=False):
|
|
||||||
if load:
|
|
||||||
del(self._state_consistencies)
|
|
||||||
else:
|
|
||||||
self._state_consistencies = None
|
|
||||||
|
|
||||||
|
|
||||||
class IPOption(Option):
|
class IPOption(Option):
|
||||||
"represents the choice of an ip"
|
"represents the choice of an ip"
|
||||||
|
@ -904,10 +896,14 @@ class NetmaskOption(Option):
|
||||||
|
|
||||||
def _cons_network_netmask(self, opts, vals):
|
def _cons_network_netmask(self, opts, vals):
|
||||||
#opts must be (netmask, network) options
|
#opts must be (netmask, network) options
|
||||||
|
if None in vals:
|
||||||
|
return
|
||||||
self.__cons_netmask(opts, vals[0], vals[1], False)
|
self.__cons_netmask(opts, vals[0], vals[1], False)
|
||||||
|
|
||||||
def _cons_ip_netmask(self, opts, vals):
|
def _cons_ip_netmask(self, opts, vals):
|
||||||
#opts must be (netmask, ip) options
|
#opts must be (netmask, ip) options
|
||||||
|
if None in vals:
|
||||||
|
return
|
||||||
self.__cons_netmask(opts, vals[0], vals[1], True)
|
self.__cons_netmask(opts, vals[0], vals[1], True)
|
||||||
|
|
||||||
def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net):
|
def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net):
|
||||||
|
@ -955,6 +951,8 @@ class BroadcastOption(Option):
|
||||||
def _cons_broadcast(self, opts, vals):
|
def _cons_broadcast(self, opts, vals):
|
||||||
if len(vals) != 3:
|
if len(vals) != 3:
|
||||||
raise ConfigError(_('invalid len for vals'))
|
raise ConfigError(_('invalid len for vals'))
|
||||||
|
if None in vals:
|
||||||
|
return
|
||||||
broadcast, network, netmask = vals
|
broadcast, network, netmask = vals
|
||||||
if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast):
|
if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast):
|
||||||
raise ValueError(_('invalid broadcast {0} ({1}) with network {2} '
|
raise ValueError(_('invalid broadcast {0} ({1}) with network {2} '
|
||||||
|
@ -964,7 +962,12 @@ class BroadcastOption(Option):
|
||||||
|
|
||||||
|
|
||||||
class DomainnameOption(Option):
|
class DomainnameOption(Option):
|
||||||
"represents the choice of a domain name"
|
"""represents the choice of a domain name
|
||||||
|
netbios: for MS domain
|
||||||
|
hostname: to identify the device
|
||||||
|
domainname:
|
||||||
|
fqdn: with tld, not supported yet
|
||||||
|
"""
|
||||||
__slots__ = ('_type', '_allow_ip')
|
__slots__ = ('_type', '_allow_ip')
|
||||||
_opt_type = 'domainname'
|
_opt_type = 'domainname'
|
||||||
|
|
||||||
|
@ -973,10 +976,6 @@ class DomainnameOption(Option):
|
||||||
callback_params=None, validator=None, validator_params=None,
|
callback_params=None, validator=None, validator_params=None,
|
||||||
properties=None, allow_ip=False, type_='domainname',
|
properties=None, allow_ip=False, type_='domainname',
|
||||||
warnings_only=False):
|
warnings_only=False):
|
||||||
#netbios: for MS domain
|
|
||||||
#hostname: to identify the device
|
|
||||||
#domainname:
|
|
||||||
#fqdn: with tld, not supported yet
|
|
||||||
if type_ not in ['netbios', 'hostname', 'domainname']:
|
if type_ not in ['netbios', 'hostname', 'domainname']:
|
||||||
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
|
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
|
||||||
self._type = type_
|
self._type = type_
|
||||||
|
@ -1030,9 +1029,9 @@ class OptionDescription(BaseOption):
|
||||||
"""
|
"""
|
||||||
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
|
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
|
||||||
'_state_group_type', '_properties', '_children',
|
'_state_group_type', '_properties', '_children',
|
||||||
'_consistencies', '_calc_properties', '__weakref__',
|
'_cache_consistencies', '_calc_properties', '__weakref__',
|
||||||
'_readonly', '_impl_informations', '_state_requires',
|
'_readonly', '_impl_informations', '_state_requires',
|
||||||
'_state_consistencies', '_stated', '_state_readonly')
|
'_stated', '_state_readonly')
|
||||||
_opt_type = 'optiondescription'
|
_opt_type = 'optiondescription'
|
||||||
|
|
||||||
def __init__(self, name, doc, children, requires=None, properties=None):
|
def __init__(self, name, doc, children, requires=None, properties=None):
|
||||||
|
@ -1053,6 +1052,7 @@ class OptionDescription(BaseOption):
|
||||||
old = child
|
old = child
|
||||||
self._children = (tuple(child_names), tuple(children))
|
self._children = (tuple(child_names), tuple(children))
|
||||||
self._cache_paths = None
|
self._cache_paths = None
|
||||||
|
self._cache_consistencies = None
|
||||||
# the group_type is useful for filtering OptionDescriptions in a config
|
# the group_type is useful for filtering OptionDescriptions in a config
|
||||||
self._group_type = groups.default
|
self._group_type = groups.default
|
||||||
|
|
||||||
|
@ -1126,11 +1126,11 @@ class OptionDescription(BaseOption):
|
||||||
if not force_no_consistencies and \
|
if not force_no_consistencies and \
|
||||||
option._consistencies is not None:
|
option._consistencies is not None:
|
||||||
for consistency in option._consistencies:
|
for consistency in option._consistencies:
|
||||||
func, left_opts = consistency
|
func, all_cons_opts = consistency
|
||||||
for opt in left_opts:
|
for opt in all_cons_opts:
|
||||||
_consistencies.setdefault(opt,
|
_consistencies.setdefault(opt,
|
||||||
[]).append((func,
|
[]).append((func,
|
||||||
left_opts))
|
all_cons_opts))
|
||||||
else:
|
else:
|
||||||
_currpath.append(attr)
|
_currpath.append(attr)
|
||||||
option.impl_build_cache(cache_path,
|
option.impl_build_cache(cache_path,
|
||||||
|
@ -1142,7 +1142,12 @@ class OptionDescription(BaseOption):
|
||||||
if save:
|
if save:
|
||||||
self._cache_paths = (tuple(cache_option), tuple(cache_path))
|
self._cache_paths = (tuple(cache_option), tuple(cache_path))
|
||||||
if not force_no_consistencies:
|
if not force_no_consistencies:
|
||||||
self._consistencies = _consistencies
|
if _consistencies != {}:
|
||||||
|
self._cache_consistencies = {}
|
||||||
|
for opt, cons in _consistencies.items():
|
||||||
|
if opt not in cache_option:
|
||||||
|
raise ConfigError(_('consistency with option {0} which is not in Config').format(opt._name))
|
||||||
|
self._cache_consistencies[opt] = tuple(cons)
|
||||||
self._readonly = True
|
self._readonly = True
|
||||||
|
|
||||||
def impl_get_opt_by_path(self, path):
|
def impl_get_opt_by_path(self, path):
|
||||||
|
@ -1213,15 +1218,18 @@ class OptionDescription(BaseOption):
|
||||||
def impl_get_group_type(self):
|
def impl_get_group_type(self):
|
||||||
return self._group_type
|
return self._group_type
|
||||||
|
|
||||||
def _valid_consistency(self, right_opt, right_val, context=None, index=None):
|
def _valid_consistency(self, option, value, context, index):
|
||||||
#[('_cons_not_equal', (opt1, opt2))]
|
if self._cache_consistencies is None:
|
||||||
consistencies = self._consistencies.get(right_opt)
|
return True
|
||||||
|
#consistencies is something like [('_cons_not_equal', (opt1, opt2))]
|
||||||
|
consistencies = self._cache_consistencies.get(option)
|
||||||
if consistencies is not None:
|
if consistencies is not None:
|
||||||
for func, opts in consistencies:
|
for func, all_cons_opts in consistencies:
|
||||||
#opts[0] is the option where func is set
|
#all_cons_opts[0] is the option where func is set
|
||||||
#opts is left_opts
|
ret = all_cons_opts[0]._launch_consistency(func, option,
|
||||||
ret = opts[0]._launch_consistency(func, right_opt, right_val,
|
value,
|
||||||
context, index, opts)
|
context, index,
|
||||||
|
all_cons_opts)
|
||||||
if ret is False:
|
if ret is False:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -1261,6 +1269,7 @@ class OptionDescription(BaseOption):
|
||||||
"""
|
"""
|
||||||
if descr is None:
|
if descr is None:
|
||||||
self._cache_paths = None
|
self._cache_paths = None
|
||||||
|
self._cache_consistencies = None
|
||||||
self.impl_build_cache(force_no_consistencies=True)
|
self.impl_build_cache(force_no_consistencies=True)
|
||||||
descr = self
|
descr = self
|
||||||
self._group_type = getattr(groups, self._state_group_type)
|
self._group_type = getattr(groups, self._state_group_type)
|
||||||
|
|
|
@ -455,10 +455,10 @@ class Multi(list):
|
||||||
value_slave.append(slave.impl_getdefault_multi(),
|
value_slave.append(slave.impl_getdefault_multi(),
|
||||||
force=True)
|
force=True)
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, index, value):
|
||||||
self._validate(value)
|
self._validate(value, index)
|
||||||
#assume not checking mandatory property
|
#assume not checking mandatory property
|
||||||
super(Multi, self).__setitem__(key, value)
|
super(Multi, self).__setitem__(index, value)
|
||||||
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
|
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
|
||||||
|
|
||||||
def append(self, value, force=False):
|
def append(self, value, force=False):
|
||||||
|
@ -476,7 +476,8 @@ class Multi(list):
|
||||||
#Force None il return a list
|
#Force None il return a list
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = None
|
value = None
|
||||||
self._validate(value)
|
index = self.__len__()
|
||||||
|
self._validate(value, index)
|
||||||
super(Multi, self).append(value)
|
super(Multi, self).append(value)
|
||||||
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
||||||
self,
|
self,
|
||||||
|
@ -486,7 +487,6 @@ class Multi(list):
|
||||||
path = values._get_opt_path(slave)
|
path = values._get_opt_path(slave)
|
||||||
if not values._is_default_owner(path):
|
if not values._is_default_owner(path):
|
||||||
if slave.impl_has_callback():
|
if slave.impl_has_callback():
|
||||||
index = self.__len__() - 1
|
|
||||||
dvalue = values._getcallback_value(slave, index=index)
|
dvalue = values._getcallback_value(slave, index=index)
|
||||||
else:
|
else:
|
||||||
dvalue = slave.impl_getdefault_multi()
|
dvalue = slave.impl_getdefault_multi()
|
||||||
|
@ -538,11 +538,11 @@ class Multi(list):
|
||||||
super(Multi, self).extend(iterable)
|
super(Multi, self).extend(iterable)
|
||||||
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
|
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
|
||||||
|
|
||||||
def _validate(self, value):
|
def _validate(self, value, force_index):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
try:
|
try:
|
||||||
self.opt.impl_validate(value, context=self.context(),
|
self.opt.impl_validate(value, context=self.context(),
|
||||||
force_no_multi=True)
|
force_index=force_index)
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
raise ValueError(_("invalid value {0} "
|
raise ValueError(_("invalid value {0} "
|
||||||
"for option {1}: {2}"
|
"for option {1}: {2}"
|
||||||
|
|
Loading…
Reference in New Issue