reorganize

This commit is contained in:
egarette@silique.fr 2023-05-11 15:44:48 +02:00
parent 30cd543a21
commit 6805cecfd5
50 changed files with 2517 additions and 1979 deletions

View file

@ -1179,7 +1179,11 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
cfg2_ = cfg.config(confread).unrestraint
else:
cfg2_ = cfg.unrestraint
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?
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'])

View file

@ -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()

View file

@ -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)

View file

@ -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()

View file

@ -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()

View file

@ -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'

View file

@ -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,6 +292,7 @@ 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'
with pytest.raises(ConfigError):
assert cfg.option('val1').option.defaultmulti() == None
cfg.option('val1').value.reset()
assert cfg.option('val1').value.get() == 'val'
@ -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')

View file

@ -65,18 +65,22 @@ def test_mod_read_only_write():
'everything_frozen',
'mandatory',
'empty',
'force_store_value'}
'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'}
'force_store_value',
}
assert config.property.getdefault('read_write', 'remove') == {'permissive',
'everything_frozen',
'mandatory',
'empty'}
'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,))

View file

@ -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()

View file

@ -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,))

View file

@ -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)

View file

@ -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'])

View file

@ -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()

View file

@ -14,20 +14,19 @@
# 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 inspect import ismethod, getdoc, signature
from time import time
from typing import List, Set, Any, Optional, Callable, Union, Dict
from inspect import getdoc
from typing import List, Set, Any, Optional, Callable, Dict
from warnings import catch_warnings, simplefilter
from functools import wraps
from copy import deepcopy
from .error import ConfigError, LeadershipError, PropertiesOptionError, ValueErrorWarning
from .error import ConfigError, LeadershipError, ValueErrorWarning
from .i18n import _
from .setting import ConfigBag, OptionBag, owners, groups, Undefined, undefined, \
FORBIDDEN_SET_PROPERTIES, SPECIAL_PROPERTIES, EXPIRATION_TIME
from .setting import ConfigBag, OptionBag, owners, groups, undefined, \
FORBIDDEN_SET_PROPERTIES, SPECIAL_PROPERTIES
from .config import KernelConfig, KernelGroupConfig, KernelMetaConfig, KernelMixConfig
from .option import RegexpOption, OptionDescription
from .option import RegexpOption, OptionDescription, ChoiceOption
from .todict import TiramisuDict
@ -42,13 +41,12 @@ class TiramisuHelp:
def display(doc=''):
if _display: # pragma: no cover
print(doc)
options = []
all_modules = dir(self)
modules = []
max_len = 0
force = False
for module_name in all_modules:
if module_name in ['forcepermissive', 'unrestraint']:
if module_name in ['forcepermissive', 'unrestraint', 'nowarnings']:
force = True
max_len = max(max_len, len('forcepermissive'))
elif module_name != 'help' and not module_name.startswith('_'):
@ -60,8 +58,16 @@ class TiramisuHelp:
display()
if force:
display(_('Settings:'))
display(self._tmpl_help.format('forcepermissive', _('Access to option without verifying permissive properties')).expandtabs(max_len + 10))
display(self._tmpl_help.format('unrestraint', _('Access to option without property restriction')).expandtabs(max_len + 10))
display(self._tmpl_help.format('forcepermissive',
_('Access to option without verifying permissive '
'properties'),
).expandtabs(max_len + 10))
display(self._tmpl_help.format('unrestraint',
_('Access to option without property restriction')
).expandtabs(max_len + 10))
display(self._tmpl_help.format('nowarnings',
_('Do not warnings during validation')
).expandtabs(max_len + 10))
display()
if isinstance(self, TiramisuDispatcherOption):
doc = _(getdoc(self.__call__))
@ -81,7 +87,6 @@ class TiramisuHelp:
class CommonTiramisu(TiramisuHelp):
_allow_optiondescription = True
_validate_properties = True
def _get_options_bag(self) -> OptionBag:
@ -98,14 +103,6 @@ class CommonTiramisu(TiramisuHelp):
return options_bag
def load_option(func):
def wrapped(self, *args, **kwargs):
options_bag = self._get_options_bag()
return func(self, options_bag, *args, **kwargs)
wrapped.func = func
return wrapped
def option_type(typ):
if not isinstance(typ, list):
types = [typ]
@ -115,16 +112,17 @@ def option_type(typ):
def wrapper(func):
@wraps(func)
def wrapped(*args, **kwargs):
config_bag = args[0]._config_bag
if args[0]._config_bag.context.impl_type == 'group' and 'group' in types:
self = args[0]
config_bag = self._config_bag
if self._config_bag.context.impl_type == 'group' and 'group' in types:
options_bag = [OptionBag(None,
None,
args[0]._config_bag,
path=args[0]._path,
self._config_bag,
path=self._path,
)]
kwargs['is_group'] = True
return func(args[0], options_bag, *args[1:], **kwargs)
options_bag = args[0]._get_options_bag()
return func(self, options_bag, *args[1:], **kwargs)
options_bag = self._get_options_bag()
option = options_bag[-1].option
if option.impl_is_optiondescription() and 'optiondescription' in types or \
not option.impl_is_optiondescription() and (
@ -133,24 +131,29 @@ def option_type(typ):
'option' in types or \
option.impl_is_leader() and 'leader' in types or \
option.impl_is_follower() and 'follower' in types or \
option.get_type() == 'choice' and 'choice' in types)):
if not option.impl_is_optiondescription() and not option.impl_is_symlinkoption() and option.impl_is_follower():
if 'with_index' in types and args[0]._index is not None:
raise ConfigError(_(f'please do not specify index ({args[0].__class__.__name__}.{func.__name__})'))
if 'with_index' not in types and args[0]._index is None:
raise ConfigError(_(f'please specify index with a follower option ({args[0].__class__.__name__}.{func.__name__})'))
elif args[0]._index is not None:
raise ConfigError(_(f'please specify an index only for follower option ({args[0].__class__.__name__}.{func.__name__})'))
return func(args[0], options_bag, *args[1:], **kwargs)
raise ConfigError(_(f'please specify a valid sub function ({args[0].__class__.__name__}.{func.__name__})'))
isinstance(option, ChoiceOption) and 'choice' in types)):
if not option.impl_is_optiondescription() and \
not option.impl_is_symlinkoption() and \
option.impl_is_follower():
if 'with_index' not in types and 'with_or_without_index' not in types and \
self._index is not None:
msg = _('please do not specify index '
f'({self.__class__.__name__}.{func.__name__})')
raise ConfigError(_(msg))
if 'with_index' in types and self._index is None:
msg = _('please specify index with a follower option '
f'({self.__class__.__name__}.{func.__name__})')
raise ConfigError(msg)
return func(self, options_bag, *args[1:], **kwargs)
msg = _('please specify a valid sub function '
f'({self.__class__.__name__}.{func.__name__})')
raise ConfigError(msg)
wrapped.func = func
return wrapped
return wrapper
class CommonTiramisuOption(CommonTiramisu):
_allow_optiondescription = False
_follower_need_index = True
_validate_properties = False
def __init__(self,
@ -167,60 +170,14 @@ class CommonTiramisuOption(CommonTiramisu):
class _TiramisuOptionWalk:
def _filter(self,
opt,
subconfig,
config_bag,
):
option_bag = OptionBag(opt,
None,
config_bag,
)
def _walk(self,
option,
recursive,
type_,
group_type,
config_bag,
subconfig,
):
options = []
for opt in option.get_children(config_bag):
try:
#FIXME trop compliqué devrait faire avec get_sub_option_bag ou get_children ne devrait pas lister les variables disables
subsubconfig = self._filter(opt,
subconfig,
config_bag,
)
except PropertiesOptionError:
continue
if opt.impl_is_optiondescription():
if recursive:
options.extend(self._walk(opt,
recursive,
type_,
group_type,
config_bag,
subsubconfig))
if type_ == 'option' or (type_ == 'optiondescription' and \
group_type and opt.impl_get_group_type() != group_type):
continue
elif type_ == 'optiondescription':
continue
options.append(TiramisuOption(opt.impl_getpath(),
None,
config_bag,
))
return options
def _list2(self,
def _list(self,
root_option_bag,
type,
group_type,
recursive,
):
assert type in ('all', 'option', 'optiondescription'), _('unknown list type {}').format(type)
assert type in ('all', 'option', 'optiondescription'), \
_('unknown list type {}').format(type)
assert group_type is None or isinstance(group_type, groups.GroupType), \
_("unknown group_type: {0}").format(group_type)
options = []
@ -233,7 +190,14 @@ class _TiramisuOptionWalk:
group_type=group_type,
):
if isinstance(option_bag, dict):
for opt_bag in option_bag.values():
for opts_bag in option_bag.values():
if isinstance(opts_bag, OptionBag):
options.append(TiramisuOption(opts_bag.path,
opts_bag.index,
self._config_bag,
))
else:
for opt_bag in opts_bag:
options.append(TiramisuOption(opt_bag.path,
opt_bag.index,
self._config_bag,
@ -245,82 +209,48 @@ class _TiramisuOptionWalk:
))
return options
def _list(self,
type,
group_type,
recursive,
root_option,
subconfig,
config_bag,
):
assert type in ('all', 'option', 'optiondescription'), _('unknown list type {}').format(type)
assert group_type is None or isinstance(group_type, groups.GroupType), \
_("unknown group_type: {0}").format(group_type)
if config_bag.properties and 'warnings' in config_bag.properties:
config_bag = config_bag.copy()
config_bag.remove_warnings()
options = []
for opt in self._walk(root_option,
recursive,
type,
group_type,
config_bag,
subconfig,
):
options.append(opt)
return options
class _TiramisuOptionOptionDescription(CommonTiramisuOption):
"""Manage option"""
_allow_optiondescription = True
_follower_need_index = False
_validate_properties = False
@load_option
@option_type(['optiondescription', 'option', 'with_or_without_index'])
def get(self, options_bag: List[OptionBag]):
"""Get Tiramisu option"""
option_bag = options_bag[-1]
return option_bag.option
@load_option
@option_type(['optiondescription'])
def isleadership(self, options_bag: List[OptionBag]):
"""Test if option is a leader or a follower"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_leadership()
@load_option
@option_type(['optiondescription', 'option', 'with_or_without_index'])
def doc(self, options_bag: List[OptionBag]):
"""Get option document"""
option_bag = options_bag[-1]
return option_bag.option.impl_get_display_name()
@load_option
@option_type(['optiondescription', 'option', 'with_or_without_index'])
def description(self, options_bag: List[OptionBag]):
"""Get option description"""
option_bag = options_bag[-1]
return option_bag.option.impl_get_information('doc', None)
@load_option
def name(self,
options_bag: List[OptionBag],
follow_symlink: bool=False,
) -> str:
@option_type(['optiondescription', 'option', 'symlink', 'with_or_without_index'])
def name(self, options_bag: List[OptionBag]) -> str:
"""Get option name"""
option_bag = options_bag[-1]
if not follow_symlink or \
option_bag.option.impl_is_optiondescription() or \
not option_bag.option.impl_is_symlinkoption():
return option_bag.option.impl_getname()
return option_bag.option.impl_getopt().impl_getname()
@load_option
@option_type(['optiondescription', 'option', 'with_or_without_index', 'symlink'])
def path(self, options_bag: List[OptionBag]) -> str:
"""Get option path"""
option_bag = options_bag[-1]
return option_bag.path
@load_option
@option_type(['optiondescription', 'option', 'symlink'])
def has_dependency(self,
options_bag: List[OptionBag],
self_is_dep=True,
@ -329,7 +259,7 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
option_bag = options_bag[-1]
return option_bag.option.impl_has_dependency(self_is_dep)
@load_option
@option_type(['optiondescription', 'option'])
def dependencies(self, options_bag: List[OptionBag]):
"""Get dependencies from this option"""
option_bag = options_bag[-1]
@ -341,13 +271,13 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
))
return options
@load_option
@option_type(['optiondescription', 'option', 'with_or_without_index'])
def isoptiondescription(self, options_bag: List[OptionBag]):
"""Test if option is an optiondescription"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_optiondescription()
@load_option
@option_type(['optiondescription', 'option', 'with_index'])
def properties(self,
options_bag: List[OptionBag],
only_raises=False,
@ -364,7 +294,6 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
return settings.getproperties(option_bag,
apply_requires=False,
)
# do not check cache properties/permissives which are not save (unrestraint, ...)
return settings.calc_raises_properties(option_bag,
apply_requires=False,
uncalculated=uncalculated,
@ -383,55 +312,51 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
class TiramisuOptionOption(_TiramisuOptionOptionDescription):
"""Manage option"""
@load_option
@option_type(['option', 'symlink', 'with_or_without_index'])
def ismulti(self, options_bag: List[OptionBag]):
"""Test if option could have multi value"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_multi()
@load_option
@option_type(['option', 'symlink', 'with_or_without_index'])
def issubmulti(self, options_bag: List[OptionBag]):
"""Test if option could have submulti value"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_submulti()
@load_option
@option_type(['option', 'with_or_without_index'])
def isleader(self, options_bag: List[OptionBag]):
"""Test if option is a leader"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_leader()
return options_bag[-1].option.impl_is_leader()
@load_option
@option_type(['option', 'with_or_without_index'])
def isfollower(self, options_bag: List[OptionBag]):
"""Test if option is a follower"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_follower()
return options_bag[-1].option.impl_is_follower()
@load_option
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def isdynamic(self, options_bag: List[OptionBag]):
"""Test if option is a dynamic optiondescription"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_dynsymlinkoption()
return options_bag[-1].option.impl_is_dynsymlinkoption()
@load_option
@option_type(['option', 'symlink', 'with_or_without_index'])
def issymlinkoption(self, options_bag: List[OptionBag]) -> bool:
"""Test if option is a symlink option"""
option_bag = options_bag[-1]
return option_bag.option.impl_is_symlinkoption()
return options_bag[-1].option.impl_is_symlinkoption()
@load_option
@option_type(['option', 'with_or_without_index', 'symlink'])
def default(self, options_bag: List[OptionBag]):
"""Get default value for an option (not for optiondescription)"""
option_bag = options_bag[-1]
return option_bag.option.impl_getdefault()
return options_bag[-1].option.impl_getdefault()
@load_option
@option_type(['option', 'with_or_without_index', 'symlink'])
def defaultmulti(self, options_bag: List[OptionBag]):
"""Get default value when added a value for a multi option (not for optiondescription)"""
option_bag = options_bag[-1]
return option_bag.option.impl_getdefault_multi()
if not options_bag[-1].option.impl_is_multi():
raise ConfigError(_('only multi value has defaultmulti'))
return options_bag[-1].option.impl_getdefault_multi()
@load_option
@option_type(['option', 'optiondescription', 'symlink', 'with_or_without_index'])
def type(self, options_bag: List[OptionBag]):
"""Get de option type"""
option_bag = options_bag[-1]
@ -439,7 +364,7 @@ class TiramisuOptionOption(_TiramisuOptionOptionDescription):
return 'optiondescription'
return option_bag.option.get_type()
@load_option
@option_type('option')
def pattern(self, options_bag: List[OptionBag]) -> str:
"""Get the option pattern"""
option_bag = options_bag[-1]
@ -456,15 +381,16 @@ class TiramisuOptionOption(_TiramisuOptionOptionDescription):
#FIXME only from 0.0.0.0 to 255.255.255.255
return r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
@load_option
@option_type('option')
def leader(self, options_bag: List[OptionBag]):
"""Get the leader option for a follower option"""
option_bag = options_bag[-1]
return TiramisuOption(option_bag.option.impl_get_leadership().get_leader().impl_getpath(),
path = options_bag[-1].option.impl_get_leadership().get_leader().impl_getpath()
return TiramisuOption(path,
None,
self._config_bag)
self._config_bag,
)
@load_option
@option_type(['option', 'with_or_without_index'])
def index(self, options_bag: List[OptionBag]):
"""Get then index of option"""
return options_bag[-1].index
@ -474,7 +400,7 @@ class TiramisuOptionOwner(CommonTiramisuOption):
#FIXME optiondescription must not have Owner!
"""Manage option's owner"""
@option_type(['symlink', 'option'])
@option_type(['symlink', 'option', 'with_index'])
def get(self, options_bag: List[OptionBag]):
"""Get owner for a specified option"""
option_bag = options_bag[-1]
@ -482,17 +408,15 @@ class TiramisuOptionOwner(CommonTiramisuOption):
parent_option_bag = options_bag[-2]
else:
parent_option_bag = None
return self._config_bag.context.get_owner(option_bag,
parent_option_bag,
)
return self._config_bag.context.get_owner(option_bag)
@option_type(['symlink', 'option'])
@option_type(['symlink', 'option', 'with_index'])
def isdefault(self, options_bag: List[OptionBag]):
"""Is option has defaut value"""
option_bag = options_bag[-1]
return self._config_bag.context.get_values().is_default_owner(option_bag)
@option_type('option')
@option_type(['option', 'with_index'])
def set(self,
options_bag: List[OptionBag],
owner: str,
@ -511,11 +435,9 @@ class TiramisuOptionOwner(CommonTiramisuOption):
class TiramisuOptionProperty(CommonTiramisuOption):
"""Manage option's property"""
_allow_optiondescription = True
_follower_need_index = False
_validate_properties = False
@load_option
@option_type(['option', 'optiondescription', 'with_index'])
def get(self,
options_bag: List[OptionBag],
only_raises=False,
@ -525,14 +447,13 @@ class TiramisuOptionProperty(CommonTiramisuOption):
option_bag = options_bag[-1]
if not only_raises:
return option_bag.properties
# do not check cache properties/permissives which are not save (unrestraint, ...)
settings = self._config_bag.context.get_settings()
ret = settings.calc_raises_properties(option_bag,
uncalculated=uncalculated,
)
return ret
@load_option
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def add(self,
options_bag: List[OptionBag],
prop,):
@ -542,7 +463,7 @@ class TiramisuOptionProperty(CommonTiramisuOption):
raise ConfigError(_('cannot add this property: "{0}"').format(
' '.join(prop)))
settings = self._config_bag.context.get_settings()
props = settings._getproperties(option_bag.path,
props = settings.get_stored_properties(option_bag.path,
option_bag.index,
option_bag.option.impl_getproperties(),
)
@ -550,7 +471,7 @@ class TiramisuOptionProperty(CommonTiramisuOption):
props | {prop},
)
@load_option
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def remove(self,
options_bag: List[OptionBag],
prop,
@ -562,7 +483,7 @@ class TiramisuOptionProperty(CommonTiramisuOption):
props - {prop},
)
@load_option
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def reset(self, options_bag: List[OptionBag]):
"""Reset all personalised properties"""
option_bag = options_bag[-1]
@ -571,16 +492,14 @@ class TiramisuOptionProperty(CommonTiramisuOption):
class TiramisuOptionPermissive(CommonTiramisuOption):
"""Manage option's permissive"""
_allow_optiondescription = True
_follower_need_index = False
@load_option
@option_type(['option', 'optiondescription', 'symlink', 'with_index'])
def get(self, options_bag: List[OptionBag]):
"""Get permissives value"""
option_bag = options_bag[-1]
return self._config_bag.context.get_settings().getpermissives(option_bag)
@load_option
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def set(self,
options_bag: List[OptionBag],
permissives,
@ -591,7 +510,7 @@ class TiramisuOptionPermissive(CommonTiramisuOption):
permissives=permissives,
)
@load_option
@option_type(['option', 'optiondescription', 'with_index'])
def reset(self, options_bag: List[OptionBag]):
"""Reset all personalised permissive"""
option_bag = options_bag[-1]
@ -600,40 +519,36 @@ class TiramisuOptionPermissive(CommonTiramisuOption):
class TiramisuOptionInformation(CommonTiramisuOption):
"""Manage option's informations"""
_allow_optiondescription = True
_follower_need_index = False
@load_option
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def get(self,
options_bag: List[OptionBag],
key: str,
name: str,
default=undefined,
) -> Any:
"""Get information"""
option_bag = options_bag[-1]
try:
return self._config_bag.context.get_values().get_information(self._config_bag,
option_bag,
key,
return self._config_bag.context.get_values().get_information(option_bag,
name,
undefined,
)
except ValueError:
return option_bag.option.impl_get_information(key, default)
return option_bag.option.impl_get_information(name, default)
@load_option
@option_type(['option', 'optiondescription'])
def set(self,
options_bag: List[OptionBag],
key: str,
value: Any) -> None:
"""Set information"""
option_bag = options_bag[-1]
self._config_bag.context.get_values().set_information(self._config_bag,
option_bag,
self._config_bag.context.get_values().set_information(option_bag,
key,
value,
)
@load_option
@option_type(['option', 'optiondescription'])
def reset(self,
options_bag: List[OptionBag],
key: str,
@ -644,7 +559,7 @@ class TiramisuOptionInformation(CommonTiramisuOption):
path=option_bag.path,
)
@load_option
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def list(self,
options_bag: List[OptionBag],
) -> list:
@ -657,19 +572,14 @@ class TiramisuOptionInformation(CommonTiramisuOption):
class TiramisuOptionValue(CommonTiramisuOption):
"""Manage option's value"""
_allow_optiondescription = True
_follower_need_index = True
_validate_properties = True
@option_type('optiondescription')
def dict(self, options_bag: List[OptionBag]):
"""Dict with path as key and value"""
#FIXME : .nowarnings comme .forcepermissive if not withwarning and self._config_bag.properties and 'warnings' in self._config_bag.properties:
# option_bag.config_bag = self._config_bag.copy()
# option_bag.config_bag.remove_warnings()
return self._config_bag.context.make_dict(options_bag[-1])
@option_type(['option', 'symlink'])
@option_type(['option', 'symlink', 'with_index'])
def get(self,
options_bag: List[OptionBag],
):
@ -688,7 +598,7 @@ class TiramisuOptionValue(CommonTiramisuOption):
parent_option_bag,
)
@option_type('option')
@option_type(['option', 'with_index'])
def set(self,
options_bag: List[OptionBag],
value,
@ -701,17 +611,18 @@ class TiramisuOptionValue(CommonTiramisuOption):
idx = value.index(undefined)
soption_bag = option_bag.copy()
soption_bag.index = idx
value[idx] = values.getdefaultvalue(soption_bag)
value[idx] = values.get_default_value(soption_bag)
elif value == undefined:
value = values.getdefaultvalue(option_bag)
if option_bag.option.impl_is_leader() and len(value) < self._config_bag.context.get_length_leadership(options_bag[-2]):
value = values.get_default_value(option_bag)
if option_bag.option.impl_is_leader() and \
len(value) < self._config_bag.context.get_length_leadership(options_bag[-2]):
raise LeadershipError(_('cannot reduce length of the leader "{}"'
'').format(option_bag.option.impl_get_display_name()))
return option_bag.config_bag.context.set_value(option_bag,
value,
)
@option_type(['group', 'option'])
@option_type(['group', 'option', 'with_index'])
def reset(self,
options_bag: List[OptionBag],
is_group: bool=False,
@ -723,26 +634,18 @@ class TiramisuOptionValue(CommonTiramisuOption):
self._config_bag,
)
else:
self._config_bag.context.delattr(option_bag)
values = self._config_bag.context.get_values()
if option_bag.index is not None:
values.reset_follower(option_bag)
else:
values.reset(option_bag)
@option_type('option')
@option_type(['option', 'with_index', 'symlink'])
def default(self, options_bag: List[OptionBag]):
"""Get default value (default of option or calculated value)"""
option_bag = options_bag[-1]
values = self._config_bag.context.get_values()
if option_bag.option.impl_is_symlinkoption():
value = []
length = self._config_bag.context.get_length_leadership(options_bag[-2])
for idx in range(length):
soption_bag = OptionBag(options_bag.option,
idx,
self._config_bag,
)
value.append(values.getdefaultvalue(soption_bag))
return value
return values.getdefaultvalue(option_bag)
return self._config_bag.context.get_values().get_default_value(options_bag[-1])
@option_type('option')
@option_type(['option', 'with_index'])
def valid(self, options_bag: List[OptionBag]):
"""The if the option's value is valid"""
option_bag = options_bag[-1]
@ -757,7 +660,7 @@ class TiramisuOptionValue(CommonTiramisuOption):
return False
return True
@option_type('choice')
@option_type(['choice', 'with_index'])
def list(self, options_bag: List[OptionBag]):
"""All values available for a ChoiceOption"""
option_bag = options_bag[-1]
@ -766,19 +669,15 @@ class TiramisuOptionValue(CommonTiramisuOption):
@option_type('leader')
def pop(self,
options_bag: List[OptionBag],
index: int):
index: int,
):
"""Pop a value"""
option_bag = options_bag[-1]
if len(options_bag) > 1:
leadership_option_bag = options_bag[-2]
else:
leadership_option_bag = None
self._config_bag.context.pop_leader(option_bag,
leadership_option_bag,
self._config_bag.context.get_values().reset_leadership(options_bag[-1],
options_bag[-2],
index,
)
@option_type(['leader', 'follower', 'with_index'])
@option_type(['leader', 'follower', 'with_or_without_index'])
def len(self, options_bag: List[OptionBag]):
"""Length for a follower option"""
return self._config_bag.context.get_length_leadership(options_bag[-2])
@ -786,7 +685,7 @@ class TiramisuOptionValue(CommonTiramisuOption):
def _registers(_registers: Dict[str, type],
prefix: str,
extra_type: Optional[type]=None):
):
for module_name in globals().keys():
if module_name != prefix and module_name.startswith(prefix):
module = globals()[module_name]
@ -893,7 +792,7 @@ class TiramisuOption(CommonTiramisu, TiramisuConfig):
group_type=None,
):
"""List options (by default list only option)"""
return self._list2(options_bag[-1],
return self._list(options_bag[-1],
type,
group_type,
recursive,
@ -943,8 +842,7 @@ class TiramisuContextInformation(TiramisuConfig):
"""Get an information"""
values = self._config_bag.context.get_values()
try:
return values.get_information(self._config_bag,
None,
return values.get_information(None,
name,
undefined,
)
@ -1115,7 +1013,6 @@ class TiramisuContextProperty(TiramisuConfig):
def add(self, prop):
"""Add a config property"""
settings = self._config_bag.context.get_settings()
props = set(self.get())
if prop not in props:
props.add(prop)
@ -1128,12 +1025,8 @@ class TiramisuContextProperty(TiramisuConfig):
props.remove(prop)
self._set(frozenset(props))
def get(self,
default=False):
def get(self):
"""Get all config properties"""
if default:
config = self._config_bag.context
properties = config.get_settings().get_context_properties(config.properties_cache)
return self._config_bag.properties
def _set(self,
@ -1168,7 +1061,6 @@ class TiramisuContextProperty(TiramisuConfig):
force_store_value = 'force_store_value' not in self._config_bag.properties
else:
force_store_value = False
settings = self._config_bag.context.get_settings()
self._config_bag.context.get_settings()._properties = deepcopy(properties)
self._config_bag.context.reset_cache(None, None)
self._reset_config_properties()
@ -1206,7 +1098,8 @@ class TiramisuContextProperty(TiramisuConfig):
def getdefault(self,
type: Optional[str]=None,
when: Optional[str]=None) -> Set[str]:
when: Optional[str]=None,
) -> Set[str]:
setting = self._config_bag.context.get_settings()
if type is None and when is None:
return setting.default_properties
@ -1216,14 +1109,11 @@ class TiramisuContextProperty(TiramisuConfig):
if type == 'read_only':
if when == 'append':
return setting.ro_append
else:
return setting.ro_remove
elif type == 'read_write':
if type == 'read_write':
if when == 'append':
return setting.rw_append
else:
return setting.rw_remove
else:
raise ValueError(_('unknown type {}').format(type))
@ -1302,7 +1192,8 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
):
option = TiramisuOption(path,
None,
self._config_bag)
self._config_bag,
)
if first:
return option
options.append(option)
@ -1318,7 +1209,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
None,
self._config_bag,
)
return self._list2(root_option_bag,
return self._list(root_option_bag,
type,
group_type,
recursive,
@ -1336,10 +1227,12 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
def dict(self,
clearable="all",
remotable="minimum",
form=[],
form=None,
force=False,
):
"""Convert config and option to tiramisu format"""
if form is None:
form = []
if force or self._tiramisu_dict is None:
self._load_dict(clearable, remotable)
return self._tiramisu_dict.todict(form)
@ -1356,7 +1249,6 @@ class _TiramisuContextConfigReset():
def reset(self):
"""Remove all datas to current config (informations, values, properties, ...)"""
# Option's values
settings = self._config_bag.context.get_settings()
context_owner = self._config_bag.context.get_values().get_context_owner()
self._config_bag.context.get_values()._values = {None: {None: [None, context_owner]}}
# Option's informations
@ -1470,6 +1362,7 @@ class _TiramisuContextMixConfig(_TiramisuContextGroupConfig, _TiramisuContextCon
def add(self,
config):
"""Add config from MetaConfig"""
# pylint: disable=protected-access
self._config_bag.context.add_config(config._config_bag.context)
def parents(self):
@ -1495,7 +1388,8 @@ class TiramisuContextCache(TiramisuConfig):
self._config_bag.context.reset_cache(None, None)
def set_expiration_time(self,
time: int) -> None:
time: int,
) -> None:
"""Change expiration time value"""
self._config_bag.expiration_time = time
@ -1505,6 +1399,8 @@ class TiramisuContextCache(TiramisuConfig):
class TiramisuAPI(TiramisuHelp):
"""TiramisuAPI common class
"""
_registers = {}
def __init__(self,
@ -1520,16 +1416,19 @@ class TiramisuAPI(TiramisuHelp):
config_bag = self._config_bag
return TiramisuDispatcherOption(config_bag,
self._orig_config_bags)
elif subfunc in ['forcepermissive', 'unrestraint']:
if subfunc in ['forcepermissive', 'unrestraint', 'nowarnings']:
if self._orig_config_bags:
raise ConfigError(_('do not use unrestraint and forcepermissive together'))
msg = _('do not use unrestraint, nowarnings or forcepermissive together')
raise ConfigError(msg)
config_bag = self._config_bag.copy()
if subfunc == 'unrestraint':
config_bag.unrestraint()
elif subfunc == 'nowarnings':
config_bag.nowarnings()
else:
config_bag.set_permissive()
return TiramisuAPI(config_bag, [self._config_bag])
elif subfunc == 'config':
if subfunc == 'config':
config_type = self._config_bag.context.impl_type
if config_type == 'group':
config = _TiramisuContextGroupConfig
@ -1541,7 +1440,7 @@ class TiramisuAPI(TiramisuHelp):
config = _TiramisuContextConfig
return config(self._config_bag,
self._orig_config_bags)
elif subfunc in self._registers:
if subfunc in self._registers:
config_bag = self._config_bag
# del config_bag.permissives
return self._registers[subfunc](config_bag,
@ -1549,18 +1448,21 @@ class TiramisuAPI(TiramisuHelp):
raise ConfigError(_('please specify a valid sub function ({})').format(subfunc))
def __dir__(self):
return list(self._registers.keys()) + ['unrestraint', 'forcepermissive', 'config']
return list(self._registers.keys()) + \
['unrestraint', 'forcepermissive', 'nowarnings', 'config']
class TiramisuDispatcherOption(TiramisuContextOption):
"""Select an option"""
def __call__(self,
path: str,
index: Optional[int]=None) -> TiramisuOption:
index: Optional[int]=None,
) -> TiramisuOption:
"""Select an option by path"""
return TiramisuOption(path,
index,
self._config_bag)
self._config_bag,
)
class Config(TiramisuAPI):
@ -1591,18 +1493,23 @@ class Config(TiramisuAPI):
del self._config_bag.context
del self._config_bag
del self._orig_config_bags
except:
except ConfigError:
pass
class MetaConfig(TiramisuAPI):
"""MetaConfig object that enables us to handle the sub configuration's options with common root optiondescription"""
"""MetaConfig object that enables us to handle the sub configuration's options
with common root optiondescription
"""
# pylint: disable=too-few-public-methods
def __init__(self,
children: 'Config'=[],
children: 'Config'=None,
name=None,
optiondescription: Optional[OptionDescription]=None,
display_name=None
) -> None:
if children is None:
children = []
if isinstance(children, KernelMetaConfig):
config = children
else:
@ -1628,7 +1535,10 @@ class MetaConfig(TiramisuAPI):
class MixConfig(TiramisuAPI):
"""MixConfig object that enables us to handle the sub configuration's options with differents root optiondescription"""
"""MixConfig object that enables us to handle the sub configuration's options
with differents root optiondescription
"""
# pylint: disable=too-few-public-methods
def __init__(self,
optiondescription: OptionDescription,
children: List[Config],
@ -1661,6 +1571,7 @@ class MixConfig(TiramisuAPI):
class GroupConfig(TiramisuAPI):
"""GroupConfig that enables us to access the sub configuration's options"""
# pylint: disable=too-few-public-methods
def __init__(self,
children,
name=None,

View file

@ -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}" '

View file

@ -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:
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

View file

@ -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,14 +246,11 @@ 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
found = False
@ -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,12 +316,15 @@ 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:
@ -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):
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,
options_bag = self.get_sub_option_bag(option_bag, # pylint: disable=no-member
follower_path,
idx,
True,
leadership_length=leadership_length,
)[-1],
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,26 +440,14 @@ 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,
) -> Any:
"""set value
"""
self.get_settings().validate_properties(option_bag)
return self.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)
def get_value(self,
option_bag,
parent_option_bag=None,
@ -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,20 +529,18 @@ 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))
else:
for opt_bag in options_bag:
owner = self.get_owner(opt_bag)
if owner != owners.default:
@ -578,16 +551,6 @@ class _SubConfig:
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,
self._impl_values.set_information(None, # pylint: disable=no-member
key,
value,
)
context = config_bag.context
cache = context.get_description()._cache_dependencies_information.get(key, [])
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,
next(self.find(root_option_bag,
bytype=None,
byname=byname,
byvalue=undefined,
raise_if_not_found=raise_if_not_found,
with_option=True,
):
break
))
byname = None
ret = []
@ -1035,25 +1007,30 @@ class KernelGroupConfig(_CommonConfig):
None,
cconfig_bag,
)
for path in child.find(root_option_bag,
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':

View file

@ -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))
# 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:
return
if '/' in network_value:
network_obj = ip_network('{0}'.format(network_value))
else:
if netmask_value is None:
return
network_obj = ip_network('{0}/{1}'.format(network_value,
netmask_value))
if ip_interface(ip) not in network_obj:
if netmask is None:
msg = _('this IP is not in network {0}{1}').format(network_value,
network_display_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 = _(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)
@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 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 is None or netmask['value'] is None:
return
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 {network["value"]} ({network["name"]})')
else:
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):
for val in values[1:]:
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()
if values[0]['value'] == val['value'] is not None:
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')
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,10 +226,12 @@ 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),
>>> val2 = StrOption('val2', '', callback=calc_value,
... callback_params=Params(ParamOption(val1, True),
... default=ParamValue('default_value'),
... condition=ParamOption(boolean),
... expected=ParamValue(True)))
@ -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,
oper = {'mul': mul,
'add': add,
'div': truediv,
'sub': sub}[operator]
'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,9 +563,6 @@ 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_',
@ -538,21 +579,19 @@ class CalcValuePropertyHelp(CalcValue):
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():
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_arg(name, f'"{value}"'))
msgs.append(self.build_property_message(name, f'"{value}"'))
msg = display_list(msgs, self.condition_operator.lower())
else:
return [(action, f'"{action}"')]
return [(action, f'"{action}" ({msg})')]
return
## calc_properties.setdefault(action, []).append(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)

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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()

