add SubMulti

This commit is contained in:
Emmanuel Garette 2014-04-25 22:57:08 +02:00
parent b6a0f188b2
commit 9112a8c5b0
13 changed files with 909 additions and 86 deletions

View file

@ -1,3 +1,8 @@
XXXXXXXXXXXXX Emmanuel Garette <egarette@cadoles.com>
* add SubMulti:
a SubMulti is a multi in a multi variable
Sat Apr 12 11:37:27 CEST 2014 Emmanuel Garette <egarette@cadoles.com> Sat Apr 12 11:37:27 CEST 2014 Emmanuel Garette <egarette@cadoles.com>
* behavior change in master/slave part of code: * behavior change in master/slave part of code:

View file

@ -3,7 +3,7 @@ import autopath
#from py.test import raises #from py.test import raises
from tiramisu.config import Config, GroupConfig, MetaConfig from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.option import BoolOption, IntOption, OptionDescription from tiramisu.option import BoolOption, IntOption, StrOption, OptionDescription, submulti
import weakref import weakref
@ -137,3 +137,21 @@ def test_deref_metaconfig():
assert w() is not None assert w() is not None
del(meta) del(meta)
assert w() is None assert w() is None
def test_deref_submulti():
multi = StrOption('multi', '', multi=submulti)
od = OptionDescription('od', '', [multi])
cfg = Config(od)
cfg.cfgimpl_get_settings().remove('cache')
w = weakref.ref(cfg.multi)
assert w() is None
cfg.multi.append([])
w = weakref.ref(cfg.multi)
assert w() is None
m = cfg.multi
w = weakref.ref(m)
z = weakref.ref(w()[0])
del(m)
assert w() is None
assert z() is None

View file

@ -438,8 +438,9 @@ def test_callback_multi_callback():
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert cfg.val1.val1 == ['val'] assert cfg.val1.val1 == ['val']
cfg.val1.val1 = ['val1']
cfg.val1.val1.append() cfg.val1.val1.append()
assert cfg.val1.val1 == ['val', 'val'] assert cfg.val1.val1 == ['val1', 'val']
def test_callback_master_and_slaves_master(): def test_callback_master_and_slaves_master():

View file

@ -355,6 +355,7 @@ def test_multi_insert():
c.var.insert(0, 'nok') c.var.insert(0, 'nok')
assert c.var == ['nok', 'ok'] assert c.var == ['nok', 'ok']
assert c.getowner(var) != owners.default assert c.getowner(var) != owners.default
raises(ValueError, 'c.var.insert(0, 1)')
def test_multi_insert_master(): def test_multi_insert_master():
@ -427,6 +428,7 @@ def test_multi_extend():
c.var.extend(['pok']) c.var.extend(['pok'])
assert c.var == ['ok', 'nok', 'pok'] assert c.var == ['ok', 'nok', 'pok']
assert c.getowner(var) != owners.default assert c.getowner(var) != owners.default
raises(ValueError, 'c.var.extend([1])')
def test_multi_extend_master(): def test_multi_extend_master():

639
test/test_submulti.py Normal file
View file

