symlink could be used in a leadership

This commit is contained in:
egarette@silique.fr 2024-08-07 08:55:43 +02:00
parent b5346f9c57
commit 9c36fb8fb2
13 changed files with 364 additions and 65 deletions

View file

@ -446,7 +446,7 @@ def test_config_od_name(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('val.i').name() == 'i' assert cfg.option('val.i').name() == 'i'
assert cfg.option('val.s').name() == 's' assert cfg.option('val.s').name() == 's'
assert cfg.option('val.s').type() == _('integer') assert cfg.option('val.s').type() == 'integer'
assert cfg.option('val').type() == 'optiondescription' assert cfg.option('val').type() == 'optiondescription'
# assert not list_sessions() # assert not list_sessions()
@ -458,7 +458,7 @@ def test_config_od_type(config_type):
cfg = Config(o2) cfg = Config(o2)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('val').type() == 'optiondescription' assert cfg.option('val').type() == 'optiondescription'
assert cfg.option('val.i').type() == _('integer') assert cfg.option('val.i').type() == 'integer'
# assert not list_sessions() # assert not list_sessions()

View file

@ -626,25 +626,195 @@ def test_reset_properties_force_store_value():
gcgroup = OptionDescription('gc', '', [gcdummy]) gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup]) od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1) cfg = Config(od1)
assert cfg.property.exportation() == {None: {None: frozenset({'validator', 'warnings', 'cache'})}} assert cfg.property.exportation() == {'properties': {None: {None: frozenset({'cache',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.add('frozen') cfg.property.add('frozen')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{None: {None: set(('frozen', 'cache', 'validator', 'warnings'))}} {
'properties': {None: {None: frozenset({'cache',
'frozen',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.reset() cfg.property.reset()
assert cfg.property.exportation() == {None: {}} assert cfg.property.exportation() == {'properties': {None: {}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.property.exportation() == {None: {}, 'gc.dummy': {None: frozenset({'test'})}} assert cfg.property.exportation() == {
'properties': {None: {},
'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.reset() cfg.property.reset()
assert cfg.property.exportation() == {None: {}, 'gc.dummy': {None: frozenset({'test'})}} assert cfg.property.exportation() == {
'properties': {None: {},
'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.add('frozen') cfg.property.add('frozen')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: frozenset({'test'})}} {
'properties': {None: {None: frozenset({'frozen'})},
'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.add('frozen') cfg.property.add('frozen')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: frozenset({'test'})}} {
'properties': {None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: frozenset({'test'})}} {
'properties': {None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
# assert not list_sessions() # assert not list_sessions()
@ -674,10 +844,79 @@ def test_set_modified_value():
gcgroup = OptionDescription('gc', '', [gcdummy]) gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup]) od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1) cfg = Config(od1)
assert cfg.property.exportation() == {None: {None: frozenset({'warnings', 'validator', 'cache'})}} assert cfg.property.exportation() == {
cfg.property.importation({None: {None: set(('frozen', 'cache', 'validator', 'warnings'))}}) 'properties': {None: {None: frozenset({'cache',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.importation({
'properties': {None: {None: frozenset({'cache',
'frozen',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
})
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{None: {None: set(('frozen', 'cache', 'validator', 'warnings'))}} {
'properties': {None: {None: frozenset({'cache',
'frozen',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
# assert not list_sessions() # assert not list_sessions()
@ -749,8 +988,7 @@ def test_pprint():
list_disabled = '"disabled" (' + display_list([msg_is.format('Test int option', '"1"'), msg_is.format('string2', '"string"')], add_quote=False) + ')' list_disabled = '"disabled" (' + display_list([msg_is.format('Test int option', '"1"'), msg_is.format('string2', '"string"')], add_quote=False) + ')'
list_hidden = '"hidden" (' + msg_is_not.format('Test int option', display_list([2, 3, 4], separator='or', add_quote=True)) + ')' list_hidden = '"hidden" (' + msg_is_not.format('Test int option', display_list([2, 3, 4], separator='or', add_quote=True)) + ')'
print('FIXME') assert str(err) == _(msg_error.format('option', 'Test string option', properties, display_list([list_disabled, list_hidden], add_quote=False)))
# assert str(err) == _(msg_error.format('option', 'Test string option', properties, display_list([list_disabled, list_hidden], add_quote=False)))
del err del err
err = None err = None
@ -759,8 +997,7 @@ def test_pprint():
except PropertiesOptionError as error: except PropertiesOptionError as error:
err = error err = error
print('FIXME') assert str(err) == msg_error.format('optiondescription', 'options', prop, '"hidden" (' + msg_is.format('Test int option', '"1"') + ')')
# assert str(err) == msg_error.format('optiondescription', 'options', prop, '"hidden" (' + msg_is.format('Test int option', '"1"') + ')')
#err = None #err = None
#try: #try:

View file

@ -334,7 +334,7 @@ def test_validator_warning(config_type):
assert len(w) == 1 assert len(w) == 1
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == opt2 assert w[0].message.opt() == opt2
assert str(w[0].message) == msg_err.format('val', opt2.get_type(), 'opt2') + ', ' + 'test error return_false' assert str(w[0].message) == msg_err.format('val', _(opt2.get_type()), 'opt2') + ', ' + 'test error return_false'
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
cfg.nowarnings.option('opt2').value.set('val') cfg.nowarnings.option('opt2').value.set('val')
@ -352,7 +352,7 @@ def test_validator_warning(config_type):
assert len(w) == 1 assert len(w) == 1
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == opt3 assert w[0].message.opt() == opt3
assert str(w[0].message) == msg_err.format('val1', opt3.get_type(), 'opt3') + ', ' + 'test error' assert str(w[0].message) == msg_err.format('val1', _(opt3.get_type()), 'opt3') + ', ' + 'test error'
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -365,9 +365,9 @@ def test_validator_warning(config_type):
assert len(w) == 2 assert len(w) == 2
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == opt2 assert w[0].message.opt() == opt2
assert str(w[0].message) == msg_err.format('val', opt2.get_type(), 'opt2') + ', ' + 'test error return_false' assert str(w[0].message) == msg_err.format('val', _(opt2.get_type()), 'opt2') + ', ' + 'test error return_false'
assert w[1].message.opt() == opt3 assert w[1].message.opt() == opt3
assert str(w[1].message) == msg_err.format('val1', opt3.get_type(), 'opt3') + ', ' + 'test error' assert str(w[1].message) == msg_err.format('val1', _(opt3.get_type()), 'opt3') + ', ' + 'test error'
# assert not list_sessions() # assert not list_sessions()
@ -436,13 +436,13 @@ def test_validator_warning_leadership(config_type):
assert len(w) == 1 assert len(w) == 1
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == netmask_admin_eth0 assert w[0].message.opt() == netmask_admin_eth0
assert str(w[0].message) == msg_err.format('val1', netmask_admin_eth0.get_type(), display_name_netmask) + ', test error' assert str(w[0].message) == msg_err.format('val1', _(netmask_admin_eth0.get_type()), display_name_netmask) + ', test error'
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w and w[0].message.opt() == ip_admin_eth0 assert w and w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 2 assert len(w) == 2
# #
@ -450,7 +450,7 @@ def test_validator_warning_leadership(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val1', 'val1']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val1', 'val1'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == ip_admin_eth0 assert w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 3 assert len(w) == 3
# #
@ -458,7 +458,7 @@ def test_validator_warning_leadership(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val', 'val1']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val', 'val1'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == ip_admin_eth0 assert w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 3 assert len(w) == 3
# #
@ -467,7 +467,7 @@ def test_validator_warning_leadership(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val1', 'val']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val1', 'val'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == ip_admin_eth0 assert w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 3 assert len(w) == 3
# assert not list_sessions() # assert not list_sessions()

View file

@ -197,11 +197,9 @@ def test_requires_same_action(config_type):
if config_type == 'tiramisu': if config_type == 'tiramisu':
submsg = '"new" (' + _('the value of "{0}" is {1}').format('activate_service', '"False"') + ')' submsg = '"new" (' + _('the value of "{0}" is {1}').format('activate_service', '"False"') + ')'
submsg = '"disabled" (' + str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'activate_service_web', _('property'), submsg)) + ')' submsg = '"disabled" (' + str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'activate_service_web', _('property'), submsg)) + ')'
print('FIXME') assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', _('property'), submsg))
# assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', _('property'), submsg))
#access to cache #access to cache
print('FIXME') assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', _('property'), submsg))
# assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', _('property'), submsg))
else: else:
# FIXME # FIXME
assert str(err) == 'error' assert str(err) == 'error'
@ -428,9 +426,10 @@ def test_requires_transitive_unrestraint(config_type):
# #
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
assert cfg_ori.unrestraint.option('activate_service_web').property.get() == {'disabled'} assert cfg_ori.option('activate_service_web').property.get() == {'disabled'}
print('FIXME') print('FIXME')
# assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'} # assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'}
assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled'}
# assert not list_sessions() # assert not list_sessions()

View file

@ -297,11 +297,59 @@ def test_symlink_leader():
def test_symlink_followers(): def test_symlink_followers():
a = StrOption('a', "", multi=True)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = SymLinkOption('netmask_admin_eth0', a) netmask_admin_eth0 = SymLinkOption('netmask_admin_eth0', ip_admin_eth0)
with pytest.raises(ValueError): leader = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) od1 = OptionDescription('opt', '', [leader])
cfg = Config(od1)
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': []}
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2'])
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == 'val2'
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.get() == 'user'
assert parse_od_get(cfg.value.get()) == {
'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1',
'ip_admin_eth0.netmask_admin_eth0': 'val1'},
{'ip_admin_eth0.ip_admin_eth0': 'val2',
'ip_admin_eth0.netmask_admin_eth0': 'val2'}],
}
def test_symlink_leader_default():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", ['val1', 'val2'], multi=True)
netmask_admin_eth0 = SymLinkOption('netmask_admin_eth0', ip_admin_eth0)
leader = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od1 = OptionDescription('opt', '', [leader])
cfg = Config(od1)
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == 'val2'
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.isdefault()
assert parse_od_get(cfg.value.get()) == {
'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1',
'ip_admin_eth0.netmask_admin_eth0': 'val1'},
{'ip_admin_eth0.ip_admin_eth0': 'val2',
'ip_admin_eth0.netmask_admin_eth0': 'val2'}],
}
def test_symlink_followers_2():
variable1 = StrOption('variable1', "", multi=True)
variable2 = StrOption('variable2', "", multi=True)
variable3 = SymLinkOption('variable3', variable2)
leader = Leadership('variable1', '', [variable1, variable2, variable3])
od1 = OptionDescription('opt', '', [leader])
cfg = Config(od1)
assert parse_od_get(cfg.value.get()) == {'variable1.variable1': []}
cfg.option('variable1.variable1').value.set(['val1', 'val2'])
cfg.option('variable1.variable2', 0).value.set('ival1')
cfg.option('variable1.variable2', 1).value.set('ival2')
assert cfg.option('variable1.variable3', 1).owner.get() == 'user'
assert cfg.option('variable1.variable3', 1).value.get() == 'ival2'
assert parse_od_get(cfg.value.get()) == {'variable1.variable1': [{'variable1.variable1': 'val1',
'variable1.variable2': 'ival1',
'variable1.variable3': 'ival1'},
{'variable1.variable1': 'val2',
'variable1.variable2': 'ival2',
'variable1.variable3': 'ival2'}],
}
def test_symlink_with_leader(config_type): def test_symlink_with_leader(config_type):

View file

@ -436,6 +436,8 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
need_help=True, need_help=True,
validate_properties=self._validate_properties, validate_properties=self._validate_properties,
) )
if isinstance(subconfig, list):
raise ConfigError(_('cannot get option from a follower symlink without index'))
subconfig.true_path = subconfig.path subconfig.true_path = subconfig.path
return TiramisuOption(subconfig.path, return TiramisuOption(subconfig.path,
subconfig.index, subconfig.index,
@ -1149,8 +1151,8 @@ class TiramisuContextProperty(TiramisuConfig, PropertyPermissive):
settings._properties = deepcopy(properties) settings._properties = deepcopy(properties)
settings.ro_append = data['ro_append'].copy() settings.ro_append = data['ro_append'].copy()
settings.ro_remove = data['ro_remove'].copy() settings.ro_remove = data['ro_remove'].copy()
settings.ro_append = data['rw_append'].copy() settings.rw_append = data['rw_append'].copy()
settings.ro_remove = data['rw_remove'].copy() settings.rw_remove = data['rw_remove'].copy()
context.reset_cache(None, None) context.reset_cache(None, None)
self._reset_config_properties(settings) self._reset_config_properties(settings)
if force_store_value: if force_store_value:

View file

@ -298,7 +298,7 @@ class SubConfig:
*, *,
uncalculated: bool=False, uncalculated: bool=False,
): ):
if self.option.impl_is_leadership(): if self.option.impl_is_leadership() and not uncalculated:
yield from self.get_leadership_children(validate_properties) yield from self.get_leadership_children(validate_properties)
else: else:
for child in self.option.get_children(): for child in self.option.get_children():
@ -357,7 +357,6 @@ class SubConfig:
) )
if check_index and index is not None: if check_index and index is not None:
if option.impl_is_optiondescription() or \ if option.impl_is_optiondescription() or \
option.impl_is_symlinkoption() or \
not option.impl_is_follower(): not option.impl_is_follower():
raise ConfigError('index must be set only with a follower option') raise ConfigError('index must be set only with a follower option')
length = self.get_length_leadership() length = self.get_length_leadership()
@ -735,6 +734,7 @@ class _Config(CCache):
:return: option's value if name is an option name, OptionDescription :return: option's value if name is an option name, OptionDescription
otherwise otherwise
""" """
original_index = subconfig.index
subconfig = self._get(subconfig, subconfig = self._get(subconfig,
need_help, need_help,
) )
@ -769,6 +769,8 @@ class _Config(CCache):
self.get_settings().validate_mandatory(subconfig, self.get_settings().validate_mandatory(subconfig,
value, value,
) )
if original_index != subconfig.index:
value = value[original_index]
return value return value
def _get(self, def _get(self,
@ -787,7 +789,7 @@ class _Config(CCache):
true_path=subconfig.path, true_path=subconfig.path,
validate_properties=validate_properties, validate_properties=validate_properties,
) )
if suboption.impl_is_follower(): if suboption.impl_is_follower() and subconfig.index is None:
subconfig = self.get_sub_config(subconfig.config_bag, # pylint: disable=no-member subconfig = self.get_sub_config(subconfig.config_bag, # pylint: disable=no-member
suboption.impl_getpath(), suboption.impl_getpath(),
None, None,
@ -805,9 +807,13 @@ class _Config(CCache):
)) ))
return ret return ret
if suboption.impl_is_leader():
index = None
else:
index = subconfig.index
s_subconfig = self.get_sub_config(subconfig.config_bag, # pylint: disable=no-member s_subconfig = self.get_sub_config(subconfig.config_bag, # pylint: disable=no-member
suboption.impl_getpath(), suboption.impl_getpath(),
None, index,
validate_properties=validate_properties, validate_properties=validate_properties,
true_path=subconfig.path, true_path=subconfig.path,
) )

View file

@ -181,7 +181,7 @@ class _CommonError:
msg = self.prefix msg = self.prefix
except AttributeError: except AttributeError:
self.prefix = self.tmpl.format(self.val, self.prefix = self.tmpl.format(self.val,
self.display_type, _(self.display_type),
self.name) self.name)
msg = self.prefix msg = self.prefix
if self.err_msg: if self.err_msg:

View file

@ -577,7 +577,7 @@ class CalcValuePropertyHelp(CalcValue):
else: else:
calc_values = [calculated_expected] calc_values = [calculated_expected]
display_value = display_list([str(val) for val in calc_values], display_value = display_list([str(val) for val in calc_values],
'or', separator='or',
add_quote=True) add_quote=True)
msg = self.build_property_message(name, display_value) msg = self.build_property_message(name, display_value)
else: else:
@ -585,7 +585,7 @@ class CalcValuePropertyHelp(CalcValue):
for key, value in calculated_expected.items(): for key, value in calculated_expected.items():
name = self.get_indexed_name(key) name = self.get_indexed_name(key)
msgs.append(self.build_property_message(name, f'"{value}"')) msgs.append(self.build_property_message(name, f'"{value}"'))
msg = display_list(msgs, self.condition_operator.lower()) msg = display_list(msgs, separator=self.condition_operator.lower())
return [(action, f'"{action}" ({msg})')] return [(action, f'"{action}" ({msg})')]

View file

@ -606,8 +606,8 @@ msgid "integer"
msgstr "nombre" msgstr "nombre"
#: tiramisu/option/intoption.py:55 #: tiramisu/option/intoption.py:55
msgid "value must be greater than \"{0}\"" msgid "value must be equal or greater than \"{0}\""
msgstr "valeur doit être supérieur à {0}" msgstr "valeur doit être supérieur ou égal à {0}"
#: tiramisu/option/intoption.py:58 #: tiramisu/option/intoption.py:58
msgid "value must be less than \"{0}\"" msgid "value must be less than \"{0}\""

View file

@ -54,9 +54,9 @@ class IntOption(Option):
min_number = self.impl_get_extra('min_number') min_number = self.impl_get_extra('min_number')
if min_number is not None and value < min_number: if min_number is not None and value < min_number:
if warnings_only: if warnings_only:
msg = 'value should be greater than "{0}"' msg = 'value should be equal or greater than "{0}"'
else: else:
msg = 'value must be greater than "{0}"' msg = 'value must be equal or greater than "{0}"'
raise ValueError(_(msg).format(min_number)) raise ValueError(_(msg).format(min_number))
max_number = self.impl_get_extra('max_number') max_number = self.impl_get_extra('max_number')
if max_number is not None and value > max_number: if max_number is not None and value > max_number:

View file

@ -57,10 +57,9 @@ class Leadership(OptionDescription):
if len(children) < 2: if len(children) < 2:
raise ValueError(_('a leader and a follower are mandatories in leadership "{}"' raise ValueError(_('a leader and a follower are mandatories in leadership "{}"'
'').format(name)) '').format(name))
leader = children[0]
for idx, child in enumerate(children): for idx, child in enumerate(children):
if __debug__: if __debug__:
self._check_child_is_valid(child) self._check_child_is_valid(child, idx, children)
if idx != 0: if idx != 0:
if __debug__: if __debug__:
self._check_default_value(child) self._check_default_value(child)
@ -70,14 +69,21 @@ class Leadership(OptionDescription):
child._add_dependency(self) child._add_dependency(self)
child._leadership = weakref.ref(self) child._leadership = weakref.ref(self)
if __debug__: if __debug__:
leader = children[0]
for prop in leader.impl_getproperties(): for prop in leader.impl_getproperties():
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation): if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
raise LeadershipError(_('leader cannot have "{}" property').format(prop)) raise LeadershipError(_('leader cannot have "{}" property').format(prop))
def _check_child_is_valid(self, child: BaseOption): def _check_child_is_valid(self,
child: BaseOption,
index: int,
children: [BaseOption],
) -> None:
if child.impl_is_symlinkoption(): if child.impl_is_symlinkoption():
raise ValueError(_('leadership "{0}" shall not have ' if not index:
"a symlinkoption").format(self.impl_get_display_name(None))) raise ValueError(_('leadership "{0}" shall not have '
"a symlinkoption").format(self.impl_get_display_name(None)))
return
if not isinstance(child, Option): if not isinstance(child, Option):
raise ValueError(_('leadership "{0}" shall not have ' raise ValueError(_('leadership "{0}" shall not have '
'a subgroup').format(self.impl_get_display_name(None))) 'a subgroup').format(self.impl_get_display_name(None)))
@ -88,6 +94,8 @@ class Leadership(OptionDescription):
child.impl_get_display_name(None))) child.impl_get_display_name(None)))
def _check_default_value(self, child: BaseOption): def _check_default_value(self, child: BaseOption):
if child.impl_is_symlinkoption():
return
default = child.impl_getdefault() default = child.impl_getdefault()
if default != []: if default != []:
if child.impl_is_submulti() and isinstance(default, (list, tuple)): if child.impl_is_submulti() and isinstance(default, (list, tuple)):
@ -257,12 +265,3 @@ class Leadership(OptionDescription):
def impl_is_leadership(self) -> None: def impl_is_leadership(self) -> None:
return True return True
#
# def to_dynoption(self,
# rootpath: str,
# suffixes: Optional[list],
# ) -> SynDynLeadership:
# return SynDynLeadership(self,
# rootpath,
# suffixes,
# )

View file

@ -29,7 +29,9 @@ from ..i18n import _
class SymLinkOption(BaseOption): class SymLinkOption(BaseOption):
"""SymLinkOption link to an other option """SymLinkOption link to an other option
""" """
__slots__ = ('_opt',) __slots__ = ('_opt',
'_leadership',
)
def __init__(self, def __init__(self,
name: str, name: str,
@ -44,6 +46,7 @@ class SymLinkOption(BaseOption):
raise ValueError(_(f'malformed symlink second parameters must be an option for "{name}", not {opt}')) raise ValueError(_(f'malformed symlink second parameters must be an option for "{name}", not {opt}'))
self._name = name self._name = name
self._opt = opt self._opt = opt
self._leadership = None
opt._add_dependency(self) opt._add_dependency(self)
def __getattr__(self, def __getattr__(self,
@ -63,8 +66,13 @@ class SymLinkOption(BaseOption):
def impl_is_leader(self) -> bool: def impl_is_leader(self) -> bool:
return False return False
def impl_is_follower(self) -> bool: def impl_is_follower(self):
return False """check if option is a leader in a follower
"""
leadership = self._leadership
if leadership is None:
return False
return not leadership().is_leader(self)
def impl_getopt(self) -> BaseOption: def impl_getopt(self) -> BaseOption:
"""get to linked option """get to linked option