View file

@ -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

View file

@ -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)
) -> None:
"""nothing to valide
"""
def validate_with_option(self,
value: Any,
option_bag: OptionBag) -> None:
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'

View file

@ -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

View file

@ -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,10 +113,9 @@ 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
def _validate_domain(self,

View file

@ -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:

View file

@ -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')

View file

@ -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,

View file

@ -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:

View file

@ -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()

View file

@ -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:

View file

@ -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,6 +58,21 @@ class Leadership(OptionDescription):
leader = children[0]
for idx, child in enumerate(children):
if __debug__:
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)
child._add_dependency(self)
child._leadership = weakref.ref(self)
if __debug__:
for prop in leader.impl_getproperties():
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()))
@ -68,63 +84,66 @@ class Leadership(OptionDescription):
'"{1}" is not a multi'
'').format(self.impl_get_display_name(),
child.impl_get_display_name()))
if idx != 0:
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 "{0}" '
'in leadership "{1}"'
'').format(child.impl_get_display_name(),
self.impl_get_display_name()))
if idx != 0:
# remove empty property for follower
child._properties = frozenset(child._properties - {'empty', 'unique'})
followers.append(child)
child._add_dependency(self)
child._leadership = weakref.ref(self)
if __debug__:
for prop in leader.impl_getproperties():
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
raise LeadershipError(_('leader cannot have "{}" property').format(prop))
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,22 +153,24 @@ class Leadership(OptionDescription):
values.reset(soption_bag)
def follower_force_store_value(self,
values,
value,
option_bag,
config_bag: 'ConfigBag',
owner,
dyn=None,
) -> None:
settings = option_bag.config_bag.context.get_settings()
"""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 'force_store_value' not in foption_bag.properties:
continue
if idx == 0:
indexes = [None]
else:
@ -157,48 +178,35 @@ class Leadership(OptionDescription):
for index in indexes:
foption_bag_index = OptionBag(follower,
index,
option_bag.config_bag,
config_bag,
)
values.set_storage_value(foption_bag_index.path,
index,
values.getvalue(foption_bag_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
)
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,
properties=set(), # do not check force_default_on_freeze
# or force_metaconfig_on_freeze
)
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

View file

@ -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')

View file

@ -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

View file

@ -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,

View file

@ -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,
self.impl_validate(default,
option_bag,
check_error=False)
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):
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)
#__________________________________________________________________________
# 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:
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
if not loaded:
calculation_validator(_value,
_index)
_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()

View file

@ -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,
yield child.to_dynoption(subpath,
suffix,
child))
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

View file

@ -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')

View file

@ -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'))

View file

@ -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(':')

View file

@ -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:

View file

@ -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()

View file

@ -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]

View file

@ -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

View file

@ -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)

View file

@ -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')

View file

@ -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,
{},
{},
"""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)
cache.setcache(None,
props = self.get_stored_properties(None,
None,
self.default_properties,
)
cache.setcache(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,
p_props = self.get_stored_properties(option_bag.path,
None,
option.impl_getproperties(),
)
if index is not None:
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,33 +741,31 @@ 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:
"""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 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,
'mandatory' in option_bag.properties and values.isempty(option_bag,
value,
force_allow_empty_list=force_allow_empty_list,
index=option_bag.index,
False,
):
raise PropertiesOptionError(option_bag,
['mandatory'],
self,
)
if 'empty' in option_bag.properties and values.isempty(option_bag.option,
if 'empty' in option_bag.properties and values.isempty(option_bag,
value,
force_allow_empty_list=True,
index=option_bag.index,
True,
):
raise PropertiesOptionError(option_bag,
['empty'],
@ -781,15 +773,20 @@ class Settings(object):
)
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

View file

@ -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(),

View file

@ -15,38 +15,36 @@
# 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:
default_values = {None: {None: [None, owners.user]}}
self._values = default_values
#______________________________________________________________________
@ -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,
def get_value(self,
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`
) -> 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,
# 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)):
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 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.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())
value = self.get_calculated_value(option_bag,
option_bag.option.impl_getdefault_multi(),
)
return value
def calculate_reset_cache(self,
def get_calculated_value(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,
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,
option_bag.config_bag.properties,
option_bag.properties,
'value')
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,19 +259,18 @@ 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,
leader = option_bag.option.impl_get_leadership()
leader.follower_force_store_value(value,
option_bag.config_bag,
owners.forced,
)
@ -285,13 +278,16 @@ class Values:
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,
val = self.get_calculated_value(option_bag,
value,
False,)
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,12 +470,16 @@ 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,
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:
@ -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,26 +547,36 @@ 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):
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_context = context.gen_fake_values()
fake_value = fake_context.get_values()
config_bag = option_bag.config_bag.copy()
config_bag.remove_validation()
@ -571,45 +584,52 @@ class Values:
soption_bag = option_bag.copy()
soption_bag.config_bag = config_bag
fake_value.reset_follower(soption_bag)
value = fake_value.getdefaultvalue(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.getdefaultvalue(option_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,
)
self.resetvalue_index(option_bag)
context.reset_cache(option_bag)
def resetvalue_index(self,
path,
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,
):
if path in self._values and index in self._values[path]:
del self._values[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]