@ -0,0 +1,639 @@
# coding: utf-8
import autopath
from tiramisu.setting import groups, owners
from tiramisu.config import Config
from tiramisu.option import StrOption, OptionDescription, submulti
from tiramisu.value import SubMulti, Multi
from tiramisu.error import SlaveError
from py.test import raises
def return_val():
return 'val'
def return_list(value=None):
return ['val', 'val']
def return_list2(value=None):
return [['val', 'val']]
def test_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
assert cfg.getowner(multi) == owners.default
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
assert cfg.getowner(multi) == owners.default
assert cfg.multi3 == [['yes']]
assert cfg.multi3[0] == ['yes']
assert cfg.multi3[0][0] == 'yes'
cfg.multi3[0]
assert cfg.getowner(multi) == owners.default
def test_append_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
cfg.multi.append()
assert cfg.getowner(multi) == owner
assert cfg.multi == [[]]
cfg.multi.append(['no'])
assert cfg.multi == [[], ['no']]
#
assert cfg.multi2 == []
assert cfg.getowner(multi2) == owners.default
cfg.multi2.append()
assert cfg.getowner(multi2) == owner
assert cfg.multi2 == [['yes']]
cfg.multi2.append(['no'])
assert cfg.multi2 == [['yes'], ['no']]
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
cfg.multi3.append()
assert cfg.getowner(multi3) == owner
assert cfg.multi3 == [['yes'], []]
cfg.multi3.append(['no'])
assert cfg.multi3 == [['yes'], [], ['no']]
def test_append_unvalide_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
raises(ValueError, "cfg.multi.append(1)")
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
#
assert cfg.multi2 == []
raises(ValueError, "cfg.multi2.append('no')")
assert cfg.getowner(multi) == owners.default
assert cfg.multi2 == []
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
raises(ValueError, "cfg.multi3[0].append(1)")
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
raises(ValueError, "cfg.multi3[0].append([])")
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
def test_pop_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == []
assert cfg.getowner(multi3) == owners.default
cfg.multi = [['no', 'yes'], ['peharps']]
assert cfg.getowner(multi) == owner
assert cfg.multi == [['no', 'yes'], ['peharps']]
cfg.multi[0].pop(1)
assert cfg.multi == [['no'], ['peharps']]
cfg.multi[0].pop(0)
assert cfg.multi == [[], ['peharps']]
cfg.multi.pop(1)
assert cfg.multi == [[]]
cfg.multi.pop(0)
assert cfg.multi == []
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
cfg.multi3.pop(0)
assert cfg.getowner(multi) == owner
assert cfg.multi3 == []
del(cfg.multi3)
assert cfg.getowner(multi3) == owners.default
cfg.multi3[0].pop(0)
assert cfg.getowner(multi3) == owner
assert cfg.multi3 == [[]]
def test_sort_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
cfg.multi.sort()
assert cfg.getowner(multi) == owner
cfg.multi = [['no', 'yes'], ['peharps']]
cfg.multi.sort()
assert cfg.multi == [['no', 'yes'], ['peharps']]
cfg.multi.sort(reverse=True)
assert cfg.multi == [['peharps'], ['no', 'yes']]
cfg.multi[1].sort(reverse=True)
assert cfg.multi == [['peharps'], ['yes', 'no']]
cfg.multi[1].sort()
assert cfg.multi == [['peharps'], ['no', 'yes']]
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
cfg.multi3.sort()
assert cfg.getowner(multi) == owner
assert cfg.multi3 == [['yes']]
del(cfg.multi3)
assert cfg.getowner(multi3) == owners.default
cfg.multi3[0].sort()
assert cfg.getowner(multi) == owner
assert cfg.multi3 == [['yes']]
def test_reverse_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
cfg.multi.reverse()
assert cfg.getowner(multi) == owner
cfg.multi = [['no', 'yes'], ['peharps']]
cfg.multi.reverse()
assert cfg.multi == [['peharps'], ['no', 'yes']]
cfg.multi[1].reverse()
assert cfg.multi == [['peharps'], ['yes', 'no']]
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
cfg.multi3.reverse()
assert cfg.getowner(multi) == owner
assert cfg.multi3 == [['yes']]
del(cfg.multi3)
assert cfg.getowner(multi3) == owners.default
cfg.multi3[0].reverse()
assert cfg.getowner(multi) == owner
assert cfg.multi3 == [['yes']]
def test_insert_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
cfg.multi.insert(0, ['no'])
assert cfg.getowner(multi) == owner
assert cfg.multi == [['no']]
assert isinstance(cfg.multi, Multi)
assert isinstance(cfg.multi[0], SubMulti)
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
cfg.multi3.insert(1, [])
assert cfg.getowner(multi3) == owner
assert cfg.multi3 == [['yes'], []]
cfg.multi3.insert(0, ['no'])
assert cfg.multi3 == [['no'], ['yes'], []]
del(cfg.multi3)
assert cfg.getowner(multi3) == owners.default
cfg.multi3[0].insert(0, 'no')
assert cfg.getowner(multi3) == owner
assert cfg.multi3 == [['no', 'yes']]
def test_insert_unvalide_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
raises(ValueError, "cfg.multi.insert(0, 1)")
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
raises(ValueError, "cfg.multi3[0].insert(0, 1)")
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
def test_extend_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
cfg.multi.extend([['no']])
assert cfg.getowner(multi) == owner
assert cfg.multi == [['no']]
assert isinstance(cfg.multi, Multi)
assert isinstance(cfg.multi[0], SubMulti)
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
cfg.multi3.extend([[]])
assert cfg.getowner(multi3) == owner
assert cfg.multi3 == [['yes'], []]
cfg.multi3.extend([['no']])
assert cfg.multi3 == [['yes'], [], ['no']]
del(cfg.multi3)
assert cfg.getowner(multi3) == owners.default
cfg.multi3[0].extend(['no'])
assert cfg.getowner(multi3) == owner
assert cfg.multi3 == [['yes', 'no']]
def test_extend_unvalide_submulti():
multi = StrOption('multi', '', multi=submulti)
multi2 = StrOption('multi2', '', default_multi='yes', multi=submulti)
multi3 = StrOption('multi3', '', default=[['yes']], multi=submulti)
od = OptionDescription('od', '', [multi, multi2, multi3])
cfg = Config(od)
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
raises(ValueError, "cfg.multi.extend([[1]])")
assert cfg.multi == []
assert cfg.getowner(multi) == owners.default
#
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
raises(ValueError, "cfg.multi3[0].extend([1])")
assert cfg.multi3 == [['yes']]
assert cfg.getowner(multi3) == owners.default
def test_callback_submulti_str():
multi = StrOption('multi', '', multi=submulti, callback=return_val)
od = OptionDescription('od', '', [multi])
cfg = Config(od)
cfg.read_write()
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.getowner(multi) == owners.default
assert cfg.multi == [['val']]
cfg.multi.append()
assert cfg.getowner(multi) == owner
assert cfg.multi == [['val'], ['val']]
del(cfg.multi)
assert cfg.getowner(multi) == owners.default
cfg.multi[0].append()
assert cfg.getowner(multi) == owner
assert cfg.multi == [['val', 'val']]
def test_callback_submulti_list():
multi = StrOption('multi', '', multi=submulti, callback=return_list)
od = OptionDescription('od', '', [multi])
cfg = Config(od)
cfg.read_write()
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == [['val', 'val']]
assert cfg.getowner(multi) == owners.default
cfg.multi.append()
assert cfg.getowner(multi) == owner
assert cfg.multi == [['val', 'val'], ['val', 'val']]
del(cfg.multi)
assert cfg.getowner(multi) == owners.default
cfg.multi[0].append()
assert cfg.getowner(multi) == owner
assert cfg.multi == [['val', 'val', None]]
def test_callback_submulti_list_list():
multi = StrOption('multi', '', multi=submulti, callback=return_list2)
od = OptionDescription('od', '', [multi])
cfg = Config(od)
cfg.read_write()
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.multi == [['val', 'val']]
assert cfg.getowner(multi) == owners.default
cfg.multi.append()
assert cfg.getowner(multi) == owner
assert cfg.multi == [['val', 'val'], []]
del(cfg.multi)
assert cfg.getowner(multi) == owners.default
cfg.multi[0].append()
assert cfg.getowner(multi) == owner
assert cfg.multi == [['val', 'val', None]]
#FIXME multi sur une master
def test_groups_with_master_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
assert interface1.impl_get_group_type() == groups.master
def test_groups_with_master_in_config_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
Config(interface1)
assert interface1.impl_get_group_type() == groups.master
def test_values_with_master_and_slaves_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
owner = cfg.cfgimpl_get_settings().getowner()
assert interface1.impl_get_group_type() == groups.master
assert cfg.getowner(ip_admin_eth0) == owners.default
assert cfg.getowner(netmask_admin_eth0) == owners.default
assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
assert cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"]
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [[]]
assert cfg.getowner(ip_admin_eth0) == owner
assert cfg.getowner(netmask_admin_eth0) == owners.default
cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.147"]
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [[], []]
raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.append(None)')
raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.pop(0)')
cfg.ip_admin_eth0.netmask_admin_eth0[0].append('255.255.255.0')
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [['255.255.255.0'], []]
cfg.ip_admin_eth0.netmask_admin_eth0[0].pop(0)
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [[], []]
raises(ValueError, 'cfg.ip_admin_eth0.netmask_admin_eth0 = ["255.255.255.0", "255.255.255.0"]')
def test_reset_values_with_master_and_slaves_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
owner = cfg.cfgimpl_get_settings().getowner()
assert interface1.impl_get_group_type() == groups.master
assert cfg.getowner(ip_admin_eth0) == owners.default
assert cfg.getowner(netmask_admin_eth0) == owners.default
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
assert cfg.getowner(ip_admin_eth0) == owner
assert cfg.getowner(netmask_admin_eth0) == owners.default
del(cfg.ip_admin_eth0.ip_admin_eth0)
assert cfg.getowner(ip_admin_eth0) == owners.default
assert cfg.getowner(netmask_admin_eth0) == owners.default
assert cfg.ip_admin_eth0.ip_admin_eth0 == []
assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
#
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.netmask_admin_eth0[0].append('255.255.255.0')
assert cfg.getowner(ip_admin_eth0) == owner
assert cfg.getowner(netmask_admin_eth0) == owner
del(cfg.ip_admin_eth0.ip_admin_eth0)
assert cfg.getowner(ip_admin_eth0) == owners.default
assert cfg.getowner(netmask_admin_eth0) == owners.default
assert cfg.ip_admin_eth0.ip_admin_eth0 == []
assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
def test_values_with_master_and_slaves_slave_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0']]")
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0']]
cfg.ip_admin_eth0.netmask_admin_eth0[0] = ['255.255.255.0']
cfg.ip_admin_eth0.netmask_admin_eth0[0][0] = '255.255.255.0'
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0'], ['255.255.255.0']]")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = []")
del(cfg.ip_admin_eth0.netmask_admin_eth0)
cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0']]
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [['255.255.255.0'], []]
cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0'], ['255.255.255.0']]
raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.pop(1)')
def test_values_with_master_and_slaves_master_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145"]
cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.145"]
cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0'], ['255.255.255.0']]
raises(SlaveError, 'cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145"]')
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [['255.255.255.0'], ['255.255.255.0']]
cfg.ip_admin_eth0.ip_admin_eth0.pop(1)
assert cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"]
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [['255.255.255.0']]
del(cfg.ip_admin_eth0.ip_admin_eth0)
assert cfg.ip_admin_eth0.ip_admin_eth0 == []
assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
def test_values_with_master_and_slaves_master_error_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.145"]
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0']]")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0'], ['255.255.255.0'], ['255.255.255.0']]")
cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0'], ['255.255.255.0']]
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0']]")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = [['255.255.255.0'], ['255.255.255.0'], ['255.255.255.0']]")
def test_values_with_master_owner_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
owner = cfg.cfgimpl_get_settings().getowner()
assert cfg.getowner(ip_admin_eth0) == owners.default
assert cfg.getowner(netmask_admin_eth0) == owners.default
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
assert cfg.getowner(ip_admin_eth0) == owner
assert cfg.getowner(netmask_admin_eth0) == owners.default
cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
assert cfg.getowner(ip_admin_eth0) == owner
assert cfg.getowner(netmask_admin_eth0) == owners.default
def test_values_with_master_disabled_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.netmask_admin_eth0 = [["192.168.230.145"]]
cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
del(cfg.ip_admin_eth0.netmask_admin_eth0)
cfg.cfgimpl_get_settings()[netmask_admin_eth0].append('disabled')
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
#delete with value in disabled var
cfg.cfgimpl_get_settings()[netmask_admin_eth0].remove('disabled')
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.netmask_admin_eth0 = [["192.168.230.145"]]
cfg.cfgimpl_get_settings()[netmask_admin_eth0].append('disabled')
cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
#append with value in disabled var
cfg.cfgimpl_get_settings()[netmask_admin_eth0].remove('disabled')
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
cfg.ip_admin_eth0.netmask_admin_eth0 = [["192.168.230.145"]]
cfg.cfgimpl_get_settings()[netmask_admin_eth0].append('disabled')
cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.230.43')
def test_multi_insert_master_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.insert(0, 'nok')")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.insert(0, 'nok')")
cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0[0].insert(0, 'nok')")
def test_multi_sort_master_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.sort()")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.sort()")
cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0[0].sort()")
def test_multi_reverse_master_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.reverse()")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.reverse()")
cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0[0].reverse()")
def test_multi_extend_master_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.extend(['ok'])")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.extend(['ok'])")
cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0[0].extend(['ok'])")
def test_slave_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.ip_admin_eth0.ip_admin_eth0.__class__.__name__ == 'Multi'
assert cfg.ip_admin_eth0.netmask_admin_eth0.__class__.__name__ == 'Multi'
cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
assert cfg.ip_admin_eth0.ip_admin_eth0.__class__.__name__ == 'Multi'
assert cfg.ip_admin_eth0.netmask_admin_eth0.__class__.__name__ == 'Multi'
assert cfg.ip_admin_eth0.ip_admin_eth0[0].__class__.__name__ == 'str'
assert cfg.ip_admin_eth0.netmask_admin_eth0[0].__class__.__name__ == 'SubMulti'
def test__master_is_submulti():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=submulti)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
owner = cfg.cfgimpl_get_settings().getowner()
assert interface1.impl_get_group_type() == groups.master
assert cfg.getowner(ip_admin_eth0) == owners.default
assert cfg.getowner(netmask_admin_eth0) == owners.default
assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
cfg.ip_admin_eth0.ip_admin_eth0.append(["192.168.230.145"])
assert cfg.ip_admin_eth0.ip_admin_eth0 == [["192.168.230.145"]]
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [None]
assert cfg.getowner(ip_admin_eth0) == owner
assert cfg.getowner(netmask_admin_eth0) == owners.default
cfg.ip_admin_eth0.ip_admin_eth0 = [["192.168.230.145"], ["192.168.230.147"]]
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [None, None]
raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.append(None)')
raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.pop(0)')
cfg.ip_admin_eth0.ip_admin_eth0[0].append('192.168.1.1')
assert cfg.ip_admin_eth0.ip_admin_eth0 == [["192.168.230.145", '192.168.1.1'], ["192.168.230.147"]]
cfg.ip_admin_eth0.ip_admin_eth0[0].pop(0)
assert cfg.ip_admin_eth0.ip_admin_eth0 == [["192.168.1.1"], ["192.168.230.147"]]
raises(ValueError, 'cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.1.1", "192.168.1.1"]')

