split tiramisu/option.py and add MasterSlaves object
This commit is contained in:
parent
d1e86f6d48
commit
cd4d3527c7
|
@ -0,0 +1,10 @@
|
||||||
|
Sat Apr 12 11:37:27 CEST 2014 Emmanuel Garette <egarette@cadoles.com>
|
||||||
|
|
||||||
|
* behavior change in master/slave part of code:
|
||||||
|
if slave has a default value greater than master's one, it's raise
|
||||||
|
SlaveError, didn't try to reduce the slave's length
|
||||||
|
* tiramisu/option.py: split into tiramisu/option directory
|
||||||
|
* tiramisu/option/masterslave.py: master/slaves have no a special
|
||||||
|
object MasterSlaves for all code related to master/slaves options
|
||||||
|
* tiramisu/option/masterslave.py: master and slaves values (length,
|
||||||
|
consistency, ...) are now check every time
|
|
@ -1,8 +1,8 @@
|
||||||
import autopath
|
import autopath
|
||||||
from py.test import raises
|
from py.test import raises
|
||||||
|
|
||||||
from tiramisu.setting import groups
|
|
||||||
from tiramisu.config import Config
|
from tiramisu.config import Config
|
||||||
|
from tiramisu.setting import groups, owners
|
||||||
from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
|
from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
|
||||||
StrOption, OptionDescription, SymLinkOption
|
StrOption, OptionDescription, SymLinkOption
|
||||||
from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError, ConfigError
|
from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError, ConfigError
|
||||||
|
@ -430,6 +430,17 @@ def test_callback_multi_list_extend():
|
||||||
assert cfg.val1 == ['1', '2', '3', '4', '5']
|
assert cfg.val1 == ['1', '2', '3', '4', '5']
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_multi_callback():
|
||||||
|
val1 = StrOption('val1', "", multi=True, callback=return_val)
|
||||||
|
interface1 = OptionDescription('val1', '', [val1])
|
||||||
|
maconfig = OptionDescription('rootconfig', '', [interface1])
|
||||||
|
cfg = Config(maconfig)
|
||||||
|
cfg.read_write()
|
||||||
|
assert cfg.val1.val1 == ['val']
|
||||||
|
cfg.val1.val1.append()
|
||||||
|
assert cfg.val1.val1 == ['val', 'val']
|
||||||
|
|
||||||
|
|
||||||
def test_callback_master_and_slaves_master():
|
def test_callback_master_and_slaves_master():
|
||||||
val1 = StrOption('val1', "", multi=True, callback=return_val)
|
val1 = StrOption('val1', "", multi=True, callback=return_val)
|
||||||
val2 = StrOption('val2', "", multi=True)
|
val2 = StrOption('val2', "", multi=True)
|
||||||
|
@ -541,8 +552,6 @@ def test_callback_master_and_slaves_slave_cal():
|
||||||
cfg.val3 = ['val1']
|
cfg.val3 = ['val1']
|
||||||
assert cfg.val1.val1 == ['val1']
|
assert cfg.val1.val1 == ['val1']
|
||||||
assert cfg.val1.val2 == ['val']
|
assert cfg.val1.val2 == ['val']
|
||||||
assert cfg.val1.val1 == ['val1']
|
|
||||||
assert cfg.val1.val2 == ['val']
|
|
||||||
del(cfg.val1.val1)
|
del(cfg.val1.val1)
|
||||||
cfg.val1.val2 = ['val']
|
cfg.val1.val2 = ['val']
|
||||||
cfg.val3 = ['val1', 'val2']
|
cfg.val3 = ['val1', 'val2']
|
||||||
|
@ -571,8 +580,8 @@ def test_callback_master_and_slaves_slave_cal2():
|
||||||
assert cfg.val1.val1 == ['val', 'val']
|
assert cfg.val1.val1 == ['val', 'val']
|
||||||
assert cfg.val1.val2 == ['val2', 'val2']
|
assert cfg.val1.val2 == ['val2', 'val2']
|
||||||
cfg.val3.pop(1)
|
cfg.val3.pop(1)
|
||||||
# # cannot remove slave's value because master is calculated
|
# cannot remove slave's value because master is calculated
|
||||||
# # so raise
|
# so raise
|
||||||
raises(SlaveError, "cfg.val1.val1")
|
raises(SlaveError, "cfg.val1.val1")
|
||||||
raises(SlaveError, "cfg.val1.val2")
|
raises(SlaveError, "cfg.val1.val2")
|
||||||
cfg.val3 = ['val', 'val']
|
cfg.val3 = ['val', 'val']
|
||||||
|
@ -585,6 +594,88 @@ def test_callback_master_and_slaves_slave_cal2():
|
||||||
assert cfg.val1.val2 == ['val2', 'val2']
|
assert cfg.val1.val2 == ['val2', 'val2']
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_master_and_slaves_master_disabled():
|
||||||
|
#properties must be transitive
|
||||||
|
val1 = StrOption('val1', "", multi=True, properties=('disabled',))
|
||||||
|
val2 = StrOption('val2', "", multi=True)
|
||||||
|
interface1 = OptionDescription('val1', '', [val1, val2])
|
||||||
|
interface1.impl_set_group_type(groups.master)
|
||||||
|
maconfig = OptionDescription('rootconfig', '', [interface1])
|
||||||
|
cfg = Config(maconfig)
|
||||||
|
cfg.read_write()
|
||||||
|
raises(PropertiesOptionError, "cfg.val1.val1")
|
||||||
|
raises(PropertiesOptionError, "cfg.val1.val1.append('yes')")
|
||||||
|
raises(PropertiesOptionError, "cfg.val1.val2")
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_master_and_slaves_master_callback_disabled():
|
||||||
|
val0 = StrOption('val0', "", multi=True, properties=('disabled',))
|
||||||
|
val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val0, False),)})
|
||||||
|
val2 = StrOption('val2', "", multi=True)
|
||||||
|
interface1 = OptionDescription('val1', '', [val1, val2])
|
||||||
|
interface1.impl_set_group_type(groups.master)
|
||||||
|
maconfig = OptionDescription('rootconfig', '', [interface1, val0])
|
||||||
|
cfg = Config(maconfig)
|
||||||
|
cfg.read_write()
|
||||||
|
raises(ConfigError, "cfg.val1.val1")
|
||||||
|
raises(ConfigError, "cfg.val1.val2")
|
||||||
|
cfg.cfgimpl_get_settings().remove('disabled')
|
||||||
|
cfg.val1.val1 = []
|
||||||
|
cfg.cfgimpl_get_settings().append('disabled')
|
||||||
|
assert cfg.val1.val1 == []
|
||||||
|
assert cfg.val1.val2 == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_master_and_slaves_slave_disabled():
|
||||||
|
val1 = StrOption('val1', "", multi=True)
|
||||||
|
val2 = StrOption('val2', "", multi=True, properties=('disabled',))
|
||||||
|
interface1 = OptionDescription('val1', '', [val1, val2])
|
||||||
|
interface1.impl_set_group_type(groups.master)
|
||||||
|
maconfig = OptionDescription('rootconfig', '', [interface1])
|
||||||
|
cfg = Config(maconfig)
|
||||||
|
cfg.read_write()
|
||||||
|
assert cfg.val1.val1 == []
|
||||||
|
raises(PropertiesOptionError, "cfg.val1.val2")
|
||||||
|
cfg.val1.val1.append('yes')
|
||||||
|
assert cfg.val1.val1 == ['yes']
|
||||||
|
cfg.cfgimpl_get_settings().remove('disabled')
|
||||||
|
assert cfg.val1.val2 == [None]
|
||||||
|
cfg.val1.val2 = ['no']
|
||||||
|
cfg.val1.val1.append('yes2')
|
||||||
|
cfg.val1.val1.append('yes3')
|
||||||
|
cfg.val1.val2[2] = 'no1'
|
||||||
|
assert cfg.val1.val2 == ['no', None, 'no1']
|
||||||
|
cfg.cfgimpl_get_settings().append('disabled')
|
||||||
|
cfg.val1.val1.pop(0)
|
||||||
|
assert cfg.val1.val1 == ['yes2', 'yes3']
|
||||||
|
cfg.cfgimpl_get_settings().remove('disabled')
|
||||||
|
assert cfg.val1.val2 == [None, 'no1']
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_master_and_slaves_slave_callback_disabled():
|
||||||
|
val0 = StrOption('val0', "", multi=True, properties=('disabled',))
|
||||||
|
val1 = StrOption('val1', "", multi=True)
|
||||||
|
val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val0, False),)})
|
||||||
|
interface1 = OptionDescription('val1', '', [val1, val2])
|
||||||
|
interface1.impl_set_group_type(groups.master)
|
||||||
|
maconfig = OptionDescription('rootconfig', '', [interface1, val0])
|
||||||
|
cfg = Config(maconfig)
|
||||||
|
cfg.read_write()
|
||||||
|
assert cfg.val1.val1 == []
|
||||||
|
raises(ConfigError, "cfg.val1.val2")
|
||||||
|
cfg.val1.val1.append('yes')
|
||||||
|
assert cfg.val1.val1 == ['yes']
|
||||||
|
cfg.cfgimpl_get_settings().remove('disabled')
|
||||||
|
assert cfg.val1.val2 == [None]
|
||||||
|
cfg.val1.val2 = ['no']
|
||||||
|
cfg.val1.val1.append('yes1')
|
||||||
|
cfg.val1.val2[1] = 'no1'
|
||||||
|
cfg.cfgimpl_get_settings().append('disabled')
|
||||||
|
cfg.val1.val1.pop(0)
|
||||||
|
assert cfg.val1.val1 == ['yes1']
|
||||||
|
assert cfg.val1.val2 == ['no1']
|
||||||
|
|
||||||
|
|
||||||
def test_callback_master_and_slaves_slave_list():
|
def test_callback_master_and_slaves_slave_list():
|
||||||
val1 = StrOption('val1', "", multi=True)
|
val1 = StrOption('val1', "", multi=True)
|
||||||
val2 = StrOption('val2', "", multi=True, callback=return_list)
|
val2 = StrOption('val2', "", multi=True, callback=return_list)
|
||||||
|
@ -593,20 +684,20 @@ def test_callback_master_and_slaves_slave_list():
|
||||||
maconfig = OptionDescription('rootconfig', '', [interface1])
|
maconfig = OptionDescription('rootconfig', '', [interface1])
|
||||||
cfg = Config(maconfig)
|
cfg = Config(maconfig)
|
||||||
cfg.read_write()
|
cfg.read_write()
|
||||||
assert cfg.val1.val2 == []
|
#len is equal to 2 for slave and 0 for master
|
||||||
|
raises(SlaveError, "cfg.val1.val2")
|
||||||
cfg.val1.val1 = ['val1', 'val2']
|
cfg.val1.val1 = ['val1', 'val2']
|
||||||
assert cfg.val1.val1 == ['val1', 'val2']
|
assert cfg.val1.val1 == ['val1', 'val2']
|
||||||
assert cfg.val1.val2 == ['val', 'val']
|
assert cfg.val1.val2 == ['val', 'val']
|
||||||
cfg.val1.val1 = ['val1']
|
|
||||||
#wrong len
|
#wrong len
|
||||||
raises(SlaveError, 'cfg.val1.val2')
|
raises(SlaveError, "cfg.val1.val1 = ['val1']")
|
||||||
|
|
||||||
|
|
||||||
def test_callback_master_and_slaves_value():
|
def test_callback_master_and_slaves_value():
|
||||||
|
val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
|
||||||
val1 = StrOption('val1', "", multi=True)
|
val1 = StrOption('val1', "", multi=True)
|
||||||
val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)})
|
val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)})
|
||||||
val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)})
|
val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)})
|
||||||
val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
|
|
||||||
val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)})
|
val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)})
|
||||||
val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val5, False),)})
|
val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val5, False),)})
|
||||||
interface1 = OptionDescription('val1', '', [val1, val2, val3, val5, val6])
|
interface1 = OptionDescription('val1', '', [val1, val2, val3, val5, val6])
|
||||||
|
@ -614,27 +705,24 @@ def test_callback_master_and_slaves_value():
|
||||||
maconfig = OptionDescription('rootconfig', '', [interface1, val4])
|
maconfig = OptionDescription('rootconfig', '', [interface1, val4])
|
||||||
cfg = Config(maconfig)
|
cfg = Config(maconfig)
|
||||||
cfg.read_write()
|
cfg.read_write()
|
||||||
assert cfg.val1.val1 == []
|
cfg.val4 == ['val10', 'val11']
|
||||||
assert cfg.val1.val2 == []
|
raises(SlaveError, "cfg.val1.val1")
|
||||||
assert cfg.val1.val3 == []
|
raises(SlaveError, "cfg.val1.val2")
|
||||||
assert cfg.val1.val5 == []
|
raises(SlaveError, "cfg.val1.val3")
|
||||||
assert cfg.val1.val6 == []
|
raises(SlaveError, "cfg.val1.val5")
|
||||||
|
raises(SlaveError, "cfg.val1.val6")
|
||||||
#
|
#
|
||||||
cfg.val1.val1 = ['val1']
|
#default calculation has greater length
|
||||||
assert cfg.val1.val1 == ['val1']
|
raises(SlaveError, "cfg.val1.val1 = ['val1']")
|
||||||
assert cfg.val1.val2 == ['val1']
|
|
||||||
assert cfg.val1.val3 == ['yes']
|
|
||||||
assert cfg.val1.val5 == ['val10']
|
|
||||||
assert cfg.val1.val6 == ['val10']
|
|
||||||
#
|
#
|
||||||
cfg.val1.val1.append('val2')
|
cfg.val1.val1 = ['val1', 'val2']
|
||||||
assert cfg.val1.val1 == ['val1', 'val2']
|
assert cfg.val1.val1 == ['val1', 'val2']
|
||||||
assert cfg.val1.val2 == ['val1', 'val2']
|
assert cfg.val1.val2 == ['val1', 'val2']
|
||||||
assert cfg.val1.val3 == ['yes', 'yes']
|
assert cfg.val1.val3 == ['yes', 'yes']
|
||||||
assert cfg.val1.val5 == ['val10', 'val11']
|
assert cfg.val1.val5 == ['val10', 'val11']
|
||||||
assert cfg.val1.val6 == ['val10', 'val11']
|
assert cfg.val1.val6 == ['val10', 'val11']
|
||||||
#
|
#
|
||||||
cfg.val1.val1 = ['val1', 'val2', 'val3']
|
cfg.val1.val1.append('val3')
|
||||||
assert cfg.val1.val1 == ['val1', 'val2', 'val3']
|
assert cfg.val1.val1 == ['val1', 'val2', 'val3']
|
||||||
assert cfg.val1.val2 == ['val1', 'val2', 'val3']
|
assert cfg.val1.val2 == ['val1', 'val2', 'val3']
|
||||||
assert cfg.val1.val3 == ['yes', 'yes', 'yes']
|
assert cfg.val1.val3 == ['yes', 'yes', 'yes']
|
||||||
|
@ -705,9 +793,16 @@ def test_callback_master_and_other_master_slave():
|
||||||
assert cfg.val4.val6 == ['no', None]
|
assert cfg.val4.val6 == ['no', None]
|
||||||
cfg.val1.val1 = ['yes', 'yes', 'yes']
|
cfg.val1.val1 = ['yes', 'yes', 'yes']
|
||||||
cfg.val1.val2 = ['no', 'no', 'no']
|
cfg.val1.val2 = ['no', 'no', 'no']
|
||||||
assert cfg.val4.val4 == ['val10', 'val11']
|
raises(SlaveError, "cfg.val4.val4")
|
||||||
assert cfg.val4.val5 == ['yes', 'yes']
|
raises(SlaveError, "cfg.val4.val5")
|
||||||
assert cfg.val4.val6 == ['no', 'no']
|
raises(SlaveError, "cfg.val4.val6")
|
||||||
|
cfg.val4.getattr('val4', validate=False).append('val12')
|
||||||
|
assert cfg.val4.val4 == ['val10', 'val11', 'val12']
|
||||||
|
assert cfg.val4.val5 == ['yes', 'yes', 'yes']
|
||||||
|
assert cfg.val4.val6 == ['no', 'no', 'no']
|
||||||
|
|
||||||
|
|
||||||
|
#FIXME: slave est un symlink
|
||||||
|
|
||||||
|
|
||||||
def test_callback_different_type():
|
def test_callback_different_type():
|
||||||
|
@ -757,6 +852,19 @@ def test_callback_two_disabled():
|
||||||
raises(PropertiesOptionError, 'cfg.od2.opt2')
|
raises(PropertiesOptionError, 'cfg.od2.opt2')
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_two_disabled2():
|
||||||
|
opt1 = BoolOption('opt1', '', properties=('hidden',))
|
||||||
|
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('hidden',))
|
||||||
|
od1 = OptionDescription('od1', '', [opt1])
|
||||||
|
od2 = OptionDescription('od2', '', [opt2])
|
||||||
|
maconfig = OptionDescription('rootconfig', '', [od1, od2])
|
||||||
|
cfg = Config(maconfig)
|
||||||
|
cfg.read_write()
|
||||||
|
cfg.cfgimpl_get_settings().setpermissive(('hidden',))
|
||||||
|
raises(PropertiesOptionError, 'cfg.od2.opt2')
|
||||||
|
assert cfg.getowner(opt2, force_permissive=True) == owners.default
|
||||||
|
|
||||||
|
|
||||||
def test_callback_calculating_disabled():
|
def test_callback_calculating_disabled():
|
||||||
opt1 = BoolOption('opt1', '', properties=('disabled',))
|
opt1 = BoolOption('opt1', '', properties=('disabled',))
|
||||||
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)})
|
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)})
|
||||||
|
@ -779,6 +887,17 @@ def test_callback_calculating_mandatory():
|
||||||
raises(ConfigError, 'cfg.od2.opt2')
|
raises(ConfigError, 'cfg.od2.opt2')
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_calculating_mandatory_multi():
|
||||||
|
opt1 = BoolOption('opt1', '', multi=True, properties=('disabled',))
|
||||||
|
opt2 = BoolOption('opt2', '', multi=True, callback=return_value, callback_params={'': ((opt1, False),)}, properties=('mandatory',))
|
||||||
|
od1 = OptionDescription('od1', '', [opt1])
|
||||||
|
od2 = OptionDescription('od2', '', [opt2])
|
||||||
|
maconfig = OptionDescription('rootconfig', '', [od1, od2])
|
||||||
|
cfg = Config(maconfig)
|
||||||
|
cfg.read_only()
|
||||||
|
raises(ConfigError, 'cfg.od2.opt2')
|
||||||
|
|
||||||
|
|
||||||
def test_callback_two_disabled_multi():
|
def test_callback_two_disabled_multi():
|
||||||
opt1 = BoolOption('opt1', '', properties=('disabled',))
|
opt1 = BoolOption('opt1', '', properties=('disabled',))
|
||||||
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',), multi=True)
|
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',), multi=True)
|
||||||
|
|
|
@ -321,7 +321,7 @@ def test_consistency_broadcast_error():
|
||||||
c.impl_add_consistency('broadcast', a)
|
c.impl_add_consistency('broadcast', a)
|
||||||
c = Config(od)
|
c = Config(od)
|
||||||
c.a = ['192.168.1.0']
|
c.a = ['192.168.1.0']
|
||||||
c.b = ['255.255.255.0']
|
raises(ConfigError, "c.b = ['255.255.255.0']")
|
||||||
raises(ConfigError, "c.c = ['192.168.1.255']")
|
raises(ConfigError, "c.c = ['192.168.1.255']")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -242,6 +242,30 @@ def test_requires_transitive():
|
||||||
assert props == ['disabled']
|
assert props == ['disabled']
|
||||||
|
|
||||||
|
|
||||||
|
def test_requires_transitive_owner():
|
||||||
|
a = BoolOption('activate_service', '', True)
|
||||||
|
b = BoolOption('activate_service_web', '', True,
|
||||||
|
requires=[{'option': a, 'expected': False, 'action': 'disabled'}])
|
||||||
|
|
||||||
|
d = IPOption('ip_address_service_web', '',
|
||||||
|
requires=[{'option': b, 'expected': False, 'action': 'disabled'}])
|
||||||
|
od = OptionDescription('service', '', [a, b, d])
|
||||||
|
c = Config(od)
|
||||||
|
c.read_write()
|
||||||
|
c.activate_service
|
||||||
|
c.activate_service_web
|
||||||
|
c.ip_address_service_web
|
||||||
|
#no more default value
|
||||||
|
c.ip_address_service_web = '1.1.1.1'
|
||||||
|
c.activate_service = False
|
||||||
|
props = []
|
||||||
|
try:
|
||||||
|
c.ip_address_service_web
|
||||||
|
except PropertiesOptionError as err:
|
||||||
|
props = err.proptype
|
||||||
|
assert props == ['disabled']
|
||||||
|
|
||||||
|
|
||||||
def test_requires_transitive_bis():
|
def test_requires_transitive_bis():
|
||||||
a = BoolOption('activate_service', '', True)
|
a = BoolOption('activate_service', '', True)
|
||||||
abis = BoolOption('activate_service_bis', '', True)
|
abis = BoolOption('activate_service_bis', '', True)
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
# ____________________________________________________________
|
# ____________________________________________________________
|
||||||
"enables us to carry out a calculation and return an option's value"
|
"enables us to carry out a calculation and return an option's value"
|
||||||
from tiramisu.error import PropertiesOptionError, ConfigError
|
from tiramisu.error import PropertiesOptionError, ConfigError
|
||||||
from tiramisu.setting import multitypes
|
|
||||||
from tiramisu.i18n import _
|
from tiramisu.i18n import _
|
||||||
|
from tiramisu.setting import undefined
|
||||||
# ____________________________________________________________
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
|
||||||
def carry_out_calculation(option, config, callback, callback_params,
|
def carry_out_calculation(option, config, callback, callback_params,
|
||||||
index=None, max_len=None):
|
index=undefined):
|
||||||
"""a function that carries out a calculation for an option's value
|
"""a function that carries out a calculation for an option's value
|
||||||
|
|
||||||
:param name: the option
|
:param name: the option
|
||||||
|
@ -38,8 +38,6 @@ def carry_out_calculation(option, config, callback, callback_params,
|
||||||
:type callback_params: dict
|
:type callback_params: dict
|
||||||
:param index: if an option is multi, only calculates the nth value
|
:param index: if an option is multi, only calculates the nth value
|
||||||
:type index: int
|
:type index: int
|
||||||
:param max_len: max length for a multi
|
|
||||||
:type max_len: int
|
|
||||||
|
|
||||||
The callback_params is a dict. Key is used to build args (if key is '')
|
The callback_params is a dict. Key is used to build args (if key is '')
|
||||||
and kwargs (otherwise). Values are tuple of:
|
and kwargs (otherwise). Values are tuple of:
|
||||||
|
@ -129,7 +127,7 @@ def carry_out_calculation(option, config, callback, callback_params,
|
||||||
* if callback_params={'value': ((opt1, False), (opt2, False))}
|
* if callback_params={'value': ((opt1, False), (opt2, False))}
|
||||||
=> raises ValueError()
|
=> raises ValueError()
|
||||||
|
|
||||||
If index is not None, return a value, otherwise return:
|
If index is not undefined, return a value, otherwise return:
|
||||||
|
|
||||||
* a list if one parameters have multi option
|
* a list if one parameters have multi option
|
||||||
* a value otherwise
|
* a value otherwise
|
||||||
|
@ -154,15 +152,8 @@ def carry_out_calculation(option, config, callback, callback_params,
|
||||||
).impl_get_path_by_opt(opt)
|
).impl_get_path_by_opt(opt)
|
||||||
# get value
|
# get value
|
||||||
try:
|
try:
|
||||||
if option.impl_is_multi() and \
|
|
||||||
(option.impl_get_multitype() == multitypes.master or
|
|
||||||
(option.impl_get_multitype() == multitypes.slave and
|
|
||||||
option.impl_get_master_slaves() != opt)):
|
|
||||||
validate = True
|
|
||||||
else:
|
|
||||||
validate = False
|
|
||||||
value = config.getattr(path, force_permissive=True,
|
value = config.getattr(path, force_permissive=True,
|
||||||
validate=validate)
|
validate=False)
|
||||||
# convert to list, not modifie this multi
|
# convert to list, not modifie this multi
|
||||||
if value.__class__.__name__ == 'Multi':
|
if value.__class__.__name__ == 'Multi':
|
||||||
value = list(value)
|
value = list(value)
|
||||||
|
@ -175,20 +166,13 @@ def carry_out_calculation(option, config, callback, callback_params,
|
||||||
err.proptype,
|
err.proptype,
|
||||||
option._name))
|
option._name))
|
||||||
|
|
||||||
is_multi = False
|
if opt.impl_is_master_slaves() and \
|
||||||
if opt.impl_is_multi():
|
opt.impl_get_master_slaves().in_same_group(option):
|
||||||
#opt is master, search if option is a slave
|
|
||||||
if opt.impl_get_multitype() == multitypes.master:
|
|
||||||
if option in opt.impl_get_master_slaves():
|
|
||||||
is_multi = True
|
|
||||||
#opt is slave, search if option is an other slaves
|
|
||||||
elif opt.impl_get_multitype() == multitypes.slave:
|
|
||||||
if option in opt.impl_get_master_slaves(
|
|
||||||
).impl_get_master_slaves():
|
|
||||||
is_multi = True
|
|
||||||
if is_multi:
|
|
||||||
len_multi = len(value)
|
len_multi = len(value)
|
||||||
one_is_multi = True
|
one_is_multi = True
|
||||||
|
is_multi = True
|
||||||
|
else:
|
||||||
|
is_multi = False
|
||||||
tcparams.setdefault(key, []).append((value, is_multi))
|
tcparams.setdefault(key, []).append((value, is_multi))
|
||||||
else:
|
else:
|
||||||
# callbk is a value and not a multi
|
# callbk is a value and not a multi
|
||||||
|
@ -199,7 +183,7 @@ def carry_out_calculation(option, config, callback, callback_params,
|
||||||
# if no index, return a list
|
# if no index, return a list
|
||||||
if one_is_multi:
|
if one_is_multi:
|
||||||
ret = []
|
ret = []
|
||||||
if index is not None:
|
if index is not undefined:
|
||||||
range_ = [index]
|
range_ = [index]
|
||||||
else:
|
else:
|
||||||
range_ = range(len_multi)
|
range_ = range(len_multi)
|
||||||
|
@ -218,7 +202,7 @@ def carry_out_calculation(option, config, callback, callback_params,
|
||||||
else:
|
else:
|
||||||
kwargs[key] = val
|
kwargs[key] = val
|
||||||
calc = calculate(callback, args, kwargs)
|
calc = calculate(callback, args, kwargs)
|
||||||
if index is not None:
|
if index is not undefined:
|
||||||
ret = calc
|
ret = calc
|
||||||
else:
|
else:
|
||||||
ret.append(calc)
|
ret.append(calc)
|
||||||
|
@ -237,11 +221,7 @@ def carry_out_calculation(option, config, callback, callback_params,
|
||||||
kwargs[key] = couple[0]
|
kwargs[key] = couple[0]
|
||||||
ret = calculate(callback, args, kwargs)
|
ret = calculate(callback, args, kwargs)
|
||||||
if callback_params != {}:
|
if callback_params != {}:
|
||||||
if isinstance(ret, list) and max_len:
|
if isinstance(ret, list) and index is not undefined:
|
||||||
ret = ret[:max_len]
|
|
||||||
if len(ret) < max_len:
|
|
||||||
ret = ret + [None] * (max_len - len(ret))
|
|
||||||
if isinstance(ret, list) and index is not None:
|
|
||||||
if len(ret) < index + 1:
|
if len(ret) < index + 1:
|
||||||
ret = None
|
ret = None
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -228,6 +228,7 @@ class SubConfig(object):
|
||||||
:return: option's value if name is an option name, OptionDescription
|
:return: option's value if name is an option name, OptionDescription
|
||||||
otherwise
|
otherwise
|
||||||
"""
|
"""
|
||||||
|
#FIXME force_properties vraiment utile maintenant ?
|
||||||
# attribute access by passing a path,
|
# attribute access by passing a path,
|
||||||
# for instance getattr(self, "creole.general.family.adresse_ip_eth0")
|
# for instance getattr(self, "creole.general.family.adresse_ip_eth0")
|
||||||
if '.' in name:
|
if '.' in name:
|
||||||
|
@ -243,6 +244,8 @@ class SubConfig(object):
|
||||||
else:
|
else:
|
||||||
subpath = self._impl_path + '.' + name
|
subpath = self._impl_path + '.' + name
|
||||||
# symlink options
|
# symlink options
|
||||||
|
#FIXME a gerer plutot dans l'option ca ...
|
||||||
|
#FIXME je n'en sais rien en fait ... :/
|
||||||
if isinstance(opt_or_descr, SymLinkOption):
|
if isinstance(opt_or_descr, SymLinkOption):
|
||||||
context = self._cfgimpl_get_context()
|
context = self._cfgimpl_get_context()
|
||||||
path = context.cfgimpl_get_description().impl_get_path_by_opt(
|
path = context.cfgimpl_get_description().impl_get_path_by_opt(
|
||||||
|
@ -257,7 +260,7 @@ class SubConfig(object):
|
||||||
force_properties=force_properties)
|
force_properties=force_properties)
|
||||||
return SubConfig(opt_or_descr, self._impl_context, subpath)
|
return SubConfig(opt_or_descr, self._impl_context, subpath)
|
||||||
else:
|
else:
|
||||||
return self.cfgimpl_get_values().getitem(
|
return self.cfgimpl_get_values()._get_cached_item(
|
||||||
opt_or_descr, path=subpath,
|
opt_or_descr, path=subpath,
|
||||||
validate=validate,
|
validate=validate,
|
||||||
force_properties=force_properties,
|
force_properties=force_properties,
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
from .masterslave import MasterSlaves
|
||||||
|
from .optiondescription import OptionDescription
|
||||||
|
from .baseoption import Option, SymLinkOption
|
||||||
|
from .option import (ChoiceOption, BoolOption, IntOption, FloatOption,
|
||||||
|
StrOption, UnicodeOption, IPOption, PortOption,
|
||||||
|
NetworkOption, NetmaskOption, BroadcastOption,
|
||||||
|
DomainnameOption, EmailOption, URLOption, UsernameOption,
|
||||||
|
FilenameOption)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (MasterSlaves, OptionDescription, Option, SymLinkOption,
|
||||||
|
ChoiceOption, BoolOption, IntOption, FloatOption,
|
||||||
|
StrOption, UnicodeOption, IPOption, PortOption,
|
||||||
|
NetworkOption, NetmaskOption, BroadcastOption,
|
||||||
|
DomainnameOption, EmailOption, URLOption, UsernameOption,
|
||||||
|
FilenameOption)
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"option types and option description"
|
# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
|
||||||
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
|
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
# under the terms of the GNU Lesser General Public License as published by the
|
# under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
@ -20,16 +19,14 @@
|
||||||
# the whole pypy projet is under MIT licence
|
# the whole pypy projet is under MIT licence
|
||||||
# ____________________________________________________________
|
# ____________________________________________________________
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
from IPy import IP
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from tiramisu.error import ConfigError, ConflictError, ValueWarning
|
|
||||||
from tiramisu.setting import groups, multitypes, log
|
|
||||||
from tiramisu.i18n import _
|
from tiramisu.i18n import _
|
||||||
|
from tiramisu.setting import log
|
||||||
from tiramisu.autolib import carry_out_calculation
|
from tiramisu.autolib import carry_out_calculation
|
||||||
|
from tiramisu.error import ConfigError, ValueWarning
|
||||||
|
|
||||||
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',
|
||||||
|
@ -48,6 +45,46 @@ def valid_name(name):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def validate_callback(callback, callback_params, type_):
|
||||||
|
if type(callback) != FunctionType:
|
||||||
|
raise ValueError(_('{0} must be a function').format(type_))
|
||||||
|
if callback_params is not None:
|
||||||
|
if not isinstance(callback_params, dict):
|
||||||
|
raise ValueError(_('{0}_params must be a dict').format(type_))
|
||||||
|
for key, callbacks in callback_params.items():
|
||||||
|
if key != '' and len(callbacks) != 1:
|
||||||
|
raise ValueError(_("{0}_params with key {1} mustn't have "
|
||||||
|
"length different to 1").format(type_,
|
||||||
|
key))
|
||||||
|
if not isinstance(callbacks, tuple):
|
||||||
|
raise ValueError(_('{0}_params must be tuple for key "{1}"'
|
||||||
|
).format(type_, key))
|
||||||
|
for callbk in callbacks:
|
||||||
|
if isinstance(callbk, tuple):
|
||||||
|
if len(callbk) == 1:
|
||||||
|
if callbk != (None,):
|
||||||
|
raise ValueError(_('{0}_params with length of '
|
||||||
|
'tuple as 1 must only have '
|
||||||
|
'None as first value'))
|
||||||
|
elif len(callbk) != 2:
|
||||||
|
raise ValueError(_('{0}_params must only have 1 or 2 '
|
||||||
|
'as length'))
|
||||||
|
else:
|
||||||
|
option, force_permissive = callbk
|
||||||
|
if type_ == 'validator' and not force_permissive:
|
||||||
|
raise ValueError(_('validator not support tuple'))
|
||||||
|
if not isinstance(option, Option) and not \
|
||||||
|
isinstance(option, SymLinkOption):
|
||||||
|
raise ValueError(_('{0}_params must have an option'
|
||||||
|
' not a {0} for first argument'
|
||||||
|
).format(type_, type(option)))
|
||||||
|
if force_permissive not in [True, False]:
|
||||||
|
raise ValueError(_('{0}_params must have a boolean'
|
||||||
|
' not a {0} for second argument'
|
||||||
|
).format(type_, type(
|
||||||
|
force_permissive)))
|
||||||
#____________________________________________________________
|
#____________________________________________________________
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -264,9 +301,9 @@ class Option(BaseOption):
|
||||||
Reminder: an Option object is **not** a container for the value.
|
Reminder: an Option object is **not** a container for the value.
|
||||||
"""
|
"""
|
||||||
__slots__ = ('_multi', '_validator', '_default_multi', '_default',
|
__slots__ = ('_multi', '_validator', '_default_multi', '_default',
|
||||||
'_state_callback', '_callback', '_multitype',
|
'_state_callback', '_callback', '_consistencies',
|
||||||
'_consistencies', '_warnings_only', '_master_slaves',
|
'_warnings_only', '_master_slaves', '_state_consistencies',
|
||||||
'_state_consistencies', '__weakref__')
|
'__weakref__')
|
||||||
_empty = ''
|
_empty = ''
|
||||||
|
|
||||||
def __init__(self, name, doc, default=None, default_multi=None,
|
def __init__(self, name, doc, default=None, default_multi=None,
|
||||||
|
@ -327,7 +364,6 @@ class Option(BaseOption):
|
||||||
if self._multi:
|
if self._multi:
|
||||||
if default is None:
|
if default is None:
|
||||||
default = []
|
default = []
|
||||||
self._multitype = multitypes.default
|
|
||||||
self._default_multi = default_multi
|
self._default_multi = default_multi
|
||||||
self._warnings_only = warnings_only
|
self._warnings_only = warnings_only
|
||||||
self.impl_validate(default)
|
self.impl_validate(default)
|
||||||
|
@ -390,9 +426,9 @@ class Option(BaseOption):
|
||||||
:type context: :class:`tiramisu.config.Config`
|
:type context: :class:`tiramisu.config.Config`
|
||||||
:param validate: if true enables ``self._validator`` validation
|
:param validate: if true enables ``self._validator`` validation
|
||||||
:type validate: boolean
|
:type validate: boolean
|
||||||
:param force_no_multi: if multi, value has to be a list
|
:param force_index: if multi, value has to be a list
|
||||||
not if force_no_multi is True
|
not if force_index is not None
|
||||||
:type force_no_multi: boolean
|
:type force_index: integer
|
||||||
"""
|
"""
|
||||||
if not validate:
|
if not validate:
|
||||||
return
|
return
|
||||||
|
@ -421,6 +457,8 @@ class Option(BaseOption):
|
||||||
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(
|
||||||
|
_value, _index), exc_info=True)
|
||||||
raise ValueError(_('invalid value for option {0}: {1}'
|
raise ValueError(_('invalid value for option {0}: {1}'
|
||||||
'').format(self._name, err))
|
'').format(self._name, err))
|
||||||
error = None
|
error = None
|
||||||
|
@ -476,14 +514,28 @@ class Option(BaseOption):
|
||||||
|
|
||||||
def impl_getdefault(self):
|
def impl_getdefault(self):
|
||||||
"accessing the default value"
|
"accessing the default value"
|
||||||
|
if isinstance(self._default, list):
|
||||||
|
return copy(self._default)
|
||||||
return self._default
|
return self._default
|
||||||
|
|
||||||
def impl_getdefault_multi(self):
|
def impl_getdefault_multi(self):
|
||||||
"accessing the default value for a multi"
|
"accessing the default value for a multi"
|
||||||
return self._default_multi
|
return self._default_multi
|
||||||
|
|
||||||
def impl_get_multitype(self):
|
def impl_is_master_slaves(self, type_='both'):
|
||||||
return self._multitype
|
"""FIXME
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._master_slaves
|
||||||
|
if type_ in ('both', 'master') and \
|
||||||
|
self._master_slaves.is_master(self):
|
||||||
|
return True
|
||||||
|
if type_ in ('both', 'slave') and \
|
||||||
|
not self._master_slaves.is_master(self):
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
def impl_get_master_slaves(self):
|
def impl_get_master_slaves(self):
|
||||||
return self._master_slaves
|
return self._master_slaves
|
||||||
|
@ -632,805 +684,6 @@ class Option(BaseOption):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ChoiceOption(Option):
|
|
||||||
"""represents a choice out of several objects.
|
|
||||||
|
|
||||||
The option can also have the value ``None``
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ('_values', '_open_values')
|
|
||||||
_opt_type = 'string'
|
|
||||||
|
|
||||||
def __init__(self, name, doc, values, default=None, default_multi=None,
|
|
||||||
requires=None, multi=False, callback=None,
|
|
||||||
callback_params=None, open_values=False, validator=None,
|
|
||||||
validator_params=None, properties=None, warnings_only=False):
|
|
||||||
"""
|
|
||||||
:param values: is a list of values the option can possibly take
|
|
||||||
"""
|
|
||||||
if not isinstance(values, tuple):
|
|
||||||
raise TypeError(_('values must be a tuple for {0}').format(name))
|
|
||||||
self._values = values
|
|
||||||
if open_values not in (True, False):
|
|
||||||
raise TypeError(_('open_values must be a boolean for '
|
|
||||||
'{0}').format(name))
|
|
||||||
self._open_values = open_values
|
|
||||||
super(ChoiceOption, self).__init__(name, doc, default=default,
|
|
||||||
default_multi=default_multi,
|
|
||||||
callback=callback,
|
|
||||||
callback_params=callback_params,
|
|
||||||
requires=requires,
|
|
||||||
multi=multi,
|
|
||||||
validator=validator,
|
|
||||||
validator_params=validator_params,
|
|
||||||
properties=properties,
|
|
||||||
warnings_only=warnings_only)
|
|
||||||
|
|
||||||
def impl_get_values(self):
|
|
||||||
return self._values
|
|
||||||
|
|
||||||
def impl_is_openvalues(self):
|
|
||||||
return self._open_values
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if not self.impl_is_openvalues() and not value in self.impl_get_values():
|
|
||||||
raise ValueError(_('value {0} is not permitted, '
|
|
||||||
'only {1} is allowed'
|
|
||||||
'').format(value, self._values))
|
|
||||||
|
|
||||||
|
|
||||||
class BoolOption(Option):
|
|
||||||
"represents a choice between ``True`` and ``False``"
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'bool'
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if not isinstance(value, bool):
|
|
||||||
raise ValueError(_('invalid boolean'))
|
|
||||||
|
|
||||||
|
|
||||||
class IntOption(Option):
|
|
||||||
"represents a choice of an integer"
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'int'
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if not isinstance(value, int):
|
|
||||||
raise ValueError(_('invalid integer'))
|
|
||||||
|
|
||||||
|
|
||||||
class FloatOption(Option):
|
|
||||||
"represents a choice of a floating point number"
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'float'
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if not isinstance(value, float):
|
|
||||||
raise ValueError(_('invalid float'))
|
|
||||||
|
|
||||||
|
|
||||||
class StrOption(Option):
|
|
||||||
"represents the choice of a string"
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'string'
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if not isinstance(value, str):
|
|
||||||
raise ValueError(_('invalid string'))
|
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info[0] >= 3:
|
|
||||||
#UnicodeOption is same as StrOption in python 3+
|
|
||||||
class UnicodeOption(StrOption):
|
|
||||||
__slots__ = tuple()
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
class UnicodeOption(Option):
|
|
||||||
"represents the choice of a unicode string"
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'unicode'
|
|
||||||
_empty = u''
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if not isinstance(value, unicode):
|
|
||||||
raise ValueError(_('invalid unicode'))
|
|
||||||
|
|
||||||
|
|
||||||
class SymLinkOption(BaseOption):
|
|
||||||
__slots__ = ('_name', '_opt', '_state_opt')
|
|
||||||
_opt_type = 'symlink'
|
|
||||||
#not return _opt consistencies
|
|
||||||
_consistencies = None
|
|
||||||
|
|
||||||
def __init__(self, name, opt):
|
|
||||||
self._name = name
|
|
||||||
if not isinstance(opt, Option):
|
|
||||||
raise ValueError(_('malformed symlinkoption '
|
|
||||||
'must be an option '
|
|
||||||
'for symlink {0}').format(name))
|
|
||||||
self._opt = opt
|
|
||||||
self._readonly = True
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name in ('_name', '_opt', '_opt_type', '_readonly'):
|
|
||||||
return object.__getattr__(self, name)
|
|
||||||
else:
|
|
||||||
return getattr(self._opt, name)
|
|
||||||
|
|
||||||
def _impl_getstate(self, descr):
|
|
||||||
super(SymLinkOption, self)._impl_getstate(descr)
|
|
||||||
self._state_opt = descr.impl_get_path_by_opt(self._opt)
|
|
||||||
|
|
||||||
def _impl_setstate(self, descr):
|
|
||||||
self._opt = descr.impl_get_opt_by_path(self._state_opt)
|
|
||||||
del(self._state_opt)
|
|
||||||
super(SymLinkOption, self)._impl_setstate(descr)
|
|
||||||
|
|
||||||
|
|
||||||
class IPOption(Option):
|
|
||||||
"represents the choice of an ip"
|
|
||||||
__slots__ = ('_private_only', '_allow_reserved')
|
|
||||||
_opt_type = 'ip'
|
|
||||||
|
|
||||||
def __init__(self, name, doc, default=None, default_multi=None,
|
|
||||||
requires=None, multi=False, callback=None,
|
|
||||||
callback_params=None, validator=None, validator_params=None,
|
|
||||||
properties=None, private_only=False, allow_reserved=False,
|
|
||||||
warnings_only=False):
|
|
||||||
self._private_only = private_only
|
|
||||||
self._allow_reserved = allow_reserved
|
|
||||||
super(IPOption, self).__init__(name, doc, default=default,
|
|
||||||
default_multi=default_multi,
|
|
||||||
callback=callback,
|
|
||||||
callback_params=callback_params,
|
|
||||||
requires=requires,
|
|
||||||
multi=multi,
|
|
||||||
validator=validator,
|
|
||||||
validator_params=validator_params,
|
|
||||||
properties=properties,
|
|
||||||
warnings_only=warnings_only)
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
# sometimes an ip term starts with a zero
|
|
||||||
# but this does not fit in some case, for example bind does not like it
|
|
||||||
try:
|
|
||||||
for val in value.split('.'):
|
|
||||||
if val.startswith("0") and len(val) > 1:
|
|
||||||
raise ValueError(_('invalid IP'))
|
|
||||||
except AttributeError:
|
|
||||||
#if integer for example
|
|
||||||
raise ValueError(_('invalid IP'))
|
|
||||||
# 'standard' validation
|
|
||||||
try:
|
|
||||||
IP('{0}/32'.format(value))
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(_('invalid IP'))
|
|
||||||
|
|
||||||
def _second_level_validation(self, value, warnings_only):
|
|
||||||
ip = IP('{0}/32'.format(value))
|
|
||||||
if not self._allow_reserved and ip.iptype() == 'RESERVED':
|
|
||||||
if warnings_only:
|
|
||||||
msg = _("IP is in reserved class")
|
|
||||||
else:
|
|
||||||
msg = _("invalid IP, mustn't be in reserved class")
|
|
||||||
raise ValueError(msg)
|
|
||||||
if self._private_only and not ip.iptype() == 'PRIVATE':
|
|
||||||
if warnings_only:
|
|
||||||
msg = _("IP is not in private class")
|
|
||||||
else:
|
|
||||||
msg = _("invalid IP, must be in private class")
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
def _cons_in_network(self, opts, vals, warnings_only):
|
|
||||||
if len(vals) != 3:
|
|
||||||
raise ConfigError(_('invalid len for vals'))
|
|
||||||
if None in vals:
|
|
||||||
return
|
|
||||||
ip, network, netmask = vals
|
|
||||||
if IP(ip) not in IP('{0}/{1}'.format(network, netmask)):
|
|
||||||
if warnings_only:
|
|
||||||
msg = _('IP {0} ({1}) not in network {2} ({3}) with netmask {4}'
|
|
||||||
' ({5})')
|
|
||||||
else:
|
|
||||||
msg = _('invalid IP {0} ({1}) not in network {2} ({3}) with '
|
|
||||||
'netmask {4} ({5})')
|
|
||||||
raise ValueError(msg.format(ip, opts[0]._name, network,
|
|
||||||
opts[1]._name, netmask, opts[2]._name))
|
|
||||||
|
|
||||||
|
|
||||||
class PortOption(Option):
|
|
||||||
"""represents the choice of a port
|
|
||||||
The port numbers are divided into three ranges:
|
|
||||||
the well-known ports,
|
|
||||||
the registered ports,
|
|
||||||
and the dynamic or private ports.
|
|
||||||
You can actived this three range.
|
|
||||||
Port number 0 is reserved and can't be used.
|
|
||||||
see: http://en.wikipedia.org/wiki/Port_numbers
|
|
||||||
"""
|
|
||||||
__slots__ = ('_allow_range', '_allow_zero', '_min_value', '_max_value')
|
|
||||||
_opt_type = 'port'
|
|
||||||
|
|
||||||
def __init__(self, name, doc, default=None, default_multi=None,
|
|
||||||
requires=None, multi=False, callback=None,
|
|
||||||
callback_params=None, validator=None, validator_params=None,
|
|
||||||
properties=None, allow_range=False, allow_zero=False,
|
|
||||||
allow_wellknown=True, allow_registred=True,
|
|
||||||
allow_private=False, warnings_only=False):
|
|
||||||
self._allow_range = allow_range
|
|
||||||
self._min_value = None
|
|
||||||
self._max_value = None
|
|
||||||
ports_min = [0, 1, 1024, 49152]
|
|
||||||
ports_max = [0, 1023, 49151, 65535]
|
|
||||||
is_finally = False
|
|
||||||
for index, allowed in enumerate([allow_zero,
|
|
||||||
allow_wellknown,
|
|
||||||
allow_registred,
|
|
||||||
allow_private]):
|
|
||||||
if self._min_value is None:
|
|
||||||
if allowed:
|
|
||||||
self._min_value = ports_min[index]
|
|
||||||
elif not allowed:
|
|
||||||
is_finally = True
|
|
||||||
elif allowed and is_finally:
|
|
||||||
raise ValueError(_('inconsistency in allowed range'))
|
|
||||||
if allowed:
|
|
||||||
self._max_value = ports_max[index]
|
|
||||||
|
|
||||||
if self._max_value is None:
|
|
||||||
raise ValueError(_('max value is empty'))
|
|
||||||
|
|
||||||
super(PortOption, self).__init__(name, doc, default=default,
|
|
||||||
default_multi=default_multi,
|
|
||||||
callback=callback,
|
|
||||||
callback_params=callback_params,
|
|
||||||
requires=requires,
|
|
||||||
multi=multi,
|
|
||||||
validator=validator,
|
|
||||||
validator_params=validator_params,
|
|
||||||
properties=properties,
|
|
||||||
warnings_only=warnings_only)
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if self._allow_range and ":" in str(value):
|
|
||||||
value = str(value).split(':')
|
|
||||||
if len(value) != 2:
|
|
||||||
raise ValueError(_('invalid port, range must have two values '
|
|
||||||
'only'))
|
|
||||||
if not value[0] < value[1]:
|
|
||||||
raise ValueError(_('invalid port, first port in range must be'
|
|
||||||
' smaller than the second one'))
|
|
||||||
else:
|
|
||||||
value = [value]
|
|
||||||
|
|
||||||
for val in value:
|
|
||||||
try:
|
|
||||||
int(val)
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(_('invalid port'))
|
|
||||||
if not self._min_value <= int(val) <= self._max_value:
|
|
||||||
raise ValueError(_('invalid port, must be an between {0} '
|
|
||||||
'and {1}').format(self._min_value,
|
|
||||||
self._max_value))
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkOption(Option):
|
|
||||||
"represents the choice of a network"
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'network'
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
try:
|
|
||||||
IP(value)
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(_('invalid network address'))
|
|
||||||
|
|
||||||
def _second_level_validation(self, value, warnings_only):
|
|
||||||
ip = IP(value)
|
|
||||||
if ip.iptype() == 'RESERVED':
|
|
||||||
if warnings_only:
|
|
||||||
msg = _("network address is in reserved class")
|
|
||||||
else:
|
|
||||||
msg = _("invalid network address, mustn't be in reserved class")
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class NetmaskOption(Option):
|
|
||||||
"represents the choice of a netmask"
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'netmask'
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
try:
|
|
||||||
IP('0.0.0.0/{0}'.format(value))
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(_('invalid netmask address'))
|
|
||||||
|
|
||||||
def _cons_network_netmask(self, opts, vals, warnings_only):
|
|
||||||
#opts must be (netmask, network) options
|
|
||||||
if None in vals:
|
|
||||||
return
|
|
||||||
self.__cons_netmask(opts, vals[0], vals[1], False, warnings_only)
|
|
||||||
|
|
||||||
def _cons_ip_netmask(self, opts, vals, warnings_only):
|
|
||||||
#opts must be (netmask, ip) options
|
|
||||||
if None in vals:
|
|
||||||
return
|
|
||||||
self.__cons_netmask(opts, vals[0], vals[1], True, warnings_only)
|
|
||||||
|
|
||||||
def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net,
|
|
||||||
warnings_only):
|
|
||||||
if len(opts) != 2:
|
|
||||||
raise ConfigError(_('invalid len for opts'))
|
|
||||||
msg = None
|
|
||||||
try:
|
|
||||||
ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
|
|
||||||
make_net=make_net)
|
|
||||||
#if cidr == 32, ip same has network
|
|
||||||
if ip.prefixlen() != 32:
|
|
||||||
try:
|
|
||||||
IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
|
|
||||||
make_net=not make_net)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if make_net:
|
|
||||||
msg = _("invalid IP {0} ({1}) with netmask {2},"
|
|
||||||
" this IP is a network")
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
if not make_net:
|
|
||||||
msg = _('invalid network {0} ({1}) with netmask {2}')
|
|
||||||
if msg is not None:
|
|
||||||
raise ValueError(msg.format(val_ipnetwork, opts[1]._name,
|
|
||||||
val_netmask))
|
|
||||||
|
|
||||||
|
|
||||||
class BroadcastOption(Option):
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'broadcast'
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
try:
|
|
||||||
IP('{0}/32'.format(value))
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(_('invalid broadcast address'))
|
|
||||||
|
|
||||||
def _cons_broadcast(self, opts, vals, warnings_only):
|
|
||||||
if len(vals) != 3:
|
|
||||||
raise ConfigError(_('invalid len for vals'))
|
|
||||||
if None in vals:
|
|
||||||
return
|
|
||||||
broadcast, network, netmask = vals
|
|
||||||
if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast):
|
|
||||||
raise ValueError(_('invalid broadcast {0} ({1}) with network {2} '
|
|
||||||
'({3}) and netmask {4} ({5})').format(
|
|
||||||
broadcast, opts[0]._name, network,
|
|
||||||
opts[1]._name, netmask, opts[2]._name))
|
|
||||||
|
|
||||||
|
|
||||||
class DomainnameOption(Option):
|
|
||||||
"""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', '_allow_without_dot', '_domain_re')
|
|
||||||
_opt_type = 'domainname'
|
|
||||||
|
|
||||||
def __init__(self, name, doc, default=None, default_multi=None,
|
|
||||||
requires=None, multi=False, callback=None,
|
|
||||||
callback_params=None, validator=None, validator_params=None,
|
|
||||||
properties=None, allow_ip=False, type_='domainname',
|
|
||||||
warnings_only=False, allow_without_dot=False):
|
|
||||||
if type_ not in ['netbios', 'hostname', 'domainname']:
|
|
||||||
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
|
|
||||||
self._type = type_
|
|
||||||
if allow_ip not in [True, False]:
|
|
||||||
raise ValueError(_('allow_ip must be a boolean'))
|
|
||||||
if allow_without_dot not in [True, False]:
|
|
||||||
raise ValueError(_('allow_without_dot must be a boolean'))
|
|
||||||
self._allow_ip = allow_ip
|
|
||||||
self._allow_without_dot = allow_without_dot
|
|
||||||
end = ''
|
|
||||||
extrachar = ''
|
|
||||||
extrachar_mandatory = ''
|
|
||||||
if self._type != 'netbios':
|
|
||||||
allow_number = '\d'
|
|
||||||
else:
|
|
||||||
allow_number = ''
|
|
||||||
if self._type == 'netbios':
|
|
||||||
length = 14
|
|
||||||
elif self._type == 'hostname':
|
|
||||||
length = 62
|
|
||||||
elif self._type == 'domainname':
|
|
||||||
length = 62
|
|
||||||
if allow_without_dot is False:
|
|
||||||
extrachar_mandatory = '\.'
|
|
||||||
else:
|
|
||||||
extrachar = '\.'
|
|
||||||
end = '+[a-z]*'
|
|
||||||
self._domain_re = re.compile(r'^(?:[a-z{0}][a-z\d\-{1}]{{,{2}}}{3}){4}$'
|
|
||||||
''.format(allow_number, extrachar, length,
|
|
||||||
extrachar_mandatory, end))
|
|
||||||
super(DomainnameOption, self).__init__(name, doc, default=default,
|
|
||||||
default_multi=default_multi,
|
|
||||||
callback=callback,
|
|
||||||
callback_params=callback_params,
|
|
||||||
requires=requires,
|
|
||||||
multi=multi,
|
|
||||||
validator=validator,
|
|
||||||
validator_params=validator_params,
|
|
||||||
properties=properties,
|
|
||||||
warnings_only=warnings_only)
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
if self._allow_ip is True:
|
|
||||||
try:
|
|
||||||
IP('{0}/32'.format(value))
|
|
||||||
return
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
if self._type == 'domainname' and not self._allow_without_dot and \
|
|
||||||
'.' not in value:
|
|
||||||
raise ValueError(_("invalid domainname, must have dot"))
|
|
||||||
if len(value) > 255:
|
|
||||||
raise ValueError(_("invalid domainname's length (max 255)"))
|
|
||||||
if len(value) < 2:
|
|
||||||
raise ValueError(_("invalid domainname's length (min 2)"))
|
|
||||||
if not self._domain_re.search(value):
|
|
||||||
raise ValueError(_('invalid domainname'))
|
|
||||||
|
|
||||||
|
|
||||||
class EmailOption(DomainnameOption):
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'email'
|
|
||||||
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
splitted = value.split('@', 1)
|
|
||||||
try:
|
|
||||||
username, domain = splitted
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(_('invalid email address, must contains one @'
|
|
||||||
))
|
|
||||||
if not self.username_re.search(username):
|
|
||||||
raise ValueError(_('invalid username in email address'))
|
|
||||||
super(EmailOption, self)._validate(domain)
|
|
||||||
|
|
||||||
|
|
||||||
class URLOption(DomainnameOption):
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'url'
|
|
||||||
proto_re = re.compile(r'(http|https)://')
|
|
||||||
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
match = self.proto_re.search(value)
|
|
||||||
if not match:
|
|
||||||
raise ValueError(_('invalid url, must start with http:// or '
|
|
||||||
'https://'))
|
|
||||||
value = value[len(match.group(0)):]
|
|
||||||
# get domain/files
|
|
||||||
splitted = value.split('/', 1)
|
|
||||||
try:
|
|
||||||
domain, files = splitted
|
|
||||||
except ValueError:
|
|
||||||
domain = value
|
|
||||||
files = None
|
|
||||||
# if port in domain
|
|
||||||
splitted = domain.split(':', 1)
|
|
||||||
try:
|
|
||||||
domain, port = splitted
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
domain = splitted[0]
|
|
||||||
port = 0
|
|
||||||
if not 0 <= int(port) <= 65535:
|
|
||||||
raise ValueError(_('invalid url, port must be an between 0 and '
|
|
||||||
'65536'))
|
|
||||||
# validate domainname
|
|
||||||
super(URLOption, self)._validate(domain)
|
|
||||||
# validate file
|
|
||||||
if files is not None and files != '' and not self.path_re.search(files):
|
|
||||||
raise ValueError(_('invalid url, must ends with filename'))
|
|
||||||
|
|
||||||
|
|
||||||
class UsernameOption(Option):
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'username'
|
|
||||||
#regexp build with 'man 8 adduser' informations
|
|
||||||
username_re = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
match = self.username_re.search(value)
|
|
||||||
if not match:
|
|
||||||
raise ValueError(_('invalid username'))
|
|
||||||
|
|
||||||
|
|
||||||
class FilenameOption(Option):
|
|
||||||
__slots__ = tuple()
|
|
||||||
_opt_type = 'file'
|
|
||||||
path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
|
|
||||||
|
|
||||||
def _validate(self, value):
|
|
||||||
match = self.path_re.search(value)
|
|
||||||
if not match:
|
|
||||||
raise ValueError(_('invalid filename'))
|
|
||||||
|
|
||||||
|
|
||||||
class OptionDescription(BaseOption):
|
|
||||||
"""Config's schema (organisation, group) and container of Options
|
|
||||||
The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
|
|
||||||
"""
|
|
||||||
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
|
|
||||||
'_state_group_type', '_properties', '_children',
|
|
||||||
'_cache_consistencies', '_calc_properties', '__weakref__',
|
|
||||||
'_readonly', '_impl_informations', '_state_requires',
|
|
||||||
'_stated', '_state_readonly')
|
|
||||||
_opt_type = 'optiondescription'
|
|
||||||
|
|
||||||
def __init__(self, name, doc, children, requires=None, properties=None):
|
|
||||||
"""
|
|
||||||
:param children: a list of options (including optiondescriptions)
|
|
||||||
|
|
||||||
"""
|
|
||||||
super(OptionDescription, self).__init__(name, doc, requires, properties)
|
|
||||||
child_names = [child._name for child in children]
|
|
||||||
#better performance like this
|
|
||||||
valid_child = copy(child_names)
|
|
||||||
valid_child.sort()
|
|
||||||
old = None
|
|
||||||
for child in valid_child:
|
|
||||||
if child == old:
|
|
||||||
raise ConflictError(_('duplicate option name: '
|
|
||||||
'{0}').format(child))
|
|
||||||
old = child
|
|
||||||
self._children = (tuple(child_names), tuple(children))
|
|
||||||
self._cache_paths = None
|
|
||||||
self._cache_consistencies = None
|
|
||||||
# the group_type is useful for filtering OptionDescriptions in a config
|
|
||||||
self._group_type = groups.default
|
|
||||||
|
|
||||||
def impl_getdoc(self):
|
|
||||||
return self.impl_get_information('doc')
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name in self.__slots__:
|
|
||||||
return object.__getattribute__(self, name)
|
|
||||||
try:
|
|
||||||
return self._children[1][self._children[0].index(name)]
|
|
||||||
except ValueError:
|
|
||||||
raise AttributeError(_('unknown Option {0} '
|
|
||||||
'in OptionDescription {1}'
|
|
||||||
'').format(name, self._name))
|
|
||||||
|
|
||||||
def impl_getkey(self, config):
|
|
||||||
return tuple([child.impl_getkey(getattr(config, child._name))
|
|
||||||
for child in self.impl_getchildren()])
|
|
||||||
|
|
||||||
def impl_getpaths(self, include_groups=False, _currpath=None):
|
|
||||||
"""returns a list of all paths in self, recursively
|
|
||||||
_currpath should not be provided (helps with recursion)
|
|
||||||
"""
|
|
||||||
if _currpath is None:
|
|
||||||
_currpath = []
|
|
||||||
paths = []
|
|
||||||
for option in self.impl_getchildren():
|
|
||||||
attr = option._name
|
|
||||||
if isinstance(option, OptionDescription):
|
|
||||||
if include_groups:
|
|
||||||
paths.append('.'.join(_currpath + [attr]))
|
|
||||||
paths += option.impl_getpaths(include_groups=include_groups,
|
|
||||||
_currpath=_currpath + [attr])
|
|
||||||
else:
|
|
||||||
paths.append('.'.join(_currpath + [attr]))
|
|
||||||
return paths
|
|
||||||
|
|
||||||
def impl_getchildren(self):
|
|
||||||
return self._children[1]
|
|
||||||
|
|
||||||
def impl_build_cache(self,
|
|
||||||
cache_path=None,
|
|
||||||
cache_option=None,
|
|
||||||
_currpath=None,
|
|
||||||
_consistencies=None,
|
|
||||||
force_no_consistencies=False):
|
|
||||||
if _currpath is None and self._cache_paths is not None:
|
|
||||||
# cache already set
|
|
||||||
return
|
|
||||||
if _currpath is None:
|
|
||||||
save = True
|
|
||||||
_currpath = []
|
|
||||||
if not force_no_consistencies:
|
|
||||||
_consistencies = {}
|
|
||||||
else:
|
|
||||||
save = False
|
|
||||||
if cache_path is None:
|
|
||||||
cache_path = []
|
|
||||||
cache_option = []
|
|
||||||
for option in self.impl_getchildren():
|
|
||||||
attr = option._name
|
|
||||||
if option in cache_option:
|
|
||||||
raise ConflictError(_('duplicate option: {0}').format(option))
|
|
||||||
|
|
||||||
cache_option.append(option)
|
|
||||||
if not force_no_consistencies:
|
|
||||||
option._readonly = True
|
|
||||||
cache_path.append(str('.'.join(_currpath + [attr])))
|
|
||||||
if not isinstance(option, OptionDescription):
|
|
||||||
if not force_no_consistencies and \
|
|
||||||
option._consistencies is not None:
|
|
||||||
for consistency in option._consistencies:
|
|
||||||
func, all_cons_opts, params = consistency
|
|
||||||
for opt in all_cons_opts:
|
|
||||||
_consistencies.setdefault(opt,
|
|
||||||
[]).append((func,
|
|
||||||
all_cons_opts,
|
|
||||||
params))
|
|
||||||
else:
|
|
||||||
_currpath.append(attr)
|
|
||||||
option.impl_build_cache(cache_path,
|
|
||||||
cache_option,
|
|
||||||
_currpath,
|
|
||||||
_consistencies,
|
|
||||||
force_no_consistencies)
|
|
||||||
_currpath.pop()
|
|
||||||
if save:
|
|
||||||
self._cache_paths = (tuple(cache_option), tuple(cache_path))
|
|
||||||
if not force_no_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
|
|
||||||
|
|
||||||
def impl_get_opt_by_path(self, path):
|
|
||||||
try:
|
|
||||||
return self._cache_paths[0][self._cache_paths[1].index(path)]
|
|
||||||
except ValueError:
|
|
||||||
raise AttributeError(_('no option for path {0}').format(path))
|
|
||||||
|
|
||||||
def impl_get_path_by_opt(self, opt):
|
|
||||||
try:
|
|
||||||
return self._cache_paths[1][self._cache_paths[0].index(opt)]
|
|
||||||
except ValueError:
|
|
||||||
raise AttributeError(_('no option {0} found').format(opt))
|
|
||||||
|
|
||||||
# ____________________________________________________________
|
|
||||||
def impl_set_group_type(self, group_type):
|
|
||||||
"""sets a given group object to an OptionDescription
|
|
||||||
|
|
||||||
:param group_type: an instance of `GroupType` or `MasterGroupType`
|
|
||||||
that lives in `setting.groups`
|
|
||||||
"""
|
|
||||||
if self._group_type != groups.default:
|
|
||||||
raise TypeError(_('cannot change group_type if already set '
|
|
||||||
'(old {0}, new {1})').format(self._group_type,
|
|
||||||
group_type))
|
|
||||||
if isinstance(group_type, groups.GroupType):
|
|
||||||
self._group_type = group_type
|
|
||||||
if isinstance(group_type, groups.MasterGroupType):
|
|
||||||
#if master (same name has group) is set
|
|
||||||
#for collect all slaves
|
|
||||||
slaves = []
|
|
||||||
master = None
|
|
||||||
for child in self.impl_getchildren():
|
|
||||||
if isinstance(child, OptionDescription):
|
|
||||||
raise ValueError(_("master group {0} shall not have "
|
|
||||||
"a subgroup").format(self._name))
|
|
||||||
if isinstance(child, SymLinkOption):
|
|
||||||
raise ValueError(_("master group {0} shall not have "
|
|
||||||
"a symlinkoption").format(self._name))
|
|
||||||
if not child.impl_is_multi():
|
|
||||||
raise ValueError(_("not allowed option {0} "
|
|
||||||
"in group {1}"
|
|
||||||
": this option is not a multi"
|
|
||||||
"").format(child._name, self._name))
|
|
||||||
if child._name == self._name:
|
|
||||||
child._multitype = multitypes.master
|
|
||||||
master = child
|
|
||||||
else:
|
|
||||||
slaves.append(child)
|
|
||||||
if master is None:
|
|
||||||
raise ValueError(_('master group with wrong'
|
|
||||||
' master name for {0}'
|
|
||||||
).format(self._name))
|
|
||||||
if master._callback is not None and master._callback[1] is not None:
|
|
||||||
for key, callbacks in master._callback[1].items():
|
|
||||||
for callbk in callbacks:
|
|
||||||
if isinstance(callbk, tuple):
|
|
||||||
if callbk[0] in slaves:
|
|
||||||
raise ValueError(_("callback of master's option shall "
|
|
||||||
"not refered a slave's ones"))
|
|
||||||
master._master_slaves = tuple(slaves)
|
|
||||||
for child in self.impl_getchildren():
|
|
||||||
if child != master:
|
|
||||||
child._master_slaves = master
|
|
||||||
child._multitype = multitypes.slave
|
|
||||||
else:
|
|
||||||
raise ValueError(_('group_type: {0}'
|
|
||||||
' not allowed').format(group_type))
|
|
||||||
|
|
||||||
def impl_get_group_type(self):
|
|
||||||
return self._group_type
|
|
||||||
|
|
||||||
def _valid_consistency(self, option, value, context, index):
|
|
||||||
if self._cache_consistencies is None:
|
|
||||||
return True
|
|
||||||
#consistencies is something like [('_cons_not_equal', (opt1, opt2))]
|
|
||||||
consistencies = self._cache_consistencies.get(option)
|
|
||||||
if consistencies is not None:
|
|
||||||
for func, all_cons_opts, params in consistencies:
|
|
||||||
warnings_only = params.get('warnings_only', False)
|
|
||||||
#all_cons_opts[0] is the option where func is set
|
|
||||||
try:
|
|
||||||
all_cons_opts[0]._launch_consistency(func, option,
|
|
||||||
value,
|
|
||||||
context, index,
|
|
||||||
all_cons_opts,
|
|
||||||
warnings_only)
|
|
||||||
except ValueError as err:
|
|
||||||
if warnings_only:
|
|
||||||
raise ValueWarning(err.message, option)
|
|
||||||
else:
|
|
||||||
raise err
|
|
||||||
|
|
||||||
def _impl_getstate(self, descr=None):
|
|
||||||
"""enables us to export into a dict
|
|
||||||
:param descr: parent :class:`tiramisu.option.OptionDescription`
|
|
||||||
"""
|
|
||||||
if descr is None:
|
|
||||||
self.impl_build_cache()
|
|
||||||
descr = self
|
|
||||||
super(OptionDescription, self)._impl_getstate(descr)
|
|
||||||
self._state_group_type = str(self._group_type)
|
|
||||||
for option in self.impl_getchildren():
|
|
||||||
option._impl_getstate(descr)
|
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
"""special method to enable the serialization with pickle
|
|
||||||
"""
|
|
||||||
stated = True
|
|
||||||
try:
|
|
||||||
# the `_state` attribute is a flag that which tells us if
|
|
||||||
# the serialization can be performed
|
|
||||||
self._stated
|
|
||||||
except AttributeError:
|
|
||||||
# if cannot delete, _impl_getstate never launch
|
|
||||||
# launch it recursivement
|
|
||||||
# _stated prevent __getstate__ launch more than one time
|
|
||||||
# _stated is delete, if re-serialize, re-lauch _impl_getstate
|
|
||||||
self._impl_getstate()
|
|
||||||
stated = False
|
|
||||||
return super(OptionDescription, self).__getstate__(stated)
|
|
||||||
|
|
||||||
def _impl_setstate(self, descr=None):
|
|
||||||
"""enables us to import from a dict
|
|
||||||
:param descr: parent :class:`tiramisu.option.OptionDescription`
|
|
||||||
"""
|
|
||||||
if descr is None:
|
|
||||||
self._cache_paths = None
|
|
||||||
self._cache_consistencies = None
|
|
||||||
self.impl_build_cache(force_no_consistencies=True)
|
|
||||||
descr = self
|
|
||||||
self._group_type = getattr(groups, self._state_group_type)
|
|
||||||
del(self._state_group_type)
|
|
||||||
super(OptionDescription, self)._impl_setstate(descr)
|
|
||||||
for option in self.impl_getchildren():
|
|
||||||
option._impl_setstate(descr)
|
|
||||||
|
|
||||||
def __setstate__(self, state):
|
|
||||||
super(OptionDescription, self).__setstate__(state)
|
|
||||||
try:
|
|
||||||
self._stated
|
|
||||||
except AttributeError:
|
|
||||||
self._impl_setstate()
|
|
||||||
|
|
||||||
|
|
||||||
def validate_requires_arg(requires, name):
|
def validate_requires_arg(requires, name):
|
||||||
"""check malformed requirements
|
"""check malformed requirements
|
||||||
and tranform dict to internal tuple
|
and tranform dict to internal tuple
|
||||||
|
@ -1524,41 +777,32 @@ def validate_requires_arg(requires, name):
|
||||||
return frozenset(config_action.keys()), tuple(ret)
|
return frozenset(config_action.keys()), tuple(ret)
|
||||||
|
|
||||||
|
|
||||||
def validate_callback(callback, callback_params, type_):
|
class SymLinkOption(BaseOption):
|
||||||
if type(callback) != FunctionType:
|
__slots__ = ('_name', '_opt', '_state_opt')
|
||||||
raise ValueError(_('{0} must be a function').format(type_))
|
_opt_type = 'symlink'
|
||||||
if callback_params is not None:
|
#not return _opt consistencies
|
||||||
if not isinstance(callback_params, dict):
|
_consistencies = None
|
||||||
raise ValueError(_('{0}_params must be a dict').format(type_))
|
|
||||||
for key, callbacks in callback_params.items():
|
def __init__(self, name, opt):
|
||||||
if key != '' and len(callbacks) != 1:
|
self._name = name
|
||||||
raise ValueError(_("{0}_params with key {1} mustn't have "
|
if not isinstance(opt, Option):
|
||||||
"length different to 1").format(type_,
|
raise ValueError(_('malformed symlinkoption '
|
||||||
key))
|
'must be an option '
|
||||||
if not isinstance(callbacks, tuple):
|
'for symlink {0}').format(name))
|
||||||
raise ValueError(_('{0}_params must be tuple for key "{1}"'
|
self._opt = opt
|
||||||
).format(type_, key))
|
self._readonly = True
|
||||||
for callbk in callbacks:
|
|
||||||
if isinstance(callbk, tuple):
|
def __getattr__(self, name):
|
||||||
if len(callbk) == 1:
|
if name in ('_name', '_opt', '_opt_type', '_readonly'):
|
||||||
if callbk != (None,):
|
return object.__getattr__(self, name)
|
||||||
raise ValueError(_('{0}_params with length of '
|
|
||||||
'tuple as 1 must only have '
|
|
||||||
'None as first value'))
|
|
||||||
elif len(callbk) != 2:
|
|
||||||
raise ValueError(_('{0}_params must only have 1 or 2 '
|
|
||||||
'as length'))
|
|
||||||
else:
|
else:
|
||||||
option, force_permissive = callbk
|
return getattr(self._opt, name)
|
||||||
if type_ == 'validator' and not force_permissive:
|
|
||||||
raise ValueError(_('validator not support tuple'))
|
def _impl_getstate(self, descr):
|
||||||
if not isinstance(option, Option) and not \
|
super(SymLinkOption, self)._impl_getstate(descr)
|
||||||
isinstance(option, SymLinkOption):
|
self._state_opt = descr.impl_get_path_by_opt(self._opt)
|
||||||
raise ValueError(_('{0}_params must have an option'
|
|
||||||
' not a {0} for first argument'
|
def _impl_setstate(self, descr):
|
||||||
).format(type_, type(option)))
|
self._opt = descr.impl_get_opt_by_path(self._state_opt)
|
||||||
if force_permissive not in [True, False]:
|
del(self._state_opt)
|
||||||
raise ValueError(_('{0}_params must have a boolean'
|
super(SymLinkOption, self)._impl_setstate(descr)
|
||||||
' not a {0} for second argument'
|
|
||||||
).format(type_, type(
|
|
||||||
force_permissive)))
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"master slave support"
|
||||||
|
# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
# Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
# option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# The original `Config` design model is unproudly borrowed from
|
||||||
|
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||||
|
# the whole pypy projet is under MIT licence
|
||||||
|
# ____________________________________________________________
|
||||||
|
from tiramisu.i18n import _
|
||||||
|
from tiramisu.setting import log
|
||||||
|
from tiramisu.error import SlaveError, ConfigError
|
||||||
|
from .baseoption import SymLinkOption, Option
|
||||||
|
|
||||||
|
|
||||||
|
class MasterSlaves(object):
|
||||||
|
__slots__ = ('master', 'slaves')
|
||||||
|
|
||||||
|
def __init__(self, name, childs):
|
||||||
|
#if master (same name has group) is set
|
||||||
|
#for collect all slaves
|
||||||
|
self.master = None
|
||||||
|
slaves = []
|
||||||
|
for child in childs:
|
||||||
|
if isinstance(child, SymLinkOption):
|
||||||
|
raise ValueError(_("master group {0} shall not have "
|
||||||
|
"a symlinkoption").format(name))
|
||||||
|
if not isinstance(child, Option):
|
||||||
|
raise ValueError(_("master group {0} shall not have "
|
||||||
|
"a subgroup").format(name))
|
||||||
|
if not child.impl_is_multi():
|
||||||
|
raise ValueError(_("not allowed option {0} "
|
||||||
|
"in group {1}"
|
||||||
|
": this option is not a multi"
|
||||||
|
"").format(child._name, name))
|
||||||
|
if child._name == name:
|
||||||
|
self.master = child
|
||||||
|
else:
|
||||||
|
slaves.append(child)
|
||||||
|
if self.master is None:
|
||||||
|
raise ValueError(_('master group with wrong'
|
||||||
|
' master name for {0}'
|
||||||
|
).format(name))
|
||||||
|
if self.master._callback is not None and self.master._callback[1] is not None:
|
||||||
|
for key, callbacks in self.master._callback[1].items():
|
||||||
|
for callbk in callbacks:
|
||||||
|
if isinstance(callbk, tuple):
|
||||||
|
if callbk[0] in slaves:
|
||||||
|
raise ValueError(_("callback of master's option shall "
|
||||||
|
"not refered a slave's ones"))
|
||||||
|
#everything is ok, store references
|
||||||
|
self.slaves = tuple(slaves)
|
||||||
|
for child in childs:
|
||||||
|
child._master_slaves = self
|
||||||
|
|
||||||
|
def is_master(self, opt):
|
||||||
|
return opt == self.master
|
||||||
|
|
||||||
|
def in_same_group(self, opt):
|
||||||
|
return opt == self.master or opt in self.slaves
|
||||||
|
|
||||||
|
def reset(self, values):
|
||||||
|
for slave in self.slaves:
|
||||||
|
values.reset(slave)
|
||||||
|
|
||||||
|
def pop(self, values, index):
|
||||||
|
#FIXME pas test de meta ...
|
||||||
|
for slave in self.slaves:
|
||||||
|
if not values.is_default_owner(slave, validate_properties=False,
|
||||||
|
validate_meta=False):
|
||||||
|
values._get_cached_item(slave, validate=False,
|
||||||
|
validate_properties=False
|
||||||
|
).pop(index, force=True)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getitem(self, values, opt, path, validate, force_permissive,
|
||||||
|
force_properties, validate_properties):
|
||||||
|
if opt == self.master:
|
||||||
|
value = values._get_validated_value(opt, path, validate,
|
||||||
|
force_permissive,
|
||||||
|
force_properties,
|
||||||
|
validate_properties)
|
||||||
|
if validate is True:
|
||||||
|
masterlen = len(value)
|
||||||
|
for slave in self.slaves:
|
||||||
|
try:
|
||||||
|
slave_path = values._get_opt_path(slave)
|
||||||
|
slave_value = values._get_validated_value(slave,
|
||||||
|
slave_path,
|
||||||
|
False,
|
||||||
|
False,
|
||||||
|
None, False,
|
||||||
|
None) # not undefined
|
||||||
|
slavelen = len(slave_value)
|
||||||
|
self.validate_slave_length(masterlen, slavelen, slave._name)
|
||||||
|
except ConfigError:
|
||||||
|
pass
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
value = values._get_validated_value(opt, path, validate,
|
||||||
|
force_permissive,
|
||||||
|
force_properties,
|
||||||
|
validate_properties,
|
||||||
|
None) # not undefined
|
||||||
|
return self.get_slave_value(values, opt, value, validate, validate_properties)
|
||||||
|
|
||||||
|
def setitem(self, values, opt, value, path):
|
||||||
|
if opt == self.master:
|
||||||
|
masterlen = len(value)
|
||||||
|
for slave in self.slaves:
|
||||||
|
slave_path = values._get_opt_path(slave)
|
||||||
|
slave_value = values._get_validated_value(slave,
|
||||||
|
slave_path,
|
||||||
|
False,
|
||||||
|
False,
|
||||||
|
None, False,
|
||||||
|
None) # not undefined
|
||||||
|
slavelen = len(slave_value)
|
||||||
|
self.validate_slave_length(masterlen, slavelen, slave._name)
|
||||||
|
else:
|
||||||
|
self.validate_slave_length(self.get_length(values), len(value),
|
||||||
|
opt._name, setitem=True)
|
||||||
|
|
||||||
|
def get_length(self, values, validate=True):
|
||||||
|
masterp = values._get_opt_path(self.master)
|
||||||
|
return len(self.getitem(values, self.master, masterp, validate, False,
|
||||||
|
None, True))
|
||||||
|
|
||||||
|
def validate_slave_length(self, masterlen, valuelen, name, setitem=False):
|
||||||
|
if valuelen > masterlen or (valuelen < masterlen and setitem):
|
||||||
|
log.debug('validate_slave_length: masterlen: {0}, valuelen: {1}, '
|
||||||
|
'setitem: {2}'.format(masterlen, valuelen, setitem))
|
||||||
|
raise SlaveError(_("invalid len for the slave: {0}"
|
||||||
|
" which has {1} as master").format(
|
||||||
|
name, self.master._name))
|
||||||
|
|
||||||
|
def get_slave_value(self, values, opt, value, validate=True,
|
||||||
|
validate_properties=True):
|
||||||
|
"""
|
||||||
|
if master has length 0:
|
||||||
|
return []
|
||||||
|
if master has length bigger than 0:
|
||||||
|
if default owner:
|
||||||
|
if has callback:
|
||||||
|
if return a list:
|
||||||
|
list same length as master: return list
|
||||||
|
list is smaller than master: return list + None
|
||||||
|
list is greater than master: raise SlaveError
|
||||||
|
if has default value:
|
||||||
|
list same length as master: return list
|
||||||
|
list is smaller than master: return list + None
|
||||||
|
list is greater than master: raise SlaveError
|
||||||
|
if has default_multi value:
|
||||||
|
return default_multi * master's length
|
||||||
|
if has value:
|
||||||
|
list same length as master: return list
|
||||||
|
list is smaller than master: return list + None
|
||||||
|
list is greater than master: raise SlaveError
|
||||||
|
"""
|
||||||
|
#if slave, had values until master's one
|
||||||
|
masterlen = self.get_length(values, validate)
|
||||||
|
valuelen = len(value)
|
||||||
|
if validate:
|
||||||
|
self.validate_slave_length(masterlen, valuelen, opt._name)
|
||||||
|
path = values._get_opt_path(opt)
|
||||||
|
if valuelen < masterlen:
|
||||||
|
for num in range(0, masterlen - valuelen):
|
||||||
|
index = valuelen + num
|
||||||
|
value.append(values._get_validated_value(opt, path, True,
|
||||||
|
False, None,
|
||||||
|
validate_properties,
|
||||||
|
index=index),
|
||||||
|
setitem=False,
|
||||||
|
force=True)
|
||||||
|
return value
|
|
@ -0,0 +1,525 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"option types and option description"
|
||||||
|
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
# Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
# option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# The original `Config` design model is unproudly borrowed from
|
||||||
|
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||||
|
# the whole pypy projet is under MIT licence
|
||||||
|
# ____________________________________________________________
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from IPy import IP
|
||||||
|
|
||||||
|
from tiramisu.error import ConfigError
|
||||||
|
from tiramisu.i18n import _
|
||||||
|
from .baseoption import Option
|
||||||
|
|
||||||
|
|
||||||
|
class ChoiceOption(Option):
|
||||||
|
"""represents a choice out of several objects.
|
||||||
|
|
||||||
|
The option can also have the value ``None``
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('_values', '_open_values')
|
||||||
|
_opt_type = 'string'
|
||||||
|
|
||||||
|
def __init__(self, name, doc, values, default=None, default_multi=None,
|
||||||
|
requires=None, multi=False, callback=None,
|
||||||
|
callback_params=None, open_values=False, validator=None,
|
||||||
|
validator_params=None, properties=None, warnings_only=False):
|
||||||
|
"""
|
||||||
|
:param values: is a list of values the option can possibly take
|
||||||
|
"""
|
||||||
|
if not isinstance(values, tuple):
|
||||||
|
raise TypeError(_('values must be a tuple for {0}').format(name))
|
||||||
|
self._values = values
|
||||||
|
if open_values not in (True, False):
|
||||||
|
raise TypeError(_('open_values must be a boolean for '
|
||||||
|
'{0}').format(name))
|
||||||
|
self._open_values = open_values
|
||||||
|
super(ChoiceOption, self).__init__(name, doc, default=default,
|
||||||
|
default_multi=default_multi,
|
||||||
|
callback=callback,
|
||||||
|
callback_params=callback_params,
|
||||||
|
requires=requires,
|
||||||
|
multi=multi,
|
||||||
|
validator=validator,
|
||||||
|
validator_params=validator_params,
|
||||||
|
properties=properties,
|
||||||
|
warnings_only=warnings_only)
|
||||||
|
|
||||||
|
def impl_get_values(self):
|
||||||
|
return self._values
|
||||||
|
|
||||||
|
def impl_is_openvalues(self):
|
||||||
|
return self._open_values
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if not self.impl_is_openvalues() and not value in self.impl_get_values():
|
||||||
|
raise ValueError(_('value {0} is not permitted, '
|
||||||
|
'only {1} is allowed'
|
||||||
|
'').format(value, self._values))
|
||||||
|
|
||||||
|
|
||||||
|
class BoolOption(Option):
|
||||||
|
"represents a choice between ``True`` and ``False``"
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'bool'
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if not isinstance(value, bool):
|
||||||
|
raise ValueError(_('invalid boolean'))
|
||||||
|
|
||||||
|
|
||||||
|
class IntOption(Option):
|
||||||
|
"represents a choice of an integer"
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'int'
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if not isinstance(value, int):
|
||||||
|
raise ValueError(_('invalid integer'))
|
||||||
|
|
||||||
|
|
||||||
|
class FloatOption(Option):
|
||||||
|
"represents a choice of a floating point number"
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'float'
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if not isinstance(value, float):
|
||||||
|
raise ValueError(_('invalid float'))
|
||||||
|
|
||||||
|
|
||||||
|
class StrOption(Option):
|
||||||
|
"represents the choice of a string"
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'string'
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise ValueError(_('invalid string'))
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
#UnicodeOption is same as StrOption in python 3+
|
||||||
|
class UnicodeOption(StrOption):
|
||||||
|
__slots__ = tuple()
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
class UnicodeOption(Option):
|
||||||
|
"represents the choice of a unicode string"
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'unicode'
|
||||||
|
_empty = u''
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if not isinstance(value, unicode):
|
||||||
|
raise ValueError(_('invalid unicode'))
|
||||||
|
|
||||||
|
|
||||||
|
class IPOption(Option):
|
||||||
|
"represents the choice of an ip"
|
||||||
|
__slots__ = ('_private_only', '_allow_reserved')
|
||||||
|
_opt_type = 'ip'
|
||||||
|
|
||||||
|
def __init__(self, name, doc, default=None, default_multi=None,
|
||||||
|
requires=None, multi=False, callback=None,
|
||||||
|
callback_params=None, validator=None, validator_params=None,
|
||||||
|
properties=None, private_only=False, allow_reserved=False,
|
||||||
|
warnings_only=False):
|
||||||
|
self._private_only = private_only
|
||||||
|
self._allow_reserved = allow_reserved
|
||||||
|
super(IPOption, self).__init__(name, doc, default=default,
|
||||||
|
default_multi=default_multi,
|
||||||
|
callback=callback,
|
||||||
|
callback_params=callback_params,
|
||||||
|
requires=requires,
|
||||||
|
multi=multi,
|
||||||
|
validator=validator,
|
||||||
|
validator_params=validator_params,
|
||||||
|
properties=properties,
|
||||||
|
warnings_only=warnings_only)
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
# sometimes an ip term starts with a zero
|
||||||
|
# but this does not fit in some case, for example bind does not like it
|
||||||
|
try:
|
||||||
|
for val in value.split('.'):
|
||||||
|
if val.startswith("0") and len(val) > 1:
|
||||||
|
raise ValueError(_('invalid IP'))
|
||||||
|
except AttributeError:
|
||||||
|
#if integer for example
|
||||||
|
raise ValueError(_('invalid IP'))
|
||||||
|
# 'standard' validation
|
||||||
|
try:
|
||||||
|
IP('{0}/32'.format(value))
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(_('invalid IP'))
|
||||||
|
|
||||||
|
def _second_level_validation(self, value, warnings_only):
|
||||||
|
ip = IP('{0}/32'.format(value))
|
||||||
|
if not self._allow_reserved and ip.iptype() == 'RESERVED':
|
||||||
|
if warnings_only:
|
||||||
|
msg = _("IP is in reserved class")
|
||||||
|
else:
|
||||||
|
msg = _("invalid IP, mustn't be in reserved class")
|
||||||
|
raise ValueError(msg)
|
||||||
|
if self._private_only and not ip.iptype() == 'PRIVATE':
|
||||||
|
if warnings_only:
|
||||||
|
msg = _("IP is not in private class")
|
||||||
|
else:
|
||||||
|
msg = _("invalid IP, must be in private class")
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
def _cons_in_network(self, opts, vals, warnings_only):
|
||||||
|
if len(vals) != 3:
|
||||||
|
raise ConfigError(_('invalid len for vals'))
|
||||||
|
if None in vals:
|
||||||
|
return
|
||||||
|
ip, network, netmask = vals
|
||||||
|
if IP(ip) not in IP('{0}/{1}'.format(network, netmask)):
|
||||||
|
if warnings_only:
|
||||||
|
msg = _('IP {0} ({1}) not in network {2} ({3}) with netmask {4}'
|
||||||
|
' ({5})')
|
||||||
|
else:
|
||||||
|
msg = _('invalid IP {0} ({1}) not in network {2} ({3}) with '
|
||||||
|
'netmask {4} ({5})')
|
||||||
|
raise ValueError(msg.format(ip, opts[0]._name, network,
|
||||||
|
opts[1]._name, netmask, opts[2]._name))
|
||||||
|
|
||||||
|
|
||||||
|
class PortOption(Option):
|
||||||
|
"""represents the choice of a port
|
||||||
|
The port numbers are divided into three ranges:
|
||||||
|
the well-known ports,
|
||||||
|
the registered ports,
|
||||||
|
and the dynamic or private ports.
|
||||||
|
You can actived this three range.
|
||||||
|
Port number 0 is reserved and can't be used.
|
||||||
|
see: http://en.wikipedia.org/wiki/Port_numbers
|
||||||
|
"""
|
||||||
|
__slots__ = ('_allow_range', '_allow_zero', '_min_value', '_max_value')
|
||||||
|
_opt_type = 'port'
|
||||||
|
|
||||||
|
def __init__(self, name, doc, default=None, default_multi=None,
|
||||||
|
requires=None, multi=False, callback=None,
|
||||||
|
callback_params=None, validator=None, validator_params=None,
|
||||||
|
properties=None, allow_range=False, allow_zero=False,
|
||||||
|
allow_wellknown=True, allow_registred=True,
|
||||||
|
allow_private=False, warnings_only=False):
|
||||||
|
self._allow_range = allow_range
|
||||||
|
self._min_value = None
|
||||||
|
self._max_value = None
|
||||||
|
ports_min = [0, 1, 1024, 49152]
|
||||||
|
ports_max = [0, 1023, 49151, 65535]
|
||||||
|
is_finally = False
|
||||||
|
for index, allowed in enumerate([allow_zero,
|
||||||
|
allow_wellknown,
|
||||||
|
allow_registred,
|
||||||
|
allow_private]):
|
||||||
|
if self._min_value is None:
|
||||||
|
if allowed:
|
||||||
|
self._min_value = ports_min[index]
|
||||||
|
elif not allowed:
|
||||||
|
is_finally = True
|
||||||
|
elif allowed and is_finally:
|
||||||
|
raise ValueError(_('inconsistency in allowed range'))
|
||||||
|
if allowed:
|
||||||
|
self._max_value = ports_max[index]
|
||||||
|
|
||||||
|
if self._max_value is None:
|
||||||
|
raise ValueError(_('max value is empty'))
|
||||||
|
|
||||||
|
super(PortOption, self).__init__(name, doc, default=default,
|
||||||
|
default_multi=default_multi,
|
||||||
|
callback=callback,
|
||||||
|
callback_params=callback_params,
|
||||||
|
requires=requires,
|
||||||
|
multi=multi,
|
||||||
|
validator=validator,
|
||||||
|
validator_params=validator_params,
|
||||||
|
properties=properties,
|
||||||
|
warnings_only=warnings_only)
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if self._allow_range and ":" in str(value):
|
||||||
|
value = str(value).split(':')
|
||||||
|
if len(value) != 2:
|
||||||
|
raise ValueError(_('invalid port, range must have two values '
|
||||||
|
'only'))
|
||||||
|
if not value[0] < value[1]:
|
||||||
|
raise ValueError(_('invalid port, first port in range must be'
|
||||||
|
' smaller than the second one'))
|
||||||
|
else:
|
||||||
|
value = [value]
|
||||||
|
|
||||||
|
for val in value:
|
||||||
|
try:
|
||||||
|
int(val)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(_('invalid port'))
|
||||||
|
if not self._min_value <= int(val) <= self._max_value:
|
||||||
|
raise ValueError(_('invalid port, must be an between {0} '
|
||||||
|
'and {1}').format(self._min_value,
|
||||||
|
self._max_value))
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkOption(Option):
|
||||||
|
"represents the choice of a network"
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'network'
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
try:
|
||||||
|
IP(value)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(_('invalid network address'))
|
||||||
|
|
||||||
|
def _second_level_validation(self, value, warnings_only):
|
||||||
|
ip = IP(value)
|
||||||
|
if ip.iptype() == 'RESERVED':
|
||||||
|
if warnings_only:
|
||||||
|
msg = _("network address is in reserved class")
|
||||||
|
else:
|
||||||
|
msg = _("invalid network address, mustn't be in reserved class")
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class NetmaskOption(Option):
|
||||||
|
"represents the choice of a netmask"
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'netmask'
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
try:
|
||||||
|
IP('0.0.0.0/{0}'.format(value))
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(_('invalid netmask address'))
|
||||||
|
|
||||||
|
def _cons_network_netmask(self, opts, vals, warnings_only):
|
||||||
|
#opts must be (netmask, network) options
|
||||||
|
if None in vals:
|
||||||
|
return
|
||||||
|
self.__cons_netmask(opts, vals[0], vals[1], False, warnings_only)
|
||||||
|
|
||||||
|
def _cons_ip_netmask(self, opts, vals, warnings_only):
|
||||||
|
#opts must be (netmask, ip) options
|
||||||
|
if None in vals:
|
||||||
|
return
|
||||||
|
self.__cons_netmask(opts, vals[0], vals[1], True, warnings_only)
|
||||||
|
|
||||||
|
def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net,
|
||||||
|
warnings_only):
|
||||||
|
if len(opts) != 2:
|
||||||
|
raise ConfigError(_('invalid len for opts'))
|
||||||
|
msg = None
|
||||||
|
try:
|
||||||
|
ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
|
||||||
|
make_net=make_net)
|
||||||
|
#if cidr == 32, ip same has network
|
||||||
|
if ip.prefixlen() != 32:
|
||||||
|
try:
|
||||||
|
IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
|
||||||
|
make_net=not make_net)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if make_net:
|
||||||
|
msg = _("invalid IP {0} ({1}) with netmask {2},"
|
||||||
|
" this IP is a network")
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
if not make_net:
|
||||||
|
msg = _('invalid network {0} ({1}) with netmask {2}')
|
||||||
|
if msg is not None:
|
||||||
|
raise ValueError(msg.format(val_ipnetwork, opts[1]._name,
|
||||||
|
val_netmask))
|
||||||
|
|
||||||
|
|
||||||
|
class BroadcastOption(Option):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'broadcast'
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
try:
|
||||||
|
IP('{0}/32'.format(value))
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(_('invalid broadcast address'))
|
||||||
|
|
||||||
|
def _cons_broadcast(self, opts, vals, warnings_only):
|
||||||
|
if len(vals) != 3:
|
||||||
|
raise ConfigError(_('invalid len for vals'))
|
||||||
|
if None in vals:
|
||||||
|
return
|
||||||
|
broadcast, network, netmask = vals
|
||||||
|
if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast):
|
||||||
|
raise ValueError(_('invalid broadcast {0} ({1}) with network {2} '
|
||||||
|
'({3}) and netmask {4} ({5})').format(
|
||||||
|
broadcast, opts[0]._name, network,
|
||||||
|
opts[1]._name, netmask, opts[2]._name))
|
||||||
|
|
||||||
|
|
||||||
|
class DomainnameOption(Option):
|
||||||
|
"""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', '_allow_without_dot', '_domain_re')
|
||||||
|
_opt_type = 'domainname'
|
||||||
|
|
||||||
|
def __init__(self, name, doc, default=None, default_multi=None,
|
||||||
|
requires=None, multi=False, callback=None,
|
||||||
|
callback_params=None, validator=None, validator_params=None,
|
||||||
|
properties=None, allow_ip=False, type_='domainname',
|
||||||
|
warnings_only=False, allow_without_dot=False):
|
||||||
|
if type_ not in ['netbios', 'hostname', 'domainname']:
|
||||||
|
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
|
||||||
|
self._type = type_
|
||||||
|
if allow_ip not in [True, False]:
|
||||||
|
raise ValueError(_('allow_ip must be a boolean'))
|
||||||
|
if allow_without_dot not in [True, False]:
|
||||||
|
raise ValueError(_('allow_without_dot must be a boolean'))
|
||||||
|
self._allow_ip = allow_ip
|
||||||
|
self._allow_without_dot = allow_without_dot
|
||||||
|
end = ''
|
||||||
|
extrachar = ''
|
||||||
|
extrachar_mandatory = ''
|
||||||
|
if self._type != 'netbios':
|
||||||
|
allow_number = '\d'
|
||||||
|
else:
|
||||||
|
allow_number = ''
|
||||||
|
if self._type == 'netbios':
|
||||||
|
length = 14
|
||||||
|
elif self._type == 'hostname':
|
||||||
|
length = 62
|
||||||
|
elif self._type == 'domainname':
|
||||||
|
length = 62
|
||||||
|
if allow_without_dot is False:
|
||||||
|
extrachar_mandatory = '\.'
|
||||||
|
else:
|
||||||
|
extrachar = '\.'
|
||||||
|
end = '+[a-z]*'
|
||||||
|
self._domain_re = re.compile(r'^(?:[a-z{0}][a-z\d\-{1}]{{,{2}}}{3}){4}$'
|
||||||
|
''.format(allow_number, extrachar, length,
|
||||||
|
extrachar_mandatory, end))
|
||||||
|
super(DomainnameOption, self).__init__(name, doc, default=default,
|
||||||
|
default_multi=default_multi,
|
||||||
|
callback=callback,
|
||||||
|
callback_params=callback_params,
|
||||||
|
requires=requires,
|
||||||
|
multi=multi,
|
||||||
|
validator=validator,
|
||||||
|
validator_params=validator_params,
|
||||||
|
properties=properties,
|
||||||
|
warnings_only=warnings_only)
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
if self._allow_ip is True:
|
||||||
|
try:
|
||||||
|
IP('{0}/32'.format(value))
|
||||||
|
return
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if self._type == 'domainname' and not self._allow_without_dot and \
|
||||||
|
'.' not in value:
|
||||||
|
raise ValueError(_("invalid domainname, must have dot"))
|
||||||
|
if len(value) > 255:
|
||||||
|
raise ValueError(_("invalid domainname's length (max 255)"))
|
||||||
|
if len(value) < 2:
|
||||||
|
raise ValueError(_("invalid domainname's length (min 2)"))
|
||||||
|
if not self._domain_re.search(value):
|
||||||
|
raise ValueError(_('invalid domainname'))
|
||||||
|
|
||||||
|
|
||||||
|
class EmailOption(DomainnameOption):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'email'
|
||||||
|
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
splitted = value.split('@', 1)
|
||||||
|
try:
|
||||||
|
username, domain = splitted
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(_('invalid email address, must contains one @'
|
||||||
|
))
|
||||||
|
if not self.username_re.search(username):
|
||||||
|
raise ValueError(_('invalid username in email address'))
|
||||||
|
super(EmailOption, self)._validate(domain)
|
||||||
|
|
||||||
|
|
||||||
|
class URLOption(DomainnameOption):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'url'
|
||||||
|
proto_re = re.compile(r'(http|https)://')
|
||||||
|
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
match = self.proto_re.search(value)
|
||||||
|
if not match:
|
||||||
|
raise ValueError(_('invalid url, must start with http:// or '
|
||||||
|
'https://'))
|
||||||
|
value = value[len(match.group(0)):]
|
||||||
|
# get domain/files
|
||||||
|
splitted = value.split('/', 1)
|
||||||
|
try:
|
||||||
|
domain, files = splitted
|
||||||
|
except ValueError:
|
||||||
|
domain = value
|
||||||
|
files = None
|
||||||
|
# if port in domain
|
||||||
|
splitted = domain.split(':', 1)
|
||||||
|
try:
|
||||||
|
domain, port = splitted
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
domain = splitted[0]
|
||||||
|
port = 0
|
||||||
|
if not 0 <= int(port) <= 65535:
|
||||||
|
raise ValueError(_('invalid url, port must be an between 0 and '
|
||||||
|
'65536'))
|
||||||
|
# validate domainname
|
||||||
|
super(URLOption, self)._validate(domain)
|
||||||
|
# validate file
|
||||||
|
if files is not None and files != '' and not self.path_re.search(files):
|
||||||
|
raise ValueError(_('invalid url, must ends with filename'))
|
||||||
|
|
||||||
|
|
||||||
|
class UsernameOption(Option):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'username'
|
||||||
|
#regexp build with 'man 8 adduser' informations
|
||||||
|
username_re = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
match = self.username_re.search(value)
|
||||||
|
if not match:
|
||||||
|
raise ValueError(_('invalid username'))
|
||||||
|
|
||||||
|
|
||||||
|
class FilenameOption(Option):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_opt_type = 'file'
|
||||||
|
path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
|
||||||
|
|
||||||
|
def _validate(self, value):
|
||||||
|
match = self.path_re.search(value)
|
||||||
|
if not match:
|
||||||
|
raise ValueError(_('invalid filename'))
|
|
@ -0,0 +1,263 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
# Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
# option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# The original `Config` design model is unproudly borrowed from
|
||||||
|
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||||
|
# the whole pypy projet is under MIT licence
|
||||||
|
# ____________________________________________________________
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from tiramisu.i18n import _
|
||||||
|
from tiramisu.setting import groups, log
|
||||||
|
from .baseoption import BaseOption
|
||||||
|
from . import MasterSlaves
|
||||||
|
from tiramisu.error import ConfigError, ConflictError, ValueWarning
|
||||||
|
|
||||||
|
|
||||||
|
class OptionDescription(BaseOption):
|
||||||
|
"""Config's schema (organisation, group) and container of Options
|
||||||
|
The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
|
||||||
|
"""
|
||||||
|
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
|
||||||
|
'_state_group_type', '_properties', '_children',
|
||||||
|
'_cache_consistencies', '_calc_properties', '__weakref__',
|
||||||
|
'_readonly', '_impl_informations', '_state_requires',
|
||||||
|
'_stated', '_state_readonly')
|
||||||
|
_opt_type = 'optiondescription'
|
||||||
|
|
||||||
|
def __init__(self, name, doc, children, requires=None, properties=None):
|
||||||
|
"""
|
||||||
|
:param children: a list of options (including optiondescriptions)
|
||||||
|
|
||||||
|
"""
|
||||||
|
super(OptionDescription, self).__init__(name, doc, requires, properties)
|
||||||
|
child_names = [child._name for child in children]
|
||||||
|
#better performance like this
|
||||||
|
valid_child = copy(child_names)
|
||||||
|
valid_child.sort()
|
||||||
|
old = None
|
||||||
|
for child in valid_child:
|
||||||
|
if child == old:
|
||||||
|
raise ConflictError(_('duplicate option name: '
|
||||||
|
'{0}').format(child))
|
||||||
|
old = child
|
||||||
|
self._children = (tuple(child_names), tuple(children))
|
||||||
|
self._cache_paths = None
|
||||||
|
self._cache_consistencies = None
|
||||||
|
# the group_type is useful for filtering OptionDescriptions in a config
|
||||||
|
self._group_type = groups.default
|
||||||
|
|
||||||
|
def impl_getdoc(self):
|
||||||
|
return self.impl_get_information('doc')
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name in self.__slots__:
|
||||||
|
return object.__getattribute__(self, name)
|
||||||
|
try:
|
||||||
|
return self._children[1][self._children[0].index(name)]
|
||||||
|
except ValueError:
|
||||||
|
log.debug('__getattr__', exc_info=True)
|
||||||
|
raise AttributeError(_('unknown Option {0} '
|
||||||
|
'in OptionDescription {1}'
|
||||||
|
'').format(name, self._name))
|
||||||
|
|
||||||
|
def impl_getkey(self, config):
|
||||||
|
return tuple([child.impl_getkey(getattr(config, child._name))
|
||||||
|
for child in self.impl_getchildren()])
|
||||||
|
|
||||||
|
def impl_getpaths(self, include_groups=False, _currpath=None):
|
||||||
|
"""returns a list of all paths in self, recursively
|
||||||
|
_currpath should not be provided (helps with recursion)
|
||||||
|
"""
|
||||||
|
if _currpath is None:
|
||||||
|
_currpath = []
|
||||||
|
paths = []
|
||||||
|
for option in self.impl_getchildren():
|
||||||
|
attr = option._name
|
||||||
|
if isinstance(option, OptionDescription):
|
||||||
|
if include_groups:
|
||||||
|
paths.append('.'.join(_currpath + [attr]))
|
||||||
|
paths += option.impl_getpaths(include_groups=include_groups,
|
||||||
|
_currpath=_currpath + [attr])
|
||||||
|
else:
|
||||||
|
paths.append('.'.join(_currpath + [attr]))
|
||||||
|
return paths
|
||||||
|
|
||||||
|
def impl_getchildren(self):
|
||||||
|
return self._children[1]
|
||||||
|
|
||||||
|
def impl_build_cache(self,
|
||||||
|
cache_path=None,
|
||||||
|
cache_option=None,
|
||||||
|
_currpath=None,
|
||||||
|
_consistencies=None,
|
||||||
|
force_no_consistencies=False):
|
||||||
|
if _currpath is None and self._cache_paths is not None:
|
||||||
|
# cache already set
|
||||||
|
return
|
||||||
|
if _currpath is None:
|
||||||
|
save = True
|
||||||
|
_currpath = []
|
||||||
|
if not force_no_consistencies:
|
||||||
|
_consistencies = {}
|
||||||
|
else:
|
||||||
|
save = False
|
||||||
|
if cache_path is None:
|
||||||
|
cache_path = []
|
||||||
|
cache_option = []
|
||||||
|
for option in self.impl_getchildren():
|
||||||
|
attr = option._name
|
||||||
|
if option in cache_option:
|
||||||
|
raise ConflictError(_('duplicate option: {0}').format(option))
|
||||||
|
|
||||||
|
cache_option.append(option)
|
||||||
|
if not force_no_consistencies:
|
||||||
|
option._readonly = True
|
||||||
|
cache_path.append(str('.'.join(_currpath + [attr])))
|
||||||
|
if not isinstance(option, OptionDescription):
|
||||||
|
if not force_no_consistencies and \
|
||||||
|
option._consistencies is not None:
|
||||||
|
for consistency in option._consistencies:
|
||||||
|
func, all_cons_opts, params = consistency
|
||||||
|
for opt in all_cons_opts:
|
||||||
|
_consistencies.setdefault(opt,
|
||||||
|
[]).append((func,
|
||||||
|
all_cons_opts,
|
||||||
|
params))
|
||||||
|
else:
|
||||||
|
_currpath.append(attr)
|
||||||
|
option.impl_build_cache(cache_path,
|
||||||
|
cache_option,
|
||||||
|
_currpath,
|
||||||
|
_consistencies,
|
||||||
|
force_no_consistencies)
|
||||||
|
_currpath.pop()
|
||||||
|
if save:
|
||||||
|
self._cache_paths = (tuple(cache_option), tuple(cache_path))
|
||||||
|
if not force_no_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
|
||||||
|
|
||||||
|
def impl_get_opt_by_path(self, path):
|
||||||
|
try:
|
||||||
|
return self._cache_paths[0][self._cache_paths[1].index(path)]
|
||||||
|
except ValueError:
|
||||||
|
raise AttributeError(_('no option for path {0}').format(path))
|
||||||
|
|
||||||
|
def impl_get_path_by_opt(self, opt):
|
||||||
|
try:
|
||||||
|
return self._cache_paths[1][self._cache_paths[0].index(opt)]
|
||||||
|
except ValueError:
|
||||||
|
raise AttributeError(_('no option {0} found').format(opt))
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
def impl_set_group_type(self, group_type):
|
||||||
|
"""sets a given group object to an OptionDescription
|
||||||
|
|
||||||
|
:param group_type: an instance of `GroupType` or `MasterGroupType`
|
||||||
|
that lives in `setting.groups`
|
||||||
|
"""
|
||||||
|
if self._group_type != groups.default:
|
||||||
|
raise TypeError(_('cannot change group_type if already set '
|
||||||
|
'(old {0}, new {1})').format(self._group_type,
|
||||||
|
group_type))
|
||||||
|
if isinstance(group_type, groups.GroupType):
|
||||||
|
self._group_type = group_type
|
||||||
|
if isinstance(group_type, groups.MasterGroupType):
|
||||||
|
MasterSlaves(self._name, self.impl_getchildren())
|
||||||
|
else:
|
||||||
|
raise ValueError(_('group_type: {0}'
|
||||||
|
' not allowed').format(group_type))
|
||||||
|
|
||||||
|
def impl_get_group_type(self):
|
||||||
|
return self._group_type
|
||||||
|
|
||||||
|
def _valid_consistency(self, option, value, context, index):
|
||||||
|
if self._cache_consistencies is None:
|
||||||
|
return True
|
||||||
|
#consistencies is something like [('_cons_not_equal', (opt1, opt2))]
|
||||||
|
consistencies = self._cache_consistencies.get(option)
|
||||||
|
if consistencies is not None:
|
||||||
|
for func, all_cons_opts, params in consistencies:
|
||||||
|
warnings_only = params.get('warnings_only', False)
|
||||||
|
#all_cons_opts[0] is the option where func is set
|
||||||
|
try:
|
||||||
|
all_cons_opts[0]._launch_consistency(func, option,
|
||||||
|
value,
|
||||||
|
context, index,
|
||||||
|
all_cons_opts,
|
||||||
|
warnings_only)
|
||||||
|
except ValueError as err:
|
||||||
|
if warnings_only:
|
||||||
|
raise ValueWarning(err.message, option)
|
||||||
|
else:
|
||||||
|
raise err
|
||||||
|
|
||||||
|
def _impl_getstate(self, descr=None):
|
||||||
|
"""enables us to export into a dict
|
||||||
|
:param descr: parent :class:`tiramisu.option.OptionDescription`
|
||||||
|
"""
|
||||||
|
if descr is None:
|
||||||
|
self.impl_build_cache()
|
||||||
|
descr = self
|
||||||
|
super(OptionDescription, self)._impl_getstate(descr)
|
||||||
|
self._state_group_type = str(self._group_type)
|
||||||
|
for option in self.impl_getchildren():
|
||||||
|
option._impl_getstate(descr)
|
||||||
|
|
||||||
|
def __getstate__(self):
|
||||||
|
"""special method to enable the serialization with pickle
|
||||||
|
"""
|
||||||
|
stated = True
|
||||||
|
try:
|
||||||
|
# the `_state` attribute is a flag that which tells us if
|
||||||
|
# the serialization can be performed
|
||||||
|
self._stated
|
||||||
|
except AttributeError:
|
||||||
|
# if cannot delete, _impl_getstate never launch
|
||||||
|
# launch it recursivement
|
||||||
|
# _stated prevent __getstate__ launch more than one time
|
||||||
|
# _stated is delete, if re-serialize, re-lauch _impl_getstate
|
||||||
|
self._impl_getstate()
|
||||||
|
stated = False
|
||||||
|
return super(OptionDescription, self).__getstate__(stated)
|
||||||
|
|
||||||
|
def _impl_setstate(self, descr=None):
|
||||||
|
"""enables us to import from a dict
|
||||||
|
:param descr: parent :class:`tiramisu.option.OptionDescription`
|
||||||
|
"""
|
||||||
|
if descr is None:
|
||||||
|
self._cache_paths = None
|
||||||
|
self._cache_consistencies = None
|
||||||
|
self.impl_build_cache(force_no_consistencies=True)
|
||||||
|
descr = self
|
||||||
|
self._group_type = getattr(groups, self._state_group_type)
|
||||||
|
del(self._state_group_type)
|
||||||
|
super(OptionDescription, self)._impl_setstate(descr)
|
||||||
|
for option in self.impl_getchildren():
|
||||||
|
option._impl_setstate(descr)
|
||||||
|
|
||||||
|
def __setstate__(self, state):
|
||||||
|
super(OptionDescription, self).__setstate__(state)
|
||||||
|
try:
|
||||||
|
self._stated
|
||||||
|
except AttributeError:
|
||||||
|
self._impl_setstate()
|
|
@ -101,6 +101,9 @@ rw_remove = set(['permissive', 'everything_frozen', 'mandatory'])
|
||||||
|
|
||||||
|
|
||||||
log = getLogger('tiramisu')
|
log = getLogger('tiramisu')
|
||||||
|
#FIXME
|
||||||
|
#import logging
|
||||||
|
#logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
# ____________________________________________________________
|
# ____________________________________________________________
|
||||||
|
@ -211,34 +214,12 @@ def populate_owners():
|
||||||
setattr(owners, 'addowner', addowner)
|
setattr(owners, 'addowner', addowner)
|
||||||
|
|
||||||
|
|
||||||
def populate_multitypes():
|
|
||||||
"""all multi option should have a type, this type is automaticly set do
|
|
||||||
not touch this
|
|
||||||
|
|
||||||
default
|
|
||||||
default's multi option set if not master or slave
|
|
||||||
|
|
||||||
master
|
|
||||||
master's option in a group with master's type, name of this option
|
|
||||||
should be the same name of the optiondescription
|
|
||||||
|
|
||||||
slave
|
|
||||||
slave's option in a group with master's type
|
|
||||||
|
|
||||||
"""
|
|
||||||
setattr(multitypes, 'default', multitypes.DefaultMultiType('default'))
|
|
||||||
setattr(multitypes, 'master', multitypes.MasterMultiType('master'))
|
|
||||||
setattr(multitypes, 'slave', multitypes.SlaveMultiType('slave'))
|
|
||||||
|
|
||||||
|
|
||||||
# ____________________________________________________________
|
# ____________________________________________________________
|
||||||
# populate groups, owners and multitypes with default attributes
|
# populate groups, owners and multitypes with default attributes
|
||||||
groups = GroupModule()
|
groups = GroupModule()
|
||||||
populate_groups()
|
populate_groups()
|
||||||
owners = OwnerModule()
|
owners = OwnerModule()
|
||||||
populate_owners()
|
populate_owners()
|
||||||
multitypes = MultiTypeModule()
|
|
||||||
populate_multitypes()
|
|
||||||
|
|
||||||
|
|
||||||
# ____________________________________________________________
|
# ____________________________________________________________
|
||||||
|
|
|
@ -16,11 +16,10 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# ____________________________________________________________
|
# ____________________________________________________________
|
||||||
from time import time
|
from time import time
|
||||||
from copy import copy
|
|
||||||
import sys
|
import sys
|
||||||
import weakref
|
import weakref
|
||||||
from tiramisu.error import ConfigError, SlaveError, PropertiesOptionError
|
from tiramisu.error import ConfigError, SlaveError, PropertiesOptionError
|
||||||
from tiramisu.setting import owners, multitypes, expires_time, undefined
|
from tiramisu.setting import owners, expires_time, undefined
|
||||||
from tiramisu.autolib import carry_out_calculation
|
from tiramisu.autolib import carry_out_calculation
|
||||||
from tiramisu.i18n import _
|
from tiramisu.i18n import _
|
||||||
from tiramisu.option import SymLinkOption
|
from tiramisu.option import SymLinkOption
|
||||||
|
@ -55,36 +54,65 @@ class Values(object):
|
||||||
raise ConfigError(_('the context does not exist anymore'))
|
raise ConfigError(_('the context does not exist anymore'))
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _getdefault(self, opt):
|
def _getvalue(self, opt, path, is_default, index=undefined):
|
||||||
"""
|
|
||||||
actually retrieves the default value
|
|
||||||
|
|
||||||
:param opt: the `option.Option()` object
|
|
||||||
"""
|
|
||||||
meta = self._getcontext().cfgimpl_get_meta()
|
|
||||||
if meta is not None:
|
|
||||||
value = meta.cfgimpl_get_values()[opt]
|
|
||||||
if isinstance(value, Multi):
|
|
||||||
value = list(value)
|
|
||||||
else:
|
|
||||||
value = opt.impl_getdefault()
|
|
||||||
if opt.impl_is_multi():
|
|
||||||
return copy(value)
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
def _getvalue(self, opt, path):
|
|
||||||
"""actually retrieves the value
|
"""actually retrieves the value
|
||||||
|
|
||||||
:param opt: the `option.Option()` object
|
:param opt: the `option.Option()` object
|
||||||
:returns: the option's value (or the default value if not set)
|
:returns: the option's value (or the default value if not set)
|
||||||
"""
|
"""
|
||||||
if not self._p_.hasvalue(path):
|
setting = self._getcontext().cfgimpl_get_settings()
|
||||||
# if there is no value
|
force_default = 'frozen' in setting[opt] and \
|
||||||
value = self._getdefault(opt)
|
'force_default_on_freeze' in setting[opt]
|
||||||
else:
|
if not is_default and not force_default:
|
||||||
# if there is a value
|
|
||||||
value = self._p_.getvalue(path)
|
value = self._p_.getvalue(path)
|
||||||
|
if index is not undefined:
|
||||||
|
try:
|
||||||
|
return value[index]
|
||||||
|
except IndexError:
|
||||||
|
#value is smaller than expected
|
||||||
|
#so return default value
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
#so default value
|
||||||
|
# if value has callback and is not set
|
||||||
|
if opt.impl_has_callback():
|
||||||
|
callback, callback_params = opt._callback
|
||||||
|
if callback_params is None:
|
||||||
|
callback_params = {}
|
||||||
|
value = carry_out_calculation(opt, config=self._getcontext(),
|
||||||
|
callback=callback,
|
||||||
|
callback_params=callback_params,
|
||||||
|
index=index)
|
||||||
|
try:
|
||||||
|
if isinstance(value, list) and index is not undefined:
|
||||||
|
return value[index]
|
||||||
|
return value
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
meta = self._getcontext().cfgimpl_get_meta()
|
||||||
|
if meta is not None:
|
||||||
|
#FIXME : problème de longueur si meta + slave
|
||||||
|
#doit passer de meta à pas meta
|
||||||
|
#en plus il faut gérer la longueur avec les meta !
|
||||||
|
#FIXME SymlinkOption
|
||||||
|
value = meta.cfgimpl_get_values()[opt]
|
||||||
|
if isinstance(value, Multi):
|
||||||
|
if index is not undefined:
|
||||||
|
value = value[index]
|
||||||
|
else:
|
||||||
|
value = list(value)
|
||||||
|
return value
|
||||||
|
# now try to get default value
|
||||||
|
value = opt.impl_getdefault()
|
||||||
|
if opt.impl_is_multi() and index is not undefined:
|
||||||
|
if value is None:
|
||||||
|
value = opt.impl_getdefault_multi()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
value = value[index]
|
||||||
|
except IndexError:
|
||||||
|
value = opt.impl_getdefault_multi()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def get_modified_values(self):
|
def get_modified_values(self):
|
||||||
|
@ -121,44 +149,38 @@ class Values(object):
|
||||||
opt.impl_validate(opt.impl_getdefault(),
|
opt.impl_validate(opt.impl_getdefault(),
|
||||||
context, 'validator' in setting)
|
context, 'validator' in setting)
|
||||||
context.cfgimpl_reset_cache()
|
context.cfgimpl_reset_cache()
|
||||||
if (opt.impl_is_multi() and
|
if opt.impl_is_master_slaves('master'):
|
||||||
opt.impl_get_multitype() == multitypes.master):
|
opt.impl_get_master_slaves().reset(self)
|
||||||
for slave in opt.impl_get_master_slaves():
|
|
||||||
self.reset(slave)
|
|
||||||
self._p_.resetvalue(path)
|
self._p_.resetvalue(path)
|
||||||
|
|
||||||
def _isempty(self, opt, value):
|
def _isempty(self, opt, value):
|
||||||
"convenience method to know if an option is empty"
|
"convenience method to know if an option is empty"
|
||||||
empty = opt._empty
|
empty = opt._empty
|
||||||
if (not opt.impl_is_multi() and (value is None or value == empty)) or \
|
if value is not undefined:
|
||||||
(opt.impl_is_multi() and (value == [] or
|
empty_not_multi = not opt.impl_is_multi() and (value is None or
|
||||||
None in value or empty in value)):
|
value == empty)
|
||||||
return True
|
empty_multi = opt.impl_is_multi() and (value == [] or
|
||||||
return False
|
None in value or
|
||||||
|
empty in value)
|
||||||
def _getcallback_value(self, opt, index=None, max_len=None):
|
else:
|
||||||
"""
|
empty_multi = empty_not_multi = False
|
||||||
retrieves a value for the options that have a callback
|
return empty_not_multi or empty_multi
|
||||||
|
|
||||||
:param opt: the `option.Option()` object
|
|
||||||
:param index: if an option is multi, only calculates the nth value
|
|
||||||
:type index: int
|
|
||||||
:returns: a calculated value
|
|
||||||
"""
|
|
||||||
callback, callback_params = opt._callback
|
|
||||||
if callback_params is None:
|
|
||||||
callback_params = {}
|
|
||||||
return carry_out_calculation(opt, config=self._getcontext(),
|
|
||||||
callback=callback,
|
|
||||||
callback_params=callback_params,
|
|
||||||
index=index, max_len=max_len)
|
|
||||||
|
|
||||||
def __getitem__(self, opt):
|
def __getitem__(self, opt):
|
||||||
"enables us to use the pythonic dictionary-like access to values"
|
"enables us to use the pythonic dictionary-like access to values"
|
||||||
return self.getitem(opt)
|
return self.getitem(opt)
|
||||||
|
|
||||||
def getitem(self, opt, path=None, validate=True, force_permissive=False,
|
def getitem(self, opt, validate=True, force_permissive=False,
|
||||||
force_properties=None, validate_properties=True):
|
force_properties=None):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
return self._get_cached_item(opt, validate=validate,
|
||||||
|
force_permissive=force_permissive,
|
||||||
|
force_properties=force_properties)
|
||||||
|
|
||||||
|
def _get_cached_item(self, opt, path=None, validate=True,
|
||||||
|
force_permissive=False, force_properties=None,
|
||||||
|
validate_properties=True):
|
||||||
if path is None:
|
if path is None:
|
||||||
path = self._get_opt_path(opt)
|
path = self._get_opt_path(opt)
|
||||||
ntime = None
|
ntime = None
|
||||||
|
@ -170,7 +192,7 @@ class Values(object):
|
||||||
if is_cached:
|
if is_cached:
|
||||||
if opt.impl_is_multi() and not isinstance(value, Multi):
|
if opt.impl_is_multi() and not isinstance(value, Multi):
|
||||||
#load value so don't need to validate if is not a Multi
|
#load value so don't need to validate if is not a Multi
|
||||||
value = Multi(value, self.context, opt, path, validate=False)
|
value = Multi(value, self.context, opt, path)
|
||||||
return value
|
return value
|
||||||
val = self._getitem(opt, path, validate, force_permissive,
|
val = self._getitem(opt, path, validate, force_permissive,
|
||||||
force_properties, validate_properties)
|
force_properties, validate_properties)
|
||||||
|
@ -181,85 +203,82 @@ class Values(object):
|
||||||
ntime = int(time())
|
ntime = int(time())
|
||||||
ntime = ntime + expires_time
|
ntime = ntime + expires_time
|
||||||
self._p_.setcache(path, val, ntime)
|
self._p_.setcache(path, val, ntime)
|
||||||
|
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def _getitem(self, opt, path, validate, force_permissive, force_properties,
|
def _getitem(self, opt, path, validate, force_permissive, force_properties,
|
||||||
validate_properties):
|
validate_properties):
|
||||||
# options with callbacks
|
if opt.impl_is_master_slaves():
|
||||||
|
return opt.impl_get_master_slaves().getitem(self, opt, path,
|
||||||
|
validate,
|
||||||
|
force_permissive,
|
||||||
|
force_properties,
|
||||||
|
validate_properties)
|
||||||
|
else:
|
||||||
|
return self._get_validated_value(opt, path, validate,
|
||||||
|
force_permissive,
|
||||||
|
force_properties,
|
||||||
|
validate_properties)
|
||||||
|
|
||||||
|
def _get_validated_value(self, opt, path, validate, force_permissive,
|
||||||
|
force_properties, validate_properties,
|
||||||
|
index=undefined):
|
||||||
|
"""same has getitem but don't touch the cache"""
|
||||||
|
#FIXME expliquer la différence entre index == undefined et index == None
|
||||||
context = self._getcontext()
|
context = self._getcontext()
|
||||||
setting = context.cfgimpl_get_settings()
|
setting = context.cfgimpl_get_settings()
|
||||||
is_frozen = 'frozen' in setting[opt]
|
is_default = self._is_default_owner(opt, path,
|
||||||
# For calculating properties, we need value (ie for mandatory value).
|
validate_properties=False,
|
||||||
|
validate_meta=False)
|
||||||
|
try:
|
||||||
|
if index is None:
|
||||||
|
gv_index = undefined
|
||||||
|
else:
|
||||||
|
gv_index = index
|
||||||
|
value = self._getvalue(opt, path, is_default, index=gv_index)
|
||||||
|
config_error = None
|
||||||
|
except ConfigError as err:
|
||||||
|
# For calculating properties, we need value (ie for mandatory
|
||||||
|
# value).
|
||||||
# If value is calculating with a PropertiesOptionError's option
|
# If value is calculating with a PropertiesOptionError's option
|
||||||
# _getcallback_value raise a ConfigError.
|
# _getvalue raise a ConfigError.
|
||||||
# We can not raise ConfigError if this option should raise
|
# We can not raise ConfigError if this option should raise
|
||||||
# PropertiesOptionError too. So we get config_error and raise
|
# PropertiesOptionError too. So we get config_error and raise
|
||||||
# ConfigError if properties did not raise.
|
# ConfigError if properties did not raise.
|
||||||
config_error = None
|
|
||||||
force_permissives = None
|
|
||||||
# if value has callback and is not set
|
|
||||||
# or frozen with force_default_on_freeze
|
|
||||||
if opt.impl_has_callback() and (
|
|
||||||
self._is_default_owner(opt, path, validate_properties=False) or
|
|
||||||
(is_frozen and 'force_default_on_freeze' in setting[opt])):
|
|
||||||
lenmaster = None
|
|
||||||
no_value_slave = False
|
|
||||||
if (opt.impl_is_multi() and
|
|
||||||
opt.impl_get_multitype() == multitypes.slave):
|
|
||||||
masterp = self._get_opt_path(opt.impl_get_master_slaves())
|
|
||||||
mastervalue = context.getattr(masterp, validate=False)
|
|
||||||
lenmaster = len(mastervalue)
|
|
||||||
if lenmaster == 0:
|
|
||||||
value = []
|
|
||||||
no_value_slave = True
|
|
||||||
|
|
||||||
if not no_value_slave:
|
|
||||||
try:
|
|
||||||
value = self._getcallback_value(opt, max_len=lenmaster)
|
|
||||||
except ConfigError as err:
|
|
||||||
# cannot assign config_err directly in python 3.3
|
# cannot assign config_err directly in python 3.3
|
||||||
config_error = err
|
config_error = err
|
||||||
value = None
|
# value is not set, for 'undefined' (cannot set None because of
|
||||||
# should not raise PropertiesOptionError if option is
|
# mandatory property)
|
||||||
# mandatory
|
value = undefined
|
||||||
force_permissives = set(['mandatory'])
|
|
||||||
else:
|
|
||||||
if (opt.impl_is_multi() and
|
|
||||||
opt.impl_get_multitype() == multitypes.slave):
|
|
||||||
if not isinstance(value, list):
|
|
||||||
value = [value for i in range(lenmaster)]
|
|
||||||
if config_error is None:
|
if config_error is None:
|
||||||
if opt.impl_is_multi():
|
if index is undefined:
|
||||||
value = Multi(value, self.context, opt, path, validate)
|
force_index = None
|
||||||
# frozen and force default
|
|
||||||
elif is_frozen and 'force_default_on_freeze' in setting[opt]:
|
|
||||||
value = self._getdefault(opt)
|
|
||||||
if opt.impl_is_multi():
|
|
||||||
value = Multi(value, self.context, opt, path, validate)
|
|
||||||
else:
|
else:
|
||||||
value = self._getvalue(opt, path)
|
force_index = index
|
||||||
if opt.impl_is_multi():
|
if opt.impl_is_multi():
|
||||||
# load value so don't need to validate if is not a Multi
|
if index is None and not isinstance(value, list):
|
||||||
value = Multi(value, self.context, opt, path, validate=validate)
|
value = []
|
||||||
if config_error is None and validate:
|
if force_index is None:
|
||||||
opt.impl_validate(value, context, 'validator' in setting)
|
value = Multi(value, self.context, opt, path)
|
||||||
if config_error is None and \
|
if validate:
|
||||||
self._is_default_owner(opt, path, validate_properties=False) and \
|
opt.impl_validate(value, context, 'validator' in setting,
|
||||||
'force_store_value' in setting[opt]:
|
force_index=force_index)
|
||||||
|
#FIXME pas de test avec les metas ...
|
||||||
|
#FIXME et les symlinkoption ...
|
||||||
|
if is_default and 'force_store_value' in setting[opt]:
|
||||||
self.setitem(opt, value, path, is_write=False,
|
self.setitem(opt, value, path, is_write=False,
|
||||||
force_permissive=force_permissive)
|
force_permissive=force_permissive)
|
||||||
if validate_properties:
|
if validate_properties:
|
||||||
setting.validate_properties(opt, False, False, value=value, path=path,
|
setting.validate_properties(opt, False, False, value=value,
|
||||||
|
path=path,
|
||||||
force_permissive=force_permissive,
|
force_permissive=force_permissive,
|
||||||
force_properties=force_properties,
|
force_properties=force_properties)
|
||||||
force_permissives=force_permissives)
|
|
||||||
if config_error is not None:
|
if config_error is not None:
|
||||||
raise config_error
|
raise config_error
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def __setitem__(self, opt, value):
|
def __setitem__(self, opt, value):
|
||||||
raise ValueError('you should only set value with config')
|
raise ValueError(_('you should only set value with config'))
|
||||||
|
|
||||||
def setitem(self, opt, value, path, force_permissive=False,
|
def setitem(self, opt, value, path, force_permissive=False,
|
||||||
is_write=True):
|
is_write=True):
|
||||||
|
@ -270,26 +289,11 @@ 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, setitem=True)
|
value = Multi(value, self.context, opt, path)
|
||||||
# Save old value
|
if opt.impl_is_master_slaves():
|
||||||
if opt.impl_get_multitype() == multitypes.master and \
|
opt.impl_get_master_slaves().setitem(self, opt, value, path)
|
||||||
self._p_.hasvalue(path):
|
|
||||||
old_value = self._p_.getvalue(path)
|
|
||||||
old_owner = self._p_.getowner(path, None)
|
|
||||||
else:
|
|
||||||
old_value = undefined
|
|
||||||
old_owner = undefined
|
|
||||||
self._setvalue(opt, path, value, force_permissive=force_permissive,
|
self._setvalue(opt, path, value, force_permissive=force_permissive,
|
||||||
is_write=is_write)
|
is_write=is_write)
|
||||||
if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master:
|
|
||||||
try:
|
|
||||||
value._valid_master()
|
|
||||||
except Exception, err:
|
|
||||||
if old_value is not undefined:
|
|
||||||
self._p_.setvalue(path, old_value, old_owner)
|
|
||||||
else:
|
|
||||||
self._p_.resetvalue(path)
|
|
||||||
raise err
|
|
||||||
|
|
||||||
def _setvalue(self, opt, path, value, force_permissive=False,
|
def _setvalue(self, opt, path, value, force_permissive=False,
|
||||||
force_properties=None,
|
force_properties=None,
|
||||||
|
@ -321,11 +325,13 @@ class Values(object):
|
||||||
path = self._get_opt_path(opt)
|
path = self._get_opt_path(opt)
|
||||||
return self._getowner(opt, path, force_permissive=force_permissive)
|
return self._getowner(opt, path, force_permissive=force_permissive)
|
||||||
|
|
||||||
def _getowner(self, opt, path, validate_properties=True, force_permissive=False):
|
def _getowner(self, opt, path, validate_properties=True,
|
||||||
meta = self._getcontext().cfgimpl_get_meta()
|
force_permissive=False, validate_meta=True):
|
||||||
if validate_properties:
|
if validate_properties:
|
||||||
self._getitem(opt, path, True, force_permissive, None, True)
|
self._getitem(opt, path, True, force_permissive, None, True)
|
||||||
owner = self._p_.getowner(path, owners.default)
|
owner = self._p_.getowner(path, owners.default)
|
||||||
|
if validate_meta:
|
||||||
|
meta = self._getcontext().cfgimpl_get_meta()
|
||||||
if owner is owners.default and meta is not None:
|
if owner is owners.default and meta is not None:
|
||||||
owner = meta.cfgimpl_get_values()._getowner(opt, path)
|
owner = meta.cfgimpl_get_values()._getowner(opt, path)
|
||||||
return owner
|
return owner
|
||||||
|
@ -349,17 +355,22 @@ class Values(object):
|
||||||
'').format(path, owner))
|
'').format(path, owner))
|
||||||
self._p_.setowner(path, owner)
|
self._p_.setowner(path, owner)
|
||||||
|
|
||||||
def is_default_owner(self, opt, validate_properties=True):
|
def is_default_owner(self, opt, validate_properties=True,
|
||||||
|
validate_meta=True):
|
||||||
"""
|
"""
|
||||||
:param config: *must* be only the **parent** config
|
:param config: *must* be only the **parent** config
|
||||||
(not the toplevel config)
|
(not the toplevel config)
|
||||||
:return: boolean
|
:return: boolean
|
||||||
"""
|
"""
|
||||||
path = self._get_opt_path(opt)
|
path = self._get_opt_path(opt)
|
||||||
return self._is_default_owner(opt, path, validate_properties)
|
return self._is_default_owner(opt, path,
|
||||||
|
validate_properties=validate_properties,
|
||||||
|
validate_meta=validate_meta)
|
||||||
|
|
||||||
def _is_default_owner(self, opt, path, validate_properties=True):
|
def _is_default_owner(self, opt, path, validate_properties=True,
|
||||||
return self._getowner(opt, path, validate_properties) == owners.default
|
validate_meta=True):
|
||||||
|
return self._getowner(opt, path, validate_properties,
|
||||||
|
validate_meta=validate_meta) == owners.default
|
||||||
|
|
||||||
def reset_cache(self, only_expired):
|
def reset_cache(self, only_expired):
|
||||||
"""
|
"""
|
||||||
|
@ -377,7 +388,8 @@ class Values(object):
|
||||||
:param opt: the `option.Option` object
|
:param opt: the `option.Option` object
|
||||||
:returns: a string with points like "gc.dummy.my_option"
|
:returns: a string with points like "gc.dummy.my_option"
|
||||||
"""
|
"""
|
||||||
return self._getcontext().cfgimpl_get_description().impl_get_path_by_opt(opt)
|
return self._getcontext().cfgimpl_get_description(
|
||||||
|
).impl_get_path_by_opt(opt)
|
||||||
|
|
||||||
# information
|
# information
|
||||||
def set_information(self, key, value):
|
def set_information(self, key, value):
|
||||||
|
@ -457,13 +469,11 @@ class Multi(list):
|
||||||
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')
|
||||||
|
|
||||||
def __init__(self, value, context, opt, path, validate=True,
|
def __init__(self, value, context, opt, path):
|
||||||
setitem=False):
|
|
||||||
"""
|
"""
|
||||||
: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 setitem: only if set a value
|
|
||||||
"""
|
"""
|
||||||
if isinstance(value, Multi):
|
if isinstance(value, Multi):
|
||||||
raise ValueError(_('{0} is already a Multi ').format(opt._name))
|
raise ValueError(_('{0} is already a Multi ').format(opt._name))
|
||||||
|
@ -474,11 +484,6 @@ class Multi(list):
|
||||||
self.context = context
|
self.context = context
|
||||||
if not isinstance(value, list):
|
if not isinstance(value, list):
|
||||||
value = [value]
|
value = [value]
|
||||||
if validate and self.opt.impl_get_multitype() == multitypes.slave:
|
|
||||||
value = self._valid_slave(value, setitem)
|
|
||||||
elif not setitem and validate and \
|
|
||||||
self.opt.impl_get_multitype() == multitypes.master:
|
|
||||||
self._valid_master()
|
|
||||||
super(Multi, self).__init__(value)
|
super(Multi, self).__init__(value)
|
||||||
|
|
||||||
def _getcontext(self):
|
def _getcontext(self):
|
||||||
|
@ -492,124 +497,73 @@ class Multi(list):
|
||||||
raise ConfigError(_('the context does not exist anymore'))
|
raise ConfigError(_('the context does not exist anymore'))
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _valid_slave(self, value, setitem):
|
|
||||||
#if slave, had values until master's one
|
|
||||||
context = self._getcontext()
|
|
||||||
values = context.cfgimpl_get_values()
|
|
||||||
masterp = context.cfgimpl_get_description().impl_get_path_by_opt(
|
|
||||||
self.opt.impl_get_master_slaves())
|
|
||||||
mastervalue = context.getattr(masterp, validate=False)
|
|
||||||
masterlen = len(mastervalue)
|
|
||||||
valuelen = len(value)
|
|
||||||
if valuelen > masterlen or (valuelen < masterlen and setitem):
|
|
||||||
raise SlaveError(_("invalid len for the slave: {0}"
|
|
||||||
" which has {1} as master").format(
|
|
||||||
self.opt._name, masterp))
|
|
||||||
elif valuelen < masterlen:
|
|
||||||
for num in range(0, masterlen - valuelen):
|
|
||||||
if self.opt.impl_has_callback():
|
|
||||||
# if callback add a value, but this value will not change
|
|
||||||
# anymore automaticly (because this value has owner)
|
|
||||||
index = value.__len__()
|
|
||||||
value.append(values._getcallback_value(self.opt,
|
|
||||||
index=index))
|
|
||||||
else:
|
|
||||||
value.append(self.opt.impl_getdefault_multi())
|
|
||||||
#else: same len so do nothing
|
|
||||||
return value
|
|
||||||
|
|
||||||
def _valid_master(self):
|
|
||||||
#masterlen = len(value)
|
|
||||||
values = self._getcontext().cfgimpl_get_values()
|
|
||||||
for slave in self.opt._master_slaves:
|
|
||||||
path = values._get_opt_path(slave)
|
|
||||||
Multi(values._getvalue(slave, path), self.context, slave, path)
|
|
||||||
|
|
||||||
def __setitem__(self, index, value):
|
def __setitem__(self, index, value):
|
||||||
self._validate(value, index)
|
self._validate(value, index)
|
||||||
#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)
|
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
||||||
|
self)
|
||||||
|
|
||||||
def append(self, value=undefined, force=False):
|
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
|
||||||
"""
|
"""
|
||||||
context = self._getcontext()
|
values = self._getcontext().cfgimpl_get_values()
|
||||||
if not force:
|
if not force:
|
||||||
if self.opt.impl_get_multitype() == multitypes.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}"
|
||||||
" which is a slave").format(self.opt._name))
|
" which is a slave").format(self.opt._name))
|
||||||
elif self.opt.impl_get_multitype() == multitypes.master:
|
|
||||||
values = context.cfgimpl_get_values()
|
|
||||||
if value is undefined and self.opt.impl_has_callback():
|
|
||||||
value = values._getcallback_value(self.opt)
|
|
||||||
#Force None il return a list
|
|
||||||
if isinstance(value, list):
|
|
||||||
value = None
|
|
||||||
index = self.__len__()
|
index = self.__len__()
|
||||||
if value is undefined:
|
if value is undefined:
|
||||||
value = self.opt.impl_getdefault_multi()
|
try:
|
||||||
|
value = values._get_validated_value(self.opt, self.path,
|
||||||
|
True, False, None, True,
|
||||||
|
index=index)
|
||||||
|
except IndexError:
|
||||||
|
value = None
|
||||||
self._validate(value, index)
|
self._validate(value, index)
|
||||||
super(Multi, self).append(value)
|
super(Multi, self).append(value)
|
||||||
context.cfgimpl_get_values()._setvalue(self.opt, self.path,
|
if setitem:
|
||||||
self,
|
values._setvalue(self.opt, self.path, self,
|
||||||
validate_properties=not force)
|
validate_properties=not force)
|
||||||
if not force and self.opt.impl_get_multitype() == multitypes.master:
|
|
||||||
for slave in self.opt.impl_get_master_slaves():
|
|
||||||
path = values._get_opt_path(slave)
|
|
||||||
if not values._is_default_owner(slave, path,
|
|
||||||
validate_properties=False):
|
|
||||||
if slave.impl_has_callback():
|
|
||||||
dvalue = values._getcallback_value(slave, index=index)
|
|
||||||
else:
|
|
||||||
dvalue = slave.impl_getdefault_multi()
|
|
||||||
old_value = values.getitem(slave, path, validate=False,
|
|
||||||
validate_properties=False)
|
|
||||||
if len(old_value) + 1 != self.__len__():
|
|
||||||
raise SlaveError(_("invalid len for the slave: {0}"
|
|
||||||
" which has {1} as master").format(
|
|
||||||
self.opt._name, self.__len__()))
|
|
||||||
values.getitem(slave, path, validate=False,
|
|
||||||
validate_properties=False).append(
|
|
||||||
dvalue, force=True)
|
|
||||||
|
|
||||||
def sort(self, cmp=None, key=None, reverse=False):
|
def sort(self, cmp=None, key=None, reverse=False):
|
||||||
if self.opt.impl_get_multitype() in [multitypes.slave,
|
if self.opt.impl_is_master_slaves():
|
||||||
multitypes.master]:
|
|
||||||
raise SlaveError(_("cannot sort multi option {0} if master or slave"
|
raise SlaveError(_("cannot sort multi option {0} if master or slave"
|
||||||
"").format(self.opt._name))
|
"").format(self.opt._name))
|
||||||
if sys.version_info[0] >= 3:
|
if sys.version_info[0] >= 3:
|
||||||
if cmp is not None:
|
if cmp is not None:
|
||||||
raise ValueError(_('cmp is not permitted in python v3 or greater'))
|
raise ValueError(_('cmp is not permitted in python v3 or '
|
||||||
|
'greater'))
|
||||||
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)
|
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
||||||
|
self)
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
if self.opt.impl_get_multitype() in [multitypes.slave,
|
if self.opt.impl_is_master_slaves():
|
||||||
multitypes.master]:
|
|
||||||
raise SlaveError(_("cannot reverse multi option {0} if master or "
|
raise SlaveError(_("cannot reverse multi option {0} if master or "
|
||||||
"slave").format(self.opt._name))
|
"slave").format(self.opt._name))
|
||||||
super(Multi, self).reverse()
|
super(Multi, self).reverse()
|
||||||
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
|
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
||||||
|
self)
|
||||||
|
|
||||||
def insert(self, index, obj):
|
def insert(self, index, obj):
|
||||||
if self.opt.impl_get_multitype() in [multitypes.slave,
|
if self.opt.impl_is_master_slaves():
|
||||||
multitypes.master]:
|
|
||||||
raise SlaveError(_("cannot insert multi option {0} if master or "
|
raise SlaveError(_("cannot insert multi option {0} if master or "
|
||||||
"slave").format(self.opt._name))
|
"slave").format(self.opt._name))
|
||||||
super(Multi, self).insert(index, obj)
|
super(Multi, self).insert(index, obj)
|
||||||
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
|
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
||||||
|
self)
|
||||||
|
|
||||||
def extend(self, iterable):
|
def extend(self, iterable):
|
||||||
if self.opt.impl_get_multitype() in [multitypes.slave,
|
if self.opt.impl_is_master_slaves():
|
||||||
multitypes.master]:
|
|
||||||
raise SlaveError(_("cannot extend multi option {0} if master or "
|
raise SlaveError(_("cannot extend multi option {0} if master or "
|
||||||
"slave").format(self.opt._name))
|
"slave").format(self.opt._name))
|
||||||
super(Multi, self).extend(iterable)
|
super(Multi, self).extend(iterable)
|
||||||
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
|
self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
||||||
|
self)
|
||||||
|
|
||||||
def _validate(self, value, force_index):
|
def _validate(self, value, force_index):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
|
@ -634,18 +588,14 @@ class Multi(list):
|
||||||
"""
|
"""
|
||||||
context = self._getcontext()
|
context = self._getcontext()
|
||||||
if not force:
|
if not force:
|
||||||
if self.opt.impl_get_multitype() == multitypes.slave:
|
if self.opt.impl_is_master_slaves('slave'):
|
||||||
raise SlaveError(_("cannot pop a value on a multi option {0}"
|
raise SlaveError(_("cannot pop a value on a multi option {0}"
|
||||||
" which is a slave").format(self.opt._name))
|
" which is a slave").format(self.opt._name))
|
||||||
if self.opt.impl_get_multitype() == multitypes.master:
|
if self.opt.impl_is_master_slaves('master'):
|
||||||
for slave in self.opt.impl_get_master_slaves():
|
self.opt.impl_get_master_slaves().pop(
|
||||||
values = context.cfgimpl_get_values()
|
context.cfgimpl_get_values(), index)
|
||||||
if not values.is_default_owner(slave, validate_properties=False):
|
|
||||||
#get multi without valid properties
|
|
||||||
values.getitem(slave, validate=False,
|
|
||||||
validate_properties=False
|
|
||||||
).pop(index, force=True)
|
|
||||||
#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, validate_properties=not force)
|
context.cfgimpl_get_values()._setvalue(self.opt, self.path, self,
|
||||||
|
validate_properties=not force)
|
||||||
return ret
|
return ret
|
||||||
|
|
Loading…
Reference in New Issue