reorganize
This commit is contained in:
parent
30cd543a21
commit
6805cecfd5
50 changed files with 2517 additions and 1979 deletions
|
@ -1179,7 +1179,11 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
|
|||
cfg2_ = cfg.config(confread).unrestraint
|
||||
else:
|
||||
cfg2_ = cfg.unrestraint
|
||||
assert cfg_.option(pathread).permissive.get() == frozenset()
|
||||
|
||||
if not cfg_.option(pathread).option.isfollower():
|
||||
assert cfg_.option(pathread).permissive.get() == frozenset()
|
||||
else:
|
||||
assert cfg_.option(pathread, 0).permissive.get() == frozenset()
|
||||
if kwargs.get('permissive_od', False):
|
||||
assert cfg_.option(pathread.rsplit('.', 1)[0]).permissive.get() == frozenset()
|
||||
|
||||
|
@ -1200,7 +1204,10 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
|
|||
cfg_.option(call_path).permissive.set(frozenset(['disabled']))
|
||||
|
||||
# have permissive?
|
||||
assert cfg_.option(pathread).permissive.get() == frozenset(['disabled'])
|
||||
if not cfg_.option(pathread).option.isfollower():
|
||||
assert cfg_.option(pathread).permissive.get() == frozenset(['disabled'])
|
||||
else:
|
||||
assert cfg_.option(pathread, 0).permissive.get() == frozenset(['disabled'])
|
||||
#if confwrite != confread:
|
||||
# assert cfg.config(confread).unrestraint.option(pathread).permissive.get() == frozenset(['disabled'])
|
||||
|
||||
|
|
|
@ -152,6 +152,16 @@ def test_information_config():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_information_config_list():
|
||||
od1 = make_description()
|
||||
cfg = Config(od1)
|
||||
string = 'some informations'
|
||||
cfg.information.set('info', string)
|
||||
#
|
||||
assert cfg.information.exportation() == {None: {'info': string}}
|
||||
assert set(cfg.information.list()) == {'info', 'doc'}
|
||||
|
||||
|
||||
def test_information_exportation():
|
||||
od1 = make_description()
|
||||
cfg = Config(od1)
|
||||
|
@ -199,6 +209,18 @@ def test_information_option():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_information_option_2():
|
||||
i1 = IntOption('test1', '')
|
||||
i1.impl_set_information('info', 'value')
|
||||
# it's a dict
|
||||
assert set(i1.impl_list_information()) == {'info', 'doc'}
|
||||
od1 = OptionDescription('test', '', [i1])
|
||||
cfg = Config(od1)
|
||||
# it's tuples
|
||||
assert set(cfg.option('test1').information.list()) == {'info', 'doc'}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_information_optiondescription():
|
||||
od1 = make_description()
|
||||
cfg = Config(od1)
|
||||
|
@ -390,8 +412,7 @@ def test_config_od_name(config_type):
|
|||
cfg = get_config(cfg, config_type)
|
||||
assert cfg.option('val.i').option.name() == 'i'
|
||||
assert cfg.option('val.s').option.name() == 's'
|
||||
assert cfg.option('val.s').option.name(follow_symlink=True) == 'i'
|
||||
assert cfg.option('val.s').option.type() == 'integer'
|
||||
assert cfg.option('val.s').option.type() == _('integer')
|
||||
assert cfg.option('val').option.type() == 'optiondescription'
|
||||
# assert not list_sessions()
|
||||
|
||||
|
@ -403,7 +424,7 @@ def test_config_od_type(config_type):
|
|||
cfg = Config(o2)
|
||||
cfg = get_config(cfg, config_type)
|
||||
assert cfg.option('val').option.type() == 'optiondescription'
|
||||
assert cfg.option('val.i').option.type() == 'integer'
|
||||
assert cfg.option('val.i').option.type() == _('integer')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
|
@ -1649,7 +1649,6 @@ def test_invalid_subdynod_dyndescription():
|
|||
def test_invalid_symlink_dyndescription():
|
||||
st = StrOption('st', '')
|
||||
st2 = SymLinkOption('st2', st)
|
||||
st2
|
||||
with pytest.raises(ConfigError):
|
||||
DynOptionDescription('dod', '', [st, st2], suffixes=Calculation(return_list))
|
||||
# assert not list_sessions()
|
||||
|
@ -1658,7 +1657,6 @@ def test_invalid_symlink_dyndescription():
|
|||
def test_nocallback_dyndescription():
|
||||
st = StrOption('st', '')
|
||||
st2 = StrOption('st2', '')
|
||||
st, st2
|
||||
with pytest.raises(TypeError):
|
||||
DynOptionDescription('dod', '', [st, st2])
|
||||
# assert not list_sessions()
|
||||
|
@ -1843,10 +1841,9 @@ def test_dyn_leadership_requires():
|
|||
def test_dyn_leadership_mandatory():
|
||||
nsd_zones_all = StrOption(name="nsd_zones_all", doc="nsd_zones_all", multi=True, default=['val1', 'val2'])
|
||||
is_auto = BoolOption(name="is_auto_", doc="is auto")
|
||||
# hostname = DomainnameOption(name="hostname_", multi=True, type='hostname', properties=frozenset({Calculation(func.calc_value, Params(ParamValue('frozen'), kwargs={'condition': ParamOption(is_auto, todict=True, notraisepropertyerror=True), 'expected': ParamValue(True)})), Calculation(func.calc_value, Params(ParamValue('force_default_on_freeze'), kwargs={'condition': ParamOption(is_auto, todict=True, notraisepropertyerror=True), 'expected': ParamValue(True)}))}))
|
||||
hostname = DomainnameOption(name="hostname_", doc="hostname_", multi=True, type='hostname')
|
||||
choice = ChoiceOption(name="type_", doc="type_", values=('A', 'CNAME'), multi=True, default_multi="A")
|
||||
leadership = Leadership(name="hostname_", doc="hostname_", children=[hostname, choice], properties=frozenset({Calculation(calc_value, Params(ParamValue('hidden'), kwargs={'condition': ParamOption(is_auto, todict=True, notraisepropertyerror=True), 'expected': ParamValue(True)}))}))
|
||||
leadership = Leadership(name="hostname_", doc="hostname_", children=[hostname, choice], properties=frozenset({Calculation(calc_value, Params(ParamValue('hidden'), kwargs={'condition': ParamOption(is_auto, notraisepropertyerror=True), 'expected': ParamValue(True)}))}))
|
||||
dyn = DynOptionDescription(name="nsd_zone_", doc="Zone ", suffixes=Calculation(calc_value, Params((ParamOption(nsd_zones_all, notraisepropertyerror=True)))), children=[is_auto, leadership], properties=frozenset({"normal"}))
|
||||
od1 = OptionDescription(name="nsd", doc="nsd", children=[nsd_zones_all, dyn])
|
||||
cfg = Config(od1)
|
||||
|
|
|
@ -6,11 +6,11 @@ import pytest
|
|||
|
||||
from tiramisu.setting import groups, owners
|
||||
from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \
|
||||
StrOption, OptionDescription, Leadership, Config
|
||||
StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, calc_value, Params
|
||||
from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError
|
||||
|
||||
|
||||
groups.family = groups.GroupType('family')
|
||||
groups.addgroup('family')
|
||||
|
||||
|
||||
def compare(calculated, expected):
|
||||
|
@ -94,10 +94,12 @@ def test_iter_on_groups():
|
|||
#test StopIteration
|
||||
break
|
||||
result = cfg.option('creole').list('option',
|
||||
group_type=groups.family)
|
||||
group_type=groups.family,
|
||||
)
|
||||
assert list(result) == []
|
||||
result = cfg.option('creole.general').list('optiondescription',
|
||||
group_type=groups.family)
|
||||
group_type=groups.family,
|
||||
)
|
||||
assert list(result) == []
|
||||
# assert not list_sessions()
|
||||
|
||||
|
@ -218,6 +220,61 @@ def test_groups_is_leader(config_type):
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_leader_list(config_type):
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, default_multi='value')
|
||||
interface1 = Leadership('leadership', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
od1 = OptionDescription('od', '', [interface1])
|
||||
cfg = Config(od1)
|
||||
cfg = get_config(cfg, config_type)
|
||||
ret = cfg.option.list('all')
|
||||
assert len(ret) == 1
|
||||
assert ret[0].option.name() == 'leadership'
|
||||
#
|
||||
ret = cfg.option('leadership').list('all')
|
||||
assert len(ret) == 1
|
||||
assert ret[0].option.name() == 'ip_admin_eth0'
|
||||
#
|
||||
cfg.option('leadership.ip_admin_eth0').value.set(['a', 'b'])
|
||||
cfg.option('leadership.netmask_admin_eth0', 0).value.set('c')
|
||||
cfg.option('leadership.netmask_admin_eth0', 1).value.set('d')
|
||||
ret = cfg.option('leadership').list('all')
|
||||
assert ret[0].option.name() == 'ip_admin_eth0'
|
||||
assert ret[1].option.name() == 'netmask_admin_eth0'
|
||||
# assert ret[1].option.index() == 0
|
||||
# assert ret[2].option.name() == 'netmask_admin_eth0'
|
||||
# assert ret[2].option.index() == 1
|
||||
# assert len(ret) == 3
|
||||
# if config_type == 'tiramisu-api':
|
||||
# cfg.send()
|
||||
## assert not list_sessions()
|
||||
|
||||
|
||||
def test_groups_is_multi_with_index(config_type):
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", ['val'], multi=True)
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, default_multi='value')
|
||||
interface1 = Leadership('leadership', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
var = StrOption('var', "ip réseau autorisé", multi=True)
|
||||
od2 = OptionDescription('od2', '', [var])
|
||||
od1 = OptionDescription('od', '', [interface1, od2])
|
||||
cfg = Config(od1)
|
||||
cfg = get_config(cfg, config_type)
|
||||
assert cfg.option('leadership.netmask_admin_eth0', 0).option.ismulti()
|
||||
|
||||
|
||||
def test_groups_is_information_with_index(config_type):
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", ['val'], multi=True)
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, default_multi='value')
|
||||
interface1 = Leadership('leadership', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
var = StrOption('var', "ip réseau autorisé", multi=True)
|
||||
od2 = OptionDescription('od2', '', [var])
|
||||
od1 = OptionDescription('od', '', [interface1, od2])
|
||||
cfg = Config(od1)
|
||||
cfg = get_config(cfg, config_type)
|
||||
with pytest.raises(ConfigError):
|
||||
assert cfg.option('leadership.netmask_admin_eth0', 0).information.set('key', 'value')
|
||||
|
||||
|
||||
def test_groups_with_leader_in_root():
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
|
||||
|
@ -436,6 +493,9 @@ def test_groups_with_leader_index_mandatory(config_type):
|
|||
cfg.property.read_write()
|
||||
cfg = get_config(cfg, config_type)
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.1'])
|
||||
# index not allowed for leader
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0', 0).value.get()
|
||||
# index is mandatory
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0').value.reset()
|
||||
|
@ -907,7 +967,7 @@ def test_wrong_index():
|
|||
assert cfg.option('od.ip_admin_eth0.ip_admin_eth0').option.get()
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('od.ip_admin_eth0.ip_admin_eth0', 0).option.get()
|
||||
assert cfg.option('od.ip_admin_eth0.netmask_admin_eth0', 0).option.get()
|
||||
assert cfg.option('od.ip_admin_eth0.netmask_admin_eth0').option.get()
|
||||
assert cfg.option('od.ip_admin_eth0').option.get()
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('od.ip_admin_eth0', 0).option.get()
|
||||
|
@ -1011,17 +1071,14 @@ def test_follower_properties():
|
|||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['1.1.1.1', '192.168.0.0'])
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0').property.get() == ('aproperty',)
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).property.get() == ('aproperty',)
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == ('aproperty',)
|
||||
#
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).property.add('newproperty')
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0').property.get() == ('aproperty',)
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).property.get() == ('aproperty', 'newproperty')
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == ('aproperty',)
|
||||
#
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0').property.add('newproperty1')
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0').property.get() == ('aproperty', 'newproperty1')
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).property.get() == ('aproperty', 'newproperty', 'newproperty1')
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == ('aproperty', 'newproperty1')
|
||||
# assert not list_sessions()
|
||||
|
@ -1036,3 +1093,24 @@ def test_api_get_leader(config_type):
|
|||
option = cfg.option('ip_admin_eth0.netmask_admin_eth0').option.leader()
|
||||
assert option.option.get() == ip_admin_eth0
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_leader_forbidden_properties(config_type):
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
|
||||
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
od1 = OptionDescription('conf', '', [interface1])
|
||||
cfg = Config(od1)
|
||||
with pytest.raises(LeadershipError):
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').property.add('permissive')
|
||||
|
||||
|
||||
def test_leader_forbidden_properties_callback(config_type):
|
||||
calc_property = Calculation(calc_value, Params(ParamValue('permissive')))
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True, properties=(calc_property,))
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
|
||||
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
od1 = OptionDescription('conf', '', [interface1])
|
||||
cfg = Config(od1)
|
||||
with pytest.raises(LeadershipError):
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
|
||||
|
|
|
@ -835,10 +835,10 @@ def test_meta_properties_requires1():
|
|||
opt2 = BoolOption('opt2', "")
|
||||
disabled_property = Calculation(calc_value,
|
||||
Params(ParamValue('disabled'),
|
||||
kwargs={'condition': ParamOption(opt1, todict=True),
|
||||
kwargs={'condition': ParamOption(opt1),
|
||||
'expected': ParamValue(False)}))
|
||||
od2 = OptionDescription('od2', "", [opt2], properties=(disabled_property,))
|
||||
opt3 = BoolOption('opt3', '', validators=[Calculation(valid_not_equal, Params((ParamOption(opt2), ParamSelfOption())))])
|
||||
opt3 = BoolOption('opt3', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(opt2))))])
|
||||
od = OptionDescription('root', '', [opt1, od2, opt3])
|
||||
conf1 = Config(od, name='conf1')
|
||||
conf1.property.read_write()
|
||||
|
@ -859,7 +859,7 @@ def test_meta_properties_requires_mandatory():
|
|||
'expected': ParamValue('yes'),
|
||||
'default': ParamValue(None)}))
|
||||
ip_eth0 = IPOption('ip_eth0', "ip", Calculation(return_condition, Params(kwargs={'val': ParamOption(ip_address), 'condition': ParamOption(eth0_method), 'expected': ParamValue('dhcp')})), properties=(mandatory_property,))
|
||||
ip_gw = IPOption('ip_gw', 'gw', validators=[Calculation(valid_not_equal, Params((ParamOption(ip_eth0), ParamSelfOption())))])
|
||||
ip_gw = IPOption('ip_gw', 'gw', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(ip_eth0))))])
|
||||
od = OptionDescription('root', '', [ip_gw, probes, eth0_method, ip_address, ip_eth0])
|
||||
conf1 = Config(od, name='conf1')
|
||||
conf1.property.read_write()
|
||||
|
|
|
@ -13,13 +13,17 @@ from tiramisu.i18n import _
|
|||
try:
|
||||
groups.family
|
||||
except:
|
||||
groups.family = groups.GroupType('family')
|
||||
groups.addgroup('family')
|
||||
|
||||
|
||||
def a_func():
|
||||
return None
|
||||
|
||||
|
||||
def display_name(*args):
|
||||
return 'display_name'
|
||||
|
||||
|
||||
def test_option_valid_name():
|
||||
IntOption('test', '')
|
||||
with pytest.raises(ValueError):
|
||||
|
@ -76,6 +80,16 @@ def test_option_unknown():
|
|||
cfg.option('test').value.list()
|
||||
|
||||
|
||||
def test_option_description():
|
||||
description = "it's ok"
|
||||
i = IntOption('test', description)
|
||||
od = OptionDescription('od', 'od', [i])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
assert cfg.option('od').option.description() == 'od'
|
||||
assert cfg.option('od.test').option.description() == description
|
||||
|
||||
|
||||
def test_option_get_information_default():
|
||||
description = "it's ok"
|
||||
string = 'some informations'
|
||||
|
@ -178,7 +192,7 @@ def test_unknown_option():
|
|||
|
||||
|
||||
def test_optiondescription_list():
|
||||
groups.notfamily1 = groups.GroupType('notfamily1')
|
||||
groups.addgroup('notfamily1')
|
||||
i = IntOption('test', '')
|
||||
i2 = IntOption('test', '')
|
||||
od1 = OptionDescription('od', '', [i])
|
||||
|
@ -210,7 +224,7 @@ def test_optiondescription_list():
|
|||
|
||||
|
||||
def test_optiondescription_group():
|
||||
groups.notfamily = groups.GroupType('notfamily')
|
||||
groups.addgroup('notfamily')
|
||||
i = IntOption('test', '')
|
||||
i2 = IntOption('test', '')
|
||||
od1 = OptionDescription('od', '', [i])
|
||||
|
@ -240,7 +254,7 @@ def test_optiondescription_group():
|
|||
|
||||
def test_optiondescription_group_redefined():
|
||||
try:
|
||||
groups.notfamily = groups.GroupType('notfamily')
|
||||
groups.addgroup('notfamily')
|
||||
except:
|
||||
pass
|
||||
i = IntOption('test', '')
|
||||
|
@ -301,12 +315,6 @@ def test_intoption():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_get_display_type():
|
||||
i1 = IntOption('test1', 'description', min_number=3)
|
||||
assert i1.get_display_type() == _('integer')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_option_not_in_config():
|
||||
i1 = IntOption('test1', 'description', min_number=3)
|
||||
with pytest.raises(AttributeError):
|
||||
|
@ -322,3 +330,25 @@ def test_option_unknown_func():
|
|||
cfg = Config(od)
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('test1').value.unknown()
|
||||
|
||||
|
||||
def test_option_with_index():
|
||||
i1 = IntOption('test1', 'description', [4, 5], min_number=3, multi=True)
|
||||
i2 = IntOption('test2', 'description', max_number=3)
|
||||
i3 = IntOption('test3', 'description', min_number=3, max_number=6, warnings_only=True)
|
||||
od = OptionDescription('od', '', [i1, i2, i3])
|
||||
cfg = Config(od)
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('test1', 0).value.get()
|
||||
|
||||
|
||||
def test_option_display_name():
|
||||
i1 = IntOption('test1', 'description', min_number=3)
|
||||
i2 = IntOption('test2', 'description', max_number=3)
|
||||
i3 = IntOption('test3', 'description', min_number=3, max_number=6, warnings_only=True)
|
||||
od = OptionDescription('od', '', [i1, i2, i3])
|
||||
cfg = Config(od,
|
||||
display_name=display_name,
|
||||
)
|
||||
assert cfg.option('test1').option.name() == 'test1'
|
||||
assert cfg.option('test1').option.doc() == 'display_name'
|
||||
|
|
|
@ -145,6 +145,30 @@ def test_hidden_if_in2(config_type):
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_hidden_if_in3(config_type):
|
||||
intoption = IntOption('int', 'Test int option', default=0)
|
||||
hidden_property = Calculation(calc_value,
|
||||
Params(ParamValue('hidden'),
|
||||
kwargs={'condition': ParamOption(intoption),
|
||||
'expected': ParamValue(1),
|
||||
'default_0': ParamValue(None)}))
|
||||
stroption = StrOption('str', 'Test string option', default="abc", properties=(hidden_property,))
|
||||
od1 = OptionDescription('constraints', '', [stroption, intoption])
|
||||
cfg_ori = Config(od1)
|
||||
cfg_ori.property.read_write()
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
assert not 'hidden' in cfg.option('str').property.get()
|
||||
cfg.option('int').value.set(1)
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('str').value.get()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('str').value.set('uvw')
|
||||
if config_type == 'tiramisu-api':
|
||||
cfg.send()
|
||||
assert 'hidden' in cfg_ori.unrestraint.option('str').property.get()
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_hidden_if_in_with_group(config_type):
|
||||
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
|
||||
gcdummy = BoolOption('dummy', 'dummy', default=False)
|
||||
|
@ -268,7 +292,8 @@ def test_callback(config_type):
|
|||
assert cfg.option('val1').value.get() == 'val'
|
||||
cfg.option('val1').value.set('new-val')
|
||||
assert cfg.option('val1').value.get() == 'new-val'
|
||||
assert cfg.option('val1').option.defaultmulti() == None
|
||||
with pytest.raises(ConfigError):
|
||||
assert cfg.option('val1').option.defaultmulti() == None
|
||||
cfg.option('val1').value.reset()
|
||||
assert cfg.option('val1').value.get() == 'val'
|
||||
# assert not list_sessions()
|
||||
|
@ -1543,3 +1568,8 @@ def test_calc_dependencies(config_type):
|
|||
assert dep[0].option.get() == val3
|
||||
#
|
||||
assert cfg.option('val3').option.dependencies() == []
|
||||
|
||||
|
||||
def test_callback__kwargs_wrong(config_type):
|
||||
with pytest.raises(ValueError):
|
||||
Params(kwargs='string')
|
||||
|
|
|
@ -60,23 +60,27 @@ def test_mod_read_only_write():
|
|||
config2 = Config(od1)
|
||||
assert config.property.getdefault() == {'cache', 'validator', 'warnings'}
|
||||
assert config.property.getdefault('read_only', 'append') == {'frozen',
|
||||
'disabled',
|
||||
'validator',
|
||||
'everything_frozen',
|
||||
'mandatory',
|
||||
'empty',
|
||||
'force_store_value'}
|
||||
'disabled',
|
||||
'validator',
|
||||
'everything_frozen',
|
||||
'mandatory',
|
||||
'empty',
|
||||
'force_store_value',
|
||||
}
|
||||
assert config.property.getdefault('read_only', 'remove') == {'permissive',
|
||||
'hidden'}
|
||||
'hidden',
|
||||
}
|
||||
assert config.property.getdefault('read_write', 'append') == {'frozen',
|
||||
'disabled',
|
||||
'validator',
|
||||
'hidden',
|
||||
'force_store_value'}
|
||||
'disabled',
|
||||
'validator',
|
||||
'hidden',
|
||||
'force_store_value',
|
||||
}
|
||||
assert config.property.getdefault('read_write', 'remove') == {'permissive',
|
||||
'everything_frozen',
|
||||
'mandatory',
|
||||
'empty'}
|
||||
'everything_frozen',
|
||||
'mandatory',
|
||||
'empty',
|
||||
}
|
||||
#
|
||||
config.property.setdefault(frozenset(['cache']))
|
||||
config.property.setdefault(type='read_only', when='append', properties=frozenset(['disabled']))
|
||||
|
@ -595,6 +599,18 @@ def test_append_properties_force_store_value():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_properties_get_add_reset():
|
||||
gcdummy = BoolOption('dummy', 'dummy', default=False, properties=('force_store_value',))
|
||||
gcgroup = OptionDescription('gc', '', [gcdummy])
|
||||
od1 = OptionDescription('tiramisu', '', [gcgroup])
|
||||
cfg = Config(od1)
|
||||
assert cfg.property.get() == {'validator', 'warnings', 'cache'}
|
||||
cfg.property.add('frozen')
|
||||
assert cfg.property.get() == {'validator', 'warnings', 'cache', 'frozen'}
|
||||
cfg.property.reset()
|
||||
assert cfg.property.get() == {'validator', 'warnings', 'cache'}
|
||||
|
||||
|
||||
def test_reset_properties_force_store_value():
|
||||
gcdummy = BoolOption('dummy', 'dummy', default=False, properties=('force_store_value',))
|
||||
gcgroup = OptionDescription('gc', '', [gcdummy])
|
||||
|
@ -683,7 +699,7 @@ def test_pprint():
|
|||
intoption = IntOption('int', 'Test int option', default=0)
|
||||
hidden_property = Calculation(calc_value,
|
||||
Params(ParamValue('hidden'),
|
||||
kwargs={'condition': ParamOption(intoption, todict=True),
|
||||
kwargs={'condition': ParamOption(intoption),
|
||||
'expected_0': ParamValue(2),
|
||||
'expected_1': ParamValue(3),
|
||||
'expected_2': ParamValue(4),
|
||||
|
@ -691,35 +707,28 @@ def test_pprint():
|
|||
calc_value_property_help)
|
||||
disabled_property = Calculation(calc_value,
|
||||
Params(ParamValue('disabled'),
|
||||
kwargs={'condition_0': ParamOption(intoption, todict=True),
|
||||
kwargs={'condition_0': ParamOption(intoption),
|
||||
'expected_0': ParamValue(1),
|
||||
'condition_1': ParamOption(s2, todict=True),
|
||||
'condition_1': ParamOption(s2),
|
||||
'expected_1': ParamValue('string')}),
|
||||
calc_value_property_help)
|
||||
stroption = StrOption('str', 'Test string option', default="abc", properties=(hidden_property, disabled_property))
|
||||
# requires=[{'option': intoption, 'expected': 2, 'action': 'hidden', 'inverse': True},
|
||||
# {'option': intoption, 'expected': 3, 'action': 'hidden', 'inverse': True},
|
||||
# {'option': intoption, 'expected': 4, 'action': 'hidden', 'inverse': True},
|
||||
# {'option': intoption, 'expected': 1, 'action': 'disabled'},
|
||||
# {'option': s2, 'expected': 'string', 'action': 'disabled'}])
|
||||
|
||||
val2 = StrOption('val2', "")
|
||||
hidden_property = Calculation(calc_value,
|
||||
Params(ParamValue('hidden'),
|
||||
kwargs={'condition': ParamOption(intoption, todict=True),
|
||||
kwargs={'condition': ParamOption(intoption),
|
||||
'expected': ParamValue(1)}),
|
||||
calc_value_property_help)
|
||||
descr2 = OptionDescription("options", "options", [val2], properties=(hidden_property,))
|
||||
#descr2 = OptionDescription("options", "", [val2], requires=[{'option': intoption, 'expected': 1, 'action': 'hidden'}])
|
||||
|
||||
hidden_property = Calculation(calc_value,
|
||||
Params(ParamValue('hidden'),
|
||||
kwargs={'condition': ParamOption(stroption, todict=True),
|
||||
kwargs={'condition': ParamOption(stroption),
|
||||
'expected': ParamValue('2'),
|
||||
'reverse_condition': ParamValue(True)}),
|
||||
calc_value_property_help)
|
||||
val3 = StrOption('val3', "", properties=(hidden_property,))
|
||||
#val3 = StrOption('val3', "", requires=[{'option': stroption, 'expected': '2', 'action': 'hidden', 'inverse': True}])
|
||||
|
||||
od1 = OptionDescription("options", "root option", [s, s2, s3, intoption, stroption, descr2, val3])
|
||||
cfg = Config(od1)
|
||||
|
@ -775,3 +784,95 @@ def test_pprint():
|
|||
assert str(err) == msg_error.format('option', 'string3', prop, '"hidden"')
|
||||
del err
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_pprint_not_todict():
|
||||
msg_error = _("cannot access to {0} \"{1}\" because has {2} {3}")
|
||||
msg_is_not = _('the value of "{0}" is not {1}')
|
||||
msg_is = _('the value of "{0}" is {1}')
|
||||
properties = _('properties')
|
||||
prop = _('property')
|
||||
|
||||
s = StrOption("string", "", default=["string"], default_multi="string", multi=True, properties=('hidden', 'disabled'))
|
||||
s2 = StrOption("string2", "", default="string")
|
||||
s3 = StrOption("string3", "", default=["string"], default_multi="string", multi=True, properties=('hidden',))
|
||||
intoption = IntOption('int', 'Test int option', default=0)
|
||||
hidden_property = Calculation(calc_value,
|
||||
Params(ParamValue('hidden'),
|
||||
kwargs={'condition': ParamOption(intoption),
|
||||
'expected_0': ParamValue(2),
|
||||
'expected_1': ParamValue(3),
|
||||
'expected_2': ParamValue(4),
|
||||
'reverse_condition': ParamValue(True)}),
|
||||
)
|
||||
disabled_property = Calculation(calc_value,
|
||||
Params(ParamValue('disabled'),
|
||||
kwargs={'condition_0': ParamOption(intoption),
|
||||
'expected_0': ParamValue(1),
|
||||
'condition_1': ParamOption(s2),
|
||||
'expected_1': ParamValue('string')}),
|
||||
)
|
||||
stroption = StrOption('str', 'Test string option', default="abc", properties=(hidden_property, disabled_property))
|
||||
|
||||
val2 = StrOption('val2', "")
|
||||
hidden_property = Calculation(calc_value,
|
||||
Params(ParamValue('hidden'),
|
||||
kwargs={'condition': ParamOption(intoption),
|
||||
'expected': ParamValue(1)}),
|
||||
)
|
||||
descr2 = OptionDescription("options", "options", [val2], properties=(hidden_property,))
|
||||
|
||||
hidden_property = Calculation(calc_value,
|
||||
Params(ParamValue('hidden'),
|
||||
kwargs={'condition': ParamOption(stroption),
|
||||
'expected': ParamValue('2'),
|
||||
'reverse_condition': ParamValue(True)}),
|
||||
)
|
||||
val3 = StrOption('val3', "", properties=(hidden_property,))
|
||||
|
||||
od1 = OptionDescription("options", "root option", [s, s2, s3, intoption, stroption, descr2, val3])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
cfg.option('int').value.set(1)
|
||||
err = None
|
||||
try:
|
||||
cfg.option('str').value.get()
|
||||
except PropertiesOptionError as error:
|
||||
err = error
|
||||
|
||||
list_disabled = '"disabled"'
|
||||
list_hidden = '"hidden"'
|
||||
assert str(err) == _(msg_error.format('option', 'Test string option', properties, display_list([list_disabled, list_hidden], add_quote=False)))
|
||||
del err
|
||||
|
||||
err = None
|
||||
try:
|
||||
cfg.option('options.val2').value.get()
|
||||
except PropertiesOptionError as error:
|
||||
err = error
|
||||
|
||||
assert str(err) == msg_error.format('optiondescription', 'options', prop, '"hidden"')
|
||||
|
||||
err = None
|
||||
try:
|
||||
cfg.option('string').value.get()
|
||||
except Exception as error:
|
||||
err = error
|
||||
|
||||
assert str(err) == msg_error.format('option', 'string', properties, display_list(['disabled', 'hidden'], add_quote=True))
|
||||
del err
|
||||
|
||||
err = None
|
||||
try:
|
||||
cfg.option('string3').value.get()
|
||||
except Exception as error:
|
||||
err = error
|
||||
|
||||
assert str(err) == msg_error.format('option', 'string3', prop, '"hidden"')
|
||||
del err
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_property_invalid_type():
|
||||
with pytest.raises(ValueError):
|
||||
s3 = StrOption("string3", "", default=["string"], default_multi="string", multi=True, properties=(1,))
|
||||
|
|
|
@ -6,7 +6,8 @@ import pytest
|
|||
|
||||
from tiramisu import BoolOption, StrOption, IPOption, NetmaskOption, NetworkOption, BroadcastOption, \
|
||||
IntOption, OptionDescription, Leadership, Config, Params, ParamValue, ParamOption, \
|
||||
ParamSelfOption, ParamIndex, Calculation, valid_ip_netmask, valid_network_netmask, \
|
||||
ParamSelfOption, ParamIndex, ParamInformation, ParamSelfInformation, ParamSelfOption, Calculation, \
|
||||
valid_ip_netmask, valid_network_netmask, \
|
||||
valid_in_network, valid_broadcast, valid_not_equal, undefined
|
||||
from tiramisu.setting import groups
|
||||
from tiramisu.error import ValueErrorWarning, ConfigError, PropertiesOptionError
|
||||
|
@ -105,11 +106,13 @@ def test_validator(config_type):
|
|||
assert len(w) == 1
|
||||
assert str(w[0].message) == msg
|
||||
assert cfg.option('opt2').value.valid() is False
|
||||
#
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.option('opt2').value.get()
|
||||
assert len(w) == 1
|
||||
assert str(w[0].message) == msg
|
||||
assert cfg.option('opt2').value.valid() is False
|
||||
#
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.option('opt2').value.get()
|
||||
assert len(w) == 1
|
||||
|
@ -118,6 +121,13 @@ def test_validator(config_type):
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_validator_not_valid(config_type):
|
||||
with pytest.raises(ValueError):
|
||||
StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val')
|
||||
with pytest.raises(ValueError):
|
||||
StrOption('not_calculation', '', validators=[str])
|
||||
|
||||
|
||||
def test_validator_params(config_type):
|
||||
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params((ParamSelfOption(), ParamValue('yes'))))], default='val')
|
||||
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params((ParamSelfOption(), ParamValue('yes'))))])
|
||||
|
@ -324,7 +334,14 @@ def test_validator_warning(config_type):
|
|||
assert len(w) == 1
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == opt2
|
||||
assert str(w[0].message) == msg_err.format('val', opt2._display_name, 'opt2') + ', ' + 'test error return_false'
|
||||
assert str(w[0].message) == msg_err.format('val', opt2.get_type(), 'opt2') + ', ' + 'test error return_false'
|
||||
#
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.nowarnings.option('opt2').value.set('val')
|
||||
assert len(w) == 0
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.option('opt2').value.set('val')
|
||||
assert len(w) == 1
|
||||
#
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.option('opt3').value.set(['val'])
|
||||
|
@ -335,7 +352,7 @@ def test_validator_warning(config_type):
|
|||
assert len(w) == 1
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == opt3
|
||||
assert str(w[0].message) == msg_err.format('val1', opt3._display_name, 'opt3') + ', ' + 'test error'
|
||||
assert str(w[0].message) == msg_err.format('val1', opt3.get_type(), 'opt3') + ', ' + 'test error'
|
||||
#
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with pytest.raises(ValueError):
|
||||
|
@ -348,9 +365,9 @@ def test_validator_warning(config_type):
|
|||
assert len(w) == 2
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == opt2
|
||||
assert str(w[0].message) == msg_err.format('val', opt2._display_name, 'opt2') + ', ' + 'test error return_false'
|
||||
assert str(w[0].message) == msg_err.format('val', opt2.get_type(), 'opt2') + ', ' + 'test error return_false'
|
||||
assert w[1].message.opt() == opt3
|
||||
assert str(w[1].message) == msg_err.format('val1', opt3._display_name, 'opt3') + ', ' + 'test error'
|
||||
assert str(w[1].message) == msg_err.format('val1', opt3.get_type(), 'opt3') + ', ' + 'test error'
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
@ -419,13 +436,13 @@ def test_validator_warning_leadership(config_type):
|
|||
assert len(w) == 1
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == netmask_admin_eth0
|
||||
assert str(w[0].message) == msg_err.format('val1', netmask_admin_eth0._display_name, display_name_netmask) + ', test error'
|
||||
assert str(w[0].message) == msg_err.format('val1', netmask_admin_eth0.get_type(), display_name_netmask) + ', test error'
|
||||
#
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val'])
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == ip_admin_eth0
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip) + ', test error return_false'
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
|
||||
else:
|
||||
assert len(w) == 2
|
||||
#
|
||||
|
@ -433,7 +450,7 @@ def test_validator_warning_leadership(config_type):
|
|||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val1', 'val1'])
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == ip_admin_eth0
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip) + ', test error return_false'
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
|
||||
else:
|
||||
assert len(w) == 3
|
||||
#
|
||||
|
@ -441,7 +458,7 @@ def test_validator_warning_leadership(config_type):
|
|||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val', 'val1'])
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == ip_admin_eth0
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip) + ', test error return_false'
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
|
||||
else:
|
||||
assert len(w) == 3
|
||||
#
|
||||
|
@ -450,7 +467,7 @@ def test_validator_warning_leadership(config_type):
|
|||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val1', 'val'])
|
||||
if config_type != 'tiramisu-api':
|
||||
assert w[0].message.opt() == ip_admin_eth0
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip) + ', test error return_false'
|
||||
assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
|
||||
else:
|
||||
assert len(w) == 3
|
||||
# assert not list_sessions()
|
||||
|
@ -494,11 +511,12 @@ def test_validator_dependencies():
|
|||
|
||||
def test_validator_ip_netmask(config_type):
|
||||
a = IPOption('a', '')
|
||||
b = NetmaskOption('b', '', validators=[Calculation(valid_ip_netmask, Params((ParamOption(a, todict=True), ParamSelfOption())))])
|
||||
b = NetmaskOption('b', '', validators=[Calculation(valid_ip_netmask, Params((ParamOption(a), ParamSelfOption())))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg_ori = Config(od1)
|
||||
cfg = cfg_ori
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
cfg.option('b').value.set('255.255.255.0')
|
||||
cfg.option('a').value.set('192.168.1.1')
|
||||
cfg.option('b').value.set('255.255.255.0')
|
||||
cfg.option('a').value.set('192.168.1.2')
|
||||
|
@ -528,7 +546,7 @@ def test_validator_ip_netmask(config_type):
|
|||
|
||||
def test_validator_network_netmask(config_type):
|
||||
a = NetworkOption('a', '')
|
||||
b = NetmaskOption('b', '', validators=[Calculation(valid_network_netmask, Params((ParamOption(a, todict=True), ParamSelfOption())))])
|
||||
b = NetmaskOption('b', '', validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg_ori = Config(od1)
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
|
@ -557,8 +575,8 @@ def test_validator_network_netmask(config_type):
|
|||
def test_validator_ip_in_network(config_type):
|
||||
a = NetworkOption('a', '')
|
||||
b = NetmaskOption('b', '')
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))), warnings_only=True)])
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a), ParamOption(b))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a), ParamOption(b))), warnings_only=True)])
|
||||
od1 = OptionDescription('od', '', [a, b, c, d])
|
||||
warnings.simplefilter("always", ValueErrorWarning)
|
||||
cfg = Config(od1)
|
||||
|
@ -581,8 +599,8 @@ def test_validator_ip_in_network(config_type):
|
|||
def test_validator_ip_in_network_incomplete(config_type):
|
||||
a = NetworkOption('a', '')
|
||||
b = NetmaskOption('b', '')
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))), warnings_only=True)])
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a), ParamOption(b))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a), ParamOption(b))), warnings_only=True)])
|
||||
od1 = OptionDescription('od', '', [a, b, c, d])
|
||||
warnings.simplefilter("always", ValueErrorWarning)
|
||||
cfg = Config(od1)
|
||||
|
@ -603,8 +621,8 @@ def test_validator_ip_in_network_incomplete(config_type):
|
|||
|
||||
def test_validator_ip_in_network_cidr(config_type):
|
||||
a = NetworkOption('a', '', cidr=True)
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True))), warnings_only=True)])
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a))), warnings_only=True)])
|
||||
od1 = OptionDescription('od', '', [a, c, d])
|
||||
warnings.simplefilter("always", ValueErrorWarning)
|
||||
cfg = Config(od1)
|
||||
|
@ -625,8 +643,8 @@ def test_validator_ip_in_network_cidr(config_type):
|
|||
|
||||
def test_validator_ip_in_network_cidr_incomplete(config_type):
|
||||
a = NetworkOption('a', '', cidr=True)
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True))), warnings_only=True)])
|
||||
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a))))])
|
||||
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a))), warnings_only=True)])
|
||||
od1 = OptionDescription('od', '', [a, c, d])
|
||||
warnings.simplefilter("always", ValueErrorWarning)
|
||||
cfg = Config(od1)
|
||||
|
@ -643,7 +661,7 @@ def test_validator_ip_in_network_cidr_incomplete(config_type):
|
|||
|
||||
def test_validator_ip_netmask_multi(config_type):
|
||||
a = IPOption('a', '', multi=True)
|
||||
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_ip_netmask, Params((ParamOption(a, todict=True), ParamSelfOption())))])
|
||||
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_ip_netmask, Params((ParamOption(a), ParamSelfOption())))])
|
||||
od = Leadership('a', '', [a, b])
|
||||
od2 = OptionDescription('od2', '', [od])
|
||||
cfg_ori = Config(od2)
|
||||
|
@ -886,6 +904,39 @@ def test_validator_broadcast(config_type):
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_validator_broadcast_todict(config_type):
|
||||
a = NetworkOption('a', '', multi=True)
|
||||
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
|
||||
c = BroadcastOption('c', '', multi=True, validators=[Calculation(valid_broadcast, Params((ParamOption(a), ParamOption(b), ParamSelfOption())))])
|
||||
od = Leadership('a', '', [a, b, c])
|
||||
od2 = OptionDescription('od2', '', [od])
|
||||
cfg = Config(od2)
|
||||
cfg = get_config(cfg, config_type)
|
||||
#first, test network_netmask
|
||||
cfg.option('a.a').value.set(['192.168.1.128'])
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a.a').value.set(['255.255.255.0'])
|
||||
#
|
||||
cfg.option('a.a').value.set(['192.168.1.0'])
|
||||
cfg.option('a.b', 0).value.set('255.255.255.0')
|
||||
cfg.option('a.c', 0).value.set('192.168.1.255')
|
||||
cfg.option('a.a').value.set(['192.168.1.1'])
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a.b', 0).value.get()
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a.c', 0).value.get()
|
||||
#
|
||||
cfg.option('a.a').value.set(['192.168.1.0', '192.168.2.128'])
|
||||
cfg.option('a.b', 0).value.set('255.255.255.0')
|
||||
cfg.option('a.b', 1).value.set('255.255.255.128')
|
||||
cfg.option('a.c', 0).value.set('192.168.1.255')
|
||||
cfg.option('a.c', 1).value.set('192.168.2.255')
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a.c', 1).value.set('192.168.2.128')
|
||||
cfg.option('a.c', 1).value.set('192.168.2.255')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_validator_broadcast_warnings(config_type):
|
||||
warnings.simplefilter("always", ValueErrorWarning)
|
||||
a = NetworkOption('a', '', properties=('mandatory', 'disabled'))
|
||||
|
@ -969,7 +1020,7 @@ def test_validator_has_dependency():
|
|||
def test_validator_warnings_only_more_option(config_type):
|
||||
a = IntOption('a', '')
|
||||
b = IntOption('b', '')
|
||||
d = IntOption('d', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))), warnings_only=True)])
|
||||
d = IntOption('d', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a), ParamOption(b))), warnings_only=True)])
|
||||
od1 = OptionDescription('od', '', [a, b, d])
|
||||
cfg = Config(od1)
|
||||
cfg = get_config(cfg, config_type)
|
||||
|
@ -988,7 +1039,7 @@ def test_validator_warnings_only_more_option(config_type):
|
|||
|
||||
def test_validator_error_prefix():
|
||||
a = IntOption('a', '')
|
||||
b = IntOption('b', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
|
||||
b = IntOption('b', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg = Config(od1)
|
||||
cfg.option('a').value.set(1)
|
||||
|
@ -1006,7 +1057,7 @@ def test_validator_error_prefix():
|
|||
|
||||
def test_validator_warnings_only_option(config_type):
|
||||
a = IntOption('a', '')
|
||||
b = IntOption('b', '', warnings_only=True, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
|
||||
b = IntOption('b', '', warnings_only=True, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg_ori = Config(od1)
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
|
@ -1086,7 +1137,7 @@ def test_validator_not_equal_leadership_default():
|
|||
|
||||
def test_validator_default_diff():
|
||||
a = IntOption('a', '', 3)
|
||||
b = IntOption('b', '', 1, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
|
||||
b = IntOption('b', '', 1, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg = Config(od1)
|
||||
# FIXME cfg = get_config(cfg, config_type)
|
||||
|
@ -1107,7 +1158,7 @@ def test_validator_default_diff():
|
|||
|
||||
def test_validator_permissive(config_type):
|
||||
a = IntOption('a', '', 1, properties=('hidden',))
|
||||
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
|
||||
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
|
@ -1121,7 +1172,7 @@ def test_validator_permissive(config_type):
|
|||
|
||||
def test_validator_disabled(config_type):
|
||||
a = IntOption('a', '', 1, properties=('disabled',))
|
||||
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True, raisepropertyerror=True))))])
|
||||
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, raisepropertyerror=True))))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
|
@ -1133,7 +1184,7 @@ def test_validator_disabled(config_type):
|
|||
|
||||
def test_consistency_disabled_transitive(config_type):
|
||||
a = IntOption('a', '', 1, properties=('disabled',))
|
||||
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True, notraisepropertyerror=True))))])
|
||||
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, notraisepropertyerror=True))))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
|
@ -1145,7 +1196,7 @@ def test_consistency_disabled_transitive(config_type):
|
|||
def test_consistency_double_warnings(config_type):
|
||||
a = IntOption('a', '', 1)
|
||||
b = IntOption('b', '', 1)
|
||||
c = IntOption('c', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))), warnings_only=True), Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(b, todict=True))), warnings_only=True)])
|
||||
c = IntOption('c', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))), warnings_only=True), Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(b))), warnings_only=True)])
|
||||
od = OptionDescription('od', '', [a, b, c])
|
||||
warnings.simplefilter("always", ValueErrorWarning)
|
||||
od1 = OptionDescription('od2', '', [od])
|
||||
|
@ -1179,8 +1230,8 @@ def test_consistency_warnings_error(config_type):
|
|||
a = IntOption('a', '', 1)
|
||||
b = IntOption('b', '', 1)
|
||||
c = IntOption('c', '', validators=[
|
||||
Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))), warnings_only=True),
|
||||
Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(b, todict=True))))
|
||||
Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))), warnings_only=True),
|
||||
Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(b))))
|
||||
])
|
||||
od1 = OptionDescription('od', '', [a, b, c])
|
||||
warnings.simplefilter("always", ValueErrorWarning)
|
||||
|
@ -1196,7 +1247,7 @@ def test_consistency_warnings_error(config_type):
|
|||
def test_consistency_not_equal_has_dependency():
|
||||
a = IntOption('a', '')
|
||||
b = IntOption('b', '', )
|
||||
b = IntOption('b', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
|
||||
b = IntOption('b', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
|
||||
od1 = OptionDescription('od', '', [a, b])
|
||||
cfg = Config(od1)
|
||||
assert cfg.option('a').option.has_dependency() is False
|
||||
|
@ -1204,3 +1255,25 @@ def test_consistency_not_equal_has_dependency():
|
|||
assert cfg.option('a').option.has_dependency(False) is True
|
||||
assert cfg.option('b').option.has_dependency(False) is False
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_validator_information(config_type):
|
||||
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params((ParamSelfInformation('key'), ParamValue('yes'))))], default='val')
|
||||
opt2 = StrOption('opt2', '', validators=[Calculation(return_true, Params((ParamInformation('key'), ParamValue('yes'))))], default='val')
|
||||
od1 = OptionDescription('root', '', [opt1, opt2])
|
||||
cfg = Config(od1)
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('opt1').value.get()
|
||||
cfg.option('opt1').information.set('key', 'val')
|
||||
assert cfg.option('opt1').value.get() == 'val'
|
||||
cfg.option('opt1').information.set('key', 'val1')
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('opt1').value.get()
|
||||
#
|
||||
with pytest.raises(ConfigError):
|
||||
assert cfg.option('opt2').value.get()
|
||||
cfg.information.set('key', 'val')
|
||||
assert cfg.option('opt2').value.get() == 'val'
|
||||
cfg.information.set('key', 'val1')
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('opt2').value.get()
|
||||
|
|
|
@ -56,7 +56,7 @@ def test_requires(config_type):
|
|||
a = BoolOption('activate_service', '', True)
|
||||
disabled_property = Calculation(calc_value,
|
||||
Params(ParamValue('disabled'),
|
||||
kwargs={'condition': ParamOption(a, todict=True),
|
||||
kwargs={'condition': ParamOption(a),
|
||||
'expected': ParamValue(False)}))
|
||||
b = IPOption('ip_address_service', '',
|
||||
properties=(disabled_property,))
|
||||
|
@ -81,7 +81,7 @@ def test_requires_inverse(config_type):
|
|||
a = BoolOption('activate_service', '', True)
|
||||
disabled_property = Calculation(calc_value,
|
||||
Params(ParamValue('disabled'),
|
||||
kwargs={'condition': ParamOption(a, todict=True),
|
||||
kwargs={'condition': ParamOption(a),
|
||||
'expected': ParamValue(False),
|
||||
'reverse_condition': ParamValue(True)}))
|
||||
b = IPOption('ip_address_service', '', properties=(disabled_property,))
|
||||
|
@ -158,13 +158,13 @@ def test_requires_same_action(config_type):
|
|||
activate_service = BoolOption('activate_service', '', True)
|
||||
new_property = Calculation(calc_value,
|
||||
Params(ParamValue('new'),
|
||||
kwargs={'condition': ParamOption(activate_service, todict=True),
|
||||
kwargs={'condition': ParamOption(activate_service),
|
||||
'expected': ParamValue(False)}),
|
||||
calc_value_property_help)
|
||||
activate_service_web = BoolOption('activate_service_web', '', True, properties=(new_property,))
|
||||
disabled_property = Calculation(calc_value,
|
||||
Params(ParamValue('disabled'),
|
||||
kwargs={'condition': ParamOption(activate_service_web, notraisepropertyerror=True, todict=True),
|
||||
kwargs={'condition': ParamOption(activate_service_web, notraisepropertyerror=True),
|
||||
'expected': ParamValue(False)}),
|
||||
calc_value_property_help)
|
||||
ip_address_service_web = IPOption('ip_address_service_web', '', properties=(disabled_property,))
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
from .autopath import do_autopath
|
||||
do_autopath()
|
||||
from tiramisu import BoolOption, StrOption, SymLinkOption, OptionDescription, DynOptionDescription, \
|
||||
Calculation, Params, ParamOption, ParamValue, calc_value, Config
|
||||
from pickle import dumps
|
||||
import pytest
|
||||
import sys, warnings
|
||||
|
||||
|
||||
def test_diff_opt():
|
||||
b = BoolOption('b', '')
|
||||
disabled_property = Calculation(calc_value,
|
||||
Params(ParamValue('disabled'),
|
||||
kwargs={'condition': ParamOption(b),
|
||||
'expected': ParamValue(True),
|
||||
'reverse_condition': ParamValue(True)}))
|
||||
u = StrOption('u', '', properties=(disabled_property,))
|
||||
s = SymLinkOption('s', u)
|
||||
o = OptionDescription('o', '', [b, u, s])
|
||||
o1 = OptionDescription('o1', '', [o])
|
||||
|
||||
with pytest.raises(NotImplementedError):
|
||||
dumps(o1)
|
||||
|
||||
|
||||
def test_diff_information_config():
|
||||
b = BoolOption('b', '')
|
||||
b.impl_set_information('info', 'oh')
|
||||
b.impl_set_information('info1', 'oh')
|
||||
b.impl_set_information('info2', 'oh')
|
||||
o = OptionDescription('o', '', [b])
|
||||
od1 = OptionDescription('o1', '', [o])
|
||||
cfg = Config(od1)
|
||||
c = cfg._config_bag.context
|
||||
with pytest.raises(NotImplementedError):
|
||||
dumps(c)
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_only_optiondescription():
|
||||
b = BoolOption('b', '')
|
||||
b
|
||||
with pytest.raises(NotImplementedError):
|
||||
dumps(b)
|
|
@ -8,7 +8,7 @@ import warnings
|
|||
from tiramisu.setting import groups, owners
|
||||
from tiramisu import StrOption, IntOption, OptionDescription, submulti, Leadership, Config, \
|
||||
MetaConfig, undefined, Params, ParamOption, Calculation
|
||||
from tiramisu.error import LeadershipError
|
||||
from tiramisu.error import LeadershipError, PropertiesOptionError
|
||||
|
||||
|
||||
def return_val(val=None):
|
||||
|
@ -48,6 +48,51 @@ def test_submulti():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_submulti_mandatory():
|
||||
multi = StrOption('multi', '', multi=submulti, properties=('mandatory',))
|
||||
od1 = OptionDescription('od', '', [multi])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('multi').value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('multi').value.set([['val']])
|
||||
cfg.property.read_only()
|
||||
assert cfg.option('multi').value.get() == [['val']]
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('multi').value.set([['val'], ['']])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('multi').value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('multi').value.set([['val'], [None]])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('multi').value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('multi').value.set([['val'], []])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('multi').value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('multi').value.set([['val'], ['val1', '']])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('multi').value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('multi').value.set([['val'], ['val1', '', 'val2']])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('multi').value.get()
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_submulti_default_multi_not_list():
|
||||
with pytest.raises(ValueError):
|
||||
StrOption('multi2', '', default_multi='yes', multi=submulti)
|
||||
|
@ -235,6 +280,50 @@ def test_values_with_leader_and_followers_submulti():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_values_with_leader_and_followers_submulti_mandatory():
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti, properties=('mandatory',))
|
||||
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
od1 = OptionDescription('toto', '', [interface1])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_only()
|
||||
owner = cfg.owner.get()
|
||||
assert interface1.impl_get_group_type() == groups.leadership
|
||||
assert cfg.option('ip_admin_eth0.ip_admin_eth0').owner.get() == owners.default
|
||||
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(["192.168.230.145"])
|
||||
cfg.property.read_only()
|
||||
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == ["192.168.230.145"]
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(["255.255.255.0"])
|
||||
cfg.property.read_only()
|
||||
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == ["255.255.255.0"]
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(["255.255.255.0", None])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(["255.255.255.0", ''])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
|
||||
#
|
||||
cfg.property.read_write()
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(["255.255.255.0", '', "255.255.255.0"])
|
||||
cfg.property.read_only()
|
||||
with pytest.raises(PropertiesOptionError):
|
||||
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_values_with_leader_and_followers_submulti_default_multi():
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=submulti, default_multi=['255.255.0.0', '0.0.0.0'])
|
||||
|
|
|
@ -4,10 +4,11 @@ from .autopath import do_autopath
|
|||
do_autopath()
|
||||
from .config import config_type, get_config
|
||||
|
||||
from tiramisu import BoolOption, StrOption, SymLinkOption, \
|
||||
from tiramisu import BoolOption, StrOption, SymLinkOption, submulti, \
|
||||
OptionDescription, Leadership, Config, Calculation, calc_value, Params, ParamOption, ParamValue
|
||||
from tiramisu.error import PropertiesOptionError, ConfigError
|
||||
from tiramisu.setting import groups, owners
|
||||
from tiramisu.i18n import _
|
||||
|
||||
|
||||
def return_value():
|
||||
|
@ -19,9 +20,14 @@ def test_symlink_option(config_type):
|
|||
boolopt = BoolOption("b", "", default=False)
|
||||
linkopt = SymLinkOption("c", boolopt)
|
||||
od1 = OptionDescription("opt", "",
|
||||
[linkopt, OptionDescription("s1", "", [boolopt])])
|
||||
[linkopt, OptionDescription("s1", "", [boolopt])],
|
||||
)
|
||||
cfg = Config(od1)
|
||||
cfg = get_config(cfg, config_type)
|
||||
assert not cfg.option('s1.b').option.issymlinkoption()
|
||||
assert cfg.option('c').option.issymlinkoption()
|
||||
assert cfg.option('s1.b').option.type() == _('boolean')
|
||||
assert cfg.option('c').option.type() == _('boolean')
|
||||
assert cfg.option('s1.b').value.get() is False
|
||||
cfg.option("s1.b").value.set(True)
|
||||
cfg.option("s1.b").value.set(False)
|
||||
|
@ -36,6 +42,62 @@ def test_symlink_option(config_type):
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_symlink_default(config_type):
|
||||
boolopt = BoolOption("b", "", default=False)
|
||||
linkopt = SymLinkOption("c", boolopt)
|
||||
od1 = OptionDescription("opt", "",
|
||||
[linkopt, OptionDescription("s1", "", [boolopt])],
|
||||
)
|
||||
cfg = Config(od1)
|
||||
cfg = get_config(cfg, config_type)
|
||||
assert not cfg.option('s1.b').option.ismulti()
|
||||
assert not cfg.option('c').option.ismulti()
|
||||
assert not cfg.option('s1.b').option.issubmulti()
|
||||
assert not cfg.option('c').option.issubmulti()
|
||||
assert not cfg.option('s1.b').option.default()
|
||||
assert not cfg.option('c').option.default()
|
||||
assert not cfg.option('s1.b').value.default()
|
||||
assert not cfg.option('c').value.default()
|
||||
with pytest.raises(ConfigError):
|
||||
assert not cfg.option('s1.b').option.defaultmulti()
|
||||
with pytest.raises(ConfigError):
|
||||
assert not cfg.option('c').option.defaultmulti()
|
||||
cfg.option("s1.b").value.set(True)
|
||||
assert not cfg.option('s1.b').option.default()
|
||||
assert not cfg.option('c').option.default()
|
||||
assert not cfg.option('s1.b').value.default()
|
||||
assert not cfg.option('c').value.default()
|
||||
## assert not list_sessions()
|
||||
|
||||
|
||||
def test_symlink_default_multi(config_type):
|
||||
boolopt = BoolOption("b", "", default=[False], default_multi=True, multi=True)
|
||||
linkopt = SymLinkOption("c", boolopt)
|
||||
od1 = OptionDescription("opt", "",
|
||||
[linkopt, OptionDescription("s1", "", [boolopt])],
|
||||
)
|
||||
cfg = Config(od1)
|
||||
cfg = get_config(cfg, config_type)
|
||||
assert cfg.option('s1.b').option.ismulti()
|
||||
assert cfg.option('c').option.ismulti()
|
||||
assert not cfg.option('s1.b').option.issubmulti()
|
||||
assert not cfg.option('c').option.issubmulti()
|
||||
assert cfg.option('s1.b').option.default() == [False]
|
||||
assert cfg.option('c').option.default() == [False]
|
||||
assert cfg.option('s1.b').value.default() == [False]
|
||||
assert cfg.option('c').value.default() == [False]
|
||||
assert cfg.option('s1.b').option.defaultmulti()
|
||||
assert cfg.option('c').option.defaultmulti()
|
||||
cfg.option("s1.b").value.set([True])
|
||||
assert cfg.option('s1.b').option.default() == [False]
|
||||
assert cfg.option('c').option.default() == [False]
|
||||
assert cfg.option('s1.b').value.default() == [False]
|
||||
assert cfg.option('c').value.default() == [False]
|
||||
assert cfg.option('s1.b').option.defaultmulti()
|
||||
assert cfg.option('c').option.defaultmulti()
|
||||
## assert not list_sessions()
|
||||
|
||||
|
||||
def test_symlink_assign_option(config_type):
|
||||
boolopt = BoolOption("b", "", default=False)
|
||||
linkopt = SymLinkOption("c", boolopt)
|
||||
|
@ -66,14 +128,10 @@ def test_symlink_addproperties():
|
|||
od1 = OptionDescription('opt', '', [boolopt, linkopt])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
with pytest.raises(TypeError):
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('c').property.add('new')
|
||||
try:
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('c').property.reset()
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise Exception('must raise')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
@ -94,14 +152,10 @@ def test_symlink_addpermissives():
|
|||
od1 = OptionDescription('opt', '', [boolopt, linkopt])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
with pytest.raises(TypeError):
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('c').permissive.set(frozenset(['new']))
|
||||
try:
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('c').permissive.reset()
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise Exception('must raise')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
@ -340,3 +394,14 @@ def test_symlink_list(config_type):
|
|||
list_opt.append(opt.option.path())
|
||||
assert list_opt == ['c', 's1.b']
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_submulti():
|
||||
multi = StrOption('multi', '', multi=submulti)
|
||||
multi2 = SymLinkOption('multi2', multi)
|
||||
od1 = OptionDescription('od', '', [multi, multi2])
|
||||
cfg = Config(od1)
|
||||
assert cfg.option('multi').option.ismulti()
|
||||
assert cfg.option('multi').option.issubmulti()
|
||||
assert cfg.option('multi2').option.ismulti()
|
||||
assert cfg.option('multi2').option.issubmulti()
|
||||
|
|
487
tiramisu/api.py
487
tiramisu/api.py
File diff suppressed because it is too large
Load diff
|
@ -24,6 +24,7 @@ from itertools import chain
|
|||
from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning
|
||||
from .i18n import _
|
||||
from .setting import undefined, ConfigBag, OptionBag, Undefined
|
||||
from .function import FUNCTION_WAITING_FOR_DICT
|
||||
# ____________________________________________________________
|
||||
|
||||
|
||||
|
@ -58,15 +59,15 @@ class Param:
|
|||
|
||||
|
||||
class ParamOption(Param):
|
||||
__slots__ = ('todict',
|
||||
'option',
|
||||
__slots__ = ('option',
|
||||
'notraisepropertyerror',
|
||||
'raisepropertyerror')
|
||||
'raisepropertyerror',
|
||||
)
|
||||
def __init__(self,
|
||||
option: 'Option',
|
||||
notraisepropertyerror: bool=False,
|
||||
raisepropertyerror: bool=False,
|
||||
todict: bool=False) -> None:
|
||||
) -> None:
|
||||
if __debug__ and not hasattr(option, 'impl_is_symlinkoption'):
|
||||
raise ValueError(_('paramoption needs an option not {}').format(type(option)))
|
||||
if option.impl_is_symlinkoption():
|
||||
|
@ -75,7 +76,6 @@ class ParamOption(Param):
|
|||
cur_opt = option
|
||||
assert isinstance(notraisepropertyerror, bool), _('param must have a boolean not a {} for notraisepropertyerror').format(type(notraisepropertyerror))
|
||||
assert isinstance(raisepropertyerror, bool), _('param must have a boolean not a {} for raisepropertyerror').format(type(raisepropertyerror))
|
||||
self.todict = todict
|
||||
self.option = cur_opt
|
||||
self.notraisepropertyerror = notraisepropertyerror
|
||||
self.raisepropertyerror = raisepropertyerror
|
||||
|
@ -90,12 +90,10 @@ class ParamDynOption(ParamOption):
|
|||
notraisepropertyerror: bool=False,
|
||||
raisepropertyerror: bool=False,
|
||||
optional: bool=False,
|
||||
todict: bool=False,
|
||||
) -> None:
|
||||
super().__init__(option,
|
||||
notraisepropertyerror,
|
||||
raisepropertyerror,
|
||||
todict,
|
||||
)
|
||||
self.suffix = suffix
|
||||
self.dynoptiondescription = dynoptiondescription
|
||||
|
@ -103,12 +101,11 @@ class ParamDynOption(ParamOption):
|
|||
|
||||
|
||||
class ParamSelfOption(Param):
|
||||
__slots__ = ('todict', 'whole')
|
||||
__slots__ = ('whole')
|
||||
def __init__(self,
|
||||
todict: bool=False,
|
||||
whole: bool=undefined) -> None:
|
||||
whole: bool=undefined,
|
||||
) -> None:
|
||||
"""whole: send all value for a multi, not only indexed value"""
|
||||
self.todict = todict
|
||||
if whole is not undefined:
|
||||
self.whole = whole
|
||||
|
||||
|
@ -206,23 +203,13 @@ class Calculation:
|
|||
for_settings=for_settings,
|
||||
)
|
||||
|
||||
def has_index(self, current_option):
|
||||
if hasattr(self, '_has_index'):
|
||||
return self._has_index
|
||||
self._has_index = False
|
||||
for arg in chain(self.params.args, self.params.kwargs.values()):
|
||||
if isinstance(arg, ParamOption) and arg.option.impl_get_leadership() and \
|
||||
arg.option.impl_get_leadership().in_same_group(current_option):
|
||||
self._has_index = True
|
||||
break
|
||||
return self._has_index
|
||||
|
||||
|
||||
class Break(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def manager_callback(callbk: Param,
|
||||
def manager_callback(callback: Callable,
|
||||
param: Param,
|
||||
option,
|
||||
index: Optional[int],
|
||||
orig_value,
|
||||
|
@ -231,10 +218,10 @@ def manager_callback(callbk: Param,
|
|||
for_settings: bool,
|
||||
) -> Any:
|
||||
"""replace Param by true value"""
|
||||
def calc_index(callbk, index, same_leadership):
|
||||
def calc_index(param, index, same_leadership):
|
||||
if index is not None:
|
||||
if hasattr(callbk, 'whole'):
|
||||
whole = callbk.whole
|
||||
if hasattr(param, 'whole'):
|
||||
whole = param.whole
|
||||
else:
|
||||
# if value is same_leadership, follower are isolate by default
|
||||
# otherwise option is a whole option
|
||||
|
@ -243,7 +230,7 @@ def manager_callback(callbk: Param,
|
|||
return index
|
||||
return None
|
||||
|
||||
def calc_self(callbk,
|
||||
def calc_self(param,
|
||||
option,
|
||||
index,
|
||||
value,
|
||||
|
@ -251,10 +238,8 @@ def manager_callback(callbk: Param,
|
|||
):
|
||||
# index must be apply only if follower
|
||||
is_follower = option.impl_is_follower()
|
||||
apply_index = calc_index(callbk, index, is_follower)
|
||||
apply_index = calc_index(param, index, is_follower)
|
||||
if value is undefined or (apply_index is None and is_follower):
|
||||
if config_bag is undefined:
|
||||
return undefined
|
||||
path = option.impl_getpath()
|
||||
option_bag = OptionBag(option,
|
||||
None,
|
||||
|
@ -266,7 +251,7 @@ def manager_callback(callbk: Param,
|
|||
)
|
||||
parent_option_bag, option_bag = get_option_bag(config_bag,
|
||||
option,
|
||||
callbk,
|
||||
param,
|
||||
apply_index,
|
||||
True,
|
||||
properties=properties,
|
||||
|
@ -276,17 +261,17 @@ def manager_callback(callbk: Param,
|
|||
for idx in range(config_bag.context.get_length_leadership(parent_option_bag)):
|
||||
parent_option_bag, option_bag = get_option_bag(config_bag,
|
||||
option,
|
||||
callbk,
|
||||
param,
|
||||
idx,
|
||||
True,
|
||||
properties=properties,
|
||||
)
|
||||
new_value.append(get_value(callbk,
|
||||
new_value.append(get_value(param,
|
||||
option_bag,
|
||||
path,
|
||||
))
|
||||
else:
|
||||
new_value = get_value(callbk,
|
||||
new_value = get_value(param,
|
||||
option_bag,
|
||||
path,
|
||||
)
|
||||
|
@ -297,7 +282,7 @@ def manager_callback(callbk: Param,
|
|||
value = value[apply_index]
|
||||
return value
|
||||
|
||||
def get_value(callbk,
|
||||
def get_value(param,
|
||||
option_bag,
|
||||
path,
|
||||
):
|
||||
|
@ -306,14 +291,14 @@ def manager_callback(callbk: Param,
|
|||
value = config_bag.context.get_value(option_bag)
|
||||
except PropertiesOptionError as err:
|
||||
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
|
||||
if callbk.notraisepropertyerror or callbk.raisepropertyerror:
|
||||
if param.notraisepropertyerror or param.raisepropertyerror:
|
||||
raise err from err
|
||||
raise ConfigError(_('unable to carry out a calculation for "{}"'
|
||||
', {}').format(option.impl_get_display_name(), err), err) from err
|
||||
except ValueError as err:
|
||||
raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option_bag.option.impl_get_display_name(), err)) from err
|
||||
except AttributeError as err:
|
||||
if isinstance(callbk, ParamDynOption) and callbk.optional:
|
||||
if isinstance(param, ParamDynOption) and param.optional:
|
||||
# cannot acces, simulate a propertyerror
|
||||
raise PropertiesOptionError(option_bag,
|
||||
['configerror'],
|
||||
|
@ -324,7 +309,7 @@ def manager_callback(callbk: Param,
|
|||
|
||||
def get_option_bag(config_bag,
|
||||
opt,
|
||||
callbk,
|
||||
param,
|
||||
index_,
|
||||
self_calc,
|
||||
properties=undefined,
|
||||
|
@ -352,14 +337,14 @@ def manager_callback(callbk: Param,
|
|||
)
|
||||
except PropertiesOptionError as err:
|
||||
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
|
||||
if callbk.notraisepropertyerror or callbk.raisepropertyerror:
|
||||
if param.notraisepropertyerror or param.raisepropertyerror:
|
||||
raise err from err
|
||||
raise ConfigError(_('unable to carry out a calculation for "{}"'
|
||||
', {}').format(option.impl_get_display_name(), err), err) from err
|
||||
except ValueError as err:
|
||||
raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option.impl_get_display_name(), err)) from err
|
||||
except AttributeError as err:
|
||||
if isinstance(callbk, ParamDynOption) and callbk.optional:
|
||||
if isinstance(param, ParamDynOption) and param.optional:
|
||||
# cannot acces, simulate a propertyerror
|
||||
raise PropertiesOptionError(options_bag[-1],
|
||||
['configerror'],
|
||||
|
@ -375,11 +360,11 @@ def manager_callback(callbk: Param,
|
|||
else:
|
||||
return options_bag[-1]
|
||||
|
||||
if isinstance(callbk, ParamValue):
|
||||
return callbk.value
|
||||
if isinstance(param, ParamValue):
|
||||
return param.value
|
||||
|
||||
if isinstance(callbk, ParamInformation):
|
||||
if isinstance(callbk, ParamSelfInformation):
|
||||
if isinstance(param, ParamInformation):
|
||||
if isinstance(param, ParamSelfInformation):
|
||||
option_bag = OptionBag(option,
|
||||
index,
|
||||
config_bag,
|
||||
|
@ -387,48 +372,47 @@ def manager_callback(callbk: Param,
|
|||
else:
|
||||
option_bag = None
|
||||
try:
|
||||
return config_bag.context.impl_get_information(config_bag,
|
||||
option_bag,
|
||||
callbk.information_name,
|
||||
callbk.default_value,
|
||||
return config_bag.context.impl_get_information(option_bag,
|
||||
param.information_name,
|
||||
param.default_value,
|
||||
)
|
||||
except ValueError as err:
|
||||
raise ConfigError(_('option "{}" cannot be calculated: {}').format(option.impl_get_display_name(),
|
||||
str(err),
|
||||
))
|
||||
|
||||
if isinstance(callbk, ParamIndex):
|
||||
if isinstance(param, ParamIndex):
|
||||
return index
|
||||
|
||||
if isinstance(callbk, ParamSuffix):
|
||||
if isinstance(param, ParamSuffix):
|
||||
if not option.issubdyn():
|
||||
raise ConfigError(_('option "{}" is not in a dynoptiondescription').format(option.impl_get_display_name()))
|
||||
return option.impl_getsuffix()
|
||||
|
||||
if isinstance(callbk, ParamSelfOption):
|
||||
if isinstance(param, ParamSelfOption):
|
||||
if leadership_must_have_index and option.impl_is_follower() and index is None:
|
||||
raise Break()
|
||||
value = calc_self(callbk,
|
||||
value = calc_self(param,
|
||||
option,
|
||||
index,
|
||||
orig_value,
|
||||
config_bag,
|
||||
)
|
||||
if not callbk.todict:
|
||||
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT:
|
||||
return value
|
||||
return {'name': option.impl_get_display_name(),
|
||||
'value': value,
|
||||
}
|
||||
|
||||
if isinstance(callbk, ParamOption):
|
||||
callbk_option = callbk.option
|
||||
if isinstance(param, ParamOption):
|
||||
callbk_option = param.option
|
||||
callbk_options = None
|
||||
if callbk_option.issubdyn():
|
||||
found = False
|
||||
if isinstance(callbk, ParamDynOption):
|
||||
subdyn = callbk.dynoptiondescription
|
||||
rootpath = subdyn.impl_getpath() + callbk.suffix
|
||||
suffix = callbk.suffix
|
||||
if isinstance(param, ParamDynOption):
|
||||
subdyn = param.dynoptiondescription
|
||||
rootpath = subdyn.impl_getpath() + param.suffix
|
||||
suffix = param.suffix
|
||||
callbk_option = callbk_option.to_dynoption(rootpath,
|
||||
suffix,
|
||||
subdyn,
|
||||
|
@ -462,8 +446,6 @@ def manager_callback(callbk: Param,
|
|||
callbk_options.append(doption)
|
||||
if leadership_must_have_index and callbk_option.impl_is_follower() and index is None:
|
||||
raise Break()
|
||||
if config_bag is undefined:
|
||||
return undefined
|
||||
if callbk_options is None:
|
||||
callbk_options = [callbk_option]
|
||||
values = None
|
||||
|
@ -471,7 +453,7 @@ def manager_callback(callbk: Param,
|
|||
values = []
|
||||
for callbk_option in callbk_options:
|
||||
if index is not None and callbk_option.impl_get_leadership() and \
|
||||
callbk_option.impl_get_leadership().in_same_group(option):
|
||||
callbk_option.impl_get_leadership().in_same_leadership(option):
|
||||
if not callbk_option.impl_is_follower():
|
||||
# leader
|
||||
index_ = None
|
||||
|
@ -486,11 +468,11 @@ def manager_callback(callbk: Param,
|
|||
path = callbk_option.impl_getpath()
|
||||
option_bag = get_option_bag(config_bag,
|
||||
callbk_option,
|
||||
callbk,
|
||||
param,
|
||||
index_,
|
||||
False,
|
||||
)
|
||||
value = get_value(callbk,
|
||||
value = get_value(param,
|
||||
option_bag,
|
||||
path,
|
||||
)
|
||||
|
@ -500,12 +482,10 @@ def manager_callback(callbk: Param,
|
|||
values.append(value)
|
||||
if values is not None:
|
||||
value = values
|
||||
if not callbk.todict:
|
||||
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT:
|
||||
return value
|
||||
return {'name': callbk_option.impl_get_display_name(),
|
||||
'value': value}
|
||||
raise ConfigError(_('unknown callback type {} in option {}').format(callbk,
|
||||
option.impl_get_display_name()))
|
||||
|
||||
|
||||
def carry_out_calculation(option,
|
||||
|
@ -540,9 +520,10 @@ def carry_out_calculation(option,
|
|||
args = []
|
||||
kwargs = {}
|
||||
if callback_params:
|
||||
for key, callbk in chain(fake_items(callback_params.args), callback_params.kwargs.items()):
|
||||
for key, param in chain(fake_items(callback_params.args), callback_params.kwargs.items()):
|
||||
try:
|
||||
value = manager_callback(callbk,
|
||||
value = manager_callback(callback,
|
||||
param,
|
||||
option,
|
||||
index,
|
||||
orig_value,
|
||||
|
@ -550,16 +531,14 @@ def carry_out_calculation(option,
|
|||
leadership_must_have_index,
|
||||
for_settings,
|
||||
)
|
||||
if value is undefined:
|
||||
return undefined
|
||||
if key is None:
|
||||
args.append(value)
|
||||
else:
|
||||
kwargs[key] = value
|
||||
except PropertiesOptionError as err:
|
||||
if callbk.raisepropertyerror:
|
||||
if param.raisepropertyerror:
|
||||
raise err
|
||||
if callbk.todict:
|
||||
if callback.__name__ in FUNCTION_WAITING_FOR_DICT:
|
||||
if key is None:
|
||||
args.append({'propertyerror': str(err)})
|
||||
else:
|
||||
|
@ -616,8 +595,6 @@ def calculate(option,
|
|||
raise err
|
||||
error = err
|
||||
except Exception as err:
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
error = err
|
||||
if args or kwargs:
|
||||
msg = _('unexpected error "{0}" in function "{1}" with arguments "{3}" and "{4}" '
|
||||
|
|
|
@ -16,79 +16,91 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ____________________________________________________________
|
||||
from time import time
|
||||
from .log import log
|
||||
|
||||
|
||||
def _display_classname(obj): # pragma: no cover
|
||||
return(obj.__class__.__name__.lower())
|
||||
|
||||
|
||||
class Cache:
|
||||
"""cache object
|
||||
"""
|
||||
__slots__ = ('_cache',)
|
||||
|
||||
def __init__(self):
|
||||
self._cache = {}
|
||||
|
||||
def setcache(self, path, index, val, self_props, props, validated):
|
||||
def _get_path_index(self, option_bag):
|
||||
if option_bag is None:
|
||||
path = None
|
||||
index = None
|
||||
else:
|
||||
path = option_bag.path
|
||||
index = option_bag.index
|
||||
return path, index
|
||||
|
||||
def getcache(self,
|
||||
option_bag,
|
||||
type_,
|
||||
expiration=True,
|
||||
):
|
||||
"""get the cache value fot a specified path
|
||||
"""
|
||||
no_cache = False, None, False
|
||||
path, index = self._get_path_index(option_bag)
|
||||
if path not in self._cache or index not in self._cache[path]:
|
||||
return no_cache
|
||||
value, timestamp, validated = self._cache[path][index]
|
||||
if type_ == 'context_props':
|
||||
# cached value is settings properties so value is props
|
||||
props = value
|
||||
self_props = {}
|
||||
else:
|
||||
props = option_bag.config_bag.properties
|
||||
if type_ == 'self_props':
|
||||
# cached value is self_props
|
||||
self_props = value
|
||||
else:
|
||||
self_props = option_bag.properties
|
||||
if 'cache' in props or \
|
||||
'cache' in self_props:
|
||||
if expiration and timestamp and \
|
||||
('expire' in props or \
|
||||
'expire' in self_props):
|
||||
ntime = int(time())
|
||||
if timestamp + option_bag.config_bag.expiration_time >= ntime:
|
||||
return True, value, validated
|
||||
else:
|
||||
return True, value, validated
|
||||
return no_cache
|
||||
|
||||
def setcache(self,
|
||||
option_bag,
|
||||
val,
|
||||
type_='values',
|
||||
validated=True,
|
||||
):
|
||||
"""add val in cache for a specified path
|
||||
if follower, add index
|
||||
"""
|
||||
if 'cache' in props or 'cache' in self_props:
|
||||
self._cache.setdefault(path, {})[index] = (val, int(time()), validated)
|
||||
if type_ == 'values':
|
||||
if 'cache' not in option_bag.config_bag.properties and \
|
||||
'cache' not in option_bag.properties:
|
||||
return
|
||||
elif (option_bag is None or 'cache' not in option_bag.config_bag.properties) and \
|
||||
'cache' not in val:
|
||||
return
|
||||
path, index = self._get_path_index(option_bag)
|
||||
self._cache.setdefault(path, {})[index] = (val, int(time()), validated)
|
||||
|
||||
def delcache(self, path):
|
||||
"""reset cache a a specified path
|
||||
"""
|
||||
if path in self._cache:
|
||||
del self._cache[path]
|
||||
|
||||
def get_cached(self):
|
||||
"""get cache values
|
||||
"""
|
||||
return self._cache
|
||||
|
||||
def reset_all_cache(self):
|
||||
"""reset all cache values
|
||||
"""
|
||||
self._cache.clear()
|
||||
|
||||
def getcache(self,
|
||||
path,
|
||||
expiration_time,
|
||||
index,
|
||||
props,
|
||||
self_props,
|
||||
type_,
|
||||
):
|
||||
no_cache = False, None, False
|
||||
if 'cache' in props or type_ == 'context_props':
|
||||
values = self._cache.get(path)
|
||||
if values is None:
|
||||
indexed = None
|
||||
else:
|
||||
indexed = values.get(index)
|
||||
|
||||
if indexed is None:
|
||||
return no_cache
|
||||
value, timestamp, validated = indexed
|
||||
if type_ == 'context_props':
|
||||
# cached value is settings properties so value is props
|
||||
props = value
|
||||
elif type_ == 'self_props':
|
||||
# if self_props is None, so cached value is self properties
|
||||
# so value is self_props
|
||||
self_props = value
|
||||
# recheck "cache" value
|
||||
if 'cache' in props:
|
||||
if expiration_time and timestamp and \
|
||||
('expire' in props or \
|
||||
'expire' in self_props):
|
||||
ntime = int(time())
|
||||
if timestamp + expiration_time >= ntime:
|
||||
# log.debug('getcache in cache (1) %s %s %s %s %s', path, value, _display_classname(self),
|
||||
# id(self), index)
|
||||
return True, value, validated
|
||||
# else:
|
||||
# log.debug('getcache expired value for path %s < %s',
|
||||
# timestamp + expiration_time, ntime)
|
||||
else:
|
||||
# log.debug('getcache in cache (2) %s %s %s %s %s', path, value, _display_classname(self),
|
||||
# id(self), index)
|
||||
return True, value, validated
|
||||
# log.debug('getcache %s with index %s not in %s cache',
|
||||
# path, index, _display_classname(self))
|
||||
return no_cache
|
||||
|
|
|
@ -18,16 +18,15 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"options handler global entry point"
|
||||
"""options handler global entry point
|
||||
"""
|
||||
import weakref
|
||||
from copy import copy, deepcopy
|
||||
from typing import Optional, List, Any, Union
|
||||
|
||||
|
||||
from .error import PropertiesOptionError, ConfigError, ConflictError, \
|
||||
LeadershipError
|
||||
from .option import SynDynOptionDescription, DynOptionDescription, Leadership, Option
|
||||
from .option.baseoption import BaseOption
|
||||
from .option import DynOptionDescription, Leadership, Option
|
||||
from .setting import OptionBag, ConfigBag, Settings, undefined, groups
|
||||
from .value import Values, owners
|
||||
from .i18n import _
|
||||
|
@ -48,7 +47,6 @@ class _SubConfig:
|
|||
def __init__(self,
|
||||
descr,
|
||||
context,
|
||||
config_bag,
|
||||
subpath=None,
|
||||
):
|
||||
""" Configuration option management class
|
||||
|
@ -60,11 +58,6 @@ class _SubConfig:
|
|||
:type subpath: `str` with the path name
|
||||
"""
|
||||
# main option description
|
||||
if __debug__ and (not isinstance(descr, (BaseOption, SynDynOptionDescription)) or
|
||||
not descr.impl_is_optiondescription()):
|
||||
msg = _(f'cannot create a sub config for "{descr.impl_get_display_name()}" '
|
||||
f'this is a "{descr.__class__.__name__}", not an "OptionDescription"')
|
||||
raise TypeError(msg)
|
||||
self._impl_descr = descr
|
||||
self._impl_context = context
|
||||
self._impl_path = subpath
|
||||
|
@ -74,16 +67,6 @@ class _SubConfig:
|
|||
):
|
||||
"""Get the length of leader option (useful to know follower's length)
|
||||
"""
|
||||
if option_bag.option.impl_is_symlinkoption():
|
||||
root_option_bag = OptionBag(option_bag.config_bag.context.get_description(),
|
||||
None,
|
||||
config_bag,
|
||||
)
|
||||
option_bag = self.get_sub_option_bag(root_option_bag,
|
||||
option_bag.option.impl_getopt().impl_getpath(),
|
||||
option_bag.index,
|
||||
True,
|
||||
)[-1]
|
||||
cconfig_bag = option_bag.config_bag.copy()
|
||||
cconfig_bag.remove_validation()
|
||||
option_bag = OptionBag(option_bag.option.get_leader(),
|
||||
|
@ -92,22 +75,22 @@ class _SubConfig:
|
|||
)
|
||||
return len(self.get_value(option_bag))
|
||||
|
||||
def get_path(self):
|
||||
return self._impl_path
|
||||
|
||||
def get_context(self):
|
||||
return self._impl_context()
|
||||
|
||||
def get_description(self):
|
||||
"""get root description
|
||||
"""
|
||||
assert self._impl_descr is not None, _('there is no option description for this config'
|
||||
' (may be GroupConfig)')
|
||||
return self._impl_descr
|
||||
|
||||
def get_settings(self):
|
||||
return self.get_context()._impl_settings
|
||||
"""get settings object
|
||||
"""
|
||||
return self._impl_settings # pylint: disable=no-member
|
||||
|
||||
def get_values(self):
|
||||
return self.get_context()._impl_values
|
||||
"""get values object
|
||||
"""
|
||||
return self._impl_values # pylint: disable=no-member
|
||||
|
||||
# =============================================================================
|
||||
# CACHE
|
||||
|
@ -129,18 +112,24 @@ class _SubConfig:
|
|||
)
|
||||
option_bag.config_bag.properties = option_bag.config_bag.properties | {'cache'}
|
||||
else:
|
||||
context = self.get_context()
|
||||
context._impl_values_cache.reset_all_cache()
|
||||
context.properties_cache.reset_all_cache()
|
||||
self._impl_values_cache.reset_all_cache() # pylint: disable=no-member
|
||||
self.properties_cache.reset_all_cache() # pylint: disable=no-member
|
||||
|
||||
def get_values_cache(self):
|
||||
"""get cache for values
|
||||
"""
|
||||
return self._impl_values_cache # pylint: disable=no-member
|
||||
|
||||
def reset_one_option_cache(self,
|
||||
resetted_opts,
|
||||
option_bag,
|
||||
):
|
||||
"""reset cache for one option
|
||||
"""
|
||||
if option_bag.path in resetted_opts:
|
||||
return
|
||||
resetted_opts.append(option_bag.path)
|
||||
for woption in option_bag.option._get_dependencies(option_bag.option):
|
||||
for woption in option_bag.option._get_dependencies(option_bag.option): # pylint: disable=protected-access
|
||||
option = woption()
|
||||
soption_bag = OptionBag(option,
|
||||
option_bag.index,
|
||||
|
@ -178,7 +167,6 @@ class _SubConfig:
|
|||
subpath = ''
|
||||
|
||||
for suffix in option_bag.option.get_suffixes(option_bag.config_bag):
|
||||
path_suffix = option_bag.option.convert_suffix_to_path(suffix)
|
||||
doption = option_bag.option.to_dynoption(subpath,
|
||||
suffix,
|
||||
option_bag.option,
|
||||
|
@ -192,7 +180,7 @@ class _SubConfig:
|
|||
None,
|
||||
option_bag.config_bag,
|
||||
):
|
||||
coption_bag = self.get_sub_option_bag(doption_bag,
|
||||
coption_bag = self.get_sub_option_bag(doption_bag, # pylint: disable=no-member
|
||||
coption.impl_getpath(),
|
||||
None,
|
||||
False,
|
||||
|
@ -258,15 +246,12 @@ class _SubConfig:
|
|||
:param first: return only one option if True, a list otherwise
|
||||
:return: find list or an exception if nothing has been found
|
||||
"""
|
||||
# pylint: disable=too-many-arguments,too-many-locals
|
||||
def _filter_by_value(soption_bag):
|
||||
try:
|
||||
value = soption_bag.config_bag.context.get_value(soption_bag)
|
||||
except PropertiesOptionError:
|
||||
return False
|
||||
value = self.get_value(soption_bag)
|
||||
if isinstance(value, list):
|
||||
return byvalue in value
|
||||
else:
|
||||
return value == byvalue
|
||||
return value == byvalue
|
||||
|
||||
found = False
|
||||
if only_path is not undefined:
|
||||
|
@ -286,10 +271,10 @@ class _SubConfig:
|
|||
)
|
||||
if byvalue is not undefined and not _filter_by_value(soption_bag):
|
||||
continue
|
||||
elif option_bag.config_bag.properties:
|
||||
if option_bag.config_bag.properties:
|
||||
#remove option with propertyerror, ...
|
||||
try:
|
||||
self.get_sub_option_bag(option_bag,
|
||||
self.get_sub_option_bag(option_bag, # pylint: disable=no-member
|
||||
path,
|
||||
None,
|
||||
True,
|
||||
|
@ -331,16 +316,19 @@ class _SubConfig:
|
|||
return value
|
||||
|
||||
def walk(self,
|
||||
option_bag,
|
||||
types=['option'],
|
||||
option_bag: OptionBag,
|
||||
types: List[str]=('option',),
|
||||
group_type=None,
|
||||
recursive=True,
|
||||
walked=False,
|
||||
recursive: bool=True,
|
||||
walked: bool=False,
|
||||
):
|
||||
"""walk to tree
|
||||
"""
|
||||
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments,
|
||||
if option_bag.option.impl_is_optiondescription():
|
||||
# do not return root option
|
||||
if walked:
|
||||
if 'optiondescription' in types and (group_type is None or
|
||||
if 'optiondescription' in types and (group_type is None or
|
||||
option_bag.option.impl_get_group_type() == group_type):
|
||||
yield option_bag
|
||||
if not recursive:
|
||||
|
@ -349,7 +337,7 @@ class _SubConfig:
|
|||
if not option_bag.option.impl_is_leadership():
|
||||
for opt in option_bag.option.get_children(option_bag.config_bag):
|
||||
try:
|
||||
yield from self.walk(self.get_sub_option_bag(option_bag,
|
||||
yield from self.walk(self.get_sub_option_bag(option_bag, # pylint: disable=no-member
|
||||
opt.impl_getpath(),
|
||||
None,
|
||||
True,
|
||||
|
@ -366,7 +354,7 @@ class _SubConfig:
|
|||
# it's a leadership so walk to leader and followers
|
||||
# followers has specific length
|
||||
leader, *followers = option_bag.option.get_children(option_bag.config_bag)
|
||||
leader_option_bag = self.get_sub_option_bag(option_bag,
|
||||
leader_option_bag = self.get_sub_option_bag(option_bag, # pylint: disable=no-member
|
||||
leader.impl_getpath(),
|
||||
None,
|
||||
True,
|
||||
|
@ -375,7 +363,7 @@ class _SubConfig:
|
|||
values = self.get_value(leader_option_bag,
|
||||
need_help=False,
|
||||
)
|
||||
leadership_length = len(values)
|
||||
ls_length = len(values)
|
||||
try:
|
||||
self._walk_valid_value(leader_option_bag,
|
||||
types,
|
||||
|
@ -383,21 +371,19 @@ class _SubConfig:
|
|||
)
|
||||
except PropertiesOptionError as err:
|
||||
if err.proptype in (['mandatory'], ['empty']):
|
||||
if 'mandatory' in types:
|
||||
yield leader_option_bag
|
||||
else:
|
||||
raise err
|
||||
for idx in range(leadership_length):
|
||||
yield leader_option_bag
|
||||
for idx in range(ls_length):
|
||||
followers_dict[idx] = []
|
||||
for follower in followers:
|
||||
follower_path = follower.impl_getpath()
|
||||
try:
|
||||
for f_follower_bag in self.walk(self.get_sub_option_bag(option_bag,
|
||||
follower_path,
|
||||
idx,
|
||||
True,
|
||||
leadership_length=leadership_length,
|
||||
)[-1],
|
||||
options_bag = self.get_sub_option_bag(option_bag, # pylint: disable=no-member
|
||||
follower_path,
|
||||
idx,
|
||||
True,
|
||||
leadership_length=ls_length,
|
||||
)
|
||||
for f_follower_bag in self.walk(options_bag[-1],
|
||||
types=types,
|
||||
recursive=recursive,
|
||||
group_type=group_type,
|
||||
|
@ -410,7 +396,6 @@ class _SubConfig:
|
|||
continue
|
||||
if 'option' in types:
|
||||
yield followers_dict
|
||||
# pass
|
||||
else:
|
||||
if 'mandatory' in types and not option_bag.option.impl_is_symlinkoption():
|
||||
try:
|
||||
|
@ -455,25 +440,13 @@ class _SubConfig:
|
|||
def set_value(self,
|
||||
option_bag: OptionBag,
|
||||
value: Any,
|
||||
):
|
||||
if option_bag.option.impl_is_symlinkoption():
|
||||
raise ConfigError(_("can't set value to a SymLinkOption"))
|
||||
context = option_bag.config_bag.context
|
||||
context.get_settings().validate_properties(option_bag)
|
||||
return context.get_values().set_value(option_bag,
|
||||
value,
|
||||
)
|
||||
|
||||
def delattr(self,
|
||||
option_bag):
|
||||
option = option_bag.option
|
||||
if option.impl_is_symlinkoption():
|
||||
raise ConfigError(_("can't delete a SymLinkOption"))
|
||||
values = self.get_values()
|
||||
if option_bag.index is not None:
|
||||
values.reset_follower(option_bag)
|
||||
else:
|
||||
values.reset(option_bag)
|
||||
) -> Any:
|
||||
"""set value
|
||||
"""
|
||||
self.get_settings().validate_properties(option_bag)
|
||||
return self.get_values().set_value(option_bag,
|
||||
value
|
||||
)
|
||||
|
||||
def get_value(self,
|
||||
option_bag,
|
||||
|
@ -484,7 +457,9 @@ class _SubConfig:
|
|||
:return: option's value if name is an option name, OptionDescription
|
||||
otherwise
|
||||
"""
|
||||
option_bag = self._get(option_bag, parent_option_bag, need_help)
|
||||
option_bag = self._get(option_bag,
|
||||
need_help,
|
||||
)
|
||||
if isinstance(option_bag, list):
|
||||
value = []
|
||||
for opt_bag in option_bag:
|
||||
|
@ -507,10 +482,10 @@ class _SubConfig:
|
|||
return value
|
||||
|
||||
def _get(self,
|
||||
option_bag,
|
||||
parent_option_bag,
|
||||
need_help,
|
||||
):
|
||||
option_bag: OptionBag,
|
||||
need_help: bool,
|
||||
) -> OptionBag:
|
||||
# pylint: disable=too-many-locals
|
||||
option = option_bag.option
|
||||
if option.impl_is_symlinkoption():
|
||||
suboption = option.impl_getopt()
|
||||
|
@ -534,7 +509,7 @@ class _SubConfig:
|
|||
ret.append(doption_bag)
|
||||
return ret
|
||||
if suboption.impl_is_follower():
|
||||
options_bag = self.get_sub_option_bag(option_bag.config_bag,
|
||||
options_bag = self.get_sub_option_bag(option_bag.config_bag, # pylint: disable=no-member
|
||||
suboption.impl_getpath(),
|
||||
None,
|
||||
True,
|
||||
|
@ -554,40 +529,28 @@ class _SubConfig:
|
|||
option_bag.config_bag,
|
||||
ori_option=option,
|
||||
)
|
||||
return self._get(soption_bag, None, need_help)
|
||||
return self._get(soption_bag,
|
||||
need_help,
|
||||
)
|
||||
return option_bag
|
||||
|
||||
def get_owner(self,
|
||||
option_bag: OptionBag,
|
||||
parent_option_bag=None,
|
||||
):
|
||||
options_bag = self._get(option_bag, parent_option_bag, need_help=True)
|
||||
def get_owner(self, option_bag: OptionBag):
|
||||
"""get owner
|
||||
"""
|
||||
options_bag = self._get(option_bag,
|
||||
need_help=True,
|
||||
)
|
||||
if isinstance(options_bag, list):
|
||||
if not option_bag.option.impl_is_symlinkoption():
|
||||
owner = []
|
||||
for opt_bag in options_bag:
|
||||
owner.append(self.get_owner(opt_bag))
|
||||
for opt_bag in options_bag:
|
||||
owner = self.get_owner(opt_bag)
|
||||
if owner != owners.default:
|
||||
break
|
||||
else:
|
||||
for opt_bag in options_bag:
|
||||
owner = self.get_owner(opt_bag)
|
||||
if owner != owners.default:
|
||||
break
|
||||
else:
|
||||
owner = owners.default
|
||||
owner = owners.default
|
||||
else:
|
||||
owner = self.get_values().getowner(options_bag)
|
||||
return owner
|
||||
|
||||
def pop_leader(self,
|
||||
option_bag: OptionBag,
|
||||
leadership_option_bag: OptionBag,
|
||||
index: int,
|
||||
):
|
||||
self.get_values().reset_leadership(option_bag,
|
||||
leadership_option_bag,
|
||||
index,
|
||||
)
|
||||
|
||||
|
||||
class _CommonConfig(_SubConfig):
|
||||
"abstract base class for the Config, KernelGroupConfig and the KernelMetaConfig"
|
||||
|
@ -597,18 +560,21 @@ class _CommonConfig(_SubConfig):
|
|||
'properties_cache',
|
||||
'_impl_permissives_cache',
|
||||
'parents',
|
||||
'impl_type')
|
||||
'impl_type',
|
||||
)
|
||||
|
||||
def _impl_build_all_caches(self, descr):
|
||||
if not descr.impl_already_build_caches():
|
||||
descr._group_type = groups.root
|
||||
descr._build_cache(display_name=self._display_name)
|
||||
descr._group_type = groups.root # pylint: disable=protected-access
|
||||
descr._build_cache(display_name=self._display_name) # pylint: disable=no-member,protected-access
|
||||
if not hasattr(descr, '_cache_force_store_values'):
|
||||
raise ConfigError(_('option description seems to be part of an other '
|
||||
'config'))
|
||||
|
||||
def get_parents(self):
|
||||
for parent in self.parents:
|
||||
"""get parents
|
||||
"""
|
||||
for parent in self.parents: # pylint: disable=no-member
|
||||
yield parent()
|
||||
|
||||
# information
|
||||
|
@ -622,22 +588,19 @@ class _CommonConfig(_SubConfig):
|
|||
:param key: information's key (ex: "help", "doc"
|
||||
:param value: information's value (ex: "the help string")
|
||||
"""
|
||||
self._impl_values.set_information(config_bag,
|
||||
None,
|
||||
key,
|
||||
value,
|
||||
)
|
||||
context = config_bag.context
|
||||
cache = context.get_description()._cache_dependencies_information.get(key, [])
|
||||
self._impl_values.set_information(None, # pylint: disable=no-member
|
||||
key,
|
||||
value,
|
||||
)
|
||||
cache = self.get_description()._cache_dependencies_information.get(key, []) # pylint: disable=protected-access
|
||||
for option in cache:
|
||||
option_bag = OptionBag(option,
|
||||
None,
|
||||
config_bag,
|
||||
)
|
||||
context.reset_cache(option_bag)
|
||||
self.reset_cache(option_bag)
|
||||
|
||||
def impl_get_information(self,
|
||||
config_bag,
|
||||
option_bag,
|
||||
key,
|
||||
default,
|
||||
|
@ -646,8 +609,7 @@ class _CommonConfig(_SubConfig):
|
|||
|
||||
:param key: the item string (ex: "help")
|
||||
"""
|
||||
return self._impl_values.get_information(config_bag,
|
||||
option_bag,
|
||||
return self._impl_values.get_information(option_bag, # pylint: disable=no-member
|
||||
key,
|
||||
default,
|
||||
)
|
||||
|
@ -656,24 +618,27 @@ class _CommonConfig(_SubConfig):
|
|||
key,
|
||||
raises=True,
|
||||
):
|
||||
self._impl_values.del_information(key,
|
||||
"""delete an information
|
||||
"""
|
||||
self._impl_values.del_information(key, # pylint: disable=no-member
|
||||
raises,
|
||||
)
|
||||
|
||||
def impl_list_information(self):
|
||||
return self._impl_values.list_information()
|
||||
"""list information keys for context
|
||||
"""
|
||||
return self._impl_values.list_information() # pylint: disable=no-member
|
||||
|
||||
def __getstate__(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _gen_fake_values(self):
|
||||
export = deepcopy(self.get_values()._values)
|
||||
def gen_fake_values(self) -> 'KernelConfig':
|
||||
"""generate a fake values to improve validation when assign a new value
|
||||
"""
|
||||
export = deepcopy(self.get_values()._values) # pylint: disable=protected-access
|
||||
fake_config = KernelConfig(self._impl_descr,
|
||||
force_values=export,
|
||||
force_settings=self.get_settings(),
|
||||
name=self._impl_name,
|
||||
name=self._impl_name, # pylint: disable=no-member
|
||||
)
|
||||
fake_config.parents = self.parents
|
||||
fake_config.parents = self.parents # pylint: disable=no-member
|
||||
return fake_config
|
||||
|
||||
def duplicate(self,
|
||||
|
@ -684,10 +649,11 @@ class _CommonConfig(_SubConfig):
|
|||
deep=None,
|
||||
name=None,
|
||||
):
|
||||
if not isinstance(self, (KernelConfig, KernelMixConfig)):
|
||||
raise ConfigError(_('cannot duplicate {}').format(self.__class__.__name__))
|
||||
"""duplication config
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
if name is None:
|
||||
name = self._impl_name
|
||||
name = self._impl_name # pylint: disable=no-member
|
||||
if isinstance(self, KernelConfig):
|
||||
duplicated_config = KernelConfig(self._impl_descr,
|
||||
_duplicate=True,
|
||||
|
@ -703,10 +669,10 @@ class _CommonConfig(_SubConfig):
|
|||
)
|
||||
duplicated_values = duplicated_config.get_values()
|
||||
duplicated_settings = duplicated_config.get_settings()
|
||||
duplicated_values._values = deepcopy(self.get_values()._values)
|
||||
duplicated_values._informations = deepcopy(self.get_values()._informations)
|
||||
duplicated_settings._properties = deepcopy(self.get_settings()._properties)
|
||||
duplicated_settings._permissives = deepcopy(self.get_settings()._permissives)
|
||||
duplicated_values._values = deepcopy(self.get_values()._values) # pylint: disable=protected-access
|
||||
duplicated_values._informations = deepcopy(self.get_values()._informations) # pylint: disable=protected-access
|
||||
duplicated_settings._properties = deepcopy(self.get_settings()._properties) # pylint: disable=protected-access
|
||||
duplicated_settings._permissives = deepcopy(self.get_settings()._permissives) # pylint: disable=protected-access
|
||||
duplicated_settings.ro_append = self.get_settings().ro_append
|
||||
duplicated_settings.rw_append = self.get_settings().rw_append
|
||||
duplicated_settings.ro_remove = self.get_settings().ro_remove
|
||||
|
@ -714,11 +680,11 @@ class _CommonConfig(_SubConfig):
|
|||
duplicated_settings.default_properties = self.get_settings().default_properties
|
||||
duplicated_config.reset_cache(None, None)
|
||||
if child is not None:
|
||||
duplicated_config._impl_children.append(child)
|
||||
duplicated_config._impl_children.append(child) # pylint: disable=protected-access
|
||||
child.parents.append(weakref.ref(duplicated_config))
|
||||
if self.parents:
|
||||
if self.parents: # pylint: disable=no-member
|
||||
if deep is not None:
|
||||
for parent in self.parents:
|
||||
for parent in self.parents: # pylint: disable=no-member
|
||||
wparent = parent()
|
||||
if wparent not in deep:
|
||||
deep.append(wparent)
|
||||
|
@ -731,17 +697,19 @@ class _CommonConfig(_SubConfig):
|
|||
name=subname,
|
||||
)
|
||||
else:
|
||||
duplicated_config.parents = self.parents
|
||||
for parent in self.parents:
|
||||
parent()._impl_children.append(duplicated_config)
|
||||
duplicated_config.parents = self.parents # pylint: disable=no-member
|
||||
for parent in self.parents: # pylint: disable=no-member
|
||||
parent()._impl_children.append(duplicated_config) # pylint: disable=protected-access
|
||||
return duplicated_config
|
||||
|
||||
def get_config_path(self):
|
||||
"""get config path
|
||||
"""
|
||||
path = self.impl_getname()
|
||||
for parent in self.parents:
|
||||
for parent in self.parents: # pylint: disable=no-member
|
||||
wparent = parent()
|
||||
if wparent is None: # pragma: no cover
|
||||
raise ConfigError(_('parent of {} not already exists').format(self._impl_name))
|
||||
raise ConfigError(_(f'parent of {self._impl_name} not already exists')) # pylint: disable=no-member
|
||||
path = parent().get_config_path() + '.' + path
|
||||
return path
|
||||
|
||||
|
@ -755,6 +723,7 @@ class _CommonConfig(_SubConfig):
|
|||
) -> List[OptionBag]:
|
||||
"""Get the suboption for path and the name of the option
|
||||
:returns: tuple (config, name)"""
|
||||
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments
|
||||
if isinstance(bag, ConfigBag):
|
||||
option_bag = OptionBag(self.get_description(),
|
||||
None,
|
||||
|
@ -763,8 +732,6 @@ class _CommonConfig(_SubConfig):
|
|||
else:
|
||||
option_bag = bag
|
||||
if option_bag.option != option_bag.config_bag.context.get_description():
|
||||
if not path.startswith(option_bag.path + '.'):
|
||||
raise ConfigError('cannot get sub option if not a parent')
|
||||
path = path[len(option_bag.path) + 1:]
|
||||
path = path.split('.')
|
||||
last_idx = len(path) - 1
|
||||
|
@ -789,17 +756,18 @@ class _CommonConfig(_SubConfig):
|
|||
option_index = None
|
||||
if idx == last_idx:
|
||||
if option_index is not None:
|
||||
if option.impl_is_optiondescription() or option.impl_is_symlinkoption() or not option.impl_is_follower():
|
||||
if option.impl_is_optiondescription() or \
|
||||
option.impl_is_symlinkoption() or \
|
||||
not option.impl_is_follower():
|
||||
raise ConfigError('index must be set only with a follower option')
|
||||
if leadership_length is not None:
|
||||
length = leadership_length
|
||||
else:
|
||||
length = self.get_length_leadership(sub_option_bag)
|
||||
if index >= length:
|
||||
raise LeadershipError(_('index "{}" is greater than the leadership length "{}" '
|
||||
'for option "{}"').format(index,
|
||||
length,
|
||||
option.impl_get_display_name()))
|
||||
raise LeadershipError(_(f'index "{index}" is greater than the leadership '
|
||||
f'length "{length}" for option '
|
||||
f'"{option.impl_get_display_name()}"'))
|
||||
option_properties = properties
|
||||
else:
|
||||
option_properties = undefined
|
||||
|
@ -815,17 +783,22 @@ class _CommonConfig(_SubConfig):
|
|||
return options_bag
|
||||
|
||||
def impl_getname(self):
|
||||
return self._impl_name
|
||||
"""get config name
|
||||
"""
|
||||
return self._impl_name # pylint: disable=no-member
|
||||
|
||||
|
||||
# ____________________________________________________________
|
||||
class KernelConfig(_CommonConfig):
|
||||
"main configuration management entry"
|
||||
"""main configuration management entry
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
__slots__ = ('__weakref__',
|
||||
'_impl_name',
|
||||
'_display_name',
|
||||
'_impl_symlink',
|
||||
'_storage')
|
||||
'_storage',
|
||||
)
|
||||
impl_type = 'config'
|
||||
|
||||
def __init__(self,
|
||||
|
@ -843,6 +816,7 @@ class KernelConfig(_CommonConfig):
|
|||
:param context: the current root config
|
||||
:type context: `Config`
|
||||
"""
|
||||
# pylint: disable=too-many-arguments,too-many-arguments
|
||||
self._display_name = display_name
|
||||
self.parents = []
|
||||
self._impl_symlink = []
|
||||
|
@ -870,11 +844,12 @@ class KernelConfig(_CommonConfig):
|
|||
super().__init__(descr,
|
||||
self._impl_context,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
class KernelGroupConfig(_CommonConfig):
|
||||
"""Group a config with same optiondescription tree
|
||||
"""
|
||||
__slots__ = ('__weakref__',
|
||||
'_impl_children',
|
||||
'_impl_name',
|
||||
|
@ -886,18 +861,15 @@ class KernelGroupConfig(_CommonConfig):
|
|||
children,
|
||||
display_name=None,
|
||||
name=None,
|
||||
_descr=None):
|
||||
if not isinstance(children, list):
|
||||
raise ConfigError(_("groupconfig's children must be a list"))
|
||||
_descr=None,
|
||||
):
|
||||
# pylint: disable=super-init-not-called
|
||||
names = []
|
||||
for child in children:
|
||||
if not isinstance(child, _CommonConfig):
|
||||
raise ConfigError(_("groupconfig's children must be Config, MetaConfig or "
|
||||
"GroupConfig"))
|
||||
name_ = child._impl_name
|
||||
names.append(name_)
|
||||
if len(names) != len(set(names)):
|
||||
for idx in range(1, len(names) + 1):
|
||||
while range(1, len(names) + 1):
|
||||
name = names.pop(0)
|
||||
if name in names:
|
||||
raise ConflictError(_('config name must be uniq in '
|
||||
|
@ -905,9 +877,6 @@ class KernelGroupConfig(_CommonConfig):
|
|||
|
||||
self._impl_children = children
|
||||
self.parents = []
|
||||
config_bag = ConfigBag(self,
|
||||
properties=None,
|
||||
permissives=None)
|
||||
self._display_name = display_name
|
||||
if name:
|
||||
self._impl_name = name
|
||||
|
@ -916,6 +885,8 @@ class KernelGroupConfig(_CommonConfig):
|
|||
self._impl_path = None
|
||||
|
||||
def get_children(self):
|
||||
"""get all children
|
||||
"""
|
||||
return self._impl_children
|
||||
|
||||
def reset_cache(self,
|
||||
|
@ -972,6 +943,7 @@ class KernelGroupConfig(_CommonConfig):
|
|||
value,
|
||||
)
|
||||
except PropertiesOptionError as err:
|
||||
# pylint: disable=protected-access
|
||||
ret.append(PropertiesOptionError(err._option_bag,
|
||||
err.proptype,
|
||||
err._settings,
|
||||
|
@ -994,6 +966,7 @@ class KernelGroupConfig(_CommonConfig):
|
|||
):
|
||||
"""Find first not in current KernelGroupConfig, but in each children
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
# if KernelMetaConfig, all children have same OptionDescription in
|
||||
# context so search only one time the option for all children
|
||||
if bypath is undefined and byname is not None and \
|
||||
|
@ -1002,14 +975,13 @@ class KernelGroupConfig(_CommonConfig):
|
|||
None,
|
||||
config_bag,
|
||||
)
|
||||
for bypath, byoption in self.find(root_option_bag,
|
||||
bytype=None,
|
||||
byname=byname,
|
||||
byvalue=undefined,
|
||||
raise_if_not_found=raise_if_not_found,
|
||||
with_option=True,
|
||||
):
|
||||
break
|
||||
next(self.find(root_option_bag,
|
||||
bytype=None,
|
||||
byname=byname,
|
||||
byvalue=undefined,
|
||||
raise_if_not_found=raise_if_not_found,
|
||||
with_option=True,
|
||||
))
|
||||
byname = None
|
||||
|
||||
ret = []
|
||||
|
@ -1035,25 +1007,30 @@ class KernelGroupConfig(_CommonConfig):
|
|||
None,
|
||||
cconfig_bag,
|
||||
)
|
||||
for path in child.find(root_option_bag,
|
||||
None,
|
||||
byname,
|
||||
byvalue,
|
||||
raise_if_not_found=False,
|
||||
only_path=bypath,
|
||||
only_option=byoption,
|
||||
):
|
||||
try:
|
||||
next(child.find(root_option_bag,
|
||||
None,
|
||||
byname,
|
||||
byvalue,
|
||||
raise_if_not_found=False,
|
||||
only_path=bypath,
|
||||
only_option=byoption,
|
||||
))
|
||||
ret.append(child)
|
||||
break
|
||||
except StopIteration:
|
||||
pass
|
||||
if not _sub:
|
||||
self._find_return_results(ret != [],
|
||||
raise_if_not_found)
|
||||
self._find_return_results(ret != [], # pylint: disable=use-implicit-booleaness-not-comparison
|
||||
raise_if_not_found,
|
||||
)
|
||||
return ret
|
||||
|
||||
def reset(self,
|
||||
path: str,
|
||||
config_bag: ConfigBag,
|
||||
):
|
||||
) -> None:
|
||||
"""reset value for specified path
|
||||
"""
|
||||
for child in self._impl_children:
|
||||
settings = child.get_settings()
|
||||
cconfig_bag = config_bag.copy()
|
||||
|
@ -1072,7 +1049,10 @@ class KernelGroupConfig(_CommonConfig):
|
|||
child.get_values().reset(option_bag)
|
||||
|
||||
def getconfig(self,
|
||||
name):
|
||||
name: str,
|
||||
) -> KernelConfig:
|
||||
"""get a child from a config name
|
||||
"""
|
||||
for child in self._impl_children:
|
||||
if name == child.impl_getname():
|
||||
return child
|
||||
|
@ -1080,9 +1060,12 @@ class KernelGroupConfig(_CommonConfig):
|
|||
|
||||
|
||||
class KernelMixConfig(KernelGroupConfig):
|
||||
__slots__ = ('_impl_name',
|
||||
'_impl_symlink',
|
||||
'_storage')
|
||||
"""Kernel mixconfig: this config can have differents optiondescription tree
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
__slots__ = ('_impl_symlink',
|
||||
'_storage',
|
||||
)
|
||||
impl_type = 'mix'
|
||||
|
||||
def __init__(self,
|
||||
|
@ -1124,6 +1107,7 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
"""only_config: could be set if you want modify value in all Config included in
|
||||
this KernelMetaConfig
|
||||
"""
|
||||
# pylint: disable=too-many-branches,too-many-nested-blocks,too-many-locals,too-many-arguments
|
||||
ret = []
|
||||
if only_config:
|
||||
if force_default or force_default_if_same or force_dont_change_value:
|
||||
|
@ -1147,10 +1131,11 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
obj = self
|
||||
else:
|
||||
obj = child
|
||||
validate_properties = not force_default and not force_default_if_same
|
||||
moption_bag = obj.get_sub_option_bag(cconfig_bag,
|
||||
option_bag.path,
|
||||
option_bag.index,
|
||||
not force_default and not force_default_if_same,
|
||||
validate_properties,
|
||||
)[-1]
|
||||
if force_default_if_same:
|
||||
if not child.get_values().hasvalue(option_bag.path):
|
||||
|
@ -1167,6 +1152,7 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
child_value,
|
||||
)
|
||||
except PropertiesOptionError as err:
|
||||
# pylint: disable=protected-access
|
||||
ret.append(PropertiesOptionError(err._option_bag,
|
||||
err.proptype,
|
||||
err._settings,
|
||||
|
@ -1198,10 +1184,13 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
return ret
|
||||
|
||||
def reset(self,
|
||||
path,
|
||||
only_children,
|
||||
config_bag,
|
||||
):
|
||||
path: str,
|
||||
only_children: bool,
|
||||
config_bag: ConfigBag,
|
||||
) -> None:
|
||||
"""reset value for a specified path
|
||||
"""
|
||||
# pylint: disable=arguments-differ
|
||||
rconfig_bag = config_bag.copy()
|
||||
rconfig_bag.remove_validation()
|
||||
if self.impl_type == 'meta':
|
||||
|
@ -1338,8 +1327,8 @@ class KernelMetaConfig(KernelMixConfig):
|
|||
new_children = []
|
||||
for child_name in children:
|
||||
assert isinstance(child_name, str), _('MetaConfig with optiondescription'
|
||||
' must have string has child, '
|
||||
'not {}').format(child_name)
|
||||
' must have string has child, '
|
||||
'not {}').format(child_name)
|
||||
new_children.append(KernelConfig(optiondescription, name=child_name))
|
||||
children = new_children
|
||||
descr = optiondescription
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#
|
||||
# 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/>.
|
||||
"""some functions to validates or calculates value
|
||||
"""
|
||||
from typing import Any, List, Optional
|
||||
from operator import add, mul, sub, truediv
|
||||
from ipaddress import ip_address, ip_interface, ip_network
|
||||
|
@ -20,145 +22,130 @@ from .setting import undefined
|
|||
from .error import display_list
|
||||
|
||||
|
||||
def valid_network_netmask(network: str,
|
||||
netmask: str):
|
||||
"""FIXME
|
||||
FUNCTION_WAITING_FOR_DICT = []
|
||||
|
||||
|
||||
def function_waiting_for_dict(function):
|
||||
"""functions (calculation or validation) receive by default only the value of other options
|
||||
all functions declared with this function recieve a dict with option informations
|
||||
(value, name, ...)
|
||||
"""
|
||||
if isinstance(network, dict):
|
||||
network_value = network['value']
|
||||
network_display_name = '({})'.format(network['name'])
|
||||
else:
|
||||
network_value = network
|
||||
network_display_name = ''
|
||||
if None in [network_value, netmask]:
|
||||
name = function.__name__
|
||||
if name not in FUNCTION_WAITING_FOR_DICT:
|
||||
FUNCTION_WAITING_FOR_DICT.append(name)
|
||||
return function
|
||||
|
||||
|
||||
@function_waiting_for_dict
|
||||
def valid_network_netmask(network: dict,
|
||||
netmask: dict,
|
||||
):
|
||||
"""
|
||||
validates if network and netmask are coherent
|
||||
this validator must be set to netmask option
|
||||
"""
|
||||
if None in [network['value'], netmask['value']]:
|
||||
return
|
||||
try:
|
||||
ip_network('{0}/{1}'.format(network_value, netmask))
|
||||
except ValueError:
|
||||
raise ValueError(_('network "{0}" {1}does not match with this netmask').format(network_value,
|
||||
network_display_name))
|
||||
ip_network(f'{network["value"]}/{netmask["value"]}')
|
||||
except ValueError as err:
|
||||
raise ValueError(_(f'network "{network["value"]}" ({network["name"]}) does not match '
|
||||
'with this netmask')) from err
|
||||
|
||||
def valid_ip_netmask(ip: str,
|
||||
netmask: str):
|
||||
if isinstance(ip, dict):
|
||||
ip_value = ip['value']
|
||||
ip_display_name = '({})'.format(ip['name'])
|
||||
else:
|
||||
ip_value = ip
|
||||
ip_display_name = ''
|
||||
if None in [ip_value, netmask]:
|
||||
|
||||
@function_waiting_for_dict
|
||||
def valid_ip_netmask(ip: dict, # pylint: disable=invalid-name
|
||||
netmask: dict,
|
||||
):
|
||||
"""validates if ip and netmask are coherent
|
||||
this validator must be set to netmask option
|
||||
"""
|
||||
if None in [ip['value'], netmask['value']]:
|
||||
return
|
||||
ip_netmask = ip_interface('{0}/{1}'.format(ip_value, netmask))
|
||||
ip_netmask = ip_interface(f'{ip["value"]}/{netmask["value"]}')
|
||||
if ip_netmask.ip == ip_netmask.network.network_address:
|
||||
raise ValueError(_('IP \"{0}\" {1}with this netmask is in fact a network address').format(ip_value, ip_display_name))
|
||||
elif ip_netmask.ip == ip_netmask.network.broadcast_address:
|
||||
raise ValueError(_('IP \"{0}\" {1}with this netmask is in fact a broacast address').format(ip_value, ip_display_name))
|
||||
msg = _(f'IP "{ip["value"]}" ({ip["name"]}) with this netmask is '
|
||||
'in fact a network address')
|
||||
raise ValueError(msg)
|
||||
if ip_netmask.ip == ip_netmask.network.broadcast_address:
|
||||
msg = _(f'IP "{ip["value"]}" ({ip["name"]}) with this netmask is '
|
||||
'in fact a broacast address')
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
# FIXME CIDR ?
|
||||
def valid_broadcast(network: 'NetworkOption',
|
||||
netmask: 'NetmaskOption',
|
||||
broadcast: 'BroadcastOption'):
|
||||
if isinstance(network, dict):
|
||||
network_value = network['value']
|
||||
network_display_name = ' ({})'.format(network['name'])
|
||||
else:
|
||||
network_value = network
|
||||
network_display_name = ''
|
||||
if isinstance(netmask, dict):
|
||||
netmask_value = netmask['value']
|
||||
netmask_display_name = ' ({})'.format(netmask['name'])
|
||||
else:
|
||||
netmask_value = netmask
|
||||
netmask_display_name = ''
|
||||
if ip_network('{0}/{1}'.format(network, netmask)).broadcast_address != ip_address(broadcast):
|
||||
raise ValueError(_('broadcast invalid with network {0}{1} and netmask {2}{3}'
|
||||
'').format(network_value,
|
||||
network_display_name,
|
||||
netmask_value,
|
||||
netmask_display_name))
|
||||
|
||||
|
||||
def valid_in_network(ip,
|
||||
network,
|
||||
netmask=None):
|
||||
if isinstance(network, dict):
|
||||
network_value = network['value']
|
||||
network_display_name = ' ({})'.format(network['name'])
|
||||
else:
|
||||
network_value = network
|
||||
network_display_name = ''
|
||||
if isinstance(netmask, dict):
|
||||
netmask_value = netmask['value']
|
||||
netmask_display_name = ' ({})'.format(netmask['name'])
|
||||
else:
|
||||
netmask_value = netmask
|
||||
netmask_display_name = ''
|
||||
if network_value is None:
|
||||
@function_waiting_for_dict
|
||||
def valid_broadcast(network: dict,
|
||||
netmask: dict,
|
||||
broadcast: dict,
|
||||
):
|
||||
"""validates if the broadcast is coherent with network and netmask
|
||||
"""
|
||||
if None in [network['value'], netmask['value'], broadcast['value']]:
|
||||
return
|
||||
if '/' in network_value:
|
||||
network_obj = ip_network('{0}'.format(network_value))
|
||||
if ip_network(f'{network["value"]}/{netmask["value"]}').broadcast_address != \
|
||||
ip_address(broadcast['value']):
|
||||
msg = _(f'broadcast invalid with network {network["value"]} ({network["name"]}) '
|
||||
f'and netmask {netmask["value"]} ({netmask["name"]})')
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@function_waiting_for_dict
|
||||
def valid_in_network(ip: dict, # pylint: disable=invalid-name
|
||||
network: dict,
|
||||
netmask=Optional[dict],
|
||||
):
|
||||
"""validates if an IP is in a network
|
||||
this validator must be set to ip option
|
||||
"""
|
||||
if None in [ip['value'], network['value']]:
|
||||
return
|
||||
if '/' in network['value']:
|
||||
# it's a CIDR network
|
||||
network_value = network['value']
|
||||
else:
|
||||
if netmask_value is None:
|
||||
if netmask is None or netmask['value'] is None:
|
||||
return
|
||||
network_obj = ip_network('{0}/{1}'.format(network_value,
|
||||
netmask_value))
|
||||
if ip_interface(ip) not in network_obj:
|
||||
network_value = f'{network["value"]}/{netmask["value"]}'
|
||||
network_obj = ip_network(network_value)
|
||||
ip_netmask = ip_interface(f'{ip["value"]}/{network_obj.netmask}')
|
||||
if ip_netmask not in network_obj:
|
||||
if netmask is None:
|
||||
msg = _('this IP is not in network {0}{1}').format(network_value,
|
||||
network_display_name)
|
||||
msg = _('this IP is not in network {network["value"]} ({network["name"]})')
|
||||
else:
|
||||
msg = _('this IP is not in network {0}{1} with netmask {2}{3}').format(network_value,
|
||||
network_display_name,
|
||||
netmask_value,
|
||||
netmask_display_name)
|
||||
msg = _('this IP is not in network {network["value"]} ({network["name"]}) '
|
||||
'with netmask {netmask["value"]} ({netmask["name"]})')
|
||||
raise ValueError(msg)
|
||||
|
||||
# test if ip is not network/broadcast IP
|
||||
ip_netmask = ip_interface('{0}/{1}'.format(ip, network_obj.netmask))
|
||||
if ip_netmask.ip == ip_netmask.network.network_address:
|
||||
if netmask is None:
|
||||
msg = _('this IP with the network {0}{1} is in fact a network address').format(network_value,
|
||||
network_display_name)
|
||||
else:
|
||||
msg = _('this IP with the netmask {0}{1} is in fact a network address').format(netmask_value,
|
||||
netmask_display_name)
|
||||
msg = _(f'this IP with the network {network["value"]} ({network["value"]} '
|
||||
'is in fact a network address')
|
||||
raise ValueError(msg)
|
||||
elif ip_netmask.ip == ip_netmask.network.broadcast_address:
|
||||
if netmask is None:
|
||||
msg = _('this IP with the network {0}{1} is in fact a broadcast address').format(network_value,
|
||||
network_display_name)
|
||||
else:
|
||||
msg = _('this IP with the netmask {0}{1} is in fact a broadcast address').format(netmask_value,
|
||||
netmask_display_name)
|
||||
if ip_netmask.ip == ip_netmask.network.broadcast_address:
|
||||
msg = _(f'this IP with the network {network["value"]} ({network["value"]} '
|
||||
'is in fact a broadcast address')
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@function_waiting_for_dict
|
||||
def valid_not_equal(*values):
|
||||
"""valid that two options have not same value
|
||||
"""
|
||||
equal = set()
|
||||
for idx, val in enumerate(values[1:]):
|
||||
if isinstance(val, dict):
|
||||
if 'propertyerror' in val:
|
||||
continue
|
||||
tval = val['value']
|
||||
else:
|
||||
tval = val
|
||||
if values[0] == tval is not None:
|
||||
if isinstance(val, dict):
|
||||
if equal is True:
|
||||
equal = set()
|
||||
equal.add(val['name'])
|
||||
elif not equal:
|
||||
equal = True
|
||||
if equal:
|
||||
if equal is not True:
|
||||
msg = _('value is identical to {}').format(display_list(list(equal), add_quote=True))
|
||||
else:
|
||||
msg = _('value is identical')
|
||||
raise ValueError(msg)
|
||||
for val in values[1:]:
|
||||
if 'propertyerror' in val:
|
||||
continue
|
||||
if values[0]['value'] == val['value'] is not None:
|
||||
equal.add(val['name'])
|
||||
if not equal:
|
||||
return
|
||||
msg = _(f'value is identical to {display_list(list(equal), add_quote=True)}')
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
class CalcValue:
|
||||
"""class to calc_value with different functions
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
def __call__(self,
|
||||
*args: List[Any],
|
||||
multi: bool=False,
|
||||
|
@ -175,6 +162,7 @@ class CalcValue:
|
|||
operator: Optional[str]=None,
|
||||
index: Optional[int]=None,
|
||||
**kwargs) -> Any:
|
||||
# pylint: disable=too-many-statements,too-many-branches,too-many-nested-blocks,too-many-locals
|
||||
"""calculate value
|
||||
:param args: list of value
|
||||
:param multi: value returns must be a list of value
|
||||
|
@ -183,9 +171,11 @@ class CalcValue:
|
|||
:param condition: test if condition is equal to expected value
|
||||
if there is more than one condition, set condition_0, condition_1, ...
|
||||
:param expected: value expected for all conditions
|
||||
if expected value is different between condition, set expected_0, expected_1, ...
|
||||
if expected value is different between condition, set expected_0,
|
||||
expected_1, ...
|
||||
:param no_condition_is_invalid: if no condition and not condition_0, condition_1, ... (for
|
||||
example if option is disabled) consider that condition not matching
|
||||
example if option is disabled) consider that condition not
|
||||
matching
|
||||
:param condition_operator: OR or AND operator for condition
|
||||
:param allow_none: if False, do not return list in None is present in list
|
||||
:param remove_duplicate_value: if True, remote duplicated value
|
||||
|
@ -196,28 +186,36 @@ class CalcValue:
|
|||
|
||||
examples:
|
||||
* you want to copy value from an option to an other option:
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, \
|
||||
... Params, ParamOption
|
||||
>>> val1 = StrOption('val1', '', 'val1')
|
||||
>>> val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1)))
|
||||
>>> val2 = StrOption('val2', '', callback=calc_value,
|
||||
... callback_params=Params(ParamOption(val1)))
|
||||
>>> od = OptionDescription('root', '', [val1, val2])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.value.dict()
|
||||
{'val1': 'val1', 'val2': 'val1'}
|
||||
|
||||
* you want to copy values from two options in one multi option
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, \
|
||||
... ParamOption, ParamValue
|
||||
>>> val1 = StrOption('val1', "", 'val1')
|
||||
>>> val2 = StrOption('val2', "", 'val2')
|
||||
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True)))
|
||||
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value,
|
||||
... callback_params=Params((ParamOption(val1), ParamOption(val2)),
|
||||
... multi=ParamValue(True)))
|
||||
>>> od = OptionDescription('root', '', [val1, val2, val3])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.value.dict()
|
||||
{'val1': 'val1', 'val2': 'val2', 'val3': ['val1', 'val2']}
|
||||
|
||||
* you want to copy a value from an option if it not disabled, otherwise set 'default_value'
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, \
|
||||
... ParamOption, ParamValue
|
||||
>>> val1 = StrOption('val1', '', 'val1')
|
||||
>>> val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1, True), default=ParamValue('default_value')))
|
||||
>>> val2 = StrOption('val2', '', callback=calc_value,
|
||||
... callback_params=Params(ParamOption(val1, True),
|
||||
... default=ParamValue('default_value')))
|
||||
>>> od = OptionDescription('root', '', [val1, val2])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.property.read_write()
|
||||
|
@ -228,13 +226,15 @@ class CalcValue:
|
|||
{'val2': 'default_value'}
|
||||
|
||||
* you want to copy value from an option if an other is True, otherwise set 'default_value'
|
||||
>>> from tiramisu import calc_value, BoolOption, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, BoolOption, StrOption, OptionDescription, Config, \
|
||||
... Params, ParamOption, ParamValue
|
||||
>>> boolean = BoolOption('boolean', '', True)
|
||||
>>> val1 = StrOption('val1', '', 'val1')
|
||||
>>> val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1, True),
|
||||
... default=ParamValue('default_value'),
|
||||
... condition=ParamOption(boolean),
|
||||
... expected=ParamValue(True)))
|
||||
>>> val2 = StrOption('val2', '', callback=calc_value,
|
||||
... callback_params=Params(ParamOption(val1, True),
|
||||
... default=ParamValue('default_value'),
|
||||
... condition=ParamOption(boolean),
|
||||
... expected=ParamValue(True)))
|
||||
>>> od = OptionDescription('root', '', [boolean, val1, val2])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.property.read_write()
|
||||
|
@ -245,41 +245,55 @@ class CalcValue:
|
|||
{'boolean': False, 'val1': 'val1', 'val2': 'default_value'}
|
||||
|
||||
* you want to copy option even if None is present
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, \
|
||||
... ParamOption, ParamValue
|
||||
>>> val1 = StrOption('val1', "", 'val1')
|
||||
>>> val2 = StrOption('val2', "")
|
||||
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True), allow_none=ParamValue(True)))
|
||||
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value,
|
||||
... callback_params=Params((ParamOption(val1), ParamOption(val2)),
|
||||
... multi=ParamValue(True), allow_none=ParamValue(True)))
|
||||
>>> od = OptionDescription('root', '', [val1, val2, val3])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.value.dict()
|
||||
{'val1': 'val1', 'val2': None, 'val3': ['val1', None]}
|
||||
|
||||
* you want uniq value
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, \
|
||||
... ParamOption, ParamValue
|
||||
>>> val1 = StrOption('val1', "", 'val1')
|
||||
>>> val2 = StrOption('val2', "", 'val1')
|
||||
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True), remove_duplicate_value=ParamValue(True)))
|
||||
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value,
|
||||
... callback_params=Params((ParamOption(val1), ParamOption(val2)),
|
||||
... multi=ParamValue(True), remove_duplicate_value=ParamValue(True)))
|
||||
>>> od = OptionDescription('root', '', [val1, val2, val3])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.value.dict()
|
||||
{'val1': 'val1', 'val2': 'val1', 'val3': ['val1']}
|
||||
|
||||
* you want to join two values with '.'
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, \
|
||||
... ParamOption, ParamValue
|
||||
>>> val1 = StrOption('val1', "", 'val1')
|
||||
>>> val2 = StrOption('val2', "", 'val2')
|
||||
>>> val3 = StrOption('val3', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), join=ParamValue('.')))
|
||||
>>> val3 = StrOption('val3', "", callback=calc_value,
|
||||
... callback_params=Params((ParamOption(val1),
|
||||
... ParamOption(val2)), join=ParamValue('.')))
|
||||
>>> od = OptionDescription('root', '', [val1, val2, val3])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.value.dict()
|
||||
{'val1': 'val1', 'val2': 'val2', 'val3': 'val1.val2'}
|
||||
|
||||
* you want join three values, only if almost three values are set
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, \
|
||||
... ParamOption, ParamValue
|
||||
>>> val1 = StrOption('val1', "", 'val1')
|
||||
>>> val2 = StrOption('val2', "", 'val2')
|
||||
>>> val3 = StrOption('val3', "", 'val3')
|
||||
>>> val4 = StrOption('val4', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2), ParamOption(val3, True)), join=ParamValue('.'), min_args_len=ParamValue(3)))
|
||||
>>> val4 = StrOption('val4', "", callback=calc_value,
|
||||
... callback_params=Params((ParamOption(val1),
|
||||
... ParamOption(val2),
|
||||
... ParamOption(val3, True)),
|
||||
... join=ParamValue('.'), min_args_len=ParamValue(3)))
|
||||
>>> od = OptionDescription('root', '', [val1, val2, val3, val4])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.property.read_write()
|
||||
|
@ -290,25 +304,31 @@ class CalcValue:
|
|||
{'val1': 'val1', 'val2': 'val2', 'val4': ''}
|
||||
|
||||
* you want to add all values
|
||||
>>> from tiramisu import calc_value, IntOption, OptionDescription, Config, Params, ParamOption, ParamValue
|
||||
>>> from tiramisu import calc_value, IntOption, OptionDescription, Config, Params, \
|
||||
... ParamOption, ParamValue
|
||||
>>> val1 = IntOption('val1', "", 1)
|
||||
>>> val2 = IntOption('val2', "", 2)
|
||||
>>> val3 = IntOption('val3', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), operator=ParamValue('add')))
|
||||
>>> val3 = IntOption('val3', "", callback=calc_value,
|
||||
... callback_params=Params((ParamOption(val1),
|
||||
ParamOption(val2)),
|
||||
... operator=ParamValue('add')))
|
||||
>>> od = OptionDescription('root', '', [val1, val2, val3])
|
||||
>>> cfg = Config(od)
|
||||
>>> cfg.value.dict()
|
||||
{'val1': 1, 'val2': 2, 'val3': 3}
|
||||
|
||||
"""
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.args = args
|
||||
self.condition = condition
|
||||
self.expected = expected
|
||||
self.condition_operator = condition_operator
|
||||
self.reverse_condition = reverse_condition
|
||||
self.kwargs = kwargs
|
||||
self.no_condition_is_invalid = no_condition_is_invalid
|
||||
self.no_condition_is_invalid = no_condition_is_invalid # pylint: disable=attribute-defined-outside-init
|
||||
value = self.get_value(default,
|
||||
min_args_len)
|
||||
min_args_len,
|
||||
)
|
||||
if not multi:
|
||||
if join is not None:
|
||||
if None not in value:
|
||||
|
@ -317,12 +337,13 @@ class CalcValue:
|
|||
value = None
|
||||
elif value and operator:
|
||||
new_value = value[0]
|
||||
op = {'mul': mul,
|
||||
'add': add,
|
||||
'div': truediv,
|
||||
'sub': sub}[operator]
|
||||
oper = {'mul': mul,
|
||||
'add': add,
|
||||
'div': truediv,
|
||||
'sub': sub,
|
||||
}[operator]
|
||||
for val in value[1:]:
|
||||
new_value = op(new_value, val)
|
||||
new_value = oper(new_value, val)
|
||||
value = new_value
|
||||
elif value == []:
|
||||
value = None
|
||||
|
@ -344,7 +365,9 @@ class CalcValue:
|
|||
break
|
||||
lval = len(val)
|
||||
if length_val is not None and length_val != lval:
|
||||
raise ValueError(_(f'unexpected value in calc_value with join attribute "{val}" with invalid length "{length_val}"'))
|
||||
msg = _('unexpected value in calc_value with join attribute '
|
||||
f'"{val}" with invalid length "{length_val}"')
|
||||
raise ValueError(msg)
|
||||
length_val = lval
|
||||
new_value = []
|
||||
if length_val is not None:
|
||||
|
@ -374,6 +397,9 @@ class CalcValue:
|
|||
pattern: str,
|
||||
to_dict: bool=False,
|
||||
empty_test=undefined) -> Any:
|
||||
"""get value from kwargs
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
# if value attribute exist return it's value
|
||||
# otherwise pattern_0, pattern_1, ...
|
||||
# otherwise undefined
|
||||
|
@ -385,10 +411,9 @@ class CalcValue:
|
|||
else:
|
||||
kwargs_matches = {}
|
||||
len_pattern = len(pattern)
|
||||
for key in self.kwargs.keys():
|
||||
for key, pattern_value in self.kwargs.items():
|
||||
if key.startswith(pattern):
|
||||
index = int(key[len_pattern:])
|
||||
pattern_value = self.kwargs[key]
|
||||
if isinstance(pattern_value, dict):
|
||||
pattern_value = pattern_value['value']
|
||||
kwargs_matches[index] = pattern_value
|
||||
|
@ -408,21 +433,28 @@ class CalcValue:
|
|||
return returns
|
||||
|
||||
def is_condition_matches(self,
|
||||
condition_value):
|
||||
condition_value,
|
||||
):
|
||||
"""verify the condition
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
calculated_conditions = self.value_from_kwargs(condition_value,
|
||||
'condition_',
|
||||
to_dict='all')
|
||||
to_dict='all',
|
||||
)
|
||||
if calculated_conditions is undefined:
|
||||
is_matches = not self.no_condition_is_invalid
|
||||
else:
|
||||
is_matches = None
|
||||
calculated_expected = self.value_from_kwargs(self.expected,
|
||||
'expected_',
|
||||
to_dict=True)
|
||||
to_dict=True,
|
||||
)
|
||||
calculated_reverse = self.value_from_kwargs(self.reverse_condition,
|
||||
'reverse_condition_',
|
||||
to_dict=True,
|
||||
empty_test=False)
|
||||
empty_test=False,
|
||||
)
|
||||
for idx, calculated_condition in calculated_conditions.items():
|
||||
if isinstance(calculated_expected, dict):
|
||||
if idx is not None:
|
||||
|
@ -453,14 +485,20 @@ class CalcValue:
|
|||
if is_matches:
|
||||
break
|
||||
else:
|
||||
raise ValueError(_('unexpected {} condition_operator in calc_value').format(self.condition_operator))
|
||||
msg = _(f'unexpected {self.condition_operator} condition_operator '
|
||||
'in calc_value')
|
||||
raise ValueError(msg)
|
||||
is_matches = is_matches and not self.reverse_condition \
|
||||
or not is_matches and self.reverse_condition
|
||||
return is_matches
|
||||
|
||||
def get_value(self,
|
||||
default,
|
||||
min_args_len):
|
||||
min_args_len,
|
||||
):
|
||||
"""get the value from arguments
|
||||
"""
|
||||
# retrieve the condition
|
||||
if isinstance(self.condition, dict):
|
||||
if 'value' in self.condition:
|
||||
condition_value = self.condition['value']
|
||||
|
@ -468,18 +506,19 @@ class CalcValue:
|
|||
condition_value = undefined
|
||||
else:
|
||||
condition_value = self.condition
|
||||
condition_matches = self.is_condition_matches(condition_value)
|
||||
if not condition_matches:
|
||||
# force to default
|
||||
# value is empty if condition doesn't match
|
||||
# otherwise value is arg
|
||||
if not self.is_condition_matches(condition_value):
|
||||
value = []
|
||||
else:
|
||||
value = self.get_args()
|
||||
if min_args_len and not len(value) >= min_args_len:
|
||||
value = []
|
||||
if value == []:
|
||||
if not value:
|
||||
# default value
|
||||
new_default = self.value_from_kwargs(default,
|
||||
'default_')
|
||||
'default_',
|
||||
)
|
||||
if new_default is not undefined:
|
||||
if not isinstance(new_default, list):
|
||||
value = [new_default]
|
||||
|
@ -488,29 +527,34 @@ class CalcValue:
|
|||
return value
|
||||
|
||||
def get_args(self):
|
||||
"""get all arguments
|
||||
"""
|
||||
return list(self.args)
|
||||
|
||||
|
||||
class CalcValuePropertyHelp(CalcValue):
|
||||
"""special class to display property error
|
||||
"""
|
||||
def get_name(self):
|
||||
"""get the condition name
|
||||
"""
|
||||
return self.condition['name']
|
||||
|
||||
def get_indexed_name(self, index):
|
||||
return self.kwargs.get(f'condition_{index}')['name']
|
||||
def get_indexed_name(self, index: int) -> str:
|
||||
"""get name for a specified index
|
||||
"""
|
||||
condition_index = self.kwargs.get(f'condition_{index}')
|
||||
if condition_index is not None and not isinstance(condition_index, dict):
|
||||
raise ValueError(_(f'unexpected condition_{index} must have "todict" argument'))
|
||||
return condition_index['name']
|
||||
|
||||
def has_condition_kwargs(self):
|
||||
for condition in self.kwargs:
|
||||
if condition.startswith('condition_'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def build_arg(self, name, value):
|
||||
#if isinstance(option, tuple):
|
||||
# if not reverse:
|
||||
# msg = _('the calculated value is {0}').format(display_value)
|
||||
# else:
|
||||
# msg = _('the calculated value is not {0}').format(display_value)
|
||||
#else:
|
||||
def build_property_message(self,
|
||||
name: str,
|
||||
value: Any,
|
||||
) -> str:
|
||||
"""prepare message to display error message if needed
|
||||
"""
|
||||
if not self.reverse_condition:
|
||||
msg = _('the value of "{0}" is {1}').format(name, value)
|
||||
else:
|
||||
|
@ -519,40 +563,35 @@ class CalcValuePropertyHelp(CalcValue):
|
|||
|
||||
def get_args(self):
|
||||
args = super().get_args()
|
||||
if args:
|
||||
if len(self.args) != 1:
|
||||
raise ValueError(_('only one property is allowed for a calculation'))
|
||||
action = args[0]
|
||||
calculated_expected = self.value_from_kwargs(self.expected,
|
||||
'expected_',
|
||||
to_dict=True)
|
||||
if self.condition is not undefined:
|
||||
if 'propertyerror' in self.condition:
|
||||
msg = self.condition['propertyerror']
|
||||
else:
|
||||
name = self.get_name()
|
||||
if isinstance(calculated_expected, dict):
|
||||
calc_values = calculated_expected.values()
|
||||
else:
|
||||
calc_values = [calculated_expected]
|
||||
display_value = display_list([str(val) for val in calc_values],
|
||||
'or',
|
||||
add_quote=True)
|
||||
msg = self.build_arg(name, display_value)
|
||||
elif self.has_condition_kwargs():
|
||||
msgs = []
|
||||
for key, value in calculated_expected.items():
|
||||
name = self.get_indexed_name(key)
|
||||
msgs.append(self.build_arg(name, f'"{value}"'))
|
||||
msg = display_list(msgs, self.condition_operator.lower())
|
||||
action = args[0]
|
||||
calculated_expected = self.value_from_kwargs(self.expected,
|
||||
'expected_',
|
||||
to_dict=True)
|
||||
if self.condition is not undefined:
|
||||
if 'propertyerror' in self.condition:
|
||||
msg = self.condition['propertyerror']
|
||||
else:
|
||||
return [(action, f'"{action}"')]
|
||||
return [(action, f'"{action}" ({msg})')]
|
||||
return
|
||||
## calc_properties.setdefault(action, []).append(msg)
|
||||
name = self.get_name()
|
||||
if isinstance(calculated_expected, dict):
|
||||
calc_values = calculated_expected.values()
|
||||
else:
|
||||
calc_values = [calculated_expected]
|
||||
display_value = display_list([str(val) for val in calc_values],
|
||||
'or',
|
||||
add_quote=True)
|
||||
msg = self.build_property_message(name, display_value)
|
||||
else:
|
||||
msgs = []
|
||||
for key, value in calculated_expected.items():
|
||||
name = self.get_indexed_name(key)
|
||||
msgs.append(self.build_property_message(name, f'"{value}"'))
|
||||
msg = display_list(msgs, self.condition_operator.lower())
|
||||
return [(action, f'"{action}" ({msg})')]
|
||||
|
||||
|
||||
calc_value = CalcValue()
|
||||
calc_value.__name__ = 'calc_value'
|
||||
calc_value.__name__ = 'calc_value' # pylint: disable=attribute-defined-outside-init
|
||||
# function_waiting_for_dict(calc_value)
|
||||
calc_value_property_help = CalcValuePropertyHelp()
|
||||
calc_value_property_help.__name__ = 'calc_value_property_help'
|
||||
calc_value_property_help.__name__ = 'calc_value_property_help' # pylint: disable=attribute-defined-outside-init
|
||||
function_waiting_for_dict(calc_value_property_help)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ____________________________________________________________
|
||||
from logging import getLogger, DEBUG, basicConfig, StreamHandler, Formatter
|
||||
from logging import getLogger, DEBUG, StreamHandler, Formatter
|
||||
import os
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014-2023 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
|
||||
# ____________________________________________________________
|
||||
"""all official option
|
||||
"""
|
||||
from .optiondescription import OptionDescription
|
||||
from .dynoptiondescription import DynOptionDescription
|
||||
from .syndynoptiondescription import SynDynOptionDescription, SynDynLeadership
|
||||
|
|
|
@ -18,18 +18,16 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
from types import FunctionType
|
||||
from typing import FrozenSet, Callable, Tuple, Set, Optional, Union, Any, List
|
||||
"""base option
|
||||
"""
|
||||
from typing import FrozenSet, Set, Any, List
|
||||
import weakref
|
||||
from inspect import signature
|
||||
from itertools import chain
|
||||
|
||||
|
||||
from ..i18n import _
|
||||
from ..setting import undefined, Settings
|
||||
from ..value import Values
|
||||
from ..error import ConfigError, display_list
|
||||
from ..autolib import Calculation, Params, ParamOption
|
||||
from ..setting import undefined
|
||||
from ..autolib import Calculation, ParamOption
|
||||
|
||||
STATIC_TUPLE = frozenset()
|
||||
|
||||
|
@ -38,6 +36,8 @@ submulti = 2
|
|||
|
||||
|
||||
def valid_name(name):
|
||||
"""valid option name
|
||||
"""
|
||||
if not isinstance(name, str):
|
||||
return False
|
||||
# if '.' in name:
|
||||
|
@ -73,7 +73,8 @@ class Base:
|
|||
elif isinstance(properties, tuple):
|
||||
properties = frozenset(properties)
|
||||
if is_multi:
|
||||
# if option is a multi, it cannot be 'empty' (None not allowed in the list) and cannot have multiple time the same value
|
||||
# if option is a multi, it cannot be 'empty' (None not allowed in the list)
|
||||
# and cannot have multiple time the same value
|
||||
# 'empty' and 'unique' are removed for follower's option
|
||||
if 'notunique' not in properties:
|
||||
properties = properties | {'unique'}
|
||||
|
@ -85,7 +86,8 @@ class Base:
|
|||
for prop in properties:
|
||||
if not isinstance(prop, str):
|
||||
if not isinstance(prop, Calculation):
|
||||
raise ValueError(_('invalid property type {0} for {1}, must be a string or a Calculation').format(type(prop), name))
|
||||
raise ValueError(_('invalid property type {0} for {1}, must be a string or a '
|
||||
'Calculation').format(type(prop), name))
|
||||
for param in chain(prop.params.args, prop.params.kwargs.values()):
|
||||
if isinstance(param, ParamOption):
|
||||
param.option._add_dependency(self)
|
||||
|
@ -96,7 +98,10 @@ class Base:
|
|||
_setattr(self, '_properties', properties)
|
||||
|
||||
def impl_has_dependency(self,
|
||||
self_is_dep: bool=True) -> bool:
|
||||
self_is_dep: bool=True,
|
||||
) -> bool:
|
||||
"""this has dependency
|
||||
"""
|
||||
if self_is_dep is True:
|
||||
return getattr(self, '_has_dependency', False)
|
||||
return hasattr(self, '_dependencies')
|
||||
|
@ -107,7 +112,7 @@ class Base:
|
|||
ret = set(getattr(self, '_dependencies', STATIC_TUPLE))
|
||||
if context_od and hasattr(context_od, '_dependencies'):
|
||||
# add options that have context is set in calculation
|
||||
return set(context_od._dependencies) | ret
|
||||
return set(context_od._dependencies) | ret # pylint: disable=protected-access
|
||||
return ret
|
||||
|
||||
def _get_suffixes_dependencies(self) -> Set[str]:
|
||||
|
@ -119,26 +124,32 @@ class Base:
|
|||
) -> None:
|
||||
woption = weakref.ref(option)
|
||||
options = self._get_dependencies(None)
|
||||
options.add(weakref.ref(option))
|
||||
self._dependencies = tuple(options)
|
||||
options.add(woption)
|
||||
self._dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init
|
||||
if is_suffix:
|
||||
options = list(self._get_suffixes_dependencies())
|
||||
options.append(weakref.ref(option))
|
||||
self._suffixes_dependencies = tuple(options)
|
||||
options.append(woption)
|
||||
self._suffixes_dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def impl_is_optiondescription(self) -> bool:
|
||||
"""option is an option description
|
||||
"""
|
||||
return False
|
||||
|
||||
def impl_is_dynoptiondescription(self) -> bool:
|
||||
"""option is not a dyn option description
|
||||
"""
|
||||
return False
|
||||
|
||||
def impl_getname(self) -> str:
|
||||
return self._name
|
||||
"""get name
|
||||
"""
|
||||
return self._name # pylint: disable=no-member
|
||||
|
||||
def _set_readonly(self) -> None:
|
||||
if isinstance(self._informations, dict):
|
||||
if isinstance(self._informations, dict): # pylint: disable=no-member
|
||||
_setattr = object.__setattr__
|
||||
dico = self._informations
|
||||
dico = self._informations # pylint: disable=no-member
|
||||
keys = tuple(dico.keys())
|
||||
if len(keys) == 1:
|
||||
dico = dico['doc']
|
||||
|
@ -150,21 +161,30 @@ class Base:
|
|||
_setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())]))
|
||||
|
||||
def impl_is_readonly(self) -> str:
|
||||
"""the option is readonly
|
||||
"""
|
||||
# _path is None when initialise SymLinkOption
|
||||
return hasattr(self, '_path') and self._path is not None
|
||||
return hasattr(self, '_path') and self._path is not None # pylint: disable=no-member
|
||||
|
||||
def impl_getproperties(self) -> FrozenSet[str]:
|
||||
"""get properties
|
||||
"""
|
||||
return getattr(self, '_properties', frozenset())
|
||||
|
||||
def _setsubdyn(self,
|
||||
subdyn,
|
||||
) -> None:
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self._subdyn = subdyn
|
||||
|
||||
def issubdyn(self) -> bool:
|
||||
"""is sub dynoption
|
||||
"""
|
||||
return getattr(self, '_subdyn', None) is not None
|
||||
|
||||
def getsubdyn(self):
|
||||
"""get sub dynoption
|
||||
"""
|
||||
return self._subdyn()
|
||||
|
||||
# ____________________________________________________________
|
||||
|
@ -177,7 +197,7 @@ class Base:
|
|||
|
||||
:param key: the item string (ex: "help")
|
||||
"""
|
||||
dico = self._informations
|
||||
dico = self._informations # pylint: disable=no-member
|
||||
if isinstance(dico, tuple):
|
||||
if key in dico[0]:
|
||||
return dico[1][dico[0].index(key)]
|
||||
|
@ -189,7 +209,9 @@ class Base:
|
|||
return dico[key]
|
||||
if default is not undefined:
|
||||
return default
|
||||
raise ValueError(_(f'information\'s item for "{self.impl_get_display_name()}" not found: "{key}"'))
|
||||
# pylint: disable=no-member
|
||||
raise ValueError(_(f'information\'s item for "{self.impl_get_display_name()}" '
|
||||
f'not found: "{key}"'))
|
||||
|
||||
def impl_set_information(self,
|
||||
key: str,
|
||||
|
@ -206,13 +228,15 @@ class Base:
|
|||
" read-only").format(self.__class__.__name__,
|
||||
self,
|
||||
key))
|
||||
self._informations[key] = value
|
||||
self._informations[key] = value # pylint: disable=no-member
|
||||
|
||||
def impl_list_information(self) -> Any:
|
||||
dico = self._informations
|
||||
"""get the list of information keys
|
||||
"""
|
||||
dico = self._informations # pylint: disable=no-member
|
||||
if isinstance(dico, tuple):
|
||||
return list(dico[0])
|
||||
elif isinstance(dico, str):
|
||||
if isinstance(dico, str):
|
||||
return ['doc']
|
||||
# it's a dict
|
||||
return list(dico.keys())
|
||||
|
@ -225,9 +249,6 @@ class BaseOption(Base):
|
|||
"""
|
||||
__slots__ = ('_display_name_function',)
|
||||
|
||||
def __getstate__(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __setattr__(self,
|
||||
name: str,
|
||||
value: Any) -> Any:
|
||||
|
@ -245,19 +266,16 @@ class BaseOption(Base):
|
|||
' read-only').format(self.__class__.__name__,
|
||||
self.impl_get_display_name(),
|
||||
name))
|
||||
super(BaseOption, self).__setattr__(name, value)
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def impl_getpath(self) -> str:
|
||||
"""get the path of the option
|
||||
"""
|
||||
try:
|
||||
return self._path
|
||||
except AttributeError:
|
||||
raise AttributeError(_('"{}" not part of any Config').format(self.impl_get_display_name()))
|
||||
|
||||
def impl_has_callback(self) -> bool:
|
||||
"to know if a callback has been defined or not"
|
||||
if self.impl_get_callback()[0] is not None:
|
||||
print('ca existe')
|
||||
return self.impl_get_callback()[0] is not None
|
||||
except AttributeError as err:
|
||||
raise AttributeError(_(f'"{self.impl_get_display_name()}" not part of any Config')) \
|
||||
from err
|
||||
|
||||
def _impl_get_display_name(self,
|
||||
dyn_name: Base=None,
|
||||
|
@ -287,6 +305,8 @@ class BaseOption(Base):
|
|||
)
|
||||
|
||||
def impl_get_display_name(self) -> str:
|
||||
"""get display name
|
||||
"""
|
||||
return self._get_display_name(None,
|
||||
None,
|
||||
)
|
||||
|
@ -294,19 +314,26 @@ class BaseOption(Base):
|
|||
def reset_cache(self,
|
||||
path: str,
|
||||
config_bag: 'OptionBag',
|
||||
resetted_opts: List[Base]) -> None:
|
||||
resetted_opts: List[Base], # pylint: disable=unused-argument
|
||||
) -> None:
|
||||
"""reset cache
|
||||
"""
|
||||
context = config_bag.context
|
||||
context.properties_cache.delcache(path)
|
||||
context._impl_permissives_cache.delcache(path)
|
||||
context._impl_permissives_cache.delcache(path) # pylint: disable=protected-access
|
||||
if not self.impl_is_optiondescription():
|
||||
context._impl_values_cache.delcache(path)
|
||||
context.get_values_cache().delcache(path) # pylint: disable=protected-access
|
||||
|
||||
def impl_is_symlinkoption(self) -> bool:
|
||||
"""the option is not a symlinkoption
|
||||
"""
|
||||
return False
|
||||
|
||||
def get_dependencies_information(self,
|
||||
itself=False,
|
||||
) -> List[str]:
|
||||
"""get dependencies information
|
||||
"""
|
||||
if itself:
|
||||
idx = 1
|
||||
else:
|
||||
|
|
|
@ -18,19 +18,23 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""BoolOption
|
||||
"""
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
|
||||
|
||||
class BoolOption(Option):
|
||||
"represents a choice between ``True`` and ``False``"
|
||||
"""represents a choice between ``True`` and ``False``
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'boolean'
|
||||
_display_name = _('boolean')
|
||||
_type = _('boolean')
|
||||
|
||||
def validate(self,
|
||||
value: bool) -> None:
|
||||
value: bool,
|
||||
) -> None:
|
||||
"""validate value
|
||||
"""
|
||||
if not isinstance(value, bool):
|
||||
raise ValueError()
|
||||
|
|
|
@ -18,21 +18,25 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
from ipaddress import ip_address, ip_network
|
||||
"""BroadcastOption
|
||||
"""
|
||||
from ipaddress import ip_address
|
||||
|
||||
from ..error import ConfigError
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
|
||||
|
||||
class BroadcastOption(Option):
|
||||
"""represents the choice of a broadcast
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'broadcast_address'
|
||||
_display_name = _('broadcast address')
|
||||
_type = _('broadcast address')
|
||||
|
||||
def validate(self,
|
||||
value: str) -> None:
|
||||
value: str,
|
||||
) -> None:
|
||||
"""validate
|
||||
"""
|
||||
if not isinstance(value, str):
|
||||
raise ValueError(_('invalid string'))
|
||||
if value.count('.') != 3:
|
||||
|
@ -42,5 +46,5 @@ class BroadcastOption(Option):
|
|||
raise ValueError()
|
||||
try:
|
||||
ip_address(value)
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""ChoiceOption
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
from ..setting import undefined, OptionBag
|
||||
|
@ -33,8 +35,7 @@ class ChoiceOption(Option):
|
|||
The option can also have the value ``None``
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'choice'
|
||||
_display_name = _('choice')
|
||||
_type = _('choice')
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
|
@ -56,7 +57,10 @@ class ChoiceOption(Option):
|
|||
**kwargs)
|
||||
|
||||
def impl_get_values(self,
|
||||
option_bag):
|
||||
option_bag: OptionBag,
|
||||
):
|
||||
"""get values allowed by option
|
||||
"""
|
||||
if isinstance(self._choice_values, Calculation):
|
||||
values = self._choice_values.execute(option_bag)
|
||||
if values is not undefined and not isinstance(values, list):
|
||||
|
@ -67,20 +71,18 @@ class ChoiceOption(Option):
|
|||
return values
|
||||
|
||||
def validate(self,
|
||||
value: Any) -> None:
|
||||
pass
|
||||
|
||||
def sync_validate_with_option(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag) -> None:
|
||||
if isinstance(self._choice_values, Calculation):
|
||||
return
|
||||
values = self._choice_values
|
||||
self.validate_values(value, values)
|
||||
value: Any,
|
||||
) -> None:
|
||||
"""nothing to valide
|
||||
"""
|
||||
|
||||
def validate_with_option(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag) -> None:
|
||||
value: Any,
|
||||
option_bag: OptionBag,
|
||||
loaded: bool,
|
||||
) -> None:
|
||||
if loaded and isinstance(self._choice_values, Calculation):
|
||||
return
|
||||
values = self.impl_get_values(option_bag)
|
||||
self.validate_values(value, values)
|
||||
|
||||
|
@ -88,6 +90,8 @@ class ChoiceOption(Option):
|
|||
value,
|
||||
values,
|
||||
) -> None:
|
||||
"""validate values
|
||||
"""
|
||||
if values is not undefined and value not in values:
|
||||
if len(values) == 1:
|
||||
raise ValueError(_('only "{0}" is allowed'
|
||||
|
|
|
@ -18,22 +18,24 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""DateOption
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .stroption import StrOption
|
||||
|
||||
|
||||
class DateOption(StrOption):
|
||||
"""represents the choice of a date
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'date'
|
||||
_display_name = _('date')
|
||||
_type = _('date')
|
||||
|
||||
def validate(self,
|
||||
value: str) -> None:
|
||||
super().validate(value)
|
||||
try:
|
||||
datetime.strptime(value, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""DomainnameOption
|
||||
"""
|
||||
import re
|
||||
from ipaddress import ip_interface
|
||||
from typing import Any, Optional, List
|
||||
|
@ -38,8 +40,7 @@ class DomainnameOption(StrOption):
|
|||
fqdn: with tld, not supported yet
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'domainname'
|
||||
_display_name = _('domain name')
|
||||
_type = _('domain name')
|
||||
|
||||
def __init__(self,
|
||||
name: str,
|
||||
|
@ -54,8 +55,9 @@ class DomainnameOption(StrOption):
|
|||
allow_cidr_network: bool=False,
|
||||
type: str='domainname',
|
||||
allow_without_dot: bool=False,
|
||||
allow_startswith_dot: bool=False) -> None:
|
||||
|
||||
allow_startswith_dot: bool=False,
|
||||
) -> None:
|
||||
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments
|
||||
if type not in ['netbios', 'hostname', 'domainname']:
|
||||
raise ValueError(_('unknown type {0} for hostname').format(type))
|
||||
extra = {'_dom_type': type}
|
||||
|
@ -111,11 +113,10 @@ class DomainnameOption(StrOption):
|
|||
warnings_only=warnings_only,
|
||||
extra=extra)
|
||||
|
||||
def _get_len(self, type):
|
||||
if type == 'netbios':
|
||||
def _get_len(self, type_):
|
||||
if type_ == 'netbios':
|
||||
return 15
|
||||
else:
|
||||
return 63
|
||||
return 63
|
||||
|
||||
def _validate_domain(self,
|
||||
value: str) -> None:
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""DynOptionDescription
|
||||
"""
|
||||
import re
|
||||
import weakref
|
||||
from typing import List, Callable, Any
|
||||
from typing import List, Any
|
||||
from itertools import chain
|
||||
from ..autolib import ParamOption
|
||||
|
||||
|
@ -28,7 +30,7 @@ from ..autolib import ParamOption
|
|||
from ..i18n import _
|
||||
from .optiondescription import OptionDescription
|
||||
from .baseoption import BaseOption
|
||||
from ..setting import OptionBag, ConfigBag, groups, undefined
|
||||
from ..setting import OptionBag, ConfigBag
|
||||
from ..error import ConfigError
|
||||
from ..autolib import Calculation
|
||||
|
||||
|
@ -37,6 +39,8 @@ NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
|
|||
|
||||
|
||||
class DynOptionDescription(OptionDescription):
|
||||
"""dyn option description
|
||||
"""
|
||||
__slots__ = ('_suffixes',)
|
||||
|
||||
def __init__(self,
|
||||
|
@ -46,7 +50,7 @@ class DynOptionDescription(OptionDescription):
|
|||
suffixes: Calculation,
|
||||
properties=None,
|
||||
) -> None:
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
super().__init__(name,
|
||||
doc,
|
||||
children,
|
||||
|
@ -69,6 +73,8 @@ class DynOptionDescription(OptionDescription):
|
|||
def convert_suffix_to_path(self,
|
||||
suffix: Any,
|
||||
) -> str:
|
||||
"""convert suffix to use it to a path
|
||||
"""
|
||||
if suffix is None:
|
||||
return None
|
||||
if not isinstance(suffix, str):
|
||||
|
@ -78,7 +84,10 @@ class DynOptionDescription(OptionDescription):
|
|||
return suffix
|
||||
|
||||
def get_suffixes(self,
|
||||
config_bag: ConfigBag) -> List[str]:
|
||||
config_bag: ConfigBag,
|
||||
) -> List[str]:
|
||||
"""get dynamic suffixes
|
||||
"""
|
||||
option_bag = OptionBag(self,
|
||||
None,
|
||||
config_bag,
|
||||
|
@ -90,8 +99,9 @@ class DynOptionDescription(OptionDescription):
|
|||
values_ = []
|
||||
if __debug__:
|
||||
if not isinstance(values, list):
|
||||
raise ValueError(_('DynOptionDescription suffixes for option "{}", is not a list ({})'
|
||||
'').format(self.impl_get_display_name(), values))
|
||||
raise ValueError(_('DynOptionDescription suffixes for '
|
||||
f'option "{self.impl_get_display_name()}", is not '
|
||||
f'a list ({values})'))
|
||||
for val in values:
|
||||
cval = self.convert_suffix_to_path(val)
|
||||
if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None:
|
||||
|
@ -106,8 +116,8 @@ class DynOptionDescription(OptionDescription):
|
|||
extra_values = values_.copy()
|
||||
for val in set(values_):
|
||||
extra_values.remove(val)
|
||||
raise ValueError(_('DynOptionDescription suffixes return a list with multiple value '
|
||||
'"{}"''').format(extra_values))
|
||||
raise ValueError(_('DynOptionDescription suffixes return a list with '
|
||||
f'multiple value "{extra_values}"'''))
|
||||
return values_
|
||||
|
||||
def impl_is_dynoptiondescription(self) -> bool:
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""EmailOption
|
||||
"""
|
||||
import re
|
||||
|
||||
from ..i18n import _
|
||||
|
@ -25,7 +27,8 @@ from .stroption import RegexpOption
|
|||
|
||||
|
||||
class EmailOption(RegexpOption):
|
||||
"""represents a choice of an email
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
|
||||
_type = 'email'
|
||||
_display_name = _('email address')
|
||||
_type = _('email address')
|
||||
|
|
|
@ -18,16 +18,17 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
import re
|
||||
|
||||
"""FilenameOption
|
||||
"""
|
||||
from ..i18n import _
|
||||
from .stroption import StrOption
|
||||
|
||||
|
||||
class FilenameOption(StrOption):
|
||||
"""represents a choice of a file name
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'filename'
|
||||
_display_name = _('file name')
|
||||
_type = _('file name')
|
||||
|
||||
def validate(self,
|
||||
value: str,
|
||||
|
|
|
@ -18,17 +18,18 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""FloatOption
|
||||
"""
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
|
||||
|
||||
class FloatOption(Option):
|
||||
"represents a choice of a floating point number"
|
||||
"""represents a choice of a floating point number
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'float'
|
||||
_display_name = _('float')
|
||||
_type = _('float')
|
||||
|
||||
def validate(self,
|
||||
value: float) -> None:
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""IntOption
|
||||
"""
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
|
||||
|
@ -27,8 +28,7 @@ from .option import Option
|
|||
class IntOption(Option):
|
||||
"represents a choice of an integer"
|
||||
__slots__ = tuple()
|
||||
_type = 'integer'
|
||||
_display_name = _('integer')
|
||||
_type = _('integer')
|
||||
|
||||
def __init__(self,
|
||||
*args,
|
||||
|
@ -43,7 +43,8 @@ class IntOption(Option):
|
|||
super().__init__(*args, extra=extra, **kwargs)
|
||||
|
||||
def validate(self,
|
||||
value: int) -> None:
|
||||
value: int,
|
||||
) -> None:
|
||||
if not isinstance(value, int):
|
||||
raise ValueError()
|
||||
|
||||
|
|
|
@ -18,21 +18,19 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""IPOption
|
||||
"""
|
||||
from ipaddress import ip_address, ip_interface
|
||||
|
||||
from ..error import ConfigError
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from .stroption import StrOption
|
||||
from ..function import valid_ip_netmask
|
||||
|
||||
|
||||
class IPOption(StrOption):
|
||||
"represents the choice of an ip"
|
||||
"""represents the choice of an ip
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'ip'
|
||||
_display_name = _('IP')
|
||||
_type = _('IP')
|
||||
|
||||
def __init__(self,
|
||||
*args,
|
||||
|
@ -52,19 +50,19 @@ class IPOption(StrOption):
|
|||
|
||||
def _validate_cidr(self, value):
|
||||
try:
|
||||
ip = ip_interface(value)
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
if ip.ip == ip.network.network_address:
|
||||
ip_obj = ip_interface(value)
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
if ip_obj.ip == ip_obj.network.network_address:
|
||||
raise ValueError(_("it's in fact a network address"))
|
||||
elif ip.ip == ip.network.broadcast_address:
|
||||
if ip_obj.ip == ip_obj.network.broadcast_address:
|
||||
raise ValueError(_("it's in fact a broacast address"))
|
||||
|
||||
def _validate_ip(self, value):
|
||||
try:
|
||||
new_value = str(ip_address(value))
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
str(ip_address(value))
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
|
||||
def validate(self,
|
||||
value: str) -> None:
|
||||
|
@ -79,14 +77,14 @@ class IPOption(StrOption):
|
|||
def second_level_validation(self,
|
||||
value: str,
|
||||
warnings_only: bool) -> None:
|
||||
ip = ip_interface(value)
|
||||
if not self.impl_get_extra('_allow_reserved') and ip.is_reserved:
|
||||
ip_obj = ip_interface(value)
|
||||
if not self.impl_get_extra('_allow_reserved') and ip_obj.is_reserved:
|
||||
if warnings_only:
|
||||
msg = _("shouldn't be reserved IP")
|
||||
else:
|
||||
msg = _("mustn't be reserved IP")
|
||||
raise ValueError(msg)
|
||||
if self.impl_get_extra('_private_only') and not ip.is_private:
|
||||
if self.impl_get_extra('_private_only') and not ip_obj.is_private:
|
||||
if warnings_only:
|
||||
msg = _("should be private IP")
|
||||
else:
|
||||
|
|
|
@ -20,22 +20,23 @@
|
|||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
import weakref
|
||||
from itertools import chain
|
||||
from typing import List, Iterator, Optional, Any
|
||||
from typing import List, Iterator, Optional
|
||||
|
||||
|
||||
from ..i18n import _
|
||||
from ..setting import groups, undefined, OptionBag, Settings, ALLOWED_LEADER_PROPERTIES
|
||||
from ..value import Values
|
||||
from ..setting import groups, undefined, OptionBag, ALLOWED_LEADER_PROPERTIES
|
||||
from .optiondescription import OptionDescription
|
||||
from .syndynoptiondescription import SynDynLeadership
|
||||
from .baseoption import BaseOption
|
||||
from .option import Option
|
||||
from ..error import LeadershipError
|
||||
from ..autolib import Calculation, ParamOption
|
||||
from ..autolib import Calculation
|
||||
|
||||
|
||||
class Leadership(OptionDescription):
|
||||
"""Leadership
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
__slots__ = ('leader',
|
||||
'followers',
|
||||
)
|
||||
|
@ -57,35 +58,10 @@ class Leadership(OptionDescription):
|
|||
leader = children[0]
|
||||
for idx, child in enumerate(children):
|
||||
if __debug__:
|
||||
if child.impl_is_symlinkoption():
|
||||
raise ValueError(_('leadership "{0}" shall not have '
|
||||
"a symlinkoption").format(self.impl_get_display_name()))
|
||||
if not isinstance(child, Option):
|
||||
raise ValueError(_('leadership "{0}" shall not have '
|
||||
'a subgroup').format(self.impl_get_display_name()))
|
||||
if not child.impl_is_multi():
|
||||
raise ValueError(_('only multi option allowed in leadership "{0}" but option '
|
||||
'"{1}" is not a multi'
|
||||
'').format(self.impl_get_display_name(),
|
||||
child.impl_get_display_name()))
|
||||
if idx != 0:
|
||||
default = child.impl_getdefault()
|
||||
if default != []:
|
||||
if child.impl_is_submulti() and isinstance(default, tuple):
|
||||
for val in default:
|
||||
if not isinstance(val, Calculation):
|
||||
calculation = False
|
||||
else:
|
||||
# empty default is valid
|
||||
calculation = True
|
||||
else:
|
||||
calculation = isinstance(default, Calculation)
|
||||
if not calculation:
|
||||
raise ValueError(_('not allowed default value for follower option "{0}" '
|
||||
'in leadership "{1}"'
|
||||
'').format(child.impl_get_display_name(),
|
||||
self.impl_get_display_name()))
|
||||
self._check_child_is_valid(child)
|
||||
if idx != 0:
|
||||
if __debug__:
|
||||
self._check_default_value(child)
|
||||
# remove empty property for follower
|
||||
child._properties = frozenset(child._properties - {'empty', 'unique'})
|
||||
followers.append(child)
|
||||
|
@ -96,35 +72,78 @@ class Leadership(OptionDescription):
|
|||
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
|
||||
raise LeadershipError(_('leader cannot have "{}" property').format(prop))
|
||||
|
||||
def _check_child_is_valid(self, child: BaseOption):
|
||||
if child.impl_is_symlinkoption():
|
||||
raise ValueError(_('leadership "{0}" shall not have '
|
||||
"a symlinkoption").format(self.impl_get_display_name()))
|
||||
if not isinstance(child, Option):
|
||||
raise ValueError(_('leadership "{0}" shall not have '
|
||||
'a subgroup').format(self.impl_get_display_name()))
|
||||
if not child.impl_is_multi():
|
||||
raise ValueError(_('only multi option allowed in leadership "{0}" but option '
|
||||
'"{1}" is not a multi'
|
||||
'').format(self.impl_get_display_name(),
|
||||
child.impl_get_display_name()))
|
||||
|
||||
def _check_default_value(self, child: BaseOption):
|
||||
default = child.impl_getdefault()
|
||||
if default != []:
|
||||
if child.impl_is_submulti() and isinstance(default, tuple):
|
||||
for val in default:
|
||||
if not isinstance(val, Calculation):
|
||||
calculation = False
|
||||
break
|
||||
else:
|
||||
# empty default is valid
|
||||
calculation = True
|
||||
else:
|
||||
calculation = isinstance(default, Calculation)
|
||||
if not calculation:
|
||||
raise ValueError(_('not allowed default value for follower option '
|
||||
'"{child.impl_get_display_name()}" in leadership '
|
||||
'"{self.impl_get_display_name()}"'))
|
||||
|
||||
def _setsubdyn(self,
|
||||
subdyn,
|
||||
) -> None:
|
||||
# pylint: disable=attribute-defined-outside-init,protected-access
|
||||
for chld in self._children[1]:
|
||||
chld._setsubdyn(subdyn)
|
||||
self._subdyn = subdyn
|
||||
|
||||
def is_leader(self,
|
||||
opt: Option) -> bool:
|
||||
opt: Option,
|
||||
) -> bool:
|
||||
"""the option is the leader
|
||||
"""
|
||||
leader = self.get_leader()
|
||||
return opt == leader or (opt.impl_is_dynsymlinkoption() and opt.opt == leader)
|
||||
|
||||
def get_leader(self) -> Option:
|
||||
"""get leader
|
||||
"""
|
||||
return self._children[1][0]
|
||||
|
||||
def get_followers(self) -> Iterator[Option]:
|
||||
"""get all followers
|
||||
"""
|
||||
for follower in self._children[1][1:]:
|
||||
yield follower
|
||||
|
||||
def in_same_group(self,
|
||||
opt: Option) -> bool:
|
||||
def in_same_leadership(self,
|
||||
opt: Option,
|
||||
) -> bool:
|
||||
"""check if followers are in same leadership
|
||||
"""
|
||||
if opt.impl_is_dynsymlinkoption():
|
||||
opt = opt.opt
|
||||
return opt in self._children[1]
|
||||
|
||||
def reset(self,
|
||||
values: Values,
|
||||
option_bag: OptionBag) -> None:
|
||||
config_bag = option_bag.config_bag.copy()
|
||||
def reset(self, config_bag: 'ConfigBag') -> None:
|
||||
"""reset follower value
|
||||
"""
|
||||
values = config_bag.context.get_values()
|
||||
config_bag = config_bag.copy()
|
||||
config_bag.remove_validation()
|
||||
for follower in self.get_followers():
|
||||
soption_bag = OptionBag(follower,
|
||||
|
@ -134,71 +153,60 @@ class Leadership(OptionDescription):
|
|||
values.reset(soption_bag)
|
||||
|
||||
def follower_force_store_value(self,
|
||||
values,
|
||||
value,
|
||||
option_bag,
|
||||
owner,
|
||||
dyn=None,
|
||||
) -> None:
|
||||
settings = option_bag.config_bag.context.get_settings()
|
||||
value,
|
||||
config_bag: 'ConfigBag',
|
||||
owner,
|
||||
dyn=None,
|
||||
) -> None:
|
||||
"""apply force_store_value to follower
|
||||
"""
|
||||
if value:
|
||||
if dyn is None:
|
||||
dyn = self
|
||||
for idx, follower in enumerate(dyn.get_children(option_bag.config_bag)):
|
||||
values = config_bag.context.get_values()
|
||||
for idx, follower in enumerate(dyn.get_children(config_bag)):
|
||||
foption_bag = OptionBag(follower,
|
||||
None,
|
||||
option_bag.config_bag,
|
||||
config_bag,
|
||||
)
|
||||
if 'force_store_value' in foption_bag.properties:
|
||||
if idx == 0:
|
||||
indexes = [None]
|
||||
else:
|
||||
indexes = range(len(value))
|
||||
for index in indexes:
|
||||
foption_bag_index = OptionBag(follower,
|
||||
index,
|
||||
option_bag.config_bag,
|
||||
)
|
||||
values.set_storage_value(foption_bag_index.path,
|
||||
index,
|
||||
values.getvalue(foption_bag_index),
|
||||
owner,
|
||||
)
|
||||
if 'force_store_value' not in foption_bag.properties:
|
||||
continue
|
||||
if idx == 0:
|
||||
indexes = [None]
|
||||
else:
|
||||
indexes = range(len(value))
|
||||
for index in indexes:
|
||||
foption_bag_index = OptionBag(follower,
|
||||
index,
|
||||
config_bag,
|
||||
)
|
||||
values.set_storage_value(foption_bag_index.path,
|
||||
index,
|
||||
values.get_value(foption_bag_index),
|
||||
owner,
|
||||
)
|
||||
|
||||
def pop(self,
|
||||
values: Values,
|
||||
index: int,
|
||||
option_bag: OptionBag,
|
||||
config_bag: 'ConfigBag',
|
||||
followers: Optional[List[Option]]=undefined,
|
||||
) -> None:
|
||||
"""pop leader value and follower's one
|
||||
"""
|
||||
if followers is undefined:
|
||||
# followers are not undefined only in SynDynLeadership
|
||||
followers = self.get_followers()
|
||||
config_bag = option_bag.config_bag.copy()
|
||||
config_bag = config_bag.copy()
|
||||
config_bag.remove_validation()
|
||||
values = config_bag.context.get_values()
|
||||
for follower in followers:
|
||||
follower_path = follower.impl_getpath()
|
||||
followerlen = values.get_max_length(follower_path)
|
||||
soption_bag = OptionBag(follower,
|
||||
index,
|
||||
config_bag,
|
||||
properties=set(), # do not check force_default_on_freeze or force_metaconfig_on_freeze
|
||||
properties=set(), # do not check force_default_on_freeze
|
||||
# or force_metaconfig_on_freeze
|
||||
)
|
||||
is_default = values.is_default_owner(soption_bag,
|
||||
validate_meta=False,
|
||||
)
|
||||
if not is_default and followerlen > index:
|
||||
values.resetvalue_index(follower_path,
|
||||
index,
|
||||
)
|
||||
if followerlen > index + 1:
|
||||
for idx in range(index + 1, followerlen):
|
||||
if values.hasvalue(follower_path,
|
||||
idx,
|
||||
):
|
||||
values.reduce_index(follower_path,
|
||||
idx,
|
||||
)
|
||||
values.reduce_index(soption_bag)
|
||||
|
||||
def reset_cache(self,
|
||||
path: str,
|
||||
|
@ -221,17 +229,16 @@ class Leadership(OptionDescription):
|
|||
) -> None:
|
||||
super().reset_cache(path,
|
||||
config_bag,
|
||||
resetted_opts)
|
||||
resetted_opts,
|
||||
)
|
||||
leader.reset_cache(leader.impl_getpath(),
|
||||
config_bag,
|
||||
None)
|
||||
for follower in followers:
|
||||
spath = follower.impl_getpath()
|
||||
follower.reset_cache(spath,
|
||||
follower.reset_cache(follower.impl_getpath(),
|
||||
config_bag,
|
||||
None)
|
||||
# do not reset dependencies option
|
||||
# resetted_opts.append(spath)
|
||||
None,
|
||||
)
|
||||
|
||||
def impl_is_leadership(self) -> None:
|
||||
return True
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""MACOption
|
||||
"""
|
||||
import re
|
||||
|
||||
from ..i18n import _
|
||||
|
@ -25,7 +27,8 @@ from .stroption import RegexpOption
|
|||
|
||||
|
||||
class MACOption(RegexpOption):
|
||||
"""represents the choice of a mac address
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$")
|
||||
_type = 'macaddress'
|
||||
_display_name = _('mac address')
|
||||
_type = _('mac address')
|
||||
|
|
|
@ -18,21 +18,18 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
from ipaddress import ip_interface, ip_network
|
||||
from typing import List
|
||||
|
||||
from ..error import ConfigError
|
||||
from ..setting import undefined, OptionBag, Undefined
|
||||
"""NetmaskOption
|
||||
"""
|
||||
from ipaddress import ip_network
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from .stroption import StrOption
|
||||
|
||||
|
||||
class NetmaskOption(StrOption):
|
||||
"represents the choice of a netmask"
|
||||
"""represents the choice of a netmask
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'netmask'
|
||||
_display_name = _('netmask address')
|
||||
_type = _('netmask address')
|
||||
|
||||
def validate(self,
|
||||
value: str) -> None:
|
||||
|
@ -41,6 +38,6 @@ class NetmaskOption(StrOption):
|
|||
if val.startswith("0") and len(val) > 1:
|
||||
raise ValueError()
|
||||
try:
|
||||
ip_network('0.0.0.0/{0}'.format(value))
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
ip_network(f'0.0.0.0/{value}')
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
from ipaddress import ip_address, ip_network
|
||||
"""NetworkOption
|
||||
"""
|
||||
from ipaddress import ip_network
|
||||
|
||||
from ..i18n import _
|
||||
from .stroption import StrOption
|
||||
|
@ -27,8 +29,7 @@ from .stroption import StrOption
|
|||
class NetworkOption(StrOption):
|
||||
"represents the choice of a network"
|
||||
__slots__ = tuple()
|
||||
_type = 'network'
|
||||
_display_name = _('network address')
|
||||
_type = _('network address')
|
||||
|
||||
def __init__(self,
|
||||
*args,
|
||||
|
@ -56,8 +57,8 @@ class NetworkOption(StrOption):
|
|||
raise ValueError()
|
||||
try:
|
||||
ip_network(value)
|
||||
except ValueError:
|
||||
raise ValueError()
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
|
||||
def second_level_validation(self,
|
||||
value: str,
|
||||
|
|
|
@ -20,21 +20,19 @@
|
|||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
import warnings
|
||||
import weakref
|
||||
from typing import Any, List, Callable, Optional, Dict, Union, Tuple
|
||||
from typing import Any, List, Optional, Dict
|
||||
from itertools import chain
|
||||
|
||||
from .baseoption import BaseOption, submulti, STATIC_TUPLE
|
||||
from .baseoption import BaseOption, submulti
|
||||
from ..i18n import _
|
||||
from ..setting import undefined, OptionBag, Undefined
|
||||
from ..autolib import Calculation, Params, ParamOption, ParamInformation, ParamSelfInformation
|
||||
from ..error import (ConfigError, ValueWarning, ValueErrorWarning,
|
||||
ValueOptionError, display_list)
|
||||
from ..setting import undefined, OptionBag
|
||||
from ..autolib import Calculation, ParamOption, ParamInformation, ParamSelfInformation
|
||||
from ..error import ValueWarning, ValueErrorWarning, ValueOptionError
|
||||
from .syndynoption import SynDynOption
|
||||
#ALLOWED_CONST_LIST = ['_cons_not_equal']
|
||||
|
||||
|
||||
class Option(BaseOption):
|
||||
# pylint: disable=too-many-statements,too-many-branches,too-many-arguments,too-many-locals
|
||||
"""
|
||||
Abstract base class for configuration option's.
|
||||
|
||||
|
@ -55,7 +53,7 @@ class Option(BaseOption):
|
|||
'_choice_values',
|
||||
'_choice_values_params',
|
||||
)
|
||||
_empty = ''
|
||||
_type = None
|
||||
def __init__(self,
|
||||
name: str,
|
||||
doc: str,
|
||||
|
@ -99,7 +97,7 @@ class Option(BaseOption):
|
|||
is_multi=is_multi)
|
||||
if validators is not None:
|
||||
if __debug__ and not isinstance(validators, list):
|
||||
raise ValueError(_('validators must be a list of Calculation for "{}"').format(name))
|
||||
raise ValueError(_(f'validators must be a list of Calculation for "{name}"'))
|
||||
for validator in validators:
|
||||
if __debug__ and not isinstance(validator, Calculation):
|
||||
raise ValueError(_('validators must be a Calculation for "{}"').format(name))
|
||||
|
@ -128,19 +126,20 @@ class Option(BaseOption):
|
|||
)
|
||||
try:
|
||||
self.validate(value)
|
||||
self.sync_validate_with_option(value,
|
||||
option_bag)
|
||||
self.validate_with_option(value,
|
||||
option_bag,
|
||||
loaded=True,
|
||||
)
|
||||
except ValueError as err:
|
||||
str_err = str(err)
|
||||
if not str_err:
|
||||
raise ValueError(_('invalid default_multi value "{0}" '
|
||||
'for option "{1}"').format(str(value),
|
||||
self.impl_get_display_name()))
|
||||
else:
|
||||
raise ValueError(_('invalid default_multi value "{0}" '
|
||||
'for option "{1}", {2}').format(str(value),
|
||||
self.impl_get_display_name(),
|
||||
str_err))
|
||||
self.impl_get_display_name())
|
||||
) from err
|
||||
raise ValueError(_(f'invalid default_multi value "{value}" for option '
|
||||
f'"{self.impl_get_display_name()}", {str_err}')
|
||||
) from err
|
||||
if _multi is submulti:
|
||||
if not isinstance(default_multi, Calculation):
|
||||
if not isinstance(default_multi, list):
|
||||
|
@ -158,11 +157,15 @@ class Option(BaseOption):
|
|||
undefined,
|
||||
properties=None,
|
||||
)
|
||||
self.sync_impl_validate(default,
|
||||
option_bag)
|
||||
self.sync_impl_validate(default,
|
||||
option_bag,
|
||||
check_error=False)
|
||||
self.impl_validate(default,
|
||||
option_bag,
|
||||
loaded=True,
|
||||
)
|
||||
self.impl_validate(default,
|
||||
option_bag,
|
||||
check_error=False,
|
||||
loaded=True,
|
||||
)
|
||||
self.value_dependencies(default, _dependencies_information)
|
||||
if (is_multi and default != []) or \
|
||||
(not is_multi and default is not None):
|
||||
|
@ -176,6 +179,8 @@ class Option(BaseOption):
|
|||
value: Any,
|
||||
_dependencies_information: List[str],
|
||||
) -> Any:
|
||||
"""parse dependancies to add dependencies
|
||||
"""
|
||||
if isinstance(value, list):
|
||||
for val in value:
|
||||
if isinstance(value, list):
|
||||
|
@ -189,8 +194,11 @@ class Option(BaseOption):
|
|||
value: Any,
|
||||
_dependencies_information: List[str],
|
||||
) -> Any:
|
||||
"""parse dependancy to add dependencies
|
||||
"""
|
||||
for param in chain(value.params.args, value.params.kwargs.values()):
|
||||
if isinstance(param, ParamOption):
|
||||
# pylint: disable=protected-access
|
||||
param.option._add_dependency(self)
|
||||
elif isinstance(param, ParamSelfInformation):
|
||||
_dependencies_information[1].append(param.information_name)
|
||||
|
@ -201,23 +209,28 @@ class Option(BaseOption):
|
|||
# option's information
|
||||
|
||||
def impl_is_multi(self) -> bool:
|
||||
"""is it a multi option
|
||||
"""
|
||||
return getattr(self, '_multi', 1) != 1
|
||||
|
||||
def impl_is_submulti(self) -> bool:
|
||||
"""is it a submulti option
|
||||
"""
|
||||
return getattr(self, '_multi', 1) == 2
|
||||
|
||||
def impl_is_dynsymlinkoption(self) -> bool:
|
||||
"""is a dynsymlinkoption?
|
||||
"""
|
||||
return False
|
||||
|
||||
def get_type(self) -> str:
|
||||
# _display_name for compatibility with older version than 3.0rc3
|
||||
return getattr(self, '_type', self._display_name)
|
||||
|
||||
def get_display_type(self) -> str:
|
||||
return self._display_name
|
||||
"""get the type of option
|
||||
"""
|
||||
return self._type
|
||||
|
||||
def impl_getdefault(self) -> Any:
|
||||
"accessing the default value"
|
||||
"""accessing the default value
|
||||
"""
|
||||
is_multi = self.impl_is_multi()
|
||||
default = getattr(self, '_default', undefined)
|
||||
if default is undefined:
|
||||
|
@ -225,13 +238,13 @@ class Option(BaseOption):
|
|||
default = []
|
||||
else:
|
||||
default = None
|
||||
else:
|
||||
if is_multi and isinstance(default, list):
|
||||
default = list(default)
|
||||
elif is_multi and isinstance(default, tuple):
|
||||
default = list(default)
|
||||
return default
|
||||
|
||||
def impl_getdefault_multi(self) -> Any:
|
||||
"accessing the default value for a multi"
|
||||
"""accessing the default value for a multi
|
||||
"""
|
||||
if self.impl_is_submulti():
|
||||
default_value = []
|
||||
else:
|
||||
|
@ -239,93 +252,25 @@ class Option(BaseOption):
|
|||
return getattr(self, '_default_multi', default_value)
|
||||
|
||||
def impl_get_extra(self,
|
||||
key: str) -> Any:
|
||||
key: str,
|
||||
) -> Any:
|
||||
"""if extra parameters are store get it
|
||||
"""
|
||||
extra = getattr(self, '_extra', {})
|
||||
if isinstance(extra, tuple):
|
||||
if key in extra[0]:
|
||||
return extra[1][extra[0].index(key)]
|
||||
return None
|
||||
else:
|
||||
return extra.get(key)
|
||||
return extra.get(key)
|
||||
|
||||
#__________________________________________________________________________
|
||||
# validator
|
||||
def sync_impl_validate(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag,
|
||||
check_error: bool=True) -> None:
|
||||
"""
|
||||
"""
|
||||
is_warnings_only = getattr(self, '_warnings_only', False)
|
||||
|
||||
def do_validation(_value,
|
||||
_index):
|
||||
if isinstance(_value, list):
|
||||
raise ValueError(_('which must not be a list').format(_value,
|
||||
self.impl_get_display_name()))
|
||||
if _value is not None:
|
||||
if check_error:
|
||||
# option validation
|
||||
self.validate(_value)
|
||||
self.sync_validate_with_option(_value,
|
||||
option_bag)
|
||||
if ((check_error and not is_warnings_only) or
|
||||
(not check_error and is_warnings_only)):
|
||||
try:
|
||||
self.second_level_validation(_value,
|
||||
is_warnings_only)
|
||||
except ValueError as err:
|
||||
if is_warnings_only:
|
||||
warnings.warn_explicit(ValueWarning(_value,
|
||||
self._display_name,
|
||||
self,
|
||||
'{0}'.format(err),
|
||||
_index),
|
||||
ValueWarning,
|
||||
self.__class__.__name__, 0)
|
||||
else:
|
||||
raise err
|
||||
try:
|
||||
err_index = None
|
||||
if isinstance(value, Calculation):
|
||||
pass
|
||||
elif not self.impl_is_multi():
|
||||
val = value
|
||||
do_validation(val, None)
|
||||
elif self.impl_is_submulti():
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(_('which must be a list'))
|
||||
for err_index, lval in enumerate(value):
|
||||
if isinstance(lval, Calculation):
|
||||
continue
|
||||
if not isinstance(lval, list):
|
||||
raise ValueError(_('which "{}" must be a list of list'
|
||||
'').format(lval))
|
||||
for val in lval:
|
||||
if isinstance(val, Calculation):
|
||||
continue
|
||||
do_validation(val,
|
||||
err_index)
|
||||
else:
|
||||
# it's a multi
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(_('which must be a list'))
|
||||
for err_index, val in enumerate(value):
|
||||
if isinstance(val, Calculation):
|
||||
continue
|
||||
do_validation(val,
|
||||
err_index)
|
||||
except ValueError as err:
|
||||
raise ValueOptionError(value,
|
||||
self._display_name,
|
||||
option_bag.ori_option,
|
||||
'{0}'.format(err),
|
||||
err_index)
|
||||
|
||||
def impl_validate(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag,
|
||||
check_error: bool=True) -> None:
|
||||
value: Any,
|
||||
option_bag: OptionBag,
|
||||
check_error: bool=True,
|
||||
loaded: bool=False,
|
||||
) -> None:
|
||||
"""Return True if value is really valid
|
||||
If not validate or invalid return it returns False
|
||||
"""
|
||||
|
@ -337,7 +282,6 @@ class Option(BaseOption):
|
|||
not 'validator' in config_bag.properties:
|
||||
return False
|
||||
|
||||
|
||||
def _is_not_unique(value, option_bag):
|
||||
# if set(value) has not same length than value
|
||||
if config_bag is undefined or not check_error or \
|
||||
|
@ -353,9 +297,11 @@ class Option(BaseOption):
|
|||
'').format(val))
|
||||
|
||||
def calculation_validator(val,
|
||||
_index):
|
||||
_index,
|
||||
):
|
||||
for validator in getattr(self, '_validators', []):
|
||||
calc_is_warnings_only = hasattr(validator, 'warnings_only') and validator.warnings_only
|
||||
calc_is_warnings_only = hasattr(validator, 'warnings_only') and \
|
||||
validator.warnings_only
|
||||
if ((check_error and not calc_is_warnings_only) or
|
||||
(not check_error and calc_is_warnings_only)):
|
||||
try:
|
||||
|
@ -374,26 +320,32 @@ class Option(BaseOption):
|
|||
**kwargs)
|
||||
except ValueWarning as warn:
|
||||
warnings.warn_explicit(ValueWarning(val,
|
||||
self._display_name,
|
||||
self.get_type(),
|
||||
self,
|
||||
'{0}'.format(warn),
|
||||
str(warn),
|
||||
_index),
|
||||
ValueWarning,
|
||||
self.__class__.__name__, 356)
|
||||
self.__class__.__name__, 319)
|
||||
|
||||
def do_validation(_value,
|
||||
_index):
|
||||
_index,
|
||||
):
|
||||
#
|
||||
if isinstance(_value, list):
|
||||
raise ValueError(_('which must not be a list').format(_value,
|
||||
self.impl_get_display_name()))
|
||||
self.impl_get_display_name()),
|
||||
)
|
||||
if isinstance(_value, Calculation) and config_bag is undefined:
|
||||
return False
|
||||
return
|
||||
|
||||
if _value is not None:
|
||||
if check_error:
|
||||
# option validation
|
||||
self.validate(_value)
|
||||
self.validate_with_option(_value,
|
||||
option_bag)
|
||||
option_bag,
|
||||
loaded=loaded,
|
||||
)
|
||||
if ((check_error and not is_warnings_only) or
|
||||
(not check_error and is_warnings_only)):
|
||||
try:
|
||||
|
@ -402,16 +354,18 @@ class Option(BaseOption):
|
|||
except ValueError as err:
|
||||
if is_warnings_only:
|
||||
warnings.warn_explicit(ValueWarning(_value,
|
||||
self._display_name,
|
||||
self.get_type(),
|
||||
self,
|
||||
'{0}'.format(err),
|
||||
str(err),
|
||||
_index),
|
||||
ValueWarning,
|
||||
self.__class__.__name__, 0)
|
||||
else:
|
||||
raise err
|
||||
calculation_validator(_value,
|
||||
_index)
|
||||
if not loaded:
|
||||
calculation_validator(_value,
|
||||
_index,
|
||||
)
|
||||
try:
|
||||
val = value
|
||||
err_index = force_index
|
||||
|
@ -423,11 +377,15 @@ class Option(BaseOption):
|
|||
raise ValueError(_('which must be a list'))
|
||||
for val in value:
|
||||
do_validation(val,
|
||||
force_index)
|
||||
_is_not_unique(value, option_bag)
|
||||
force_index,
|
||||
)
|
||||
_is_not_unique(value,
|
||||
option_bag,
|
||||
)
|
||||
else:
|
||||
do_validation(val,
|
||||
force_index)
|
||||
force_index,
|
||||
)
|
||||
elif isinstance(value, Calculation) and config_bag is undefined:
|
||||
pass
|
||||
elif not isinstance(value, list):
|
||||
|
@ -444,73 +402,68 @@ class Option(BaseOption):
|
|||
err_index)
|
||||
_is_not_unique(lval, option_bag)
|
||||
else:
|
||||
# FIXME subtimal, not several time is whole=True!
|
||||
# FIXME suboptimal, not several time is whole=True!
|
||||
for err_index, val in enumerate(value):
|
||||
do_validation(val,
|
||||
err_index)
|
||||
err_index,
|
||||
)
|
||||
_is_not_unique(value, option_bag)
|
||||
except ValueError as err:
|
||||
if config_bag is undefined or \
|
||||
'demoting_error_warning' not in config_bag.properties:
|
||||
raise ValueOptionError(val,
|
||||
self._display_name,
|
||||
self.get_type(),
|
||||
option_bag.ori_option,
|
||||
'{0}'.format(err),
|
||||
str(err),
|
||||
err_index) from err
|
||||
warnings.warn_explicit(ValueErrorWarning(val,
|
||||
self._display_name,
|
||||
self.get_type(),
|
||||
option_bag.ori_option,
|
||||
'{0}'.format(err),
|
||||
str(err),
|
||||
err_index),
|
||||
ValueErrorWarning,
|
||||
self.__class__.__name__, 0)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _validate_calculator(self,
|
||||
callback: Callable,
|
||||
callback_params: Optional[Params]=None) -> None:
|
||||
if callback is None:
|
||||
return
|
||||
default_multi = getattr(self, '_default_multi', None)
|
||||
is_multi = self.impl_is_multi()
|
||||
default = self.impl_getdefault()
|
||||
if (not is_multi and (default is not None or default_multi is not None)) or \
|
||||
(is_multi and (default != [] or default_multi is not None)):
|
||||
raise ValueError(_('default value not allowed if option "{0}" '
|
||||
'is calculated').format(self.impl_getname()))
|
||||
|
||||
def sync_validate_with_option(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag) -> None:
|
||||
pass
|
||||
|
||||
def validate_with_option(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag) -> None:
|
||||
pass
|
||||
option_bag: OptionBag,
|
||||
loaded: bool,
|
||||
) -> None:
|
||||
"""validation function with option
|
||||
"""
|
||||
|
||||
def second_level_validation(self,
|
||||
value: Any,
|
||||
warnings_only: bool) -> None:
|
||||
pass
|
||||
warnings_only: bool,
|
||||
) -> None:
|
||||
"""less import validation function
|
||||
"""
|
||||
|
||||
def impl_is_leader(self):
|
||||
"""check if option is a leader in a leadership
|
||||
"""
|
||||
leadership = self.impl_get_leadership()
|
||||
if leadership is None:
|
||||
return False
|
||||
return leadership.is_leader(self)
|
||||
|
||||
def impl_is_follower(self):
|
||||
"""check if option is a leader in a follower
|
||||
"""
|
||||
leadership = self.impl_get_leadership()
|
||||
if leadership is None:
|
||||
return False
|
||||
return not leadership.is_leader(self)
|
||||
|
||||
def impl_get_leadership(self):
|
||||
"""get leadership
|
||||
"""
|
||||
leadership = getattr(self, '_leadership', None)
|
||||
if leadership is None:
|
||||
return leadership
|
||||
#pylint: disable=not-callable
|
||||
return leadership()
|
||||
|
||||
def to_dynoption(self,
|
||||
|
@ -518,8 +471,14 @@ class Option(BaseOption):
|
|||
suffix: str,
|
||||
dyn_parent,
|
||||
) -> SynDynOption:
|
||||
"""tranforme a dynoption to a syndynoption
|
||||
"""
|
||||
return SynDynOption(self,
|
||||
rootpath,
|
||||
suffix,
|
||||
dyn_parent,
|
||||
)
|
||||
def validate(self, value: Any):
|
||||
"""option needs a validate function
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
|
|
@ -18,27 +18,31 @@
|
|||
# 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
|
||||
"""OptionDescription
|
||||
"""
|
||||
from typing import Optional, Iterator, Union, List
|
||||
|
||||
|
||||
from ..i18n import _
|
||||
from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined
|
||||
from .baseoption import BaseOption
|
||||
from .syndynoptiondescription import SynDynOptionDescription, SynDynLeadership
|
||||
from .syndynoptiondescription import SynDynOptionDescription
|
||||
from ..error import ConfigError, ConflictError
|
||||
|
||||
|
||||
class CacheOptionDescription(BaseOption):
|
||||
"""manage cache for option description
|
||||
"""
|
||||
__slots__ = ('_cache_force_store_values',
|
||||
'_cache_dependencies_information',
|
||||
)
|
||||
|
||||
def impl_already_build_caches(self) -> bool:
|
||||
"""is a readonly option?
|
||||
"""
|
||||
return self.impl_is_readonly()
|
||||
|
||||
def _build_cache(self,
|
||||
path='',
|
||||
_consistencies=None,
|
||||
_consistencies_id=0,
|
||||
currpath: List[str]=None,
|
||||
|
@ -49,6 +53,7 @@ class CacheOptionDescription(BaseOption):
|
|||
) -> None:
|
||||
"""validate options and set option has readonly option
|
||||
"""
|
||||
# pylint: disable=too-many-branches,too-many-arguments
|
||||
# _consistencies is None only when we start to build cache
|
||||
if _consistencies is None:
|
||||
init = True
|
||||
|
@ -65,15 +70,16 @@ class CacheOptionDescription(BaseOption):
|
|||
# cache already set
|
||||
raise ConfigError(_('option description seems to be part of an other '
|
||||
'config'))
|
||||
for option in self.get_children(config_bag=undefined,
|
||||
dyn=False):
|
||||
for option in self.get_children(config_bag=undefined, # pylint: disable=no-member
|
||||
dyn=False,
|
||||
):
|
||||
if __debug__:
|
||||
cache_option.append(option)
|
||||
sub_currpath = currpath + [option.impl_getname()]
|
||||
subpath = '.'.join(sub_currpath)
|
||||
if isinstance(option, OptionDescription):
|
||||
option._build_cache(subpath,
|
||||
_consistencies,
|
||||
# pylint: disable=protected-access
|
||||
option._build_cache(_consistencies,
|
||||
_consistencies_id,
|
||||
sub_currpath,
|
||||
cache_option,
|
||||
|
@ -84,7 +90,6 @@ class CacheOptionDescription(BaseOption):
|
|||
else:
|
||||
for information in option.get_dependencies_information():
|
||||
dependencies_information.setdefault(information, []).append(option)
|
||||
is_multi = option.impl_is_multi()
|
||||
if not option.impl_is_symlinkoption():
|
||||
properties = option.impl_getproperties()
|
||||
if 'force_store_value' in properties:
|
||||
|
@ -101,18 +106,21 @@ class CacheOptionDescription(BaseOption):
|
|||
if option.impl_is_readonly():
|
||||
raise ConflictError(_('duplicate option: {0}').format(option))
|
||||
if not self.impl_is_readonly() and display_name:
|
||||
option._display_name_function = display_name
|
||||
option._path = subpath
|
||||
option._set_readonly()
|
||||
option._display_name_function = display_name # pylint: disable=protected-access
|
||||
option._path = subpath # pylint: disable=protected-access
|
||||
option._set_readonly() # pylint: disable=protected-access
|
||||
if init:
|
||||
self._cache_force_store_values = force_store_values
|
||||
self._cache_dependencies_information = dependencies_information
|
||||
self._path = self._name
|
||||
self._cache_force_store_values = force_store_values # pylint: disable=attribute-defined-outside-init
|
||||
self._cache_dependencies_information = dependencies_information # pylint: disable=attribute-defined-outside-init
|
||||
self._path = self._name # pylint: disable=attribute-defined-outside-init,no-member
|
||||
self._set_readonly()
|
||||
|
||||
def impl_build_force_store_values(self,
|
||||
config_bag: ConfigBag,
|
||||
) -> None:
|
||||
"""set value to force_store_values option
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
def do_option_bags(option):
|
||||
if option.issubdyn():
|
||||
dynopt = option.getsubdyn()
|
||||
|
@ -146,7 +154,7 @@ class CacheOptionDescription(BaseOption):
|
|||
leader = option.impl_get_leadership().get_leader()
|
||||
for leader_option_bag in do_option_bags(leader):
|
||||
leader_option_bag.properties = frozenset()
|
||||
follower_len = len(values.getvalue(leader_option_bag))
|
||||
follower_len = len(values.get_value(leader_option_bag))
|
||||
if option.issubdyn():
|
||||
subpath = leader_option_bag.option.rootpath
|
||||
doption = option.to_dynoption(subpath,
|
||||
|
@ -164,7 +172,7 @@ class CacheOptionDescription(BaseOption):
|
|||
config_bag,
|
||||
properties=frozenset(),
|
||||
)
|
||||
value = values.getvalue(option_bag)
|
||||
value = values.get_value(option_bag)
|
||||
if value is None:
|
||||
continue
|
||||
values.set_storage_value(subpath,
|
||||
|
@ -175,7 +183,7 @@ class CacheOptionDescription(BaseOption):
|
|||
else:
|
||||
for option_bag in do_option_bags(option):
|
||||
option_bag.properties = frozenset()
|
||||
value = values.getvalue(option_bag)
|
||||
value = values.get_value(option_bag)
|
||||
if value is None:
|
||||
continue
|
||||
if values.hasvalue(option_bag.option.impl_getpath()):
|
||||
|
@ -188,6 +196,8 @@ class CacheOptionDescription(BaseOption):
|
|||
|
||||
|
||||
class OptionDescriptionWalk(CacheOptionDescription):
|
||||
"""get child of option description
|
||||
"""
|
||||
__slots__ = ('_children',)
|
||||
|
||||
def get_child(self,
|
||||
|
@ -195,16 +205,18 @@ class OptionDescriptionWalk(CacheOptionDescription):
|
|||
config_bag: ConfigBag,
|
||||
subpath: str,
|
||||
) -> Union[BaseOption, SynDynOptionDescription]:
|
||||
"""get a child
|
||||
"""
|
||||
# if not dyn
|
||||
if name in self._children[0]:
|
||||
option = self._children[1][self._children[0].index(name)]
|
||||
if name in self._children[0]: # pylint: disable=no-member
|
||||
option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member
|
||||
if option.issubdyn():
|
||||
raise AttributeError(_(f'unknown option "{name}" '
|
||||
"in root optiondescription (it's a dynamic option)"
|
||||
))
|
||||
return option
|
||||
# if dyn
|
||||
for child in self._children[1]:
|
||||
for child in self._children[1]: # pylint: disable=no-member
|
||||
if not child.impl_is_dynoptiondescription():
|
||||
continue
|
||||
cname = child.impl_getname()
|
||||
|
@ -217,7 +229,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
|
|||
suffix,
|
||||
child,
|
||||
)
|
||||
if self.impl_get_group_type() == groups.root:
|
||||
if self.impl_get_group_type() == groups.root: # pylint: disable=no-member
|
||||
raise AttributeError(_(f'unknown option "{name}" '
|
||||
'in root optiondescription'
|
||||
))
|
||||
|
@ -229,21 +241,22 @@ class OptionDescriptionWalk(CacheOptionDescription):
|
|||
config_bag: Union[ConfigBag, Undefined],
|
||||
dyn: bool=True,
|
||||
) -> Union[BaseOption, SynDynOptionDescription]:
|
||||
"""get children
|
||||
"""
|
||||
if not dyn or config_bag is undefined or \
|
||||
config_bag.context.get_description() == self:
|
||||
subpath = ''
|
||||
else:
|
||||
subpath = self.impl_getpath()
|
||||
children = []
|
||||
for child in self._children[1]:
|
||||
for child in self._children[1]: # pylint: disable=no-member
|
||||
if dyn and child.impl_is_dynoptiondescription():
|
||||
for suffix in child.get_suffixes(config_bag):
|
||||
children.append(child.to_dynoption(subpath,
|
||||
suffix,
|
||||
child))
|
||||
yield child.to_dynoption(subpath,
|
||||
suffix,
|
||||
child,
|
||||
)
|
||||
else:
|
||||
children.append(child)
|
||||
return children
|
||||
yield child
|
||||
|
||||
def get_children_recursively(self,
|
||||
bytype: Optional[BaseOption],
|
||||
|
@ -251,6 +264,8 @@ class OptionDescriptionWalk(CacheOptionDescription):
|
|||
config_bag: ConfigBag,
|
||||
self_opt: BaseOption=None,
|
||||
) -> Iterator[Union[BaseOption, SynDynOptionDescription]]:
|
||||
"""get children recursively
|
||||
"""
|
||||
if self_opt is None:
|
||||
self_opt = self
|
||||
for option in self_opt.get_children(config_bag):
|
||||
|
@ -308,8 +323,8 @@ class OptionDescription(OptionDescriptionWalk):
|
|||
if dynopt_names:
|
||||
for dynopt in dynopt_names:
|
||||
if child != dynopt and child.startswith(dynopt):
|
||||
raise ConflictError(_('the option\'s name "{}" start as '
|
||||
'the dynoptiondescription\'s name "{}"').format(child, dynopt))
|
||||
raise ConflictError(_(f'the option\'s name "{child}" start as '
|
||||
f'the dynoptiondescription\'s name "{dynopt}"'))
|
||||
old = child
|
||||
self._children = children_
|
||||
# the group_type is useful for filtering OptionDescriptions in a config
|
||||
|
@ -322,17 +337,24 @@ class OptionDescription(OptionDescriptionWalk):
|
|||
'dynoptiondescription'))
|
||||
|
||||
def impl_is_optiondescription(self) -> bool:
|
||||
"""the option is an option description
|
||||
"""
|
||||
return True
|
||||
|
||||
def impl_is_dynoptiondescription(self) -> bool:
|
||||
"""the option is not dynamic
|
||||
"""
|
||||
return False
|
||||
|
||||
def impl_is_leadership(self) -> bool:
|
||||
"""the option is not a leadership
|
||||
"""
|
||||
return False
|
||||
|
||||
# ____________________________________________________________
|
||||
def impl_set_group_type(self,
|
||||
group_type: groups.GroupType) -> None:
|
||||
group_type: groups.GroupType,
|
||||
) -> None:
|
||||
"""sets a given group object to an OptionDescription
|
||||
|
||||
:param group_type: an instance of `GroupType` or `LeadershipGroupType`
|
||||
|
@ -351,16 +373,22 @@ class OptionDescription(OptionDescriptionWalk):
|
|||
self._group_type = group_type
|
||||
|
||||
def impl_get_group_type(self) -> groups.GroupType:
|
||||
"""get the group type of option description
|
||||
"""
|
||||
return self._group_type
|
||||
|
||||
def to_dynoption(self,
|
||||
rootpath: str,
|
||||
suffix: str,
|
||||
ori_dyn) -> SynDynOptionDescription:
|
||||
"""get syn dyn option description
|
||||
"""
|
||||
return SynDynOptionDescription(self,
|
||||
rootpath,
|
||||
suffix,
|
||||
ori_dyn)
|
||||
|
||||
def impl_is_dynsymlinkoption(self) -> bool:
|
||||
"""option is not a dyn symlink option
|
||||
"""
|
||||
return False
|
||||
|
|
|
@ -18,15 +18,15 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""PasswordOption
|
||||
"""
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from .stroption import StrOption
|
||||
|
||||
|
||||
class PasswordOption(StrOption):
|
||||
"represents the choice of a password"
|
||||
"""represents the choice of a password
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'password'
|
||||
_display_name = _('password')
|
||||
_type = _('password')
|
||||
|
|
|
@ -18,12 +18,11 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""PermissionsOption
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from .intoption import IntOption
|
||||
|
||||
|
||||
|
@ -36,8 +35,7 @@ class PermissionsOption(IntOption):
|
|||
"""
|
||||
__slots__ = tuple()
|
||||
perm_re = re.compile(r"^[0-7]{3,4}$")
|
||||
_type = 'permissions'
|
||||
_display_name = _('unix file permissions')
|
||||
_type = _('unix file permissions')
|
||||
|
||||
def __init__(self,
|
||||
*args,
|
||||
|
@ -72,4 +70,4 @@ class PermissionsOption(IntOption):
|
|||
raise ValueError(_(f'{new} has more right than {old}'))
|
||||
old_digit = new_digit
|
||||
if str_value == '777':
|
||||
raise ValueError(_(f'too weak'))
|
||||
raise ValueError(_('too weak'))
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""PortOption
|
||||
"""
|
||||
import re
|
||||
|
||||
from ..i18n import _
|
||||
|
@ -36,8 +38,7 @@ class PortOption(StrOption):
|
|||
"""
|
||||
__slots__ = tuple()
|
||||
port_re = re.compile(r"^[0-9]*$")
|
||||
_type = 'port'
|
||||
_display_name = _('port')
|
||||
_port = _('port')
|
||||
|
||||
def __init__(self,
|
||||
*args,
|
||||
|
@ -81,7 +82,8 @@ class PortOption(StrOption):
|
|||
def validate(self,
|
||||
value: str) -> None:
|
||||
super().validate(value)
|
||||
if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or value.startswith('udp:')):
|
||||
if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or
|
||||
value.startswith('udp:')):
|
||||
value = [value[4:]]
|
||||
elif self.impl_get_extra('_allow_range') and ":" in str(value):
|
||||
value = value.split(':')
|
||||
|
@ -100,7 +102,8 @@ class PortOption(StrOption):
|
|||
def second_level_validation(self,
|
||||
value: str,
|
||||
warnings_only: bool) -> None:
|
||||
if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or value.startswith('udp:')):
|
||||
if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or
|
||||
value.startswith('udp:')):
|
||||
value = [value[4:]]
|
||||
elif ':' in value:
|
||||
value = value.split(':')
|
||||
|
|
|
@ -18,31 +18,40 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
import sys
|
||||
"""StrOption and RegexpOption
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
|
||||
|
||||
class StrOption(Option):
|
||||
"represents the choice of a string"
|
||||
"""represents a string
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'string'
|
||||
_display_name = _('string')
|
||||
_type = _('string')
|
||||
|
||||
def validate(self,
|
||||
value: str) -> None:
|
||||
value: str,
|
||||
) -> None:
|
||||
"""validation
|
||||
"""
|
||||
if not isinstance(value, str):
|
||||
raise ValueError()
|
||||
|
||||
|
||||
class RegexpOption(StrOption):
|
||||
"""regexp validation, this is base option use to do a custom's one
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
|
||||
def validate(self,
|
||||
value: Any) -> None:
|
||||
value: Any,
|
||||
) -> None:
|
||||
# pylint: disable=no-member
|
||||
"""validation
|
||||
"""
|
||||
super().validate(value)
|
||||
match = self._regexp.search(value)
|
||||
if not match:
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""SymLinkOption link to an other option
|
||||
"""
|
||||
from typing import Any
|
||||
from .baseoption import BaseOption, valid_name
|
||||
from ..error import ConfigError
|
||||
|
@ -25,11 +27,15 @@ from ..i18n import _
|
|||
|
||||
|
||||
class SymLinkOption(BaseOption):
|
||||
"""SymLinkOption link to an other option
|
||||
"""
|
||||
__slots__ = ('_opt',)
|
||||
|
||||
def __init__(self,
|
||||
name: str,
|
||||
opt: BaseOption) -> None:
|
||||
opt: BaseOption,
|
||||
) -> None:
|
||||
# pylint: disable=super-init-not-called
|
||||
if not valid_name(name):
|
||||
raise ValueError(_('"{0}" is an invalid name for an option').format(name))
|
||||
if not isinstance(opt, BaseOption) or \
|
||||
|
@ -55,27 +61,36 @@ class SymLinkOption(BaseOption):
|
|||
'dynoptiondescription'))
|
||||
|
||||
def impl_has_dependency(self,
|
||||
self_is_dep: bool=True) -> bool:
|
||||
self_is_dep: bool=True,
|
||||
) -> bool:
|
||||
"""If self_is_dep is True, it has dependency (self._opt), so return True
|
||||
if self_is_dep is False, cannot has validation or callback, so return False
|
||||
"""
|
||||
return self_is_dep
|
||||
|
||||
def impl_is_symlinkoption(self) -> bool:
|
||||
"""it's a symlinkoption
|
||||
"""
|
||||
return True
|
||||
|
||||
def impl_getopt(self) -> BaseOption:
|
||||
"""get to linked option
|
||||
"""
|
||||
return self._opt
|
||||
|
||||
def issubdyn(self) -> bool:
|
||||
"""it's not a sub dyn option
|
||||
"""
|
||||
return False
|
||||
|
||||
def impl_is_multi(self) -> bool:
|
||||
"""is it a multi?
|
||||
"""
|
||||
if self._opt.issubdyn():
|
||||
return True
|
||||
return self._opt.impl_is_multi()
|
||||
|
||||
def impl_is_submulti(self) -> bool:
|
||||
if self._opt.issubdyn() and self._opt.impl_is_multi():
|
||||
return True
|
||||
"""is it a submulti?
|
||||
"""
|
||||
return self._opt.impl_is_submulti()
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""SynDynOption internal option, it's an instanciate synoption
|
||||
"""
|
||||
from typing import Any
|
||||
from ..setting import undefined, OptionBag
|
||||
from .baseoption import BaseOption
|
||||
|
||||
|
||||
|
@ -49,35 +50,40 @@ class SynDynOption:
|
|||
name,
|
||||
)
|
||||
|
||||
def __eq__(self,
|
||||
left: BaseOption) -> bool:
|
||||
if not isinstance(left, SynDynOption):
|
||||
return False
|
||||
return self.opt == left.opt and \
|
||||
self.rootpath == left.rootpath and \
|
||||
self.suffix == left.suffix
|
||||
|
||||
def impl_getname(self) -> str:
|
||||
"""get option name
|
||||
"""
|
||||
return self.opt.impl_getname() + self.dyn_parent.convert_suffix_to_path(self.suffix)
|
||||
|
||||
def impl_get_display_name(self) -> str:
|
||||
return self.opt._get_display_name(dyn_name=self.impl_getname(),
|
||||
suffix=self.dyn_parent.convert_suffix_to_path(self.suffix),
|
||||
"""get option display name
|
||||
"""
|
||||
suffix = self.dyn_parent.convert_suffix_to_path(self.suffix)
|
||||
return self.opt._get_display_name(dyn_name=self.impl_getname(), # pylint: disable=protected-access
|
||||
suffix=suffix,
|
||||
)
|
||||
|
||||
def impl_getsuffix(self) -> str:
|
||||
"""get suffix
|
||||
"""
|
||||
return self.suffix
|
||||
|
||||
def impl_getpath(self) -> str:
|
||||
"""get path
|
||||
"""
|
||||
path = self.impl_getname()
|
||||
if self.rootpath:
|
||||
path = f'{self.rootpath}.{path}'
|
||||
return path
|
||||
|
||||
def impl_is_dynsymlinkoption(self) -> bool:
|
||||
"""it's a dynsymlinkoption
|
||||
"""
|
||||
return True
|
||||
|
||||
def impl_get_leadership(self):
|
||||
def impl_get_leadership(self): # pylint: disable=inconsistent-return-statements
|
||||
"""is it a leadership?
|
||||
"""
|
||||
leadership = self.opt.impl_get_leadership()
|
||||
if leadership:
|
||||
rootpath = self.rootpath.rsplit('.', 1)[0]
|
||||
|
|
|
@ -18,17 +18,21 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""SynDynOptionDescription and SynDynLeadership internal option
|
||||
it's an instanciate synoptiondescription
|
||||
"""
|
||||
from typing import Optional, Iterator, Any, List
|
||||
|
||||
|
||||
from ..i18n import _
|
||||
from ..setting import ConfigBag, groups, undefined, Settings
|
||||
from ..value import Values
|
||||
from ..setting import ConfigBag
|
||||
from .baseoption import BaseOption
|
||||
from .syndynoption import SynDynOption
|
||||
|
||||
|
||||
class SynDynOptionDescription:
|
||||
"""SynDynOptionDescription internal option, it's an instanciate synoptiondescription
|
||||
"""
|
||||
__slots__ = ('opt',
|
||||
'rootpath',
|
||||
'_suffix',
|
||||
|
@ -53,14 +57,14 @@ class SynDynOptionDescription:
|
|||
name,
|
||||
)
|
||||
|
||||
def impl_getopt(self) -> BaseOption:
|
||||
return self.opt
|
||||
|
||||
def get_child(self,
|
||||
name: str,
|
||||
config_bag: ConfigBag,
|
||||
subpath: str,
|
||||
) -> BaseOption:
|
||||
"""get child by name
|
||||
"""
|
||||
# pylint: disable=unused-argument
|
||||
suffix = self.ori_dyn.convert_suffix_to_path(self._suffix)
|
||||
if name.endswith(suffix):
|
||||
oname = name[:-len(suffix)]
|
||||
|
@ -78,15 +82,17 @@ class SynDynOptionDescription:
|
|||
'').format(name, self.impl_get_display_name()))
|
||||
|
||||
def impl_getname(self) -> str:
|
||||
"""get name
|
||||
"""
|
||||
return self.opt.impl_getname() + self.ori_dyn.convert_suffix_to_path(self._suffix)
|
||||
|
||||
def impl_is_dynoptiondescription(self) -> bool:
|
||||
return True
|
||||
|
||||
def get_children(self,
|
||||
config_bag: ConfigBag,
|
||||
dyn: bool=True,
|
||||
):
|
||||
# pylint: disable=unused-argument
|
||||
"""get children
|
||||
"""
|
||||
subpath = self.impl_getpath()
|
||||
children = []
|
||||
for child in self.opt.get_children(config_bag):
|
||||
|
@ -97,6 +103,8 @@ class SynDynOptionDescription:
|
|||
return children
|
||||
|
||||
def impl_is_dynsymlinkoption(self) -> bool:
|
||||
"""it's a dynsymlinkoption
|
||||
"""
|
||||
return True
|
||||
|
||||
def get_children_recursively(self,
|
||||
|
@ -105,6 +113,9 @@ class SynDynOptionDescription:
|
|||
config_bag: ConfigBag,
|
||||
self_opt: BaseOption=None,
|
||||
) -> BaseOption:
|
||||
# pylint: disable=unused-argument
|
||||
"""get children recursively
|
||||
"""
|
||||
for option in self.opt.get_children_recursively(bytype,
|
||||
byname,
|
||||
config_bag,
|
||||
|
@ -113,23 +124,33 @@ class SynDynOptionDescription:
|
|||
yield option
|
||||
|
||||
def impl_getpath(self) -> str:
|
||||
"""get path
|
||||
"""
|
||||
path = self.impl_getname()
|
||||
if self.rootpath:
|
||||
path = f'{self.rootpath}.{path}'
|
||||
return path
|
||||
|
||||
def impl_get_display_name(self) -> str:
|
||||
"""get display name
|
||||
"""
|
||||
return self.opt.impl_get_display_name() + str(self._suffix)
|
||||
|
||||
|
||||
class SynDynLeadership(SynDynOptionDescription):
|
||||
"""SynDynLeadership internal option, it's an instanciate synoptiondescription
|
||||
"""
|
||||
def get_leader(self) -> SynDynOption:
|
||||
"""get the leader
|
||||
"""
|
||||
return self.opt.get_leader().to_dynoption(self.impl_getpath(),
|
||||
self._suffix,
|
||||
self.ori_dyn,
|
||||
)
|
||||
|
||||
def get_followers(self) -> Iterator[SynDynOption]:
|
||||
"""get followers
|
||||
"""
|
||||
subpath = self.impl_getpath()
|
||||
for follower in self.opt.get_followers():
|
||||
yield follower.to_dynoption(subpath,
|
||||
|
@ -142,6 +163,8 @@ class SynDynLeadership(SynDynOptionDescription):
|
|||
config_bag: 'ConfigBag',
|
||||
resetted_opts: List[str],
|
||||
) -> None:
|
||||
"""reset cache
|
||||
"""
|
||||
leader = self.get_leader()
|
||||
followers = self.get_followers()
|
||||
self._reset_cache(path,
|
||||
|
@ -155,23 +178,27 @@ class SynDynLeadership(SynDynOptionDescription):
|
|||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""pop value for a follower
|
||||
"""
|
||||
self.opt.pop(*args,
|
||||
followers=self.get_followers(),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def follower_force_store_value(self,
|
||||
values,
|
||||
value,
|
||||
option_bag,
|
||||
config_bag,
|
||||
owner,
|
||||
) -> None:
|
||||
self.opt.follower_force_store_value(values,
|
||||
value,
|
||||
option_bag,
|
||||
"""force store value for a follower
|
||||
"""
|
||||
self.opt.follower_force_store_value(value,
|
||||
config_bag,
|
||||
owner,
|
||||
dyn=self,
|
||||
)
|
||||
|
||||
def impl_getsuffix(self) -> str:
|
||||
"""get suffix
|
||||
"""
|
||||
return self._suffix
|
||||
|
|
|
@ -18,22 +18,25 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""URLOption to check url value
|
||||
"""
|
||||
import re
|
||||
from typing import Any, Optional, List, Dict
|
||||
|
||||
from ..setting import undefined, Undefined, OptionBag
|
||||
from ..setting import undefined
|
||||
from ..i18n import _
|
||||
from .option import Option, Calculation
|
||||
from .option import Calculation
|
||||
from .stroption import StrOption
|
||||
from .domainnameoption import DomainnameOption
|
||||
from .portoption import PortOption
|
||||
|
||||
|
||||
class URLOption(StrOption):
|
||||
"""URLOption to check url value
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
|
||||
_type = 'url'
|
||||
_display_name = _('URL')
|
||||
_type = _('URL')
|
||||
|
||||
def __init__(self,
|
||||
name: str,
|
||||
|
@ -53,8 +56,7 @@ class URLOption(StrOption):
|
|||
allow_wellknown: bool=True,
|
||||
allow_registred: bool=True,
|
||||
allow_private: bool=False) -> None:
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
|
||||
extra = {'_domainname': DomainnameOption(name,
|
||||
doc,
|
||||
allow_ip=allow_ip,
|
||||
|
@ -80,10 +82,10 @@ class URLOption(StrOption):
|
|||
def _get_domain_port_files(self,
|
||||
value: str) -> (str, str):
|
||||
if value.startswith('http://'):
|
||||
type = 'http'
|
||||
type_ = 'http'
|
||||
value = value[7:]
|
||||
elif value.startswith('https://'):
|
||||
type = 'https'
|
||||
type_ = 'https'
|
||||
value = value[8:]
|
||||
else:
|
||||
raise ValueError(_('must start with http:// or '
|
||||
|
@ -100,7 +102,7 @@ class URLOption(StrOption):
|
|||
if len(splitted) == 1:
|
||||
domain = splitted[0]
|
||||
port = {'http': '80',
|
||||
'https': '443'}[type]
|
||||
'https': '443'}[type_]
|
||||
else:
|
||||
domain, port = splitted
|
||||
return domain, port, files
|
||||
|
@ -120,7 +122,7 @@ class URLOption(StrOption):
|
|||
raise ValueError(_('must ends with a valid resource name'))
|
||||
|
||||
def second_level_validation(self, value, warnings_only):
|
||||
domain, port, files = self._get_domain_port_files(value)
|
||||
domain, port, _ = self._get_domain_port_files(value)
|
||||
# validate port
|
||||
portoption = self.impl_get_extra('_port')
|
||||
portoption.second_level_validation(port, warnings_only)
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"""UsernameOption or GroupnameOption to check unix username/group value
|
||||
"""
|
||||
import re
|
||||
|
||||
from ..i18n import _
|
||||
|
@ -25,14 +27,16 @@ from .stroption import RegexpOption
|
|||
|
||||
|
||||
class UsernameOption(RegexpOption):
|
||||
"""UsernameOption to check unix username value
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
#regexp build with 'man 8 adduser' informations
|
||||
_regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
|
||||
_type = 'username'
|
||||
_display_name = _('unix username')
|
||||
_type = _('unix username')
|
||||
|
||||
|
||||
class GroupnameOption(UsernameOption):
|
||||
"""GroupnameOption to check unix group value
|
||||
"""
|
||||
__slots__ = tuple()
|
||||
_type = 'groupname'
|
||||
_display_name = _('unix groupname')
|
||||
_type = _('unix groupname')
|
||||
|
|
|
@ -15,94 +15,92 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ____________________________________________________________
|
||||
from typing import Union
|
||||
from typing import Union, Set
|
||||
from itertools import chain
|
||||
from .error import PropertiesOptionError, ConstError, ConfigError, LeadershipError, display_list
|
||||
from .error import PropertiesOptionError, ConstError, ConfigError, LeadershipError
|
||||
from .i18n import _
|
||||
|
||||
|
||||
"""If cache and expire is enable, time before cache is expired.
|
||||
This delay start first time value/setting is set in cache, even if
|
||||
user access several time to value/setting
|
||||
"""
|
||||
# If cache and expire is enable, time before cache is expired.
|
||||
# This delay start first time value/setting is set in cache, even if
|
||||
# user access several time to value/setting
|
||||
EXPIRATION_TIME = 5
|
||||
"""List of default properties (you can add new one if needed).
|
||||
|
||||
For common properties and personalise properties, if a propery is set for
|
||||
an Option and for the Config together, Setting raise a PropertiesOptionError
|
||||
|
||||
* Common properties:
|
||||
|
||||
hidden
|
||||
option with this property can only get value in read only mode. This
|
||||
option is not available in read write mode.
|
||||
|
||||
disabled
|
||||
option with this property cannot be set/get
|
||||
|
||||
frozen
|
||||
cannot set value for option with this properties if 'frozen' is set in
|
||||
config
|
||||
|
||||
* Special property:
|
||||
|
||||
permissive
|
||||
option with 'permissive' cannot raise PropertiesOptionError for properties
|
||||
set in permissive
|
||||
config with 'permissive', whole option in this config cannot raise
|
||||
PropertiesOptionError for properties set in permissive
|
||||
|
||||
mandatory
|
||||
should set value for option with this properties if 'mandatory' is set in
|
||||
config
|
||||
example: 'a', ['a'], [None] are valid
|
||||
None, [] are not valid
|
||||
|
||||
empty
|
||||
raise mandatory PropertiesOptionError if multi or leader have empty value
|
||||
example: ['a'] is valid
|
||||
[None] is not valid
|
||||
|
||||
unique
|
||||
raise ValueError if a value is set twice or more in a multi Option
|
||||
|
||||
* Special Config properties:
|
||||
|
||||
cache
|
||||
if set, enable cache settings and values
|
||||
|
||||
expire
|
||||
if set, settings and values in cache expire after ``expiration_time``
|
||||
|
||||
everything_frozen
|
||||
whole option in config are frozen (even if option have not frozen
|
||||
property)
|
||||
|
||||
validator
|
||||
launch validator set by user in option (this property has no effect
|
||||
for internal validator)
|
||||
|
||||
warnings
|
||||
display warnings during validation
|
||||
|
||||
demoting_error_warning
|
||||
all value errors are convert to warning (ValueErrorWarning)
|
||||
"""
|
||||
#List of default properties (you can add new one if needed).
|
||||
#
|
||||
#For common properties and personalise properties, if a propery is set for
|
||||
#an Option and for the Config together, Setting raise a PropertiesOptionError
|
||||
#
|
||||
#* Common properties:
|
||||
#
|
||||
#hidden
|
||||
# option with this property can only get value in read only mode. This
|
||||
# option is not available in read write mode.
|
||||
#
|
||||
#disabled
|
||||
# option with this property cannot be set/get
|
||||
#
|
||||
#frozen
|
||||
# cannot set value for option with this properties if 'frozen' is set in
|
||||
# config
|
||||
#
|
||||
#* Special property:
|
||||
#
|
||||
#permissive
|
||||
# option with 'permissive' cannot raise PropertiesOptionError for properties
|
||||
# set in permissive
|
||||
# config with 'permissive', whole option in this config cannot raise
|
||||
# PropertiesOptionError for properties set in permissive
|
||||
#
|
||||
#mandatory
|
||||
# should set value for option with this properties if 'mandatory' is set in
|
||||
# config
|
||||
# example: 'a', ['a'], [None] are valid
|
||||
# None, [] are not valid
|
||||
#
|
||||
#empty
|
||||
# raise mandatory PropertiesOptionError if multi or leader have empty value
|
||||
# example: ['a'] is valid
|
||||
# [None] is not valid
|
||||
#
|
||||
#unique
|
||||
# raise ValueError if a value is set twice or more in a multi Option
|
||||
#
|
||||
#* Special Config properties:
|
||||
#
|
||||
#cache
|
||||
# if set, enable cache settings and values
|
||||
#
|
||||
#expire
|
||||
# if set, settings and values in cache expire after ``expiration_time``
|
||||
#
|
||||
#everything_frozen
|
||||
# whole option in config are frozen (even if option have not frozen
|
||||
# property)
|
||||
#
|
||||
#validator
|
||||
# launch validator set by user in option (this property has no effect
|
||||
# for internal validator)
|
||||
#
|
||||
#warnings
|
||||
# display warnings during validation
|
||||
#
|
||||
#demoting_error_warning
|
||||
# all value errors are convert to warning (ValueErrorWarning)
|
||||
DEFAULT_PROPERTIES = frozenset(['cache', 'validator', 'warnings'])
|
||||
SPECIAL_PROPERTIES = {'frozen', 'mandatory', 'empty', 'force_store_value'}
|
||||
|
||||
"""Config can be in two defaut mode:
|
||||
|
||||
read_only
|
||||
you can get all variables not disabled but you cannot set any variables
|
||||
if a value has a callback without any value, callback is launch and value
|
||||
of this variable can change
|
||||
you cannot access to mandatory variable without values
|
||||
|
||||
read_write
|
||||
you can get all variables not disabled and not hidden
|
||||
you can set all variables not frozen
|
||||
"""
|
||||
#Config can be in two defaut mode:
|
||||
#
|
||||
#read_only
|
||||
# you can get all variables not disabled but you cannot set any variables
|
||||
# if a value has a callback without any value, callback is launch and value
|
||||
# of this variable can change
|
||||
# you cannot access to mandatory variable without values
|
||||
#
|
||||
#read_write
|
||||
# you can get all variables not disabled and not hidden
|
||||
# you can set all variables not frozen
|
||||
RO_APPEND = frozenset(['frozen',
|
||||
'disabled',
|
||||
'validator',
|
||||
|
@ -147,7 +145,10 @@ static_set = frozenset()
|
|||
|
||||
|
||||
# ____________________________________________________________
|
||||
class Undefined(object):
|
||||
class Undefined:
|
||||
"""Object undefined, means that there is not value
|
||||
"""
|
||||
# pylint: disable=too-few-public-methods
|
||||
def __str__(self): # pragma: no cover
|
||||
return 'Undefined'
|
||||
|
||||
|
@ -158,6 +159,8 @@ undefined = Undefined()
|
|||
|
||||
|
||||
class OptionBag:
|
||||
"""Object to store information for an option
|
||||
"""
|
||||
__slots__ = ('option', # current option
|
||||
'path',
|
||||
'index',
|
||||
|
@ -167,6 +170,7 @@ class OptionBag:
|
|||
'apply_requires', # apply requires or not for this option
|
||||
)
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self,
|
||||
option,
|
||||
index,
|
||||
|
@ -187,42 +191,39 @@ class OptionBag:
|
|||
self.path = path
|
||||
elif option:
|
||||
self.path = option.impl_getpath()
|
||||
if '.' not in self.path and option == config_bag.context.get_description():
|
||||
context = config_bag.context
|
||||
if '.' not in self.path and option == context.get_description():
|
||||
self.properties = None
|
||||
elif properties is undefined:
|
||||
self.properties = config_bag.context.get_settings().getproperties(self, apply_requires=apply_requires)
|
||||
settings = context.get_settings()
|
||||
self.properties = settings.getproperties(self,
|
||||
apply_requires=apply_requires,
|
||||
)
|
||||
if properties is not undefined:
|
||||
self.properties = properties
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key == 'ori_option':
|
||||
return self.option
|
||||
elif key == 'apply_requires':
|
||||
if key == 'apply_requires':
|
||||
return True
|
||||
return undefined
|
||||
|
||||
def __delattr__(self, key):
|
||||
if key in ['properties', 'permissives']:
|
||||
try:
|
||||
super().__delattr__(key)
|
||||
except AttributeError:
|
||||
pass
|
||||
return
|
||||
raise KeyError(_('cannot delete key "{}" for OptionBag').format(key)) # pragma: no cover
|
||||
return None
|
||||
|
||||
def copy(self):
|
||||
"""copy OptionBag
|
||||
"""
|
||||
option_bag = OptionBag(None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
for key in self.__slots__:
|
||||
if not hasattr(self, key):
|
||||
continue
|
||||
setattr(option_bag, key, getattr(self, key))
|
||||
return option_bag
|
||||
|
||||
|
||||
class ConfigBag:
|
||||
"""Object to store information for context
|
||||
"""
|
||||
__slots__ = ('context', # link to the current context
|
||||
'properties', # properties for current context
|
||||
'true_properties', # properties for current context
|
||||
|
@ -246,30 +247,37 @@ class ConfigBag:
|
|||
if key == 'true_properties':
|
||||
return self.properties
|
||||
if key == 'expiration_time':
|
||||
self.expiration_time = EXPIRATION_TIME
|
||||
self.expiration_time = EXPIRATION_TIME # pylint: disable=attribute-defined-outside-init
|
||||
return self.expiration_time
|
||||
if key == 'is_unrestraint':
|
||||
return False
|
||||
raise KeyError('unknown key "{}" for ConfigBag'.format(key)) # pragma: no cover
|
||||
raise KeyError(f'unknown key "{key}" for ConfigBag') # pragma: no cover
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
super().__setattr__(key, value)
|
||||
|
||||
def remove_warnings(self):
|
||||
def nowarnings(self):
|
||||
"""do not warnings
|
||||
"""
|
||||
self.properties = frozenset(self.properties - {'warnings'})
|
||||
|
||||
def remove_validation(self):
|
||||
"""do not validate option
|
||||
"""
|
||||
self.properties = frozenset(self.properties - {'validator'})
|
||||
|
||||
def unrestraint(self):
|
||||
self.is_unrestraint = True
|
||||
self.true_properties = self.properties
|
||||
"""do not restraint access to option
|
||||
"""
|
||||
self.is_unrestraint = True # pylint: disable=attribute-defined-outside-init
|
||||
self.true_properties = self.properties # pylint: disable=attribute-defined-outside-init
|
||||
self.properties = frozenset(['cache'])
|
||||
|
||||
def set_permissive(self):
|
||||
"""set permissive
|
||||
"""
|
||||
self.properties = frozenset(self.properties | {'permissive'})
|
||||
|
||||
def copy(self):
|
||||
"""copy the config
|
||||
"""
|
||||
kwargs = {}
|
||||
for key in self.__slots__:
|
||||
kwargs[key] = getattr(self, key)
|
||||
|
@ -277,7 +285,7 @@ class ConfigBag:
|
|||
|
||||
|
||||
# ____________________________________________________________
|
||||
class _NameSpace(object):
|
||||
class _NameSpace:
|
||||
"""convenient class that emulates a module
|
||||
and builds constants (that is, unique names)
|
||||
when attribute is added, we cannot delete it
|
||||
|
@ -299,28 +307,27 @@ class _NameSpace(object):
|
|||
|
||||
class GroupModule(_NameSpace):
|
||||
"emulates a module to manage unique group (OptionDescription) names"
|
||||
# pylint: disable=too-few-public-methods
|
||||
class GroupType(str):
|
||||
"""allowed normal group (OptionDescription) names
|
||||
*normal* means : groups that are not leader
|
||||
"""
|
||||
pass
|
||||
|
||||
class DefaultGroupType(GroupType):
|
||||
"""groups that are default (typically 'default')"""
|
||||
pass
|
||||
|
||||
class LeadershipGroupType(GroupType):
|
||||
"""allowed normal group (OptionDescription) names
|
||||
*leadership* means : groups that have the 'leadership' attribute set
|
||||
"""
|
||||
pass
|
||||
|
||||
class RootGroupType(GroupType):
|
||||
"""root means this is the root optiondescription of whole config
|
||||
"""
|
||||
pass
|
||||
|
||||
def addgroup(self, name):
|
||||
"""add a new group type
|
||||
"""
|
||||
setattr(groups, name, groups.GroupType(name))
|
||||
|
||||
|
||||
|
@ -329,14 +336,13 @@ class OwnerModule(_NameSpace):
|
|||
|
||||
owners are living in `Config._value_owners`
|
||||
"""
|
||||
# pylint: disable=too-few-public-methods
|
||||
class Owner(str):
|
||||
"""allowed owner names
|
||||
"""
|
||||
pass
|
||||
|
||||
class DefaultOwner(Owner):
|
||||
"""groups that are default (typically 'default')"""
|
||||
pass
|
||||
|
||||
def addowner(self, name):
|
||||
"""
|
||||
|
@ -348,41 +354,39 @@ class OwnerModule(_NameSpace):
|
|||
# ____________________________________________________________
|
||||
# populate groups
|
||||
groups = GroupModule()
|
||||
"""groups.default
|
||||
default group set when creating a new optiondescription"""
|
||||
groups.default = groups.DefaultGroupType('default')
|
||||
|
||||
"""groups.leadership
|
||||
leadership group is a special optiondescription, all suboptions should
|
||||
be multi option and all values should have same length, to find
|
||||
leader's option, the optiondescription's name should be same than de
|
||||
leader's option"""
|
||||
groups.leadership = groups.LeadershipGroupType('leadership')
|
||||
# groups.default: default group set when creating a new optiondescription
|
||||
groups.default = groups.DefaultGroupType('default') # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
""" groups.root
|
||||
this group is the root optiondescription of whole config"""
|
||||
groups.root = groups.RootGroupType('root')
|
||||
# groups.leadership: leadership group is a special optiondescription, all suboptions should
|
||||
# be multi option and all values should have same length, to find
|
||||
# leader's option, the optiondescription's name should be same than de
|
||||
# leader's option"""
|
||||
groups.leadership = groups.LeadershipGroupType('leadership') # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
# groups.root: this group is the root optiondescription of whole config
|
||||
groups.root = groups.RootGroupType('root') # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
|
||||
# ____________________________________________________________
|
||||
# populate owners with default attributes
|
||||
owners = OwnerModule()
|
||||
"""default
|
||||
is the config owner after init time"""
|
||||
owners.default = owners.DefaultOwner('default')
|
||||
"""user
|
||||
is the generic is the generic owner"""
|
||||
owners.user = owners.Owner('user')
|
||||
"""forced
|
||||
special owner when value is forced"""
|
||||
owners.forced = owners.Owner('forced')
|
||||
|
||||
# default: is the config owner after init time
|
||||
owners.default = owners.DefaultOwner('default') # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
# user: is the generic is the generic owner
|
||||
owners.addowner('user')
|
||||
|
||||
#forced: special owner when value is forced
|
||||
owners.addowner('forced')
|
||||
|
||||
|
||||
forbidden_owners = (owners.default, owners.forced)
|
||||
forbidden_owners = (owners.default, owners.forced) # pylint: disable=no-member
|
||||
|
||||
|
||||
# ____________________________________________________________
|
||||
class Settings(object):
|
||||
class Settings:
|
||||
"``config.Config()``'s configuration options settings"
|
||||
__slots__ = ('_properties',
|
||||
'_permissives',
|
||||
|
@ -417,33 +421,33 @@ class Settings(object):
|
|||
def get_context_properties(self,
|
||||
cache,
|
||||
):
|
||||
is_cached, props, validated = cache.getcache(None,
|
||||
None,
|
||||
None,
|
||||
{},
|
||||
{},
|
||||
'context_props',
|
||||
)
|
||||
"""get context properties
|
||||
"""
|
||||
is_cached, props, _ = cache.getcache(None,
|
||||
'context_props',
|
||||
expiration=False,
|
||||
)
|
||||
if not is_cached:
|
||||
props = self._properties.get(None, {}).get(None, self.default_properties)
|
||||
props = self.get_stored_properties(None,
|
||||
None,
|
||||
self.default_properties,
|
||||
)
|
||||
cache.setcache(None,
|
||||
None,
|
||||
props,
|
||||
{},
|
||||
props,
|
||||
True)
|
||||
type_='properties',
|
||||
)
|
||||
return props
|
||||
|
||||
def _getproperties(self,
|
||||
path,
|
||||
index,
|
||||
default_properties,
|
||||
):
|
||||
if path not in self._properties:
|
||||
ret = frozenset(default_properties)
|
||||
else:
|
||||
ret = self._properties[path].get(index, frozenset(default_properties))
|
||||
return ret
|
||||
def get_stored_properties(self,
|
||||
path: Union[None, str],
|
||||
index: Union[None, int],
|
||||
default_properties: Set[str],
|
||||
) -> Set[str]:
|
||||
"""Get the properties modified by user for a path or index
|
||||
"""
|
||||
if path not in self._properties or index not in self._properties[path]:
|
||||
return frozenset(default_properties)
|
||||
return self._properties[path][index]
|
||||
|
||||
def getproperties(self,
|
||||
option_bag,
|
||||
|
@ -451,21 +455,15 @@ class Settings(object):
|
|||
uncalculated=False,
|
||||
help_property=False,
|
||||
):
|
||||
"""get properties
|
||||
"""
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
option = option_bag.option
|
||||
config_bag = option_bag.config_bag
|
||||
if option.impl_is_symlinkoption():
|
||||
option = option.impl_getopt()
|
||||
path = option.impl_getpath()
|
||||
index = option_bag.index
|
||||
if apply_requires and not uncalculated and not help_property:
|
||||
cache = config_bag.context.properties_cache
|
||||
is_cached, props, validated = cache.getcache(path,
|
||||
config_bag.expiration_time,
|
||||
index,
|
||||
config_bag.properties,
|
||||
{},
|
||||
cache = option_bag.config_bag.context.properties_cache
|
||||
is_cached, props, validated = cache.getcache(option_bag, # pylint: disable=unused-variable
|
||||
'self_props',
|
||||
)
|
||||
else:
|
||||
|
@ -473,13 +471,16 @@ class Settings(object):
|
|||
if not is_cached:
|
||||
props = set()
|
||||
# if index, get option's properties (without index) too
|
||||
p_props = self._getproperties(path,
|
||||
None,
|
||||
option.impl_getproperties(),
|
||||
)
|
||||
if index is not None:
|
||||
p_props = self.get_stored_properties(option_bag.path,
|
||||
None,
|
||||
option.impl_getproperties(),
|
||||
)
|
||||
if option_bag.index is not None:
|
||||
p_props = chain(p_props,
|
||||
self._properties.get(path, {}).get(index, option.impl_getproperties())
|
||||
self.get_stored_properties(option_bag.path,
|
||||
option_bag.index,
|
||||
option.impl_getproperties(),
|
||||
)
|
||||
)
|
||||
for prop in p_props:
|
||||
if uncalculated or isinstance(prop, str):
|
||||
|
@ -502,43 +503,29 @@ class Settings(object):
|
|||
new_prop = (new_prop, new_prop)
|
||||
if new_prop is None:
|
||||
continue
|
||||
elif (not help_property and not isinstance(new_prop, str)) or \
|
||||
if (not help_property and not isinstance(new_prop, str)) or \
|
||||
(help_property and not isinstance(new_prop, tuple)):
|
||||
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_prop),
|
||||
option_bag.option.impl_getname(),
|
||||
prop.function.__name__))
|
||||
raise ValueError(_('invalid property type {type(new_prop)} for '
|
||||
'{option_bag.option.impl_getname()} with '
|
||||
'{prop.function.__name__} function'))
|
||||
if not option.impl_is_optiondescription() and \
|
||||
option.impl_is_leader() and \
|
||||
new_prop not in ALLOWED_LEADER_PROPERTIES:
|
||||
raise LeadershipError(_('leader cannot have "{}" property').format(new_prop))
|
||||
raise LeadershipError(_('leader cannot have "{new_prop}" property'))
|
||||
props.add(new_prop)
|
||||
props -= self.getpermissives(option_bag)
|
||||
if not uncalculated and apply_requires and not config_bag.is_unrestraint and not help_property:
|
||||
cache.setcache(path,
|
||||
index,
|
||||
if not uncalculated and apply_requires and \
|
||||
not option_bag.config_bag.is_unrestraint and \
|
||||
not help_property:
|
||||
cache.setcache(option_bag,
|
||||
props,
|
||||
props,
|
||||
config_bag.properties,
|
||||
True)
|
||||
type_='properties',
|
||||
)
|
||||
return props
|
||||
|
||||
def has_properties_index(self,
|
||||
option_bag):
|
||||
option = option_bag.option
|
||||
if option.impl_is_symlinkoption():
|
||||
option = option.impl_getopt()
|
||||
path = option.impl_getpath()
|
||||
p_props = self._properties.get(path, {}).get(None, option.impl_getproperties())
|
||||
if option_bag.index is not None:
|
||||
p_props = chain(p_props,
|
||||
self._properties.get(path, {}).get(option_bag.index, option.impl_getproperties()),
|
||||
)
|
||||
for prop in p_props:
|
||||
if not isinstance(prop, str) and prop.has_index(option_bag.option):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_context_permissives(self):
|
||||
"""get context permissives
|
||||
"""
|
||||
return self.getpermissives(None)
|
||||
|
||||
def _getpermissives(self,
|
||||
|
@ -554,6 +541,8 @@ class Settings(object):
|
|||
def getpermissives(self,
|
||||
option_bag,
|
||||
):
|
||||
"""get permissive
|
||||
"""
|
||||
if option_bag is None:
|
||||
path = None
|
||||
index = None
|
||||
|
@ -576,6 +565,8 @@ class Settings(object):
|
|||
#____________________________________________________________
|
||||
# set methods
|
||||
def set_context_properties(self, properties, context):
|
||||
"""set context properties
|
||||
"""
|
||||
self._properties.setdefault(None, {})[None] = properties
|
||||
context.reset_cache(None)
|
||||
|
||||
|
@ -587,21 +578,16 @@ class Settings(object):
|
|||
(never save properties if same has option properties)
|
||||
"""
|
||||
opt = option_bag.option
|
||||
if opt.impl_is_symlinkoption():
|
||||
raise TypeError(_("can't assign property to the symlinkoption \"{}\""
|
||||
"").format(opt.impl_get_display_name()))
|
||||
if not opt.impl_is_optiondescription() and opt.impl_is_leader():
|
||||
not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
|
||||
if not_allowed_properties:
|
||||
if len(not_allowed_properties) == 1:
|
||||
raise LeadershipError(_('leader cannot have "{}" property').format(list(not_allowed_properties)[0]))
|
||||
else:
|
||||
raise LeadershipError(_('leader cannot have {} properties').format(display_list(list(not_allowed_properties), add_quote=True)))
|
||||
if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \
|
||||
'frozen' not in properties:
|
||||
raise LeadershipError(_('a leader ({0}) cannot have '
|
||||
'"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
|
||||
'').format(opt.impl_get_display_name()))
|
||||
raise LeadershipError(_('leader cannot have "{list(not_allowed_properties)}" '
|
||||
'property'))
|
||||
if ('force_default_on_freeze' in properties or \
|
||||
'force_metaconfig_on_freeze' in properties) and 'frozen' not in properties:
|
||||
raise LeadershipError(_('a leader ({opt.impl_get_display_name()}) cannot have '
|
||||
'"force_default_on_freeze" or '
|
||||
'"force_metaconfig_on_freeze" property without "frozen"'))
|
||||
self._properties.setdefault(option_bag.path, {})[option_bag.index] = properties
|
||||
# values too because of follower values could have a PropertiesOptionError has value
|
||||
option_bag.config_bag.context.reset_cache(option_bag)
|
||||
|
@ -610,6 +596,8 @@ class Settings(object):
|
|||
def set_context_permissives(self,
|
||||
permissives,
|
||||
):
|
||||
"""set context permissive
|
||||
"""
|
||||
self.setpermissives(None,
|
||||
permissives,
|
||||
)
|
||||
|
@ -630,10 +618,6 @@ class Settings(object):
|
|||
if not isinstance(permissives, frozenset):
|
||||
raise TypeError(_('permissive must be a frozenset'))
|
||||
if option_bag is not None:
|
||||
opt = option_bag.option
|
||||
if opt and opt.impl_is_symlinkoption():
|
||||
raise TypeError(_("can't assign permissive to the symlinkoption \"{}\""
|
||||
"").format(opt.impl_get_display_name()))
|
||||
path = option_bag.path
|
||||
index = option_bag.index
|
||||
else:
|
||||
|
@ -670,25 +654,29 @@ class Settings(object):
|
|||
def reset(self,
|
||||
bag: Union[OptionBag, ConfigBag],
|
||||
):
|
||||
"""reset property
|
||||
"""
|
||||
path, index, config_bag, option_bag = \
|
||||
self._get_path_index_config_option(bag,
|
||||
"can't reset properties to "
|
||||
"the symlinkoption \"{}\"",
|
||||
)
|
||||
if path in self._properties and index in self._properties[path]:
|
||||
del(self._properties[path][index])
|
||||
del self._properties[path][index]
|
||||
config_bag.context.reset_cache(option_bag)
|
||||
|
||||
def reset_permissives(self,
|
||||
bag: Union[OptionBag, ConfigBag],
|
||||
):
|
||||
"""reset permission
|
||||
"""
|
||||
path, index, config_bag, option_bag = \
|
||||
self._get_path_index_config_option(bag,
|
||||
"can't reset permissives to "
|
||||
"the symlinkoption \"{}\"",
|
||||
)
|
||||
if path in self._permissives and index in self._permissives[path]:
|
||||
del(self._permissives[path][index])
|
||||
del self._permissives[path][index]
|
||||
config_bag.context.reset_cache(option_bag)
|
||||
|
||||
#____________________________________________________________
|
||||
|
@ -698,6 +686,8 @@ class Settings(object):
|
|||
apply_requires=True,
|
||||
uncalculated=False,
|
||||
):
|
||||
"""raise if needed
|
||||
"""
|
||||
if not uncalculated and apply_requires:
|
||||
option_properties = option_bag.properties
|
||||
else:
|
||||
|
@ -707,12 +697,14 @@ class Settings(object):
|
|||
)
|
||||
return self._calc_raises_properties(option_bag.config_bag.properties,
|
||||
option_bag.config_bag.permissives,
|
||||
option_properties)
|
||||
option_properties,
|
||||
)
|
||||
|
||||
def _calc_raises_properties(self,
|
||||
context_properties,
|
||||
context_permissives,
|
||||
option_properties):
|
||||
option_properties,
|
||||
):
|
||||
raises_properties = context_properties - SPECIAL_PROPERTIES
|
||||
# remove global permissive properties
|
||||
if raises_properties and 'permissive' in raises_properties:
|
||||
|
@ -725,6 +717,8 @@ class Settings(object):
|
|||
option_bag,
|
||||
need_help=True,
|
||||
):
|
||||
"""check properties
|
||||
"""
|
||||
config_properties = option_bag.config_bag.properties
|
||||
if not config_properties or config_properties == frozenset(['cache']):
|
||||
# if no global property
|
||||
|
@ -747,49 +741,52 @@ class Settings(object):
|
|||
raise PropertiesOptionError(option_bag,
|
||||
properties,
|
||||
self,
|
||||
help_properties=calc_properties)
|
||||
help_properties=calc_properties,
|
||||
)
|
||||
|
||||
def validate_mandatory(self,
|
||||
value,
|
||||
option_bag,
|
||||
):
|
||||
if 'mandatory' in option_bag.config_bag.properties:
|
||||
values = option_bag.config_bag.context.get_values()
|
||||
if option_bag.option.impl_is_follower():
|
||||
force_allow_empty_list = True
|
||||
else:
|
||||
force_allow_empty_list = False
|
||||
if not ('permissive' in option_bag.config_bag.properties and
|
||||
'mandatory' in option_bag.config_bag.permissives) and \
|
||||
'mandatory' in option_bag.properties and values.isempty(option_bag.option,
|
||||
value,
|
||||
force_allow_empty_list=force_allow_empty_list,
|
||||
index=option_bag.index,
|
||||
):
|
||||
raise PropertiesOptionError(option_bag,
|
||||
['mandatory'],
|
||||
self,
|
||||
)
|
||||
if 'empty' in option_bag.properties and values.isempty(option_bag.option,
|
||||
value,
|
||||
force_allow_empty_list=True,
|
||||
index=option_bag.index,
|
||||
):
|
||||
raise PropertiesOptionError(option_bag,
|
||||
['empty'],
|
||||
self,
|
||||
)
|
||||
"""verify if option is mandatory without value
|
||||
"""
|
||||
if 'mandatory' not in option_bag.config_bag.properties:
|
||||
return
|
||||
values = option_bag.config_bag.context.get_values()
|
||||
if not ('permissive' in option_bag.config_bag.properties and
|
||||
'mandatory' in option_bag.config_bag.permissives) and \
|
||||
'mandatory' in option_bag.properties and values.isempty(option_bag,
|
||||
value,
|
||||
False,
|
||||
):
|
||||
raise PropertiesOptionError(option_bag,
|
||||
['mandatory'],
|
||||
self,
|
||||
)
|
||||
if 'empty' in option_bag.properties and values.isempty(option_bag,
|
||||
value,
|
||||
True,
|
||||
):
|
||||
raise PropertiesOptionError(option_bag,
|
||||
['empty'],
|
||||
self,
|
||||
)
|
||||
|
||||
def validate_frozen(self,
|
||||
option_bag):
|
||||
option_bag,
|
||||
):
|
||||
"""verify if option is frozen
|
||||
"""
|
||||
if option_bag.config_bag.properties and \
|
||||
('everything_frozen' in option_bag.config_bag.properties or
|
||||
('frozen' in option_bag.config_bag.properties and 'frozen' in option_bag.properties)) and \
|
||||
('frozen' in option_bag.config_bag.properties and \
|
||||
'frozen' in option_bag.properties)) and \
|
||||
not (('permissive' in option_bag.config_bag.properties) and
|
||||
'frozen' in option_bag.config_bag.permissives):
|
||||
raise PropertiesOptionError(option_bag,
|
||||
['frozen'],
|
||||
self)
|
||||
self,
|
||||
)
|
||||
return False
|
||||
#____________________________________________________________
|
||||
# read only/read write
|
||||
|
@ -799,7 +796,10 @@ class Settings(object):
|
|||
append,
|
||||
config_bag,
|
||||
):
|
||||
props = self._properties.get(None, {}).get(None, self.default_properties)
|
||||
props = self.get_stored_properties(None,
|
||||
None,
|
||||
self.default_properties,
|
||||
)
|
||||
modified = False
|
||||
if remove & props:
|
||||
props = props - remove
|
||||
|
|
|
@ -207,7 +207,7 @@ class Requires(object):
|
|||
else:
|
||||
act = 'hide'
|
||||
inv_act = 'show'
|
||||
if option.get_type() == 'choice':
|
||||
if isinstance(option, ChoiceOption):
|
||||
require_option = self.tiramisu_web.config.unrestraint.option(option_path)
|
||||
values = self.tiramisu_web.get_enum(require_option,
|
||||
require_option.option.ismulti(),
|
||||
|
|
|
@ -15,39 +15,37 @@
|
|||
# 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/>.
|
||||
# ____________________________________________________________
|
||||
import weakref
|
||||
from typing import Optional, Any, Callable
|
||||
from .error import ConfigError, PropertiesOptionError
|
||||
from .setting import owners, undefined, forbidden_owners, OptionBag, ConfigBag
|
||||
from .autolib import Calculation, carry_out_calculation, Params
|
||||
from typing import Union, Optional, List, Any
|
||||
from .error import ConfigError
|
||||
from .setting import owners, undefined, forbidden_owners, OptionBag
|
||||
from .autolib import Calculation
|
||||
from .i18n import _
|
||||
|
||||
|
||||
class Values:
|
||||
"""The `Config`'s root is indeed in charge of the `Option()`'s values,
|
||||
but the values are physicaly located here, in `Values`, wich is also
|
||||
responsible of a caching utility.
|
||||
"""This class manage value (default value, stored value or calculated value
|
||||
It's also responsible of a caching utility.
|
||||
"""
|
||||
# pylint: disable=too-many-public-methods
|
||||
__slots__ = ('_values',
|
||||
'_informations',
|
||||
'__weakref__',
|
||||
)
|
||||
|
||||
def __init__(self,
|
||||
default_values=None,
|
||||
):
|
||||
default_values: Union[None, dict]=None,
|
||||
) -> None:
|
||||
"""
|
||||
Initializes the values's dict.
|
||||
|
||||
:param storage: where values or owners are stored
|
||||
:param default_values: values stored by default for this object
|
||||
|
||||
"""
|
||||
self._informations = {}
|
||||
# set default owner
|
||||
if not default_values:
|
||||
self._values = {None: {None: [None, owners.user]}}
|
||||
else:
|
||||
self._values = default_values
|
||||
default_values = {None: {None: [None, owners.user]}}
|
||||
self._values = default_values
|
||||
|
||||
#______________________________________________________________________
|
||||
# get value
|
||||
|
@ -61,17 +59,13 @@ class Values:
|
|||
"""
|
||||
# try to retrive value in cache
|
||||
setting_properties = option_bag.config_bag.properties
|
||||
cache = option_bag.config_bag.context._impl_values_cache
|
||||
is_cached, value, validated = cache.getcache(option_bag.path,
|
||||
option_bag.config_bag.expiration_time,
|
||||
option_bag.index,
|
||||
setting_properties,
|
||||
option_bag.properties,
|
||||
'value',
|
||||
cache = option_bag.config_bag.context.get_values_cache()
|
||||
is_cached, value, validated = cache.getcache(option_bag,
|
||||
'values',
|
||||
)
|
||||
# no cached value so get value
|
||||
if not is_cached:
|
||||
value = self.getvalue(option_bag)
|
||||
value = self.get_value(option_bag)
|
||||
# validates and warns value
|
||||
if not validated:
|
||||
validate = option_bag.option.impl_validate(value,
|
||||
|
@ -85,12 +79,9 @@ class Values:
|
|||
)
|
||||
# set value to cache
|
||||
if not is_cached:
|
||||
cache.setcache(option_bag.path,
|
||||
option_bag.index,
|
||||
cache.setcache(option_bag,
|
||||
value,
|
||||
option_bag.properties,
|
||||
setting_properties,
|
||||
validate,
|
||||
validated=validate,
|
||||
)
|
||||
if isinstance(value, list):
|
||||
# return a copy, so value cannot be modified
|
||||
|
@ -98,81 +89,28 @@ class Values:
|
|||
# and return it
|
||||
return value
|
||||
|
||||
def force_to_metaconfig(self, option_bag):
|
||||
# force_metaconfig_on_freeze in config => to metaconfig
|
||||
# force_metaconfig_on_freeze in option + config is kernelconfig => to metaconfig
|
||||
settings = option_bag.config_bag.context.get_settings()
|
||||
if 'force_metaconfig_on_freeze' in option_bag.properties:
|
||||
settings = option_bag.config_bag.context.get_settings()
|
||||
if 'force_metaconfig_on_freeze' in option_bag.option.impl_getproperties() and \
|
||||
not settings._properties.get(option_bag.path, {}).get(None, frozenset()):
|
||||
# if force_metaconfig_on_freeze is only in option (not in config)
|
||||
return option_bag.config_bag.context.impl_type == 'config'
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _do_value_list(self,
|
||||
value: Any,
|
||||
option_bag: OptionBag,
|
||||
):
|
||||
ret = []
|
||||
for val in value:
|
||||
if isinstance(val, (list, tuple)):
|
||||
ret.append(self._do_value_list(val, option_bag))
|
||||
elif isinstance(val, Calculation):
|
||||
ret.append(val.execute(option_bag))
|
||||
else:
|
||||
ret.append(val)
|
||||
return ret
|
||||
|
||||
def getvalue(self,
|
||||
option_bag,
|
||||
):
|
||||
"""actually retrieves the value
|
||||
|
||||
:param path: the path of the `Option`
|
||||
:param index: index for a follower `Option`
|
||||
def get_value(self,
|
||||
option_bag: OptionBag,
|
||||
) -> Any:
|
||||
"""actually retrieves the stored value or the default value (value modified by user)
|
||||
|
||||
:returns: value
|
||||
"""
|
||||
|
||||
# get owner and value from store
|
||||
# index allowed only for follower
|
||||
index = option_bag.index
|
||||
is_follower = option_bag.option.impl_is_follower()
|
||||
if index is None or not is_follower:
|
||||
_index = None
|
||||
else:
|
||||
_index = index
|
||||
value, owner = self._values.get(option_bag.path, {}).get(_index, [undefined, owners.default])
|
||||
default_value = [undefined, owners.default]
|
||||
value, owner = self._values.get(option_bag.path, {}).get(option_bag.index, default_value)
|
||||
if owner == owners.default or \
|
||||
('frozen' in option_bag.properties and \
|
||||
('force_default_on_freeze' in option_bag.properties or self.force_to_metaconfig(option_bag))):
|
||||
value = self.getdefaultvalue(option_bag)
|
||||
else:
|
||||
value = self.calc_value(option_bag, value)
|
||||
('force_default_on_freeze' in option_bag.properties or \
|
||||
self.check_force_to_metaconfig(option_bag))):
|
||||
# the value is a default value
|
||||
# get it
|
||||
value = self.get_default_value(option_bag)
|
||||
return value
|
||||
|
||||
def calc_value(self,
|
||||
option_bag,
|
||||
value,
|
||||
reset_cache=True):
|
||||
if isinstance(value, Calculation):
|
||||
try:
|
||||
value = value.execute(option_bag)
|
||||
except ConfigError as err:
|
||||
msg = _(f'error when calculating "{option_bag.option.impl_get_display_name()}": {err} : {option_bag.path}')
|
||||
raise ConfigError(msg) from err
|
||||
elif isinstance(value, (list, tuple)):
|
||||
value = self._do_value_list(value, option_bag)
|
||||
if reset_cache:
|
||||
self.calculate_reset_cache(option_bag, value)
|
||||
return value
|
||||
|
||||
def getdefaultvalue(self,
|
||||
option_bag,
|
||||
):
|
||||
def get_default_value(self,
|
||||
option_bag: OptionBag,
|
||||
) -> Any:
|
||||
"""get default value:
|
||||
- get parents config value or
|
||||
- get calculated value or
|
||||
|
@ -183,45 +121,84 @@ class Values:
|
|||
# retrieved value from parent config
|
||||
return moption_bag.config_bag.context.get_values().get_cached_value(moption_bag)
|
||||
|
||||
# now try to get default value:
|
||||
value = self.calc_value(option_bag,
|
||||
option_bag.option.impl_getdefault(),
|
||||
)
|
||||
if option_bag.index is not None and isinstance(value, (list, tuple)):
|
||||
if value and option_bag.option.impl_is_submulti():
|
||||
# first index is a list, assume other data are list too
|
||||
if isinstance(value[0], list):
|
||||
# if index, must return good value for this index
|
||||
if len(value) > option_bag.index:
|
||||
value = value[option_bag.index]
|
||||
else:
|
||||
# no value for this index, retrieve default multi value
|
||||
# default_multi is already a list for submulti
|
||||
value = self.calc_value(option_bag,
|
||||
option_bag.option.impl_getdefault_multi())
|
||||
elif option_bag.option.impl_is_multi():
|
||||
# if index, must return good value for this index
|
||||
if len(value) > option_bag.index:
|
||||
value = value[option_bag.index]
|
||||
else:
|
||||
# no value for this index, retrieve default multi value
|
||||
# default_multi is already a list for submulti
|
||||
value = self.calc_value(option_bag,
|
||||
option_bag.option.impl_getdefault_multi())
|
||||
# now try to get calculated value:
|
||||
value = self.get_calculated_value(option_bag,
|
||||
option_bag.option.impl_getdefault(),
|
||||
)
|
||||
if option_bag.index is not None and isinstance(value, (list, tuple)) \
|
||||
and (not option_bag.option.impl_is_submulti() or \
|
||||
not value or isinstance(value[0], list)):
|
||||
# if index (so slave), must return good value for this index
|
||||
# for submulti, first index is a list, assume other data are list too
|
||||
if len(value) > option_bag.index:
|
||||
value = value[option_bag.index]
|
||||
else:
|
||||
# no value for this index, retrieve default multi value
|
||||
# default_multi is already a list for submulti
|
||||
value = self.get_calculated_value(option_bag,
|
||||
option_bag.option.impl_getdefault_multi(),
|
||||
)
|
||||
return value
|
||||
|
||||
def calculate_reset_cache(self,
|
||||
option_bag,
|
||||
value):
|
||||
if not 'expire' in option_bag.properties:
|
||||
return
|
||||
cache = option_bag.config_bag.context._impl_values_cache
|
||||
is_cache, cache_value, validated = cache.getcache(option_bag.path,
|
||||
None,
|
||||
option_bag.index,
|
||||
option_bag.config_bag.properties,
|
||||
option_bag.properties,
|
||||
'value')
|
||||
def get_calculated_value(self,
|
||||
option_bag,
|
||||
value,
|
||||
reset_cache=True,
|
||||
) -> Any:
|
||||
"""value could be a calculation, in this case do calculation
|
||||
"""
|
||||
if isinstance(value, Calculation):
|
||||
try:
|
||||
value = value.execute(option_bag)
|
||||
except ConfigError as err:
|
||||
msg = _(f'error when calculating "{option_bag.option.impl_get_display_name()}": '
|
||||
f'{err} : {option_bag.path}')
|
||||
raise ConfigError(msg) from err
|
||||
elif isinstance(value, list):
|
||||
# if value is a list, do subcalculation
|
||||
for idx, val in enumerate(value):
|
||||
value[idx] = self.get_calculated_value(option_bag,
|
||||
val,
|
||||
reset_cache=False,
|
||||
)
|
||||
if reset_cache:
|
||||
self.reset_cache_after_calculation(option_bag,
|
||||
value,
|
||||
)
|
||||
return value
|
||||
|
||||
#______________________________________________________________________
|
||||
def check_force_to_metaconfig(self,
|
||||
option_bag: OptionBag,
|
||||
) -> bool:
|
||||
"""Check if the value must be retrieve from parent metaconfig or not
|
||||
"""
|
||||
# force_metaconfig_on_freeze is set to an option and context is a kernelconfig
|
||||
# => to metaconfig
|
||||
# force_metaconfig_on_freeze is set *explicitly* to an option and context is a
|
||||
# kernelmetaconfig => to sub metaconfig
|
||||
if 'force_metaconfig_on_freeze' in option_bag.properties:
|
||||
settings = option_bag.config_bag.context.get_settings()
|
||||
if option_bag.config_bag.context.impl_type == 'config':
|
||||
return True
|
||||
# it's a not a config, force to metaconfig only in *explicitly* set
|
||||
return 'force_metaconfig_on_freeze' in settings.get_stored_properties(option_bag.path,
|
||||
option_bag.index,
|
||||
frozenset(),
|
||||
)
|
||||
return False
|
||||
|
||||
def reset_cache_after_calculation(self,
|
||||
option_bag,
|
||||
value,
|
||||
):
|
||||
"""if value is modification after calculation, invalid cache
|
||||
"""
|
||||
cache = option_bag.config_bag.context.get_values_cache()
|
||||
is_cache, cache_value, _ = cache.getcache(option_bag,
|
||||
'values',
|
||||
expiration=False,
|
||||
)
|
||||
if not is_cache or cache_value == value:
|
||||
# calculation return same value as previous value,
|
||||
# so do not invalidate cache
|
||||
|
@ -232,30 +209,47 @@ class Values:
|
|||
self._set_force_value_suffix(option_bag)
|
||||
|
||||
def isempty(self,
|
||||
opt,
|
||||
value,
|
||||
force_allow_empty_list=False,
|
||||
index=None):
|
||||
"convenience method to know if an option is empty"
|
||||
empty = opt._empty
|
||||
if index in [None, undefined] and opt.impl_is_multi():
|
||||
isempty = value is None or (isinstance(value, list) and not force_allow_empty_list and value == []) or \
|
||||
(isinstance(value, list) and None in value) or empty in value
|
||||
option_bag: OptionBag,
|
||||
value: Any,
|
||||
force_allow_empty_list: bool,
|
||||
) -> bool:
|
||||
"""convenience method to know if an option is empty
|
||||
"""
|
||||
if option_bag.index is None and option_bag.option.impl_is_submulti():
|
||||
# index is not set
|
||||
isempty = True
|
||||
for val in value:
|
||||
isempty = self._isempty_multi(val, force_allow_empty_list)
|
||||
if isempty:
|
||||
break
|
||||
elif (option_bag.index is None or \
|
||||
(option_bag.index is not None and option_bag.option.impl_is_submulti())) and \
|
||||
option_bag.option.impl_is_multi():
|
||||
# it's a single list
|
||||
isempty = self._isempty_multi(value, force_allow_empty_list)
|
||||
else:
|
||||
isempty = value is None or value == empty or (opt.impl_is_submulti() and value == [])
|
||||
isempty = value is None or value == ''
|
||||
return isempty
|
||||
|
||||
def _isempty_multi(self,
|
||||
value: Any,
|
||||
force_allow_empty_list: bool,
|
||||
) -> bool:
|
||||
return (not force_allow_empty_list and value == []) or None in value or '' in value
|
||||
|
||||
#______________________________________________________________________
|
||||
# set value
|
||||
def set_value(self,
|
||||
option_bag,
|
||||
value,
|
||||
):
|
||||
context = option_bag.config_bag.context
|
||||
option_bag: OptionBag,
|
||||
value: Any,
|
||||
) -> None:
|
||||
"""set value to option
|
||||
"""
|
||||
owner = self.get_context_owner()
|
||||
if 'validator' in option_bag.config_bag.properties:
|
||||
self.setvalue_validation(value,
|
||||
option_bag)
|
||||
option_bag,
|
||||
)
|
||||
|
||||
if isinstance(value, list):
|
||||
# copy
|
||||
|
@ -265,33 +259,35 @@ class Values:
|
|||
owner,
|
||||
)
|
||||
setting_properties = option_bag.config_bag.properties
|
||||
validator = 'validator' in setting_properties and 'demoting_error_warning' not in setting_properties
|
||||
validator = 'validator' in setting_properties and \
|
||||
'demoting_error_warning' not in setting_properties
|
||||
if validator:
|
||||
cache = option_bag.config_bag.context._impl_values_cache
|
||||
cache.setcache(option_bag.path,
|
||||
option_bag.index,
|
||||
cache = option_bag.config_bag.context.get_values_cache()
|
||||
cache.setcache(option_bag,
|
||||
value,
|
||||
option_bag.properties,
|
||||
setting_properties,
|
||||
validator)
|
||||
validated=validator,
|
||||
)
|
||||
if 'force_store_value' in setting_properties and option_bag.option.impl_is_leader():
|
||||
option_bag.option.impl_get_leadership().follower_force_store_value(self,
|
||||
value,
|
||||
option_bag,
|
||||
owners.forced,
|
||||
)
|
||||
leader = option_bag.option.impl_get_leadership()
|
||||
leader.follower_force_store_value(value,
|
||||
option_bag.config_bag,
|
||||
owners.forced,
|
||||
)
|
||||
|
||||
def setvalue_validation(self,
|
||||
value,
|
||||
option_bag,
|
||||
):
|
||||
"""validate value before set value
|
||||
"""
|
||||
settings = option_bag.config_bag.context.get_settings()
|
||||
# First validate properties with this value
|
||||
opt = option_bag.option
|
||||
settings.validate_frozen(option_bag)
|
||||
val = self.calc_value(option_bag,
|
||||
value,
|
||||
False,)
|
||||
val = self.get_calculated_value(option_bag,
|
||||
value,
|
||||
False,
|
||||
)
|
||||
settings.validate_mandatory(val,
|
||||
option_bag,
|
||||
)
|
||||
|
@ -320,25 +316,25 @@ class Values:
|
|||
)
|
||||
self._set_force_value_suffix(option_bag)
|
||||
|
||||
def reduce_index(self,
|
||||
path,
|
||||
index):
|
||||
self._values[path][index - 1] = self._values[path].pop(index)
|
||||
|
||||
def set_storage_value(self,
|
||||
path,
|
||||
index,
|
||||
value,
|
||||
owner,
|
||||
):
|
||||
"""set a value
|
||||
"""
|
||||
self._values.setdefault(path, {})[index] = [value, owner]
|
||||
|
||||
def _set_force_value_suffix(self,
|
||||
option_bag: OptionBag,
|
||||
) -> None:
|
||||
def _set_force_value_suffix(self, option_bag: OptionBag) -> None:
|
||||
""" force store value for an option for suffixes
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
if 'force_store_value' not in option_bag.config_bag.properties:
|
||||
return
|
||||
for woption in option_bag.option._get_suffixes_dependencies():
|
||||
|
||||
for woption in option_bag.option._get_suffixes_dependencies(): # pylint: disable=protected-access
|
||||
# options from dependencies are weakref
|
||||
option = woption()
|
||||
force_store_options = []
|
||||
for coption in option.get_children_recursively(None,
|
||||
|
@ -350,10 +346,10 @@ class Values:
|
|||
if not force_store_options:
|
||||
continue
|
||||
rootpath = option.impl_getpath()
|
||||
settings = option_bag.config_bag.context.get_settings()
|
||||
for suffix in option.get_suffixes(option_bag.config_bag):
|
||||
for coption in force_store_options:
|
||||
subpaths = [rootpath] + coption.impl_getpath()[len(rootpath) + 1:].split('.')[:-1]
|
||||
subpaths = [rootpath] + \
|
||||
coption.impl_getpath()[len(rootpath) + 1:].split('.')[:-1]
|
||||
path_suffix = option.convert_suffix_to_path(suffix)
|
||||
subpath = '.'.join([subp + path_suffix for subp in subpaths])
|
||||
doption = coption.to_dynoption(subpath,
|
||||
|
@ -367,7 +363,7 @@ class Values:
|
|||
option_bag.config_bag,
|
||||
properties=frozenset(),
|
||||
)
|
||||
indexes = range(len(self.getvalue(loption_bag)))
|
||||
indexes = range(len(self.get_value(loption_bag)))
|
||||
else:
|
||||
indexes = [None]
|
||||
for index in indexes:
|
||||
|
@ -375,7 +371,8 @@ class Values:
|
|||
index,
|
||||
option_bag.config_bag,
|
||||
)
|
||||
self._values.setdefault(coption_bag.path, {})[index] = [self.getvalue(coption_bag), owners.forced]
|
||||
default_value = [self.get_value(coption_bag), owners.forced]
|
||||
self._values.setdefault(coption_bag.path, {})[index] = default_value
|
||||
|
||||
def _get_modified_parent(self,
|
||||
option_bag: OptionBag,
|
||||
|
@ -398,8 +395,9 @@ class Values:
|
|||
# remove force_metaconfig_on_freeze only if option in metaconfig
|
||||
# hasn't force_metaconfig_on_freeze properties
|
||||
ori_properties = doption_bag.properties
|
||||
doption_bag.properties = doption_bag.config_bag.context.get_settings().getproperties(doption_bag)
|
||||
if not self.force_to_metaconfig(doption_bag):
|
||||
settings = doption_bag.config_bag.context.get_settings()
|
||||
doption_bag.properties = settings.getproperties(doption_bag)
|
||||
if not self.check_force_to_metaconfig(doption_bag):
|
||||
doption_bag.properties = ori_properties - {'force_metaconfig_on_freeze'}
|
||||
else:
|
||||
doption_bag.properties = ori_properties
|
||||
|
@ -416,11 +414,15 @@ class Values:
|
|||
# owner
|
||||
|
||||
def is_default_owner(self,
|
||||
option_bag,
|
||||
validate_meta=True):
|
||||
option_bag: OptionBag,
|
||||
validate_meta: bool=True,
|
||||
) -> bool:
|
||||
"""is default owner for an option
|
||||
"""
|
||||
return self.getowner(option_bag,
|
||||
validate_meta=validate_meta,
|
||||
only_default=True) == owners.default
|
||||
only_default=True,
|
||||
) == owners.default
|
||||
|
||||
def hasvalue(self,
|
||||
path,
|
||||
|
@ -432,7 +434,7 @@ class Values:
|
|||
has_path = path in self._values
|
||||
if index is None:
|
||||
return has_path
|
||||
elif has_path:
|
||||
if has_path:
|
||||
return index in self._values[path]
|
||||
return False
|
||||
|
||||
|
@ -468,14 +470,18 @@ class Values:
|
|||
else:
|
||||
owner = owners.default
|
||||
else:
|
||||
owner = self._values.get(option_bag.path, {}).get(option_bag.index, [undefined, owners.default])[1]
|
||||
if validate_meta is not False and (owner is owners.default or \
|
||||
'frozen' in option_bag.properties and 'force_metaconfig_on_freeze' in option_bag.properties):
|
||||
owner = self._values.get(option_bag.path, {}).get(option_bag.index,
|
||||
[undefined, owners.default],
|
||||
)[1]
|
||||
if validate_meta is not False and (owner is owners.default or
|
||||
'frozen' in option_bag.properties and
|
||||
'force_metaconfig_on_freeze' in option_bag.properties):
|
||||
moption_bag = self._get_modified_parent(option_bag)
|
||||
if moption_bag is not None:
|
||||
owner = moption_bag.config_bag.context.get_values().getowner(moption_bag,
|
||||
only_default=only_default,
|
||||
)
|
||||
values = moption_bag.config_bag.context.get_values()
|
||||
owner = values.getowner(moption_bag,
|
||||
only_default=only_default,
|
||||
)
|
||||
elif 'force_metaconfig_on_freeze' in option_bag.properties:
|
||||
return owners.default
|
||||
return owner
|
||||
|
@ -487,32 +493,28 @@ class Values:
|
|||
"""
|
||||
sets a owner to an option
|
||||
|
||||
:param opt: the `option.Option` object
|
||||
:param option_bag: the `OptionBag` object
|
||||
:param owner: a valid owner, that is a `setting.owners.Owner` object
|
||||
"""
|
||||
opt = option_bag.option
|
||||
if opt.impl_is_symlinkoption():
|
||||
raise ConfigError(_("can't set owner for the symlinkoption \"{}\""
|
||||
"").format(opt.impl_get_display_name()))
|
||||
if owner in forbidden_owners:
|
||||
raise ValueError(_('set owner "{0}" is forbidden').format(str(owner)))
|
||||
|
||||
if not self.hasvalue(option_bag.path, option_bag.index):
|
||||
raise ConfigError(_('no value for {0} cannot change owner to {1}'
|
||||
'').format(option_bag.path, owner))
|
||||
raise ConfigError(_(f'no value for {option_bag.path} cannot change owner to {owner}'))
|
||||
option_bag.config_bag.context.get_settings().validate_frozen(option_bag)
|
||||
self._values[option_bag.path][option_bag.index][1] = owner
|
||||
#______________________________________________________________________
|
||||
# reset
|
||||
|
||||
def reset(self,
|
||||
option_bag):
|
||||
def reset(self, option_bag: OptionBag) -> None:
|
||||
"""reset value for an option
|
||||
"""
|
||||
context = option_bag.config_bag.context
|
||||
hasvalue = self.hasvalue(option_bag.path)
|
||||
setting_properties = option_bag.config_bag.properties
|
||||
|
||||
if hasvalue and 'validator' in option_bag.config_bag.properties:
|
||||
fake_context = context._gen_fake_values()
|
||||
fake_context = context.gen_fake_values()
|
||||
config_bag = option_bag.config_bag.copy()
|
||||
config_bag.remove_validation()
|
||||
config_bag.context = fake_context
|
||||
|
@ -521,16 +523,17 @@ class Values:
|
|||
fake_value = fake_context.get_values()
|
||||
fake_value.reset(soption_bag)
|
||||
soption_bag.config_bag.properties = option_bag.config_bag.properties
|
||||
value = fake_value.getdefaultvalue(soption_bag)
|
||||
value = fake_value.get_default_value(soption_bag)
|
||||
fake_value.setvalue_validation(value,
|
||||
soption_bag)
|
||||
soption_bag,
|
||||
)
|
||||
opt = option_bag.option
|
||||
if opt.impl_is_leader():
|
||||
opt.impl_get_leadership().reset(self,
|
||||
option_bag)
|
||||
opt.impl_get_leadership().reset(option_bag.config_bag)
|
||||
if hasvalue:
|
||||
if 'force_store_value' in option_bag.config_bag.properties and 'force_store_value' in option_bag.properties:
|
||||
value = self.getdefaultvalue(option_bag)
|
||||
if 'force_store_value' in option_bag.config_bag.properties and \
|
||||
'force_store_value' in option_bag.properties:
|
||||
value = self.get_default_value(option_bag)
|
||||
|
||||
self._setvalue(option_bag,
|
||||
value,
|
||||
|
@ -544,72 +547,89 @@ class Values:
|
|||
context.reset_cache(option_bag)
|
||||
if 'force_store_value' in setting_properties and option_bag.option.impl_is_leader():
|
||||
if value is None:
|
||||
value = self.getdefaultvalue(option_bag)
|
||||
option_bag.option.impl_get_leadership().follower_force_store_value(self,
|
||||
value,
|
||||
option_bag,
|
||||
owners.forced)
|
||||
value = self.get_default_value(option_bag)
|
||||
leader = option_bag.option.impl_get_leadership()
|
||||
leader.follower_force_store_value(value,
|
||||
option_bag.config_bag,
|
||||
owners.forced,
|
||||
)
|
||||
#______________________________________________________________________
|
||||
# Follower
|
||||
|
||||
def get_max_length(self, path):
|
||||
def get_max_length(self, path: str) -> int:
|
||||
"""get max index for a follower and determine the length of the follower
|
||||
"""
|
||||
values = self._values.get(path, {})
|
||||
if values:
|
||||
return max(values) + 1
|
||||
return 0
|
||||
|
||||
def reset_follower(self,
|
||||
option_bag):
|
||||
if self.hasvalue(option_bag.path,
|
||||
index=option_bag.index):
|
||||
context = option_bag.config_bag.context
|
||||
setting_properties = option_bag.config_bag.properties
|
||||
if 'validator' in setting_properties:
|
||||
fake_context = context._gen_fake_values()
|
||||
fake_value = fake_context.get_values()
|
||||
config_bag = option_bag.config_bag.copy()
|
||||
config_bag.remove_validation()
|
||||
config_bag.context = fake_context
|
||||
soption_bag = option_bag.copy()
|
||||
soption_bag.config_bag = config_bag
|
||||
fake_value.reset_follower(soption_bag)
|
||||
value = fake_value.getdefaultvalue(soption_bag)
|
||||
fake_value.setvalue_validation(value,
|
||||
soption_bag)
|
||||
if 'force_store_value' in setting_properties and 'force_store_value' in option_bag.properties:
|
||||
value = self.getdefaultvalue(option_bag)
|
||||
option_bag: OptionBag,
|
||||
) -> None:
|
||||
"""reset value for a follower
|
||||
"""
|
||||
if not self.hasvalue(option_bag.path,
|
||||
index=option_bag.index,
|
||||
):
|
||||
return
|
||||
context = option_bag.config_bag.context
|
||||
setting_properties = option_bag.config_bag.properties
|
||||
if 'validator' in setting_properties:
|
||||
fake_context = context.gen_fake_values()
|
||||
fake_value = fake_context.get_values()
|
||||
config_bag = option_bag.config_bag.copy()
|
||||
config_bag.remove_validation()
|
||||
config_bag.context = fake_context
|
||||
soption_bag = option_bag.copy()
|
||||
soption_bag.config_bag = config_bag
|
||||
fake_value.reset_follower(soption_bag)
|
||||
value = fake_value.get_default_value(soption_bag)
|
||||
fake_value.setvalue_validation(value,
|
||||
soption_bag)
|
||||
if 'force_store_value' in setting_properties and \
|
||||
'force_store_value' in option_bag.properties:
|
||||
value = self.get_default_value(option_bag)
|
||||
|
||||
self._setvalue(option_bag,
|
||||
value,
|
||||
owners.forced,
|
||||
)
|
||||
else:
|
||||
self.resetvalue_index(option_bag.path,
|
||||
option_bag.index,
|
||||
)
|
||||
context.reset_cache(option_bag)
|
||||
self._setvalue(option_bag,
|
||||
value,
|
||||
owners.forced,
|
||||
)
|
||||
else:
|
||||
self.resetvalue_index(option_bag)
|
||||
context.reset_cache(option_bag)
|
||||
|
||||
def resetvalue_index(self,
|
||||
path,
|
||||
index,
|
||||
):
|
||||
if path in self._values and index in self._values[path]:
|
||||
del self._values[path][index]
|
||||
def resetvalue_index(self, option_bag: OptionBag) -> None:
|
||||
"""reset a value for a follower at an index
|
||||
"""
|
||||
if option_bag.path in self._values and option_bag.index in self._values[option_bag.path]:
|
||||
del self._values[option_bag.path][option_bag.index]
|
||||
|
||||
def reduce_index(self, option_bag: OptionBag) -> None:
|
||||
"""reduce follower's value from a specified index
|
||||
"""
|
||||
self.resetvalue_index(option_bag)
|
||||
for index in range(option_bag.index + 1, self.get_max_length(option_bag.path)):
|
||||
if self.hasvalue(option_bag.path,
|
||||
index,
|
||||
):
|
||||
self._values[option_bag.path][index - 1] = self._values[option_bag.path].pop(index)
|
||||
|
||||
def reset_leadership(self,
|
||||
option_bag: OptionBag,
|
||||
leadership_option_bag: OptionBag,
|
||||
index: int,
|
||||
):
|
||||
) -> None:
|
||||
"""reset leadershop from an index
|
||||
"""
|
||||
current_value = self.get_cached_value(option_bag)
|
||||
length = len(current_value)
|
||||
if index >= length:
|
||||
raise IndexError(_('index {} is greater than the length {} '
|
||||
'for option "{}"').format(index,
|
||||
length,
|
||||
option_bag.option.impl_get_display_name()))
|
||||
raise IndexError(_('index {index} is greater than the length {length} '
|
||||
'for option "{option_bag.option.impl_get_display_name()}"'))
|
||||
current_value.pop(index)
|
||||
leadership_option_bag.option.pop(self,
|
||||
index,
|
||||
option_bag,
|
||||
leadership_option_bag.option.pop(index,
|
||||
option_bag.config_bag,
|
||||
)
|
||||
self.set_value(option_bag,
|
||||
current_value,
|
||||
|
@ -619,7 +639,6 @@ class Values:
|
|||
# information
|
||||
|
||||
def set_information(self,
|
||||
config_bag,
|
||||
option_bag,
|
||||
key,
|
||||
value,
|
||||
|
@ -634,55 +653,62 @@ class Values:
|
|||
else:
|
||||
path = option_bag.path
|
||||
self._informations.setdefault(path, {})[key] = value
|
||||
if path is not None:
|
||||
for option in option_bag.option.get_dependencies_information(itself=True):
|
||||
config_bag.context.reset_cache(option_bag)
|
||||
if path is None:
|
||||
return
|
||||
if key in option_bag.option.get_dependencies_information(itself=True):
|
||||
option_bag.config_bag.context.reset_cache(option_bag)
|
||||
|
||||
def get_information(self,
|
||||
config_bag,
|
||||
option_bag,
|
||||
key,
|
||||
name,
|
||||
default,
|
||||
):
|
||||
"""retrieves one information's item
|
||||
|
||||
:param key: the item string (ex: "help")
|
||||
:param name: the item string (ex: "help")
|
||||
"""
|
||||
if option_bag is None:
|
||||
path = None
|
||||
else:
|
||||
path = option_bag.path
|
||||
try:
|
||||
return self._informations[path][key]
|
||||
return self._informations[path][name]
|
||||
except KeyError as err:
|
||||
if option_bag:
|
||||
return option_bag.option.impl_get_information(key, default)
|
||||
return option_bag.option.impl_get_information(name, default)
|
||||
if default is not undefined:
|
||||
return default
|
||||
raise ValueError(_("information's item not found: {0}").format(key))
|
||||
raise ValueError(_("information's item not found: {0}").format(name)) from err
|
||||
|
||||
def del_information(self,
|
||||
key,
|
||||
raises=True,
|
||||
path=None,
|
||||
key: Any,
|
||||
raises: bool=True,
|
||||
path: str=None,
|
||||
):
|
||||
"""delete information for a specified key
|
||||
"""
|
||||
if path in self._informations and key in self._informations[path]:
|
||||
del self._informations[path][key]
|
||||
elif raises:
|
||||
raise ValueError(_(f"information's item not found \"{key}\""))
|
||||
|
||||
def list_information(self,
|
||||
path=None,
|
||||
):
|
||||
path: str=None,
|
||||
) -> List[str]:
|
||||
"""list all informations keys for a specified path
|
||||
"""
|
||||
return list(self._informations.get(path, {}).keys())
|
||||
|
||||
#____________________________________________________________
|
||||
# default owner methods
|
||||
def set_context_owner(self, owner):
|
||||
":param owner: sets the default value for owner at the Config level"
|
||||
def set_context_owner(self, owner: str) -> None:
|
||||
"""set the context owner
|
||||
"""
|
||||
if owner in forbidden_owners:
|
||||
raise ValueError(_('set owner "{0}" is forbidden').format(str(owner)))
|
||||
self._values[None][None][1] = owner
|
||||
|
||||
def get_context_owner(self):
|
||||
def get_context_owner(self) -> str:
|
||||
"""get the context owner
|
||||
"""
|
||||
return self._values[None][None][1]
|
||||
|
|
Loading…
Reference in a new issue