View file

@ -1,6 +1,6 @@
from .masterslave import MasterSlaves from .masterslave import MasterSlaves
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .baseoption import Option, SymLinkOption from .baseoption import Option, SymLinkOption, submulti
from .option import (ChoiceOption, BoolOption, IntOption, FloatOption, from .option import (ChoiceOption, BoolOption, IntOption, FloatOption,
StrOption, UnicodeOption, IPOption, PortOption, StrOption, UnicodeOption, IPOption, PortOption,
NetworkOption, NetmaskOption, BroadcastOption, NetworkOption, NetmaskOption, BroadcastOption,
@ -13,4 +13,4 @@ __all__ = ('MasterSlaves', 'OptionDescription', 'Option', 'SymLinkOption',
'StrOption', 'UnicodeOption', 'IPOption', 'PortOption', 'StrOption', 'UnicodeOption', 'IPOption', 'PortOption',
'NetworkOption', 'NetmaskOption', 'BroadcastOption', 'NetworkOption', 'NetmaskOption', 'BroadcastOption',
'DomainnameOption', 'EmailOption', 'URLOption', 'UsernameOption', 'DomainnameOption', 'EmailOption', 'URLOption', 'UsernameOption',
'FilenameOption') 'FilenameOption', 'submulti')

View file

@ -32,6 +32,14 @@ from tiramisu.storage import get_storages_option
StorageBase = get_storages_option('base') StorageBase = get_storages_option('base')
class SubMulti(object):
pass
submulti = SubMulti()
name_regexp = re.compile(r'^\d+') name_regexp = re.compile(r'^\d+')
forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first', forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
'make_dict', 'unwrap_from_path', 'read_only', 'make_dict', 'unwrap_from_path', 'read_only',
@ -123,7 +131,7 @@ class Base(StorageBase):
"for option {1}: {2}").format( "for option {1}: {2}").format(
str(default_multi), name, err)) str(default_multi), name, err))
self._multi = multi self._multi = multi
if self._multi: if self._multi is not False:
if default is None: if default is None:
default = [] default = []
self._default_multi = default_multi self._default_multi = default_multi
@ -414,7 +422,7 @@ class Option(OnlyOption):
return self._requires return self._requires
def _launch_consistency(self, func, option, value, context, index, def _launch_consistency(self, func, option, value, context, index,
all_cons_opts, warnings_only): submulti_index, all_cons_opts, warnings_only):
"""Launch consistency now """Launch consistency now
:param func: function name, this name should start with _cons_ :param func: function name, this name should start with _cons_
@ -452,17 +460,24 @@ class Option(OnlyOption):
#append value #append value
if not self.impl_is_multi() or option == opt: if not self.impl_is_multi() or option == opt:
all_cons_vals.append(opt_value) all_cons_vals.append(opt_value)
else: elif self.impl_is_submulti():
try:
all_cons_vals.append(opt_value[index][submulti_index])
except IndexError:
#value is not already set, could be higher index #value is not already set, could be higher index
#so return if no value
return
else:
try: try:
all_cons_vals.append(opt_value[index]) all_cons_vals.append(opt_value[index])
except IndexError: except IndexError:
#value is not already set, could be higher index
#so return if no value #so return if no value
return return
getattr(self, func)(all_cons_opts, all_cons_vals, warnings_only) getattr(self, func)(all_cons_opts, all_cons_vals, warnings_only)
def impl_validate(self, value, context=None, validate=True, def impl_validate(self, value, context=None, validate=True,
force_index=None): force_index=None, force_submulti_index=None):
""" """
:param value: the option's value :param value: the option's value
:param context: Config's context :param context: Config's context
@ -472,6 +487,9 @@ class Option(OnlyOption):
:param force_index: if multi, value has to be a list :param force_index: if multi, value has to be a list
not if force_index is not None not if force_index is not None
:type force_index: integer :type force_index: integer
:param force_submulti_index: if submulti, value has to be a list
not if force_submulti_index is not None
:type force_submulti_index: integer
""" """
if not validate: if not validate:
return return
@ -496,15 +514,17 @@ class Option(OnlyOption):
callback=self._validator, callback=self._validator,
callback_params=validator_params) callback_params=validator_params)
def do_validation(_value, _index=None): def do_validation(_value, _index, submulti_index):
if _value is None: if _value is None:
return return
# option validation # option validation
try: try:
self._validate(_value) self._validate(_value)
except ValueError as err: except ValueError as err:
log.debug('do_validation: value: {0} index: {1}'.format( log.debug('do_validation: value: {0}, index: {1}, '
_value, _index), exc_info=True) 'submulti_index: {2}'.format(_value, _index,
submulti_index),
exc_info=True)
raise ValueError(_('invalid value for option {0}: {1}' raise ValueError(_('invalid value for option {0}: {1}'
'').format(self.impl_getname(), err)) '').format(self.impl_getname(), err))
error = None error = None
@ -514,7 +534,8 @@ class Option(OnlyOption):
val_validator(_value) val_validator(_value)
# if not context launch consistency validation # if not context launch consistency validation
if context is not None: if context is not None:
descr._valid_consistency(self, _value, context, _index) descr._valid_consistency(self, _value, context, _index,
submulti_index)
self._second_level_validation(_value, self._warnings_only) self._second_level_validation(_value, self._warnings_only)
except ValueError as error: except ValueError as error:
log.debug(_('do_validation for {0}: error in value').format( log.debug(_('do_validation for {0}: error in value').format(
@ -530,7 +551,8 @@ class Option(OnlyOption):
try: try:
# if context launch consistency validation # if context launch consistency validation
if context is not None: if context is not None:
descr._valid_consistency(self, _value, context, _index) descr._valid_consistency(self, _value, context, _index,
submulti_index)
except ValueError as error: except ValueError as error:
log.debug(_('do_validation for {0}: error in consistency').format( log.debug(_('do_validation for {0}: error in consistency').format(
self.impl_getname()), exc_info=True) self.impl_getname()), exc_info=True)
@ -553,13 +575,34 @@ class Option(OnlyOption):
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_index is not None: if not self.impl_is_multi():
do_validation(value, force_index) do_validation(value, None, None)
elif force_index is not None:
if self.impl_is_submulti() and force_submulti_index is None:
if not isinstance(value, list):
raise ValueError(_("invalid value {0} for option {1} which"
" must be a list").format(
value, self.impl_getname()))
for idx, val in enumerate(value):
do_validation(val, force_index, idx)
else:
do_validation(value, force_index, force_submulti_index)
else: else:
if not isinstance(value, list): if not isinstance(value, list):
raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self.impl_getname())) raise ValueError(_("invalid value {0} for option {1} which "
for index, val in enumerate(value): "must be a list").format(value,
do_validation(val, index) self.impl_getname()))
for idx, val in enumerate(value):
if self.impl_is_submulti() and force_submulti_index is None:
if not isinstance(val, list):
raise ValueError(_("invalid value {0} for option {1} "
"which must be a list of list"
"").format(value,
self.impl_getname()))
for slave_idx, slave_val in enumerate(val):
do_validation(slave_val, idx, slave_idx)
else:
do_validation(val, idx, force_submulti_index)
def impl_getdefault(self): def impl_getdefault(self):
"accessing the default value" "accessing the default value"
@ -615,7 +658,10 @@ class Option(OnlyOption):
# return value # return value
def impl_is_multi(self): def impl_is_multi(self):
return self._multi return self._multi is True or self._multi is submulti
def impl_is_submulti(self):
return self._multi is submulti
def impl_add_consistency(self, func, *other_opts, **params): def impl_add_consistency(self, func, *other_opts, **params):
"""Add consistency means that value will be validate with other_opts """Add consistency means that value will be validate with other_opts
@ -647,10 +693,18 @@ class Option(OnlyOption):
if value is not None: if value is not None:
if self.impl_is_multi(): if self.impl_is_multi():
for idx, val in enumerate(value): for idx, val in enumerate(value):
self._launch_consistency(func, self, val, None, if not self.impl_is_submulti():
idx, all_cons_opts, warnings_only) self._launch_consistency(func, self, val, None, idx,
None, all_cons_opts,
warnings_only)
else: else:
self._launch_consistency(func, self, value, None, for slave_idx, val in enumerate(value):
self._launch_consistency(func, self, val, None,
idx, slave_idx,
all_cons_opts,
warnings_only)
else:
self._launch_consistency(func, self, value, None, None,
None, all_cons_opts, warnings_only) None, all_cons_opts, warnings_only)
self._add_consistency(func, all_cons_opts, params) self._add_consistency(func, all_cons_opts, params)
self.impl_validate(self.impl_getdefault()) self.impl_validate(self.impl_getdefault())
@ -808,7 +862,8 @@ def validate_requires_arg(requires, name):
'must be an option in option {0}').format(name)) 'must be an option in option {0}').format(name))
if option.impl_is_multi(): if option.impl_is_multi():
raise ValueError(_('malformed requirements option {0} ' raise ValueError(_('malformed requirements option {0} '
'must not be a multi').format(name)) 'must not be a multi for {1}').format(
option.impl_getname(), name))
if expected is not None: if expected is not None:
try: try:
option._validate(expected) option._validate(expected)

View file

@ -20,7 +20,7 @@
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.setting import log from tiramisu.setting import log, undefined
from tiramisu.error import SlaveError, ConfigError from tiramisu.error import SlaveError, ConfigError
from .baseoption import SymLinkOption, Option from .baseoption import SymLinkOption, Option
@ -87,18 +87,21 @@ class MasterSlaves(object):
pass pass
def getitem(self, values, opt, path, validate, force_permissive, def getitem(self, values, opt, path, validate, force_permissive,
force_properties, validate_properties): force_properties, validate_properties, slave_path=undefined,
slave_value=undefined):
if opt == self.master: if opt == self.master:
return self._getmaster(values, opt, path, validate, return self._getmaster(values, opt, path, validate,
force_permissive, force_properties, force_permissive, force_properties,
validate_properties) validate_properties, slave_path,
slave_value)
else: else:
return self._getslave(values, opt, path, validate, return self._getslave(values, opt, path, validate,
force_permissive, force_properties, force_permissive, force_properties,
validate_properties) validate_properties)
def _getmaster(self, values, opt, path, validate, force_permissive, def _getmaster(self, values, opt, path, validate, force_permissive,
force_properties, validate_properties): force_properties, validate_properties, c_slave_path,
c_slave_value):
value = values._get_validated_value(opt, path, validate, value = values._get_validated_value(opt, path, validate,
force_permissive, force_permissive,
force_properties, force_properties,
@ -108,6 +111,9 @@ class MasterSlaves(object):
for slave in self.slaves: for slave in self.slaves:
try: try:
slave_path = values._get_opt_path(slave) slave_path = values._get_opt_path(slave)
if c_slave_path == slave_path:
slave_value = c_slave_value
else:
slave_value = values._get_validated_value(slave, slave_value = values._get_validated_value(slave,
slave_path, slave_path,
False, False,
@ -146,10 +152,11 @@ class MasterSlaves(object):
self.validate_slave_length(self.get_length(values), len(value), self.validate_slave_length(self.get_length(values), len(value),
opt._name, setitem=True) opt._name, setitem=True)
def get_length(self, values, validate=True): def get_length(self, values, validate=True, slave_path=undefined,
slave_value=undefined):
masterp = values._get_opt_path(self.master) masterp = values._get_opt_path(self.master)
return len(self.getitem(values, self.master, masterp, validate, False, return len(self.getitem(values, self.master, masterp, validate, False,
None, True)) None, True, slave_path, slave_value))
def validate_slave_length(self, masterlen, valuelen, name, setitem=False): def validate_slave_length(self, masterlen, valuelen, name, setitem=False):
if valuelen > masterlen or (valuelen < masterlen and setitem): if valuelen > masterlen or (valuelen < masterlen and setitem):
@ -183,11 +190,11 @@ class MasterSlaves(object):
list is greater than master: raise SlaveError list is greater than master: raise SlaveError
""" """
#if slave, had values until master's one #if slave, had values until master's one
masterlen = self.get_length(values, validate) path = values._get_opt_path(opt)
masterlen = self.get_length(values, validate, path, value)
valuelen = len(value) valuelen = len(value)
if validate: if validate:
self.validate_slave_length(masterlen, valuelen, opt._name) self.validate_slave_length(masterlen, valuelen, opt._name)
path = values._get_opt_path(opt)
if valuelen < masterlen: if valuelen < masterlen:
for num in range(0, masterlen - valuelen): for num in range(0, masterlen - valuelen):
index = valuelen + num index = valuelen + num

View file

@ -134,7 +134,8 @@ class IPOption(Option):
callback_params=None, validator=None, validator_params=None, callback_params=None, validator=None, validator_params=None,
properties=None, private_only=False, allow_reserved=False, properties=None, private_only=False, allow_reserved=False,
warnings_only=False): warnings_only=False):
self._extra = {'_private_only': private_only, '_allow_reserved': allow_reserved} self._extra = {'_private_only': private_only,
'_allow_reserved': allow_reserved}
super(IPOption, self).__init__(name, doc, default=default, super(IPOption, self).__init__(name, doc, default=default,
default_multi=default_multi, default_multi=default_multi,
callback=callback, callback=callback,

View file

@ -21,7 +21,7 @@
from copy import copy from copy import copy
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.setting import groups, log from tiramisu.setting import groups # , log
from .baseoption import BaseOption from .baseoption import BaseOption
from . import MasterSlaves from . import MasterSlaves
from tiramisu.error import ConfigError, ConflictError, ValueWarning from tiramisu.error import ConfigError, ConflictError, ValueWarning
@ -162,7 +162,7 @@ class OptionDescription(BaseOption, StorageOptionDescription):
def impl_get_group_type(self): def impl_get_group_type(self):
return self._group_type return self._group_type
def _valid_consistency(self, option, value, context, index): def _valid_consistency(self, option, value, context, index, submulti_idx):
if self._cache_consistencies is None: if self._cache_consistencies is None:
return True return True
#consistencies is something like [('_cons_not_equal', (opt1, opt2))] #consistencies is something like [('_cons_not_equal', (opt1, opt2))]
@ -175,6 +175,7 @@ class OptionDescription(BaseOption, StorageOptionDescription):
all_cons_opts[0]._launch_consistency(func, option, all_cons_opts[0]._launch_consistency(func, option,
value, value,
context, index, context, index,
submulti_idx,
all_cons_opts, all_cons_opts,
warnings_only) warnings_only)
except ValueError as err: except ValueError as err:

View file

@ -223,7 +223,7 @@ populate_owners()
# ____________________________________________________________ # ____________________________________________________________
class Undefined(): class Undefined(object):
pass pass

View file

@ -28,8 +28,8 @@ class Base(object):
__slots__ = ('_name', '_requires', '_properties', '_readonly', __slots__ = ('_name', '_requires', '_properties', '_readonly',
'_calc_properties', '_informations', '_calc_properties', '_informations',
'_state_readonly', '_state_requires', '_stated', '_state_readonly', '_state_requires', '_stated',
'_multi', '_validator', '_validator_params', '_default', '_multi', '_validator', '_validator_params',
'_default_multi', '_state_callback', '_callback', '_default', '_default_multi', '_state_callback', '_callback',
'_state_callback_params', '_callback_params', '_multitype', '_state_callback_params', '_callback_params', '_multitype',
'_consistencies', '_warnings_only', '_master_slaves', '_consistencies', '_warnings_only', '_master_slaves',
'_state_consistencies', '_extra', '__weakref__') '_state_consistencies', '_extra', '__weakref__')

View file

@ -86,7 +86,13 @@ class Values(object):
index=index) index=index)
try: try:
if isinstance(value, list) and index is not undefined: if isinstance(value, list) and index is not undefined:
return value[index] #if submulti and return a list of list, just return list
if opt.impl_is_submulti():
val = value[index]
if isinstance(val, list):
value = val
else:
value = value[index]
return value return value
except IndexError: except IndexError:
pass pass
@ -219,9 +225,10 @@ class Values(object):
def _get_validated_value(self, opt, path, validate, force_permissive, def _get_validated_value(self, opt, path, validate, force_permissive,
force_properties, validate_properties, force_properties, validate_properties,
index=undefined): index=undefined, submulti_index=undefined):
"""same has getitem but don't touch the cache""" """same has getitem but don't touch the cache
#FIXME expliquer la différence entre index == undefined et index == None index is None for slave value, if value returned is not a list, just return []
"""
context = self._getcontext() context = self._getcontext()
setting = context.cfgimpl_get_settings() setting = context.cfgimpl_get_settings()
is_default = self._is_default_owner(opt, path, is_default = self._is_default_owner(opt, path,
@ -254,13 +261,24 @@ class Values(object):
else: else:
force_index = index force_index = index
if opt.impl_is_multi(): if opt.impl_is_multi():
#for slave is a multi
if index is None and not isinstance(value, list): if index is None and not isinstance(value, list):
value = [] value = []
if force_index is None: if force_index is None:
value = Multi(value, self.context, opt, path) value = Multi(value, self.context, opt, path)
elif opt.impl_is_submulti() and submulti_index is undefined:
value = SubMulti(value, self.context, opt, path,
force_index)
if validate: if validate:
if submulti_index is undefined:
force_submulti_index = None
else:
force_submulti_index = submulti_index
opt.impl_validate(value, context, 'validator' in setting, opt.impl_validate(value, context, 'validator' in setting,
force_index=force_index) force_index=force_index,
force_submulti_index=force_submulti_index)
#FIXME pas de test avec les metas ... #FIXME pas de test avec les metas ...
#FIXME et les symlinkoption ... #FIXME et les symlinkoption ...
if is_default and 'force_store_value' in setting[opt]: if is_default and 'force_store_value' in setting[opt]:
@ -291,7 +309,7 @@ class Values(object):
opt.impl_validate(value, context, opt.impl_validate(value, context,
'validator' in context.cfgimpl_get_settings()) 'validator' in context.cfgimpl_get_settings())
if opt.impl_is_multi(): if opt.impl_is_multi():
value = Multi(value, self.context, opt, path) #value = Multi(value, self.context, opt, path)
if opt.impl_is_master_slaves(): if opt.impl_is_master_slaves():
opt.impl_get_master_slaves().setitem(self, opt, value, path) opt.impl_get_master_slaves().setitem(self, opt, value, path)
self._setvalue(opt, path, value, force_permissive=force_permissive, self._setvalue(opt, path, value, force_permissive=force_permissive,
@ -309,6 +327,10 @@ class Values(object):
owner = context.cfgimpl_get_settings().getowner() owner = context.cfgimpl_get_settings().getowner()
if isinstance(value, Multi): if isinstance(value, Multi):
value = list(value) value = list(value)
if opt.impl_is_submulti():
for idx, val in enumerate(value):
if isinstance(val, SubMulti):
value[idx] = list(val)
self._p_.setvalue(path, value, owner) self._p_.setvalue(path, value, owner)
def getowner(self, opt, force_permissive=False): def getowner(self, opt, force_permissive=False):
@ -471,28 +493,47 @@ class Values(object):
# ____________________________________________________________ # ____________________________________________________________
# multi types # multi types
class Multi(list): class Multi(list):
"""multi options values container """multi options values container
that support item notation for the values of multi options""" that support item notation for the values of multi options"""
__slots__ = ('opt', 'path', 'context') __slots__ = ('opt', 'path', 'context', '__weakref__')
def __init__(self, value, context, opt, path): def __init__(self, value, context, opt, path):
""" """
:param value: the Multi wraps a list value :param value: the Multi wraps a list value
:param context: the home config that has the values :param context: the home config that has the values
:param opt: the option object that have this Multi value :param opt: the option object that have this Multi value
:param path: path of the option
""" """
if isinstance(value, Multi): if value is None:
raise ValueError(_('{0} is already a Multi ').format(opt.impl_getname())) value = []
if not opt.impl_is_submulti() and isinstance(value, Multi):
raise ValueError(_('{0} is already a Multi ').format(
opt.impl_getname()))
self.opt = opt self.opt = opt
self.path = path self.path = path
if not isinstance(context, weakref.ReferenceType): if not isinstance(context, weakref.ReferenceType):
raise ValueError('context must be a Weakref') raise ValueError('context must be a Weakref')
self.context = context self.context = context
if not isinstance(value, list): if not isinstance(value, list):
if not '_index' in self.__slots__ and opt.impl_is_submulti():
value = [[value]]
else:
value = [value]
elif value != [] and not '_index' in self.__slots__ and \
opt.impl_is_submulti() and not isinstance(value[0], list):
value = [value] value = [value]
super(Multi, self).__init__(value) super(Multi, self).__init__(value)
if opt.impl_is_submulti():
if not '_index' in self.__slots__:
for idx, val in enumerate(self):
if not isinstance(val, SubMulti):
super(Multi, self).__setitem__(idx, SubMulti(val,
context,
opt, path,
idx))
#FIXME weakref ??
self[idx].submulti = weakref.ref(self)
def _getcontext(self): def _getcontext(self):
"""context could be None, we need to test it """context could be None, we need to test it
@ -506,24 +547,32 @@ class Multi(list):
return context return context
def __setitem__(self, index, value): def __setitem__(self, index, value):
self._validate(value, index) self._setitem(index, value)
def _setitem(self, index, value):
self._validate(value, index, True)
#assume not checking mandatory property #assume not checking mandatory property
super(Multi, self).__setitem__(index, value) super(Multi, self).__setitem__(index, value)
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
self) self)
def __repr__(self, *args, **kwargs): #def __repr__(self, *args, **kwargs):
print args, kwargs # print args, kwargs
return super(Multi, self).__repr__(*args, **kwargs) # return super(Multi, self).__repr__(*args, **kwargs)
def __getitem__(self, y): #def __getitem__(self, y):
return super(Multi, self).__getitem__(y) # return super(Multi, self).__getitem__(y)
def _get_validated_value(self, index):
values = self._getcontext().cfgimpl_get_values()
return values._get_validated_value(self.opt, self.path,
True, False, None, True,
index=index)
def append(self, value=undefined, force=False, setitem=True): def append(self, value=undefined, force=False, setitem=True):
"""the list value can be updated (appened) """the list value can be updated (appened)
only if the option is a master only if the option is a master
""" """
values = self._getcontext().cfgimpl_get_values()
if not force: if not force:
if self.opt.impl_is_master_slaves('slave'): if self.opt.impl_is_master_slaves('slave'):
raise SlaveError(_("cannot append a value on a multi option {0}" raise SlaveError(_("cannot append a value on a multi option {0}"
@ -531,16 +580,17 @@ class Multi(list):
index = self.__len__() index = self.__len__()
if value is undefined: if value is undefined:
try: try:
value = values._get_validated_value(self.opt, self.path, value = self._get_validated_value(index)
True, False, None, True,
index=index)
except IndexError: except IndexError:
value = None value = None
self._validate(value, index) self._validate(value, index, True)
if not '_index' in self.__slots__ and self.opt.impl_is_submulti():
if not isinstance(value, SubMulti):
value = SubMulti(value, self.context, self.opt, self.path, index)
value.submulti = weakref.ref(self)
super(Multi, self).append(value) super(Multi, self).append(value)
if setitem: if setitem:
values._setvalue(self.opt, self.path, self, self._store(force)
validate_properties=not force)
def sort(self, cmp=None, key=None, reverse=False): def sort(self, cmp=None, key=None, reverse=False):
if self.opt.impl_is_master_slaves(): if self.opt.impl_is_master_slaves():
@ -553,43 +603,40 @@ class Multi(list):
super(Multi, self).sort(key=key, reverse=reverse) super(Multi, self).sort(key=key, reverse=reverse)
else: else:
super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse) super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse)
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self._store()
self)
def reverse(self): def reverse(self):
if self.opt.impl_is_master_slaves(): if self.opt.impl_is_master_slaves():
raise SlaveError(_("cannot reverse multi option {0} if master or " raise SlaveError(_("cannot reverse multi option {0} if master or "
"slave").format(self.opt.impl_getname())) "slave").format(self.opt.impl_getname()))
super(Multi, self).reverse() super(Multi, self).reverse()
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self._store()
self)
def insert(self, index, obj): def insert(self, index, obj):
#FIXME obj should be undefined
if self.opt.impl_is_master_slaves(): if self.opt.impl_is_master_slaves():
raise SlaveError(_("cannot insert multi option {0} if master or " raise SlaveError(_("cannot insert multi option {0} if master or "
"slave").format(self.opt.impl_getname())) "slave").format(self.opt.impl_getname()))
self._validate(obj, index, True)
super(Multi, self).insert(index, obj) super(Multi, self).insert(index, obj)
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self._store()
self)
def extend(self, iterable): def extend(self, iterable):
if self.opt.impl_is_master_slaves(): if self.opt.impl_is_master_slaves():
raise SlaveError(_("cannot extend multi option {0} if master or " raise SlaveError(_("cannot extend multi option {0} if master or "
"slave").format(self.opt.impl_getname())) "slave").format(self.opt.impl_getname()))
super(Multi, self).extend(iterable)
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
self)
def _validate(self, value, force_index):
if value is not None:
try: try:
index = self._index
except:
index = None
self._validate(iterable, index)
super(Multi, self).extend(iterable)
self._store()
def _validate(self, value, force_index, submulti=False):
if value is not None:
self.opt.impl_validate(value, context=self._getcontext(), self.opt.impl_validate(value, context=self._getcontext(),
force_index=force_index) force_index=force_index)
except ValueError as err:
raise ValueError(_("invalid value {0} "
"for option {1}: {2}"
"").format(str(value),
self.opt.impl_getname(), err))
def pop(self, index, force=False): def pop(self, index, force=False):
"""the list value can be updated (poped) """the list value can be updated (poped)
@ -611,6 +658,53 @@ class Multi(list):
context.cfgimpl_get_values(), index) context.cfgimpl_get_values(), index)
#set value without valid properties #set value without valid properties
ret = super(Multi, self).pop(index) ret = super(Multi, self).pop(index)
context.cfgimpl_get_values()._setvalue(self.opt, self.path, self, self._store(force)
validate_properties=not force)
return ret return ret
def _store(self, force=False):
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
self,
validate_properties=not force)
class SubMulti(Multi):
__slots__ = ('_index', 'submulti')
def __init__(self, value, context, opt, path, index):
"""
:param index: index (only for slave with submulti)
:type index: `int`
"""
self._index = index
super(SubMulti, self).__init__(value, context, opt, path)
def append(self, value=undefined):
super(SubMulti, self).append(value, force=True)
def pop(self, index):
return super(SubMulti, self).pop(index, force=True)
def __setitem__(self, index, value):
self._setitem(index, value)
def _store(self, force=False):
#force is unused here
self._getcontext().cfgimpl_get_values()._setvalue(self.opt,
self.path,
self.submulti())
def _validate(self, value, force_index, submulti=False):
if value is not None:
if submulti is False:
super(SubMulti, self)._validate(value, force_index)
else:
self.opt.impl_validate(value, context=self._getcontext(),
force_index=self._index,
force_submulti_index=force_index)
def _get_validated_value(self, index):
values = self._getcontext().cfgimpl_get_values()
return values._get_validated_value(self.opt, self.path,
True, False, None, True,
index=index,
submulti_index=self._index)