Compare commits
61 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dda10f76f8 | |||
| 585cda5ffc | |||
| 43d3046e4c | |||
| da8e4ded3c | |||
| 7e8940252b | |||
| 47c84895f7 | |||
| d1add3dc5a | |||
| 3fad16d0ac | |||
| e6b3e7c317 | |||
| be637e4e31 | |||
| 4bd1b8f04e | |||
| 6f2f479364 | |||
| 0513d6677e | |||
| 60e259fef2 | |||
| acca0bc040 | |||
| e0f16b14c7 | |||
| e8921795d3 | |||
| ecbd17915b | |||
| d44a5ebe32 | |||
| 5e0bf84e50 | |||
| c56cdcfa02 | |||
| aa774cbce9 | |||
| b48328d021 | |||
| 7fca8e9d62 | |||
| 9c97f92775 | |||
| 7173a93749 | |||
| e351da2069 | |||
| 47c413e60c | |||
| ac35f2428b | |||
| ebc9779173 | |||
| 379630fc38 | |||
| 65b8fb014e | |||
| fcbb9c6812 | |||
| c14a34f232 | |||
| db1b8dd48a | |||
| 5ed902a372 | |||
| c54f7cb775 | |||
| 3aaebfa34e | |||
| 1a8ad3d06e | |||
| c24b0642a7 | |||
| 7ecec88881 | |||
| 3d8eb2c80d | |||
| 3fadb1f6c3 | |||
| 65b156c47d | |||
| db82dd6d41 | |||
| 888ba21551 | |||
| b8899c98b1 | |||
| 1b640689b8 | |||
| adf94e6b15 | |||
| 2f0e1fcb0c | |||
| 6e4b22aea2 | |||
| 90947b5578 | |||
| f33713231e | |||
| b443771c30 | |||
| 6181b728ad | |||
| 4cba819e0a | |||
| 42471d42b7 | |||
| 0c993eddb0 | |||
| 22fdabb6c0 | |||
| 46a27e3a5c | |||
| baf1245c7a |
61 changed files with 5420 additions and 4069 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
|
|||
|
||||
[project]
|
||||
name = "tiramisu"
|
||||
version = "5.1.0"
|
||||
version = "5.2.0a17"
|
||||
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
|
||||
readme = "README.md"
|
||||
description = "an options controller tool"
|
||||
|
|
@ -18,6 +18,8 @@ classifiers = [
|
|||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Operating System :: OS Independent",
|
||||
"Natural Language :: English",
|
||||
|
|
@ -33,5 +35,9 @@ name = "cz_conventional_commits"
|
|||
tag_format = "$version"
|
||||
version_scheme = "pep440"
|
||||
version_provider = "pep621"
|
||||
version_files = [
|
||||
"tiramisu/__version__.py",
|
||||
"pyproject.toml:version"
|
||||
]
|
||||
#update_changelog_on_bump = true
|
||||
changelog_merge_prerelease = true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
# from json import dumps, loads
|
||||
import asyncio
|
||||
from os import environ
|
||||
try:
|
||||
from tiramisu_api import Config
|
||||
|
|
@ -63,3 +62,14 @@ def parse_od_get(dico):
|
|||
else:
|
||||
ret[k.path()] = v
|
||||
return ret
|
||||
|
||||
|
||||
def get_dependencies(option):
|
||||
ret = []
|
||||
for a in option.dependencies():
|
||||
if a.isoptiondescription():
|
||||
ret.append((a.path(), None))
|
||||
else:
|
||||
ret.append((a.path(), a.index()))
|
||||
ret.sort()
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -69,11 +69,11 @@ def test_cache_importation_property():
|
|||
cfg = Config(od1)
|
||||
cfg.option('u2').property.add('prop')
|
||||
export = cfg.property.exportation()
|
||||
assert cfg.option('u2').property.get() == {'prop'}
|
||||
assert cfg.option('u2').property.get() == {'validator', 'prop'}
|
||||
cfg.option('u2').property.add('prop2')
|
||||
assert cfg.option('u2').property.get() == {'prop', 'prop2'}
|
||||
assert cfg.option('u2').property.get() == {'validator', 'prop', 'prop2'}
|
||||
cfg.property.importation(export)
|
||||
assert cfg.option('u2').property.get() == {'prop'}
|
||||
assert cfg.option('u2').property.get() == {'validator', 'prop'}
|
||||
cfg = Config(od1)
|
||||
# assert not list_sessions()
|
||||
|
||||
|
|
@ -366,8 +366,8 @@ def test_cache_leader_and_followers():
|
|||
cfg.value.get()
|
||||
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
|
||||
val1_props = []
|
||||
val1_val1_props = ['empty', 'unique']
|
||||
val1_val2_props = []
|
||||
val1_val1_props = ['empty', 'unique', 'validator']
|
||||
val1_val2_props = ['validator']
|
||||
global_props = frozenset(global_props)
|
||||
val1_props = frozenset(val1_props)
|
||||
val1_val1_props = frozenset(val1_val1_props)
|
||||
|
|
@ -384,7 +384,7 @@ def test_cache_leader_and_followers():
|
|||
#
|
||||
cfg.option('val1.val1').value.set([None])
|
||||
val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)}
|
||||
compare(settings.get_cached(), {'val1.val1': {None: ({'empty', 'unique'}, None, True)}})
|
||||
compare(settings.get_cached(), {'val1.val1': {None: ({'validator', 'empty', 'unique'}, None, True)}})
|
||||
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
|
||||
cfg.value.get()
|
||||
#has value
|
||||
|
|
@ -416,8 +416,8 @@ def test_cache_leader_callback():
|
|||
cfg.value.get()
|
||||
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
|
||||
val1_props = []
|
||||
val1_val1_props = ['empty', 'unique']
|
||||
val1_val2_props = []
|
||||
val1_val1_props = ['empty', 'unique', 'validator']
|
||||
val1_val2_props = ['validator']
|
||||
global_props = frozenset(global_props)
|
||||
val1_props = frozenset(val1_props)
|
||||
val1_val1_props = frozenset(val1_val1_props)
|
||||
|
|
@ -429,7 +429,7 @@ def test_cache_leader_callback():
|
|||
})
|
||||
compare(values.get_cached(), {'val1.val1': {None: ([], None)}})
|
||||
cfg.option('val1.val1').value.set([None])
|
||||
compare(settings.get_cached(), {'val1.val1': {None: ({'unique', 'empty'}, None, True)}})
|
||||
compare(settings.get_cached(), {'val1.val1': {None: ({'unique', 'empty', 'validator'}, None, True)}})
|
||||
|
||||
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
|
||||
cfg.value.get()
|
||||
|
|
@ -451,24 +451,24 @@ def test_cache_requires():
|
|||
settings = cfg._config_bag.context.properties_cache
|
||||
assert values.get_cached() == {}
|
||||
assert cfg.option('ip_address_service').value.get() == None
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
|
||||
'ip_address_service': {None: (set([]), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({'validator'}, None)},
|
||||
'ip_address_service': {None: ({"validator"}, None)}})
|
||||
|
||||
compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
|
||||
'activate_service': {None: (True, None)}})
|
||||
cfg.value.get()
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
|
||||
'ip_address_service': {None: (set([]), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
|
||||
'ip_address_service': {None: ({"validator"}, None)}})
|
||||
|
||||
compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
|
||||
'activate_service': {None: (True, None)}})
|
||||
cfg.option('ip_address_service').value.set('1.1.1.1')
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}})
|
||||
|
||||
compare(values.get_cached(), {'activate_service': {None: (True, None)}, 'ip_address_service': {None: ('1.1.1.1', None, True)}})
|
||||
cfg.value.get()
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
|
||||
'ip_address_service': {None: (set([]), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
|
||||
'ip_address_service': {None: ({"validator"}, None)}})
|
||||
|
||||
compare(values.get_cached(), {'ip_address_service': {None: ('1.1.1.1', None)},
|
||||
'activate_service': {None: (True, None)}})
|
||||
|
|
@ -477,8 +477,8 @@ def test_cache_requires():
|
|||
|
||||
compare(values.get_cached(), {'activate_service': {None: (False, None)}})
|
||||
cfg.value.get()
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
|
||||
'ip_address_service': {None: (set(['disabled']), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
|
||||
'ip_address_service': {None: ({'disabled', "validator"}, None)}})
|
||||
|
||||
compare(values.get_cached(), {'activate_service': {None: (False, None)}})
|
||||
# assert not list_sessions()
|
||||
|
|
@ -499,19 +499,19 @@ def test_cache_global_properties():
|
|||
settings = cfg._config_bag.context.properties_cache
|
||||
assert values.get_cached() == {}
|
||||
assert cfg.option('ip_address_service').value.get() == None
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
|
||||
'ip_address_service': {None: (set([]), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
|
||||
'ip_address_service': {None: ({"validator"}, None)}})
|
||||
|
||||
compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
|
||||
'activate_service': {None: (True, None)}})
|
||||
cfg.property.remove('disabled')
|
||||
assert cfg.option('ip_address_service').value.get() == None
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
|
||||
'ip_address_service': {None: (set([]), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
|
||||
'ip_address_service': {None: ({"validator"}, None)}})
|
||||
cfg.property.add('test')
|
||||
assert cfg.option('ip_address_service').value.get() == None
|
||||
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
|
||||
'ip_address_service': {None: (set([]), None)}})
|
||||
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
|
||||
'ip_address_service': {None: ({"validator"}, None)}})
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,10 @@ def test_choiceoption_calc_opt_function(config_type):
|
|||
cfg = get_config(cfg, config_type)
|
||||
assert cfg.option('choice').owner.isdefault()
|
||||
#
|
||||
dep = cfg.option('str').dependencies()
|
||||
assert len(dep) == 1
|
||||
assert dep[0].path() == 'choice'
|
||||
#
|
||||
cfg.option('choice').value.set('val1')
|
||||
assert cfg.option('choice').owner.get() == owner
|
||||
#
|
||||
|
|
@ -187,7 +191,6 @@ def test_choiceoption_calc_opt_function_propertyerror():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
#def test_choiceoption_calc_opt_multi_function(config_type):
|
||||
def test_choiceoption_calc_opt_multi_function():
|
||||
# FIXME
|
||||
config_type = 'tiramisu'
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ def test_duplicated_option():
|
|||
g1
|
||||
#in same OptionDescription
|
||||
with pytest.raises(ConflictError):
|
||||
d1 = OptionDescription('od', '', [g1, g1])
|
||||
OptionDescription('od', '', [g1, g1])
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -346,6 +346,28 @@ def test_duplicated_option_diff_od():
|
|||
Config(d2)
|
||||
|
||||
|
||||
def test_duplicated_option_diff_od_2():
|
||||
g1 = IntOption('g1', '', 1)
|
||||
d1 = OptionDescription('od1', '', [g1])
|
||||
#in different OptionDescription
|
||||
d2 = OptionDescription('od2', '', [d1, g1])
|
||||
d2
|
||||
with pytest.raises(ConflictError):
|
||||
Config(d2)
|
||||
|
||||
|
||||
def test_duplicated_option_diff_od_3():
|
||||
g1 = IntOption('g1', '', 1)
|
||||
d1 = OptionDescription('od1', '', [g1])
|
||||
d3 = OptionDescription('od3', '', [g1])
|
||||
#in different OptionDescription
|
||||
d2 = OptionDescription('od2', '', [d1, d3])
|
||||
d4 = OptionDescription('od4', '', [d2])
|
||||
d4
|
||||
with pytest.raises(ConflictError):
|
||||
Config(d4)
|
||||
|
||||
|
||||
def test_cannot_assign_value_to_option_description():
|
||||
od1 = make_description()
|
||||
cfg = Config(od1)
|
||||
|
|
@ -380,12 +402,12 @@ def test_prefix_error():
|
|||
try:
|
||||
cfg.option('test1').value.set('yes')
|
||||
except Exception as err:
|
||||
assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('yes', _('integer'), 'test1')
|
||||
assert str(err) == _('"{0}" is an invalid {1} for "{2}", which is not an integer').format('yes', _('integer'), 'test1')
|
||||
try:
|
||||
cfg.option('test1').value.set('yes')
|
||||
except Exception as err:
|
||||
err.prefix = ''
|
||||
assert str(err) == _('invalid value')
|
||||
assert str(err) == _('which is not an integer')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,6 @@ from tiramisu.error import PropertiesOptionError, ValueWarning, ConfigError
|
|||
import warnings
|
||||
|
||||
|
||||
#def teardown_function(function):
|
||||
# # test_od_not_list emit a warnings because of doesn't create a Config
|
||||
# with warnings.catch_warnings(record=True) as w:
|
||||
# assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__)
|
||||
|
||||
|
||||
def make_description():
|
||||
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
|
||||
gcdummy = BoolOption('dummy', 'dummy', default=False)
|
||||
|
|
@ -352,6 +346,8 @@ def test_invalid_option():
|
|||
PortOption('a', '', allow_zero=False, allow_wellknown=False, allow_registred=False, allow_private=False)
|
||||
with raises(ValueError):
|
||||
PortOption('a', '', 'tcp:80')
|
||||
with raises(ValueError):
|
||||
PortOption('a', '', '')
|
||||
NetworkOption('a', '')
|
||||
with raises(ValueError):
|
||||
NetworkOption('a', '', 'string')
|
||||
|
|
@ -423,7 +419,7 @@ def test_config_reset():
|
|||
cfg.owner.set('test')
|
||||
assert cfg.owner.get() == 'test'
|
||||
assert not cfg.option('gc.gc2.bool').value.get()
|
||||
assert not cfg.option('boolop').property.get()
|
||||
assert cfg.option('boolop').property.get() == frozenset(["validator"])
|
||||
assert not cfg.option('boolop').permissive.get()
|
||||
assert not cfg.option('wantref').information.get('info', None)
|
||||
#
|
||||
|
|
@ -440,7 +436,7 @@ def test_config_reset():
|
|||
cfg.config.reset()
|
||||
assert cfg.owner.get() == 'test'
|
||||
assert not cfg.option('gc.gc2.bool').value.get()
|
||||
assert not cfg.option('boolop').property.get()
|
||||
assert cfg.option('boolop').property.get() == {"validator"}
|
||||
assert not cfg.option('float').permissive.get()
|
||||
assert not cfg.option('wantref').information.get('info', None)
|
||||
# assert not list_sessions()
|
||||
|
|
|
|||
|
|
@ -275,3 +275,9 @@ def test_url(config_type):
|
|||
with pytest.raises(ValueError):
|
||||
cfg.option('u').value.set('https://FOO.COM:8443')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_domainname_existence():
|
||||
DomainnameOption('d', '', 'google.fr', test_existence=True)
|
||||
with pytest.raises(ValueError):
|
||||
DomainnameOption('d', '', 'ljijouuuehyfr.com', test_existence=True)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ def test_ip(config_type):
|
|||
cfg.option('a').value.set('192.168.1.0')
|
||||
cfg.option('a').value.set('88.88.88.88')
|
||||
cfg.option('a').value.set('0.0.0.0')
|
||||
cfg.option('a').value.set('2001:db8::1')
|
||||
if config_type != 'tiramisu-api':
|
||||
# FIXME
|
||||
with pytest.raises(ValueError):
|
||||
|
|
@ -148,8 +149,7 @@ def test_network_cidr(config_type):
|
|||
cfg.option('a').value.set('192.168.1.1')
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a').value.set('192.168.1.1/24')
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a').value.set('2001:db00::0/24')
|
||||
cfg.option('a').value.set('2001:db00::0/24')
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -197,8 +197,7 @@ def test_broadcast(config_type):
|
|||
cfg.option('a').value.set(1)
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a').value.set(2)
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('a').value.set('2001:db8::1')
|
||||
cfg.option('a').value.set('2001:db8::1')
|
||||
cfg.option('a').value.set('0.0.0.0')
|
||||
cfg.option('a').value.set('255.255.255.0')
|
||||
# assert not list_sessions()
|
||||
|
|
|
|||
|
|
@ -80,15 +80,15 @@ def test_copy_force_store_value():
|
|||
assert conf.value.exportation() == {'creole.general.wantref': {None: [True, 'user']}}
|
||||
assert conf2.value.exportation() == {'creole.general.wantref': {None: [False, 'forced']}}
|
||||
# assert not list_sessions()
|
||||
#
|
||||
#
|
||||
#def test_copy_force_store_value_metaconfig():
|
||||
# od1 = make_description()
|
||||
# meta = MetaConfig([], optiondescription=od1)
|
||||
# conf = meta.config.new()
|
||||
# assert meta.property.get() == conf.property.get()
|
||||
# assert meta.permissive.get() == conf.permissive.get()
|
||||
# conf.property.read_write()
|
||||
# assert conf.value.exportation() == {'creole.general.wantref': {None: [False, 'forced']}}
|
||||
# assert meta.value.exportation() == {}
|
||||
## assert not list_sessions()
|
||||
|
||||
|
||||
def test_copy_force_store_value_metaconfig():
|
||||
od1 = make_description()
|
||||
meta = MetaConfig([], optiondescription=od1)
|
||||
conf = meta.config.new()
|
||||
assert meta.property.get() == conf.property.get()
|
||||
assert meta.permissive.get() == conf.permissive.get()
|
||||
conf.property.read_write()
|
||||
assert conf.value.exportation() == {'creole.general.wantref': {None: [False, 'forced']}}
|
||||
assert meta.value.exportation() == {}
|
||||
# assert not list_sessions()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# coding: utf-8
|
||||
from .autopath import do_autopath
|
||||
do_autopath()
|
||||
from .config import parse_od_get
|
||||
from .config import parse_od_get, get_dependencies
|
||||
import pytest
|
||||
|
||||
from tiramisu.setting import groups, owners
|
||||
|
|
@ -13,7 +13,7 @@ from tiramisu import BoolOption, StrOption, ChoiceOption, IPOption, \
|
|||
Config, \
|
||||
Params, ParamOption, ParamValue, ParamIdentifier, ParamSelfOption, ParamDynOption, ParamIndex, ParamSelfInformation, ParamInformation, \
|
||||
Calculation, calc_value
|
||||
from tiramisu.error import PropertiesOptionError, ConfigError, ConflictError, ValueOptionError
|
||||
from tiramisu.error import PropertiesOptionError, ConfigError, ConflictError, ValueOptionError, AttributeOptionError
|
||||
|
||||
|
||||
def display_name(kls, subconfig, with_quote=False) -> str:
|
||||
|
|
@ -28,6 +28,8 @@ def display_name(kls, subconfig, with_quote=False) -> str:
|
|||
class ConvertDynOptionDescription(DynOptionDescription):
|
||||
def convert_identifier_to_path(self, identifier):
|
||||
# remove dot with is illegal
|
||||
if not identifier:
|
||||
return identifier
|
||||
return identifier.replace('.', '')
|
||||
|
||||
|
||||
|
|
@ -300,23 +302,37 @@ def test_prop_dyndescription():
|
|||
od = OptionDescription('od', '', [dod])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test'])
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == {'test', "validator"}
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == {'test', "validator"}
|
||||
cfg.option('od.dodval2.st').property.add('test2')
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == {'test', "validator"}
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == {'test', 'test2', "validator"}
|
||||
#
|
||||
assert set(cfg.option('od.dodval1').property.get()) == set([])
|
||||
assert set(cfg.option('od.dodval2').property.get()) == set([])
|
||||
assert set(cfg.option('od.dodval1').property.get()) == set()
|
||||
assert set(cfg.option('od.dodval2').property.get()) == set()
|
||||
cfg.option('od.dodval1').property.add('test1')
|
||||
assert set(cfg.option('od.dodval1').property.get()) == set(['test1'])
|
||||
assert set(cfg.option('od.dodval2').property.get()) == set([])
|
||||
assert set(cfg.option('od.dodval1').property.get()) == {'test1'}
|
||||
assert set(cfg.option('od.dodval2').property.get()) == set()
|
||||
cfg.option('od.dodval1').property.remove('test1')
|
||||
assert set(cfg.option('od.dodval1').property.get()) == set([])
|
||||
assert set(cfg.option('od.dodval2').property.get()) == set([])
|
||||
assert set(cfg.option('od.dodval1').property.get()) == set()
|
||||
assert set(cfg.option('od.dodval2').property.get()) == set()
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_prop_dyndescription_uncalculated():
|
||||
st = StrOption('st', '', properties=('test',))
|
||||
od = OptionDescription('od', '', [st], properties=('test_od',))
|
||||
dod = DynOptionDescription('dod', '', [od], identifiers=Calculation(return_list))
|
||||
od2 = OptionDescription('od', '', [dod])
|
||||
cfg = Config(od2)
|
||||
assert set(cfg.option('dod.od').property.get(uncalculated=True)) == {'test_od'}
|
||||
assert set(cfg.option('dod.od.st').property.get(uncalculated=True)) == {'test', 'validator'}
|
||||
with pytest.raises(AttributeOptionError):
|
||||
set(cfg.option('dod.od').property.get())
|
||||
with pytest.raises(AttributeOptionError):
|
||||
set(cfg.option('dod.od.st').property.get())
|
||||
|
||||
|
||||
def test_prop_dyndescription_force_store_value():
|
||||
st = StrOption('st', '', properties=('force_store_value',))
|
||||
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list))
|
||||
|
|
@ -336,6 +352,7 @@ def test_prop_dyndescription_force_store_value_calculation_prefix():
|
|||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
cfg.property.read_write()
|
||||
assert get_dependencies(cfg.option('od.lst')) == [('od.dodval1', None), ('od.dodval2', None)]
|
||||
assert cfg.option('od.dodval1.st').owner.isdefault() == False
|
||||
assert cfg.option('od.dodval2.st').owner.isdefault() == False
|
||||
assert parse_od_get(cfg.value.get()) == {'od.lst': ['val1', 'val2'], 'od.dodval1.st': 'val1', 'od.dodval2.st': 'val2'}
|
||||
|
|
@ -394,6 +411,8 @@ def test_callback_dyndescription_outside1():
|
|||
od = OptionDescription('od', '', [dod, out])
|
||||
od2 = OptionDescription('od', '', [od, lst])
|
||||
cfg = Config(od2)
|
||||
#
|
||||
assert get_dependencies(cfg.option('od.dodval1.st')) == [('od.out', None)]
|
||||
assert parse_od_get(cfg.value.get()) == {'od.dodval1.st': 'val', 'od.dodval2.st': 'val', 'od.out': 'val', 'lst': ['val1', 'val2']}
|
||||
cfg.option('od.dodval1.st').value.set('val1')
|
||||
cfg.option('od.dodval2.st').value.set('val2')
|
||||
|
|
@ -417,6 +436,8 @@ def test_callback_dyndescription_outside2():
|
|||
assert parse_od_get(cfg.value.get()) == {'od.dodval1.st': None, 'od.dodval2.st': None, 'od.out': None, 'lst': ['val1', 'val2']}
|
||||
cfg.option('od.out').value.set('val1')
|
||||
assert parse_od_get(cfg.value.get()) == {'od.dodval1.st': 'val1', 'od.dodval2.st': 'val1', 'od.out': 'val1', 'lst': ['val1', 'val2']}
|
||||
#
|
||||
assert get_dependencies(cfg.option('od.out')) == [('od.dodval1.st', None), ('od.dodval2.st', None)]
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -432,6 +453,77 @@ def test_callback_dyndescription_outside3():
|
|||
assert parse_od_get(cfg.value.get()) == {'od.out': 'val1', 'lst': ['val1', 'val2']}
|
||||
|
||||
|
||||
def test_callback_dyndescription_outside_optional():
|
||||
lst = StrOption('lst', '', ['val'], multi=True)
|
||||
st = StrOption('st', '', 'val')
|
||||
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(lst))))
|
||||
out = StrOption('out', '', Calculation(calc_value, Params(ParamDynOption(st, ['unknown_val'], optional=True))))
|
||||
od = OptionDescription('od', '', [dod, out])
|
||||
od2 = OptionDescription('od', '', [od, lst])
|
||||
cfg = Config(od2)
|
||||
assert parse_od_get(cfg.value.get()) == {'od.dodval.st': 'val', 'od.out': None, 'lst': ['val']}
|
||||
cfg.option('lst').value.set(['val', 'unknown_val'])
|
||||
assert parse_od_get(cfg.value.get()) == {'od.dodval.st': 'val', 'od.dodunknown_val.st': 'val', 'od.out': 'val', 'lst': ['val', 'unknown_val']}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_dyndescription_subdyn():
|
||||
lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
|
||||
st = StrOption('st', '', 'val1')
|
||||
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list))
|
||||
out = StrOption('out', '', Calculation(return_dynval, Params(ParamDynOption(st, ['val1', None]))), multi=True, properties=('notunique',))
|
||||
dod2 = DynOptionDescription('dod2', '', [dod, out], identifiers=Calculation(return_list))
|
||||
od = OptionDescription('od', '', [dod2])
|
||||
od2 = OptionDescription('od', '', [od, lst])
|
||||
cfg = Config(od2)
|
||||
cfg.property.read_write()
|
||||
assert cfg.option('od.dod2val1.dodval1.st').value.get() == 'val1'
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2.dodval1.st').value.get()
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2val1.dod.st').value.get()
|
||||
assert set(cfg.option('od.dod2val1.dodval1.st').property.get()) == {'validator'}
|
||||
assert set(cfg.option('od.dod2val1.dodval1.st').property.get(uncalculated=True)) == {'validator'}
|
||||
assert set(cfg.option('od.dod2.dod.st').property.get(uncalculated=True)) == {'validator'}
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2.dodval1.st').property.get(uncalculated=True)
|
||||
assert cfg.option('od.dod2val1.dod.st').property.get(uncalculated=True) == {'validator'}
|
||||
#
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2.dod.st').name()
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2val1.dod.st').name()
|
||||
assert cfg.option('od.dod2.dod.st').name(uncalculated=True) == 'st'
|
||||
cfg.option('od.dod2val1.dod.st').name(uncalculated=True)
|
||||
#
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2.dod.st').path()
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2val1.dod.st').path()
|
||||
assert cfg.option('od.dod2.dod.st').path(uncalculated=True) == 'od.dod2.dod.st'
|
||||
assert cfg.option('od.dod2val1.dod.st').path(uncalculated=True) == 'od.dod2.dod.st'
|
||||
#
|
||||
assert cfg.option('od.dod2.dod.st').isoptiondescription() is False
|
||||
assert cfg.option('od.dod2val1.dodval1.st').isoptiondescription() is False
|
||||
assert cfg.option('od.dod2.dod').isoptiondescription() is True
|
||||
assert cfg.option('od.dod2val1.dodval1').isoptiondescription() is True
|
||||
assert cfg.option('od.dod2.dod').isleadership() is False
|
||||
assert cfg.option('od.dod2val1.dodval1').isleadership() is False
|
||||
#
|
||||
assert cfg.option('od.dod2.dod.st').type() == "string"
|
||||
assert cfg.option('od.dod2val1.dodval1.st').type() == "string"
|
||||
assert cfg.option('od.dod2.dod').type() == "optiondescription"
|
||||
assert cfg.option('od.dod2val1.dodval1').type() == "optiondescription"
|
||||
assert cfg.option('od.dod2.dod').group_type() == "default"
|
||||
assert cfg.option('od.dod2val1.dodval1').group_type() == "default"
|
||||
#
|
||||
with pytest.raises(AttributeOptionError):
|
||||
cfg.option('od.dod2.dod').identifiers()
|
||||
assert cfg.option('od.dod2.dod').identifiers(only_self=True) == ['val1', 'val2']
|
||||
assert cfg.option('od.dod2val1.dodval1').identifiers() == ['val1', 'val1']
|
||||
assert cfg.option('od.dod2val1.dodval1').identifiers(only_self=True) == ['val1', 'val2']
|
||||
|
||||
|
||||
def test_callback_dyndescription_subdyn():
|
||||
lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
|
||||
st = StrOption('st', '', 'val1')
|
||||
|
|
@ -443,6 +535,10 @@ def test_callback_dyndescription_subdyn():
|
|||
cfg = Config(od2)
|
||||
cfg.property.read_write()
|
||||
assert parse_od_get(cfg.value.get()) == {'od.dod2val1.dodval1.st': 'val1', 'od.dod2val1.dodval2.st': 'val1', 'od.dod2val1.out': ['val1', 'val1'], 'od.dod2val2.dodval1.st': 'val1', 'od.dod2val2.dodval2.st': 'val1', 'od.dod2val2.out': ['val1', 'val1'], 'lst': ['val1', 'val2']}
|
||||
cfg.option('lst').value.set(["val1", "val3"])
|
||||
assert parse_od_get(cfg.value.get()) == {'od.dod2val1.dodval1.st': 'val1', 'od.dod2val1.dodval3.st': 'val1', 'od.dod2val1.out': ['val1', 'val1'], 'od.dod2val3.dodval1.st': 'val1', 'od.dod2val3.dodval3.st': 'val1', 'od.dod2val3.out': ['val1', 'val1'], 'lst': ['val1', 'val3']}
|
||||
cfg.option('lst').value.set(["val1", "val3", None])
|
||||
assert parse_od_get(cfg.value.get()) == {'od.dod2val1.dodval1.st': 'val1', 'od.dod2val1.dodval3.st': 'val1', 'od.dod2val1.out': ['val1', 'val1'], 'od.dod2val3.dodval1.st': 'val1', 'od.dod2val3.dodval3.st': 'val1', 'od.dod2val3.out': ['val1', 'val1'], 'lst': ['val1', 'val3', None]}
|
||||
|
||||
|
||||
def test_callback_list_dyndescription():
|
||||
|
|
@ -602,14 +698,14 @@ def test_prop_dyndescription_context():
|
|||
od = OptionDescription('od', '', [dod, val1])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test'])
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator", 'test'}
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test'}
|
||||
cfg.option('od.dodval2.st').property.add('test2')
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator", 'test'}
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'}
|
||||
cfg.option('od.dodval1.st').permissive.add('test')
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == set([])
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
|
||||
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator"}
|
||||
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -1177,6 +1273,8 @@ def test_leadership_dyndescription():
|
|||
cfg = Config(od1)
|
||||
owner = cfg.owner.get()
|
||||
#
|
||||
assert get_dependencies(cfg.option('od.stval1.st1.st2')) == [('od.stval1.st1', None)]
|
||||
assert get_dependencies(cfg.option('od.stval2.st1.st2')) == [('od.stval2.st1', None)]
|
||||
assert parse_od_get(cfg.value.get()) == {'od.stval2.st1.st1': [], 'od.stval1.st1.st1': []}
|
||||
assert cfg.option('od.stval1.st1.st1').value.get() == []
|
||||
assert cfg.option('od.stval2.st1.st1').value.get() == []
|
||||
|
|
@ -1236,6 +1334,9 @@ def test_leadership_dyndescription_force_store_value_leader():
|
|||
od1 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
#
|
||||
assert get_dependencies(cfg.option('od.stval1.st1.st2')) == [('od.stval1.st1', None)]
|
||||
assert get_dependencies(cfg.option('od.stval2.st1.st2')) == [('od.stval2.st1', None)]
|
||||
assert cfg.option('od.stval1.st1.st1').owner.isdefault() == False
|
||||
assert cfg.option('od.stval2.st1.st1').owner.isdefault() == False
|
||||
assert cfg.option('od.stval1.st1.st2', 0).owner.isdefault() == True
|
||||
|
|
@ -1696,16 +1797,6 @@ def test_leadership_callback_samegroup_dyndescription():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_invalid_conflict_dyndescription():
|
||||
st = StrOption('st', '')
|
||||
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list))
|
||||
dodinvalid = StrOption('dodinvalid', '')
|
||||
dod, dodinvalid
|
||||
with pytest.raises(ConflictError):
|
||||
OptionDescription('od', '', [dod, dodinvalid])
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_leadership_default_multi_dyndescription4():
|
||||
st1 = StrOption('st1', "", multi=True)
|
||||
st2 = StrOption('st2', "", multi=True, default_multi='no')
|
||||
|
|
@ -2051,6 +2142,10 @@ def test_leadership_dyndescription_convert():
|
|||
assert cfg.option('od.stval2.st1.st1').value.get() == []
|
||||
assert cfg.option('od.stval1.st1.st1').owner.isdefault()
|
||||
assert cfg.option('od.stval2.st1.st1').owner.isdefault()
|
||||
assert cfg.option('od.stval2.st1.st1').identifiers() == ["val.2"]
|
||||
assert cfg.option('od.stval2.st1.st1').identifiers(convert=True) == ["val2"]
|
||||
assert cfg.option('od.stval2').identifiers(only_self=True) == ["val.1", "val.2"]
|
||||
assert cfg.option('od.stval2').identifiers(only_self=True, convert=True) == ["val1", "val2"]
|
||||
#
|
||||
cfg.option('od.stval1.st1.st1').value.set(['yes'])
|
||||
assert parse_od_get(cfg.value.get()) == {'od.stval1.st1.st1': [{'od.stval1.st1.st1': 'yes', 'od.stval1.st1.st2': None}], 'od.stval2.st1.st1': []}
|
||||
|
|
@ -2095,6 +2190,23 @@ def test_leadership_dyndescription_convert():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_leadership_dyndescription_convert_2():
|
||||
identifier = StrOption("identifier", "", ['val.1', 'val.2', None], multi=True)
|
||||
st1 = StrOption('st1', "", multi=True)
|
||||
st2 = StrOption('st2', "", multi=True)
|
||||
stm = Leadership('st1', '', [st1, st2])
|
||||
st = ConvertDynOptionDescription('st', '', [stm], identifiers=Calculation(calc_value, Params((ParamOption(identifier),))))
|
||||
od = OptionDescription('od', '', [st])
|
||||
od1 = OptionDescription('od', '', [od, identifier])
|
||||
cfg = Config(od1)
|
||||
owner = cfg.owner.get()
|
||||
#
|
||||
assert cfg.option('od.stval2.st1.st1').identifiers() == ["val.2"]
|
||||
assert cfg.option('od.stval2.st1.st1').identifiers(convert=True) == ["val2"]
|
||||
assert cfg.option('od.stval2').identifiers(only_self=True) == ["val.1", "val.2"]
|
||||
assert cfg.option('od.stval2').identifiers(only_self=True, convert=True) == ["val1", "val2"]
|
||||
|
||||
|
||||
def test_leadership_callback_samegroup_dyndescription_convert():
|
||||
st1 = StrOption('st1', "", multi=True)
|
||||
st2 = StrOption('st2', "", multi=True)
|
||||
|
|
@ -2859,3 +2971,86 @@ def test_callback_list_dyndescription_information_not_list():
|
|||
cfg.information.set('identifier', 'ival3')
|
||||
assert cfg.option('od.dodival3.st').value.get() == ['ival3', 'val2']
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_dynoption_not_duplicate():
|
||||
st = StrOption('st', '')
|
||||
st2 = StrOption('st', '')
|
||||
dod = DynOptionDescription('od_', '', [st], identifiers=["val1"])
|
||||
dod2 = DynOptionDescription('od_', '', [st2], identifiers=["val2"])
|
||||
od = OptionDescription('od', '', [dod, dod2])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
assert parse_od_get(cfg.value.get()) == {
|
||||
'od.od_val1.st': None,
|
||||
'od.od_val2.st': None,
|
||||
}
|
||||
|
||||
|
||||
def test_dynoption_duplicate_1():
|
||||
st = StrOption('st', '', "val1")
|
||||
od = OptionDescription('od_val', '', [st])
|
||||
st2 = StrOption('st', '', "val2")
|
||||
dod = DynOptionDescription('od_', '', [st2], identifiers=["val"])
|
||||
od1 = OptionDescription('od', '', [od, dod])
|
||||
od2 = OptionDescription('od', '', [od1])
|
||||
cfg = Config(od2)
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
||||
|
||||
def test_dynoption_duplicate_2():
|
||||
st2 = StrOption('st', '', "val2")
|
||||
dod = DynOptionDescription('od_', '', [st2], identifiers=["val"])
|
||||
st = StrOption('st', '', "val1")
|
||||
od = OptionDescription('od_val', '', [st])
|
||||
od1 = OptionDescription('od', '', [dod, od])
|
||||
od2 = OptionDescription('od', '', [od1])
|
||||
cfg = Config(od2)
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
||||
|
||||
def test_dynoption_duplicate_3():
|
||||
st = StrOption('od_val', '')
|
||||
st2 = StrOption('st', '', "val2")
|
||||
dod = DynOptionDescription('od_', '', [st2], identifiers=["val"])
|
||||
od1 = OptionDescription('od', '', [dod, st])
|
||||
od2 = OptionDescription('od', '', [od1])
|
||||
cfg = Config(od2)
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
||||
|
||||
def test_dynoption_duplicate_4():
|
||||
st = StrOption('st', '')
|
||||
st2 = StrOption('st', '')
|
||||
dod = DynOptionDescription('od_', '', [st], identifiers=["val"])
|
||||
dod2 = DynOptionDescription('od_', '', [st2], identifiers=["val"])
|
||||
od = OptionDescription('od', '', [dod, dod2])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
||||
|
||||
def test_dynoption_duplicate_5():
|
||||
st = StrOption('st', '')
|
||||
st2 = StrOption('st', '')
|
||||
dod = DynOptionDescription('od_', '', [st], identifiers=["val"])
|
||||
dod2 = DynOptionDescription('od', '', [st2], identifiers=["_val"])
|
||||
od = OptionDescription('od', '', [dod, dod2])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.value.get()
|
||||
with pytest.raises(ConflictError):
|
||||
cfg.option('od.od_val').value.get()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
|
||||
from tiramisu.setting import groups, owners
|
||||
from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \
|
||||
StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, calc_value, Params
|
||||
StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, ParamOption, calc_value, Params, submulti
|
||||
from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError
|
||||
|
||||
|
||||
|
|
@ -788,6 +788,19 @@ def test_values_with_leader_and_followers_leader_pop():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_values_with_leader_and_followers_leader_pop_default_value():
|
||||
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", default=["192.168.230.145", "192.168.230.146"], multi=True, properties=('notunique',))
|
||||
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", default_multi="255.255.255.0", multi=True)
|
||||
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
od1 = OptionDescription('toto', '', [interface1])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.pop(0)
|
||||
compare(cfg.value.exportation(), {'ip_admin_eth0.ip_admin_eth0': {None: [['192.168.230.146'], 'user']}})
|
||||
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == ["192.168.230.146"]
|
||||
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == '255.255.255.0'
|
||||
|
||||
|
||||
def test_follower_unique():
|
||||
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, properties=('unique',))
|
||||
|
|
@ -796,7 +809,7 @@ def test_follower_unique():
|
|||
cfg = Config(od1)
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(["192.168.230.145", "192.168.230.146"])
|
||||
# unique property is removed for a follower
|
||||
assert not cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get()
|
||||
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == {"validator"}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -1042,7 +1055,7 @@ def test_follower_force_store_value_reset():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
#def test_follower_properties():
|
||||
def test_follower_properties():
|
||||
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, properties=('aproperty',))
|
||||
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
|
|
@ -1093,3 +1106,25 @@ def test_leader_forbidden_properties_callback(config_type):
|
|||
cfg = Config(od1)
|
||||
with pytest.raises(LeadershipError):
|
||||
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
|
||||
|
||||
|
||||
def test_follower_value_not_list():
|
||||
ip_admin_eth0 = IPOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
|
||||
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "masque du sous-réseau", default_multi='255.255.255.0', multi=True, properties=('force_store_value',))
|
||||
interface0 = Leadership('interface0', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||
od1 = OptionDescription('od', '', [interface0])
|
||||
od2 = OptionDescription('root', '', [od1])
|
||||
cfg = Config(od2)
|
||||
cfg.property.read_write()
|
||||
with pytest.raises(ValueError):
|
||||
cfg.option('od.interface0.ip_admin_eth0').value.set(None)
|
||||
|
||||
|
||||
def test_default_calc():
|
||||
var1 = StrOption('var1', "", multi=True, default=['leader1', 'leader2'], properties=frozenset({"mandatory",}))
|
||||
var2 = StrOption('var2', "", default_multi=[Calculation(calc_value, Params((ParamOption(var1))))], multi=submulti, properties=frozenset({"mandatory",}))
|
||||
leader = Leadership('interface0', '', [var1, var2])
|
||||
od1 = OptionDescription('od', '', [leader])
|
||||
od2 = OptionDescription('root', '', [od1])
|
||||
cfg = Config(od2)
|
||||
assert parse_od_get(cfg.value.get()) == {'od.interface0.var1': [{'od.interface0.var1': 'leader1', 'od.interface0.var2': ['leader1']}, {'od.interface0.var1': 'leader2', 'od.interface0.var2': ['leader2']}]}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ from tiramisu.error import PropertiesOptionError, ConfigError
|
|||
from tiramisu.setting import groups
|
||||
|
||||
|
||||
#def teardown_function(function):
|
||||
# assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__)
|
||||
def is_mandatory(variable):
|
||||
return True
|
||||
|
||||
|
|
@ -688,25 +686,25 @@ def return_list(val=None, identifier=None):
|
|||
return ['val1', 'val2']
|
||||
|
||||
|
||||
#def test_mandatory_dyndescription():
|
||||
# st = StrOption('st', '', properties=('mandatory',))
|
||||
# dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list))
|
||||
# od = OptionDescription('od', '', [dod])
|
||||
# od2 = OptionDescription('od', '', [od])
|
||||
# cfg = Config(od2)
|
||||
# cfg.property.read_only()
|
||||
# compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st'])
|
||||
#
|
||||
#
|
||||
#def test_mandatory_dyndescription_context():
|
||||
# val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
|
||||
# st = StrOption('st', '', properties=('mandatory',))
|
||||
# dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(val1))))
|
||||
# od = OptionDescription('od', '', [dod, val1])
|
||||
# od2 = OptionDescription('od', '', [od])
|
||||
# cfg = Config(od2)
|
||||
# cfg.property.read_only()
|
||||
# compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st'])
|
||||
def test_mandatory_dyndescription():
|
||||
st = StrOption('st', '', properties=('mandatory',))
|
||||
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list))
|
||||
od = OptionDescription('od', '', [dod])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
cfg.property.read_only()
|
||||
compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st'])
|
||||
|
||||
|
||||
def test_mandatory_dyndescription_context():
|
||||
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
|
||||
st = StrOption('st', '', properties=('mandatory',))
|
||||
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(val1))))
|
||||
od = OptionDescription('od', '', [dod, val1])
|
||||
od2 = OptionDescription('od', '', [od])
|
||||
cfg = Config(od2)
|
||||
cfg.property.read_only()
|
||||
compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st'])
|
||||
|
||||
|
||||
def test_mandatory_callback_leader_and_followers_leader():
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -15,76 +15,76 @@ def make_metaconfig():
|
|||
return MetaConfig([], optiondescription=od2, name='metacfg1')
|
||||
|
||||
|
||||
#def test_multi_parents_path():
|
||||
# """
|
||||
# metacfg1 (1) ---
|
||||
# | -- cfg1
|
||||
# metacfg2 (2) ---
|
||||
# """
|
||||
# metacfg1 = make_metaconfig()
|
||||
# cfg1 = metacfg1.config.new(type='config', name="cfg1")
|
||||
# metacfg2 = MetaConfig([cfg1], name='metacfg2')
|
||||
# #
|
||||
# assert metacfg1.config.path() == 'metacfg1'
|
||||
# assert metacfg2.config.path() == 'metacfg2'
|
||||
# assert cfg1.config.path() == 'metacfg2.metacfg1.cfg1'
|
||||
#
|
||||
#
|
||||
#def test_multi_parents_path_same():
|
||||
# """
|
||||
# --- metacfg2 (1) ---
|
||||
# metacfg1 --| | -- cfg1
|
||||
# --- metacfg3 (2) ---
|
||||
# """
|
||||
# metacfg1 = make_metaconfig()
|
||||
# metacfg2 = metacfg1.config.new(type='metaconfig', name="metacfg2")
|
||||
# metacfg3 = metacfg1.config.new(type='metaconfig', name="metacfg3")
|
||||
# cfg1 = metacfg2.config.new(type='config', name="cfg1")
|
||||
# metacfg3.config.add(cfg1)
|
||||
# #
|
||||
# assert metacfg2.config.path() == 'metacfg1.metacfg2'
|
||||
# assert metacfg3.config.path() == 'metacfg1.metacfg3'
|
||||
# assert cfg1.config.path() == 'metacfg1.metacfg3.metacfg1.metacfg2.cfg1'
|
||||
# metacfg1.option('od1.i1').value.set(1)
|
||||
# metacfg3.option('od1.i1').value.set(2)
|
||||
# assert cfg1.option('od1.i1').value.get() == 1
|
||||
# orideep = cfg1.config.deepcopy(metaconfig_prefix="test_", name='test_cfg1')
|
||||
# deep = orideep
|
||||
# while True:
|
||||
# try:
|
||||
# children = list(deep.config.list())
|
||||
# except:
|
||||
# break
|
||||
# assert len(children) < 2
|
||||
# deep = children[0]
|
||||
# assert deep.config.path() == 'test_metacfg3.test_metacfg1.test_metacfg2.test_cfg1'
|
||||
# assert cfg1.option('od1.i1').value.get() == 1
|
||||
#
|
||||
#
|
||||
#
|
||||
#def test_multi_parents_value():
|
||||
# metacfg1 = make_metaconfig()
|
||||
# cfg1 = metacfg1.config.new(type='config', name="cfg1")
|
||||
# metacfg2 = MetaConfig([cfg1], name='metacfg2')
|
||||
# #
|
||||
# assert cfg1.option('od1.i1').value.get() == None
|
||||
# assert cfg1.option('od1.i2').value.get() == 1
|
||||
# assert cfg1.option('od1.i3').value.get() == None
|
||||
# #
|
||||
# assert metacfg1.option('od1.i1').value.get() == None
|
||||
# assert metacfg1.option('od1.i2').value.get() == 1
|
||||
# assert metacfg1.option('od1.i3').value.get() == None
|
||||
# #
|
||||
# assert metacfg2.option('od1.i1').value.get() == None
|
||||
# assert metacfg2.option('od1.i2').value.get() == 1
|
||||
# assert metacfg2.option('od1.i3').value.get() == None
|
||||
# #
|
||||
# metacfg1.option('od1.i3').value.set(3)
|
||||
# assert metacfg1.option('od1.i3').value.get() == 3
|
||||
# assert cfg1.option('od1.i3').value.get() == 3
|
||||
# assert metacfg2.option('od1.i2').value.get() == 1
|
||||
# #
|
||||
# metacfg2.option('od1.i2').value.set(4)
|
||||
# assert metacfg2.option('od1.i2').value.get() == 4
|
||||
# assert metacfg1.option('od1.i2').value.get() == 1
|
||||
# assert cfg1.option('od1.i2').value.get() == 4
|
||||
def test_multi_parents_path():
|
||||
"""
|
||||
metacfg1 (1) ---
|
||||
| -- cfg1
|
||||
metacfg2 (2) ---
|
||||
"""
|
||||
metacfg1 = make_metaconfig()
|
||||
cfg1 = metacfg1.config.new(type='config', name="cfg1")
|
||||
metacfg2 = MetaConfig([cfg1], name='metacfg2')
|
||||
#
|
||||
assert metacfg1.config.path() == 'metacfg1'
|
||||
assert metacfg2.config.path() == 'metacfg2'
|
||||
assert cfg1.config.path() == 'metacfg2.metacfg1.cfg1'
|
||||
|
||||
|
||||
def test_multi_parents_path_same():
|
||||
"""
|
||||
--- metacfg2 (1) ---
|
||||
metacfg1 --| | -- cfg1
|
||||
--- metacfg3 (2) ---
|
||||
"""
|
||||
metacfg1 = make_metaconfig()
|
||||
metacfg2 = metacfg1.config.new(type='metaconfig', name="metacfg2")
|
||||
metacfg3 = metacfg1.config.new(type='metaconfig', name="metacfg3")
|
||||
cfg1 = metacfg2.config.new(type='config', name="cfg1")
|
||||
metacfg3.config.add(cfg1)
|
||||
#
|
||||
assert metacfg2.config.path() == 'metacfg1.metacfg2'
|
||||
assert metacfg3.config.path() == 'metacfg1.metacfg3'
|
||||
assert cfg1.config.path() == 'metacfg1.metacfg3.metacfg1.metacfg2.cfg1'
|
||||
metacfg1.option('od1.i1').value.set(1)
|
||||
metacfg3.option('od1.i1').value.set(2)
|
||||
assert cfg1.option('od1.i1').value.get() == 1
|
||||
orideep = cfg1.config.deepcopy(metaconfig_prefix="test_", name='test_cfg1')
|
||||
deep = orideep
|
||||
while True:
|
||||
try:
|
||||
children = list(deep.config.list())
|
||||
except:
|
||||
break
|
||||
assert len(children) < 2
|
||||
deep = children[0]
|
||||
assert deep.config.path() == 'test_metacfg3.test_metacfg1.test_metacfg2.test_cfg1'
|
||||
assert cfg1.option('od1.i1').value.get() == 1
|
||||
|
||||
|
||||
|
||||
def test_multi_parents_value():
|
||||
metacfg1 = make_metaconfig()
|
||||
cfg1 = metacfg1.config.new(type='config', name="cfg1")
|
||||
metacfg2 = MetaConfig([cfg1], name='metacfg2')
|
||||
#
|
||||
assert cfg1.option('od1.i1').value.get() == None
|
||||
assert cfg1.option('od1.i2').value.get() == 1
|
||||
assert cfg1.option('od1.i3').value.get() == None
|
||||
#
|
||||
assert metacfg1.option('od1.i1').value.get() == None
|
||||
assert metacfg1.option('od1.i2').value.get() == 1
|
||||
assert metacfg1.option('od1.i3').value.get() == None
|
||||
#
|
||||
assert metacfg2.option('od1.i1').value.get() == None
|
||||
assert metacfg2.option('od1.i2').value.get() == 1
|
||||
assert metacfg2.option('od1.i3').value.get() == None
|
||||
#
|
||||
metacfg1.option('od1.i3').value.set(3)
|
||||
assert metacfg1.option('od1.i3').value.get() == 3
|
||||
assert cfg1.option('od1.i3').value.get() == 3
|
||||
assert metacfg2.option('od1.i2').value.get() == 1
|
||||
#
|
||||
metacfg2.option('od1.i2').value.set(4)
|
||||
assert metacfg2.option('od1.i2').value.get() == 4
|
||||
assert metacfg1.option('od1.i2').value.get() == 1
|
||||
assert cfg1.option('od1.i2').value.get() == 4
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
|
|||
valid_ip_netmask, ParamSelfOption, ParamInformation, ParamSelfInformation
|
||||
from tiramisu.error import PropertiesOptionError, ConflictError, LeadershipError, ConfigError
|
||||
from tiramisu.i18n import _
|
||||
from .config import config_type, get_config, parse_od_get
|
||||
from .config import config_type, get_config, parse_od_get, get_dependencies
|
||||
|
||||
|
||||
def return_val():
|
||||
|
|
@ -693,6 +693,7 @@ def test_callback_leader_and_followers_leader(config_type):
|
|||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
cfg = get_config(cfg, config_type)
|
||||
assert [(a.path(), a.index()) for a in cfg.option('val1').dependencies()] == [('val2.val2', None)]
|
||||
assert cfg.option('val1').value.get() == ['val']
|
||||
assert cfg.option('val2.val2').value.get() == ['val']
|
||||
#
|
||||
|
|
@ -783,10 +784,29 @@ def test_callback_leader_and_followers_leader2(config_type):
|
|||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
cfg = get_config(cfg, config_type)
|
||||
assert get_dependencies(cfg.option('val1.val2')) == [('val1', None)]
|
||||
assert get_dependencies(cfg.option('val1.val3')) == [('val1', None)] #, ('val2.val2', 0)]
|
||||
assert get_dependencies(cfg.option('val1.val4')) == [('val1', None)] #, ('val2.val3', 0)]
|
||||
#
|
||||
cfg.option('val1.val1').value.set(['val'])
|
||||
assert get_dependencies(cfg.option('val1.val2')) == [('val1', None), ('val1.val3', 0)]
|
||||
assert get_dependencies(cfg.option('val1.val2', 0)) == [('val1', None), ('val1.val3', 0)]
|
||||
assert get_dependencies(cfg.option('val1.val3')) == [('val1', None), ('val1.val4', 0)]
|
||||
assert get_dependencies(cfg.option('val1.val3', 0)) == [('val1', None), ('val1.val4', 0)]
|
||||
assert get_dependencies(cfg.option('val1.val4')) == [('val1', None)]
|
||||
|
||||
assert cfg.option('val1.val4', 0).value.get() == 'val2'
|
||||
assert cfg.option('val1.val3', 0).value.get() == 'val2'
|
||||
assert cfg.option('val1.val2', 0).value.get() == 'val2'
|
||||
#
|
||||
cfg.option('val1.val1').value.set(['val1', 'val2'])
|
||||
assert get_dependencies(cfg.option('val1.val2')) == [('val1', None), ('val1.val3', 0), ('val1.val3', 1)]
|
||||
assert get_dependencies(cfg.option('val1.val2', 0)) == [('val1', None), ('val1.val3', 0)]
|
||||
assert get_dependencies(cfg.option('val1.val2', 1)) == [('val1', None), ('val1.val3', 1)]
|
||||
assert get_dependencies(cfg.option('val1.val3')) == [('val1', None), ('val1.val4', 0), ('val1.val4', 1)]
|
||||
assert get_dependencies(cfg.option('val1.val3', 0)) == [('val1', None), ('val1.val4', 0)]
|
||||
assert get_dependencies(cfg.option('val1.val3', 1)) == [('val1', None), ('val1.val4', 1)]
|
||||
assert get_dependencies(cfg.option('val1.val4')) == [('val1', None)]
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -945,7 +965,7 @@ def test_consistency_leader_and_followers_leader_mandatory_transitive():
|
|||
try:
|
||||
cfg.option('val1.val2', 0).value.get()
|
||||
except PropertiesOptionError as error:
|
||||
assert str(error) == str(_('cannot access to {0} {1} because has {2} {3}').format('option', '"val2"', _('property'), '"disabled"'))
|
||||
assert str(error) == str(_('cannot access to {0} {1} at index "{2}" because has {3} {4}').format('option', '"val2"', 0, _('property'), '"disabled"'))
|
||||
else:
|
||||
raise Exception('must raises')
|
||||
assert list(cfg.value.mandatory()) == []
|
||||
|
|
@ -1499,6 +1519,7 @@ def test_leadership_callback_description(config_type):
|
|||
cfg.option('od.st.st1.st2', 0).value.set('yes')
|
||||
assert cfg.option('od.st.st1.st1').owner.get() == owner
|
||||
assert cfg.option('od.st.st1.st2', 0).owner.get() == owner
|
||||
assert get_dependencies(cfg.option('od.st.st1.st2')) == [('od.st.st1', None)]
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -1515,6 +1536,8 @@ def test_leadership_callback_outside(config_type):
|
|||
owner = cfg.owner.get()
|
||||
cfg.option('od.st.st1.st1').value.set(['yes'])
|
||||
assert parse_od_get(cfg.value.get()) == {'od.st.st1.st1': [{'od.st.st1.st1': 'yes', 'od.st.st1.st2': 'val2'}], 'od.st.st3': ['val2']}
|
||||
assert get_dependencies(cfg.option('od.st.st1.st2')) == [('od.st.st1', None), ('od.st.st3', None,)]
|
||||
|
||||
## assert not list_sessions()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ def test_force_default_on_freeze_multi():
|
|||
# with pytest.raises(ConfigError):
|
||||
# Config(od1)
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#def test_force_metaconfig_on_freeze_leader():
|
||||
# dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',))
|
||||
# dummy2 = BoolOption('dummy2', 'Test string option', multi=True)
|
||||
|
|
|
|||
|
|
@ -202,10 +202,10 @@ def test_property_get_unique_empty():
|
|||
od1 = OptionDescription("options", "", [s, s2, s3, s4])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
assert cfg.option('string').property.get() == {'empty', 'unique'}
|
||||
assert cfg.option('string2').property.get() == {'empty', 'notunique'}
|
||||
assert cfg.option('string3').property.get() == {'unique', 'notempty'}
|
||||
assert cfg.option('string4').property.get() == {'notunique', 'notempty'}
|
||||
assert cfg.option('string').property.get() == {"validator", 'empty', 'unique'}
|
||||
assert cfg.option('string2').property.get() == {"validator", 'empty', 'notunique'}
|
||||
assert cfg.option('string3').property.get() == {"validator", 'unique', 'notempty'}
|
||||
assert cfg.option('string4').property.get() == {"validator", 'notunique', 'notempty'}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ def test_property_only_raises():
|
|||
od1 = OptionDescription("options", "", [s, intoption, stroption])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
assert cfg.option('str').property.get() == {'empty', 'unique'}
|
||||
assert cfg.option('str').property.get() == {'empty', 'unique', 'validator'}
|
||||
assert cfg.option('str').property.get(only_raises=True) == set()
|
||||
# assert not list_sessions()
|
||||
|
||||
|
|
@ -569,23 +569,23 @@ def test_access_by_get_whith_hide():
|
|||
def test_append_properties():
|
||||
od1 = make_description()
|
||||
cfg = Config(od1)
|
||||
assert cfg.option('gc.dummy').property.get() == set()
|
||||
assert cfg.option('gc.dummy').property.get() == {"validator"}
|
||||
cfg.option('gc.dummy').property.add('test')
|
||||
assert cfg.option('gc.dummy').property.get() == {'test'}
|
||||
assert cfg.option('gc.dummy').property.get() == {'test', "validator"}
|
||||
with pytest.raises(ConfigError):
|
||||
cfg.option('gc.dummy').property.add('force_store_value')
|
||||
assert cfg.option('gc.dummy').property.get() == {'test'}
|
||||
assert cfg.option('gc.dummy').property.get() == {'test', "validator"}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_reset_properties():
|
||||
od1 = make_description()
|
||||
cfg = Config(od1)
|
||||
assert cfg.option('gc.dummy').property.get() == set()
|
||||
assert cfg.option('gc.dummy').property.get() == {"validator"}
|
||||
cfg.option('gc.dummy').property.add('frozen')
|
||||
assert cfg.option('gc.dummy').property.get() == {'frozen'}
|
||||
assert cfg.option('gc.dummy').property.get() == {"validator", 'frozen'}
|
||||
cfg.option('gc.dummy').property.reset()
|
||||
assert cfg.option('gc.dummy').property.get() == set()
|
||||
assert cfg.option('gc.dummy').property.get() == {"validator"}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -594,7 +594,7 @@ def test_properties_cached():
|
|||
od1 = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
assert cfg.option('sub.b1').property.get() == {'test'}
|
||||
assert cfg.option('sub.b1').property.get() == {'test', "validator"}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -603,9 +603,9 @@ def test_append_properties_force_store_value():
|
|||
gcgroup = OptionDescription('gc', '', [gcdummy])
|
||||
od1 = OptionDescription('tiramisu', '', [gcgroup])
|
||||
cfg = Config(od1)
|
||||
assert cfg.option('gc.dummy').property.get() == {'force_store_value'}
|
||||
assert cfg.option('gc.dummy').property.get() == {'force_store_value', "validator"}
|
||||
cfg.option('gc.dummy').property.add('test')
|
||||
assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test'}
|
||||
assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test', "validator"}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
@ -818,27 +818,27 @@ def test_reset_properties_force_store_value():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
#def test_importation_force_store_value():
|
||||
# gcdummy = BoolOption('dummy', 'dummy', default=False,
|
||||
# properties=('force_store_value',))
|
||||
# gcgroup = OptionDescription('gc', '', [gcdummy])
|
||||
# od1 = OptionDescription('tiramisu', '', [gcgroup])
|
||||
# config1 = Config(od1)
|
||||
# assert config1.value.exportation() == {}
|
||||
# config1.property.add('frozen')
|
||||
# assert config1.value.exportation() == {}
|
||||
# config1.property.add('force_store_value')
|
||||
# assert config1.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
|
||||
# exportation = config1.property.exportation()
|
||||
# config2 = Config(od1)
|
||||
# assert config2.value.exportation() == {}
|
||||
# config2.property.importation(exportation)
|
||||
# assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
|
||||
# config2.property.importation(exportation)
|
||||
# assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
|
||||
## assert not list_sessions()
|
||||
#
|
||||
#
|
||||
def test_importation_force_store_value():
|
||||
gcdummy = BoolOption('dummy', 'dummy', default=False,
|
||||
properties=('force_store_value',))
|
||||
gcgroup = OptionDescription('gc', '', [gcdummy])
|
||||
od1 = OptionDescription('tiramisu', '', [gcgroup])
|
||||
config1 = Config(od1)
|
||||
assert config1.value.exportation() == {}
|
||||
config1.property.add('frozen')
|
||||
assert config1.value.exportation() == {}
|
||||
config1.property.add('force_store_value')
|
||||
assert config1.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
|
||||
exportation = config1.property.exportation()
|
||||
config2 = Config(od1)
|
||||
assert config2.value.exportation() == {}
|
||||
config2.property.importation(exportation)
|
||||
assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
|
||||
config2.property.importation(exportation)
|
||||
assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_set_modified_value():
|
||||
gcdummy = BoolOption('dummy', 'dummy', default=False, properties=('force_store_value',))
|
||||
gcgroup = OptionDescription('gc', '', [gcdummy])
|
||||
|
|
@ -920,18 +920,18 @@ def test_set_modified_value():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
#def test_none_is_not_modified():
|
||||
# gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
|
||||
# gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value',))
|
||||
# gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
|
||||
# od1 = OptionDescription('tiramisu', '', [gcgroup])
|
||||
# cfg = Config(od1)
|
||||
# assert cfg.value.exportation() == {}
|
||||
# cfg.property.read_write()
|
||||
# assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}}
|
||||
## assert not list_sessions()
|
||||
#
|
||||
#
|
||||
def test_none_is_not_modified():
|
||||
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
|
||||
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value',))
|
||||
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
|
||||
od1 = OptionDescription('tiramisu', '', [gcgroup])
|
||||
cfg = Config(od1)
|
||||
assert cfg.value.exportation() == {}
|
||||
cfg.property.read_write()
|
||||
assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_pprint():
|
||||
msg_error = _("cannot access to {0} {1} because has {2} {3}")
|
||||
msg_is_not = _('the value of "{0}" is not {1}')
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ def return_val(value, param=None):
|
|||
|
||||
|
||||
def return_if_val(value):
|
||||
if value != 'val':
|
||||
if value not in ['val', 'val_not_raise']:
|
||||
raise ValueError('test error')
|
||||
|
||||
|
||||
|
|
@ -121,6 +121,34 @@ def test_validator(config_type):
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_validator_no_validation(config_type):
|
||||
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()))], default='val', properties=frozenset(['novalidator']))
|
||||
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()))], properties=frozenset(['novalidator']))
|
||||
od1 = OptionDescription('root', '', [opt1, opt2])
|
||||
cfg_ori = Config(od1)
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
assert cfg.option('opt1').value.get() == 'val'
|
||||
assert cfg.option('opt2').value.valid() is True
|
||||
cfg.option('opt2').value.set('val')
|
||||
|
||||
|
||||
def test_validator_no_validation2(config_type):
|
||||
opt1 = StrOption('opt1', '', properties=frozenset(['novalidator']))
|
||||
od1 = OptionDescription('root', '', [opt1])
|
||||
cfg_ori = Config(od1)
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
cfg.option('opt1').value.set(1)
|
||||
assert cfg.option('opt1').value.get() == 1
|
||||
|
||||
|
||||
def test_validator_no_validation3(config_type):
|
||||
opt1 = StrOption('opt1', '', 1, properties=frozenset(['novalidator']))
|
||||
od1 = OptionDescription('root', '', [opt1])
|
||||
cfg_ori = Config(od1)
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
assert cfg.option('opt1').value.get() == 1
|
||||
|
||||
|
||||
def test_validator_not_valid(config_type):
|
||||
with pytest.raises(ValueError):
|
||||
StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val')
|
||||
|
|
@ -313,6 +341,13 @@ def test_validator_multi(config_type):
|
|||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.option('opt1').value.set(['val', 'val1'])
|
||||
assert len(w) == 1
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cfg.option('opt1').value.set(['val1', 'val2'])
|
||||
assert len(w) == 2
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
# same value twice
|
||||
cfg.option('opt1').value.set(['val', 'val', 'val_not_raise'])
|
||||
assert len(w) == 2
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,16 @@ def test_forcepermissive_and_unrestraint(config_type):
|
|||
cfg_ori.property.read_write()
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
with pytest.raises(ConfigError):
|
||||
cfg_ori.forcepermissive.add('disabled')
|
||||
cfg_ori.unrestraint.forcepermissive
|
||||
|
||||
|
||||
def test_unrestraint_and_unrestraint(config_type):
|
||||
od1 = make_description()
|
||||
cfg_ori = Config(od1)
|
||||
cfg_ori.property.read_write()
|
||||
cfg_ori.property.read_write()
|
||||
cfg = get_config(cfg_ori, config_type)
|
||||
cfg_ori.unrestraint.unrestraint
|
||||
|
||||
|
||||
def test_permissive(config_type):
|
||||
|
|
|
|||
|
|
@ -426,9 +426,9 @@ def test_requires_transitive_unrestraint(config_type):
|
|||
#
|
||||
if config_type == 'tiramisu-api':
|
||||
cfg.send()
|
||||
assert cfg_ori.option('activate_service_web').property.get() == {'disabled'}
|
||||
assert cfg_ori.option('activate_service_web').property.get() == {'disabled', "validator"}
|
||||
# FIXME assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'}
|
||||
assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled'}
|
||||
assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled', "validator"}
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -146,8 +146,8 @@ def test_slots_option_readonly():
|
|||
with pytest.raises(AttributeError):
|
||||
q._requires = 'q'
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#def test_slots_description():
|
||||
# # __slots__ for OptionDescription should be complete for __getattr__
|
||||
# slots = set()
|
||||
|
|
|
|||
|
|
@ -528,43 +528,43 @@ def test_submulti_unique():
|
|||
# assert not list_sessions()
|
||||
|
||||
|
||||
#def test_multi_submulti_meta():
|
||||
# multi = StrOption('multi', '', multi=submulti)
|
||||
# od1 = OptionDescription('od', '', [multi])
|
||||
# cfg = Config(od1, name='cfg')
|
||||
# cfg.property.read_write()
|
||||
# cfg2 = Config(od1)
|
||||
# cfg2.property.read_write()
|
||||
# meta = MetaConfig([cfg, cfg2])
|
||||
# meta.property.read_write()
|
||||
# meta.option('multi').value.set([['val']])
|
||||
# assert meta.option('multi').value.get() == [['val']]
|
||||
# newcfg = meta.config('cfg')
|
||||
# newcfg.option('multi').value.set([['val', None]])
|
||||
# assert cfg.option('multi').value.get() == [['val', None]]
|
||||
# newcfg = meta.config('cfg')
|
||||
# assert newcfg.option('multi').value.get() == [['val', None]]
|
||||
# assert meta.option('multi').value.get() == [['val']]
|
||||
## assert not list_sessions()
|
||||
#
|
||||
#
|
||||
#def test_multi_submulti_meta_no_cache():
|
||||
# multi = StrOption('multi', '', multi=submulti)
|
||||
# multi = StrOption('multi', '', multi=submulti)
|
||||
# od1 = OptionDescription('od', '', [multi])
|
||||
# cfg = Config(od1, name='cfg')
|
||||
# cfg.property.read_write()
|
||||
# cfg2 = Config(od1)
|
||||
# cfg.property.read_write()
|
||||
# meta = MetaConfig([cfg, cfg2])
|
||||
# meta.property.read_write()
|
||||
# meta.property.remove('cache')
|
||||
# meta.option('multi').value.set([['val']])
|
||||
# assert meta.option('multi').value.get() == [['val']]
|
||||
# newcfg = meta.config('cfg')
|
||||
# newcfg.option('multi').value.set([['val', None]])
|
||||
# assert cfg.option('multi').value.get() == [['val', None]]
|
||||
# newcfg = meta.config('cfg')
|
||||
# assert newcfg.option('multi').value.get() == [['val', None]]
|
||||
# assert meta.option('multi').value.get() == [['val']]
|
||||
## assert not list_sessions()
|
||||
def test_multi_submulti_meta():
|
||||
multi = StrOption('multi', '', multi=submulti)
|
||||
od1 = OptionDescription('od', '', [multi])
|
||||
cfg = Config(od1, name='cfg')
|
||||
cfg.property.read_write()
|
||||
cfg2 = Config(od1)
|
||||
cfg2.property.read_write()
|
||||
meta = MetaConfig([cfg, cfg2])
|
||||
meta.property.read_write()
|
||||
meta.option('multi').value.set([['val']])
|
||||
assert meta.option('multi').value.get() == [['val']]
|
||||
newcfg = meta.config('cfg')
|
||||
newcfg.option('multi').value.set([['val', None]])
|
||||
assert cfg.option('multi').value.get() == [['val', None]]
|
||||
newcfg = meta.config('cfg')
|
||||
assert newcfg.option('multi').value.get() == [['val', None]]
|
||||
assert meta.option('multi').value.get() == [['val']]
|
||||
# assert not list_sessions()
|
||||
|
||||
|
||||
def test_multi_submulti_meta_no_cache():
|
||||
multi = StrOption('multi', '', multi=submulti)
|
||||
multi = StrOption('multi', '', multi=submulti)
|
||||
od1 = OptionDescription('od', '', [multi])
|
||||
cfg = Config(od1, name='cfg')
|
||||
cfg.property.read_write()
|
||||
cfg2 = Config(od1)
|
||||
cfg.property.read_write()
|
||||
meta = MetaConfig([cfg, cfg2])
|
||||
meta.property.read_write()
|
||||
meta.property.remove('cache')
|
||||
meta.option('multi').value.set([['val']])
|
||||
assert meta.option('multi').value.get() == [['val']]
|
||||
newcfg = meta.config('cfg')
|
||||
newcfg.option('multi').value.set([['val', None]])
|
||||
assert cfg.option('multi').value.get() == [['val', None]]
|
||||
newcfg = meta.config('cfg')
|
||||
assert newcfg.option('multi').value.get() == [['val', None]]
|
||||
assert meta.option('multi').value.get() == [['val']]
|
||||
# assert not list_sessions()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ def test_symlink_option(config_type):
|
|||
assert cfg.option('c').issymlinkoption()
|
||||
assert cfg.option('s1.b').type() == 'boolean'
|
||||
assert cfg.option('c').type() == 'boolean'
|
||||
assert cfg.option('s1.b').type(only_self=True) == 'boolean'
|
||||
assert cfg.option('c').type(only_self=True) == 'symlink'
|
||||
assert cfg.option('s1.b').value.get() is False
|
||||
cfg.option("s1.b").value.set(True)
|
||||
cfg.option("s1.b").value.set(False)
|
||||
|
|
@ -157,7 +159,7 @@ def test_symlink_getproperties():
|
|||
od1 = OptionDescription('opt', '', [boolopt, linkopt])
|
||||
cfg = Config(od1)
|
||||
cfg.property.read_write()
|
||||
assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test'}
|
||||
assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test', "validator"}
|
||||
# assert boolopt.impl_has_callback() == linkopt.impl_has_callback() == False
|
||||
# assert not list_sessions()
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ from .error import ConfigError
|
|||
from .api import Config, MetaConfig, GroupConfig, MixConfig
|
||||
from .option import __all__ as all_options
|
||||
from .setting import owners, groups, undefined
|
||||
from .__version__ import __version__
|
||||
|
||||
|
||||
allfuncs = [
|
||||
|
|
@ -76,4 +77,3 @@ allfuncs.extend(all_options)
|
|||
del all_options
|
||||
__all__ = tuple(allfuncs)
|
||||
del allfuncs
|
||||
__version__ = "4.1.0"
|
||||
|
|
|
|||
1
tiramisu/__version__.py
Normal file
1
tiramisu/__version__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
__version__ = "5.2.0a17"
|
||||
452
tiramisu/api.py
452
tiramisu/api.py
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2024 Team tiramisu (see AUTHORS for all contributors)
|
||||
# Copyright (C) 2017-2025 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
|
||||
|
|
@ -26,6 +26,7 @@ from .error import (
|
|||
LeadershipError,
|
||||
ValueErrorWarning,
|
||||
PropertiesOptionError,
|
||||
AttributeOptionError,
|
||||
)
|
||||
from .i18n import _
|
||||
from .setting import (
|
||||
|
|
@ -52,6 +53,13 @@ from .autolib import Calculation
|
|||
TIRAMISU_VERSION = 5
|
||||
|
||||
|
||||
class Fake_SubConfig:
|
||||
def __init__(self, config_bag, path, index):
|
||||
self.config_bag = config_bag
|
||||
self.path = path
|
||||
self.index = index
|
||||
|
||||
|
||||
class TiramisuHelp:
|
||||
_tmpl_help = " {0}\t{1}"
|
||||
|
||||
|
|
@ -116,17 +124,19 @@ class TiramisuHelp:
|
|||
|
||||
class CommonTiramisu(TiramisuHelp):
|
||||
_validate_properties = True
|
||||
_allow_dynoption = False
|
||||
|
||||
def _set_subconfig(self) -> None:
|
||||
if not self._subconfig:
|
||||
self._subconfig = self._config_bag.context.get_sub_config(
|
||||
self._config_bag,
|
||||
self._path,
|
||||
self._index,
|
||||
validate_properties=False,
|
||||
allow_dynoption=self._allow_dynoption,
|
||||
)
|
||||
try:
|
||||
self._subconfig = self._config_bag.context.get_sub_config(
|
||||
self._config_bag,
|
||||
self._path,
|
||||
self._index,
|
||||
validate_properties=False,
|
||||
allow_dynoption=True,
|
||||
)
|
||||
except AssertionError as err:
|
||||
raise ConfigError(str(err))
|
||||
|
||||
|
||||
def option_type(typ):
|
||||
|
|
@ -152,6 +162,11 @@ def option_type(typ):
|
|||
kwargs["is_group"] = True
|
||||
return func(self, options_bag, *args[1:], **kwargs)
|
||||
self._set_subconfig()
|
||||
if (
|
||||
not isinstance(typ, list) or "allow_dynoption" not in typ
|
||||
) and self._subconfig.is_dynamic_without_identifiers:
|
||||
raise AttributeOptionError(self._subconfig.path, "option-dynamic")
|
||||
|
||||
option = self._subconfig.option
|
||||
error_type = None
|
||||
if "dynamic" in types:
|
||||
|
|
@ -200,7 +215,9 @@ def option_type(typ):
|
|||
"please specify index with a follower option ({0}.{1})"
|
||||
).format(self.__class__.__name__, func.__name__)
|
||||
raise ConfigError(msg)
|
||||
if self._validate_properties and "dont_validate_property" not in types:
|
||||
if "validate_properties" in types or (
|
||||
self._validate_properties and "dont_validate_property" not in types
|
||||
):
|
||||
settings = self._config_bag.context.get_settings()
|
||||
parent = self._subconfig.parent
|
||||
if parent and parent.transitive_properties:
|
||||
|
|
@ -251,10 +268,14 @@ class _TiramisuOptionWalk:
|
|||
validate_properties: bool,
|
||||
*,
|
||||
uncalculated: bool = False,
|
||||
with_index: bool = True,
|
||||
):
|
||||
options = []
|
||||
for sub_subconfig in subconfig.get_children(
|
||||
validate_properties, uncalculated=uncalculated
|
||||
validate_properties,
|
||||
uncalculated=uncalculated,
|
||||
with_index=with_index,
|
||||
check_dynamic_without_identifiers=False,
|
||||
):
|
||||
options.append(
|
||||
TiramisuOption(
|
||||
|
|
@ -272,36 +293,71 @@ class _TiramisuOptionOptionDescription:
|
|||
|
||||
_validate_properties = False
|
||||
|
||||
@option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
|
||||
@option_type(
|
||||
[
|
||||
"optiondescription",
|
||||
"option",
|
||||
"with_or_without_index",
|
||||
"symlink",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def get(self):
|
||||
"""Get Tiramisu option"""
|
||||
return self._subconfig.option
|
||||
|
||||
@option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
|
||||
@option_type(
|
||||
[
|
||||
"optiondescription",
|
||||
"option",
|
||||
"with_or_without_index",
|
||||
"symlink",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def isoptiondescription(self):
|
||||
"""Test if option is an optiondescription"""
|
||||
return self._subconfig.option.impl_is_optiondescription()
|
||||
|
||||
@option_type(["optiondescription"])
|
||||
@option_type(["optiondescription", "allow_dynoption"])
|
||||
def isleadership(self):
|
||||
"""Test if option is a leader or a follower"""
|
||||
return self._subconfig.option.impl_is_leadership()
|
||||
|
||||
@option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
|
||||
@option_type(
|
||||
[
|
||||
"optiondescription",
|
||||
"option",
|
||||
"with_or_without_index",
|
||||
"symlink",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def description(
|
||||
self,
|
||||
with_quote: bool = False,
|
||||
uncalculated: bool = False,
|
||||
):
|
||||
"""Get option description"""
|
||||
if not uncalculated:
|
||||
return self._subconfig.option.impl_get_display_name(self._subconfig)
|
||||
return self._subconfig.option.impl_get_display_name(
|
||||
self._subconfig, with_quote=with_quote
|
||||
)
|
||||
return self._subconfig.option._get_information(
|
||||
self._subconfig,
|
||||
"doc",
|
||||
None,
|
||||
)
|
||||
|
||||
@option_type(["optiondescription", "option", "symlink", "with_or_without_index"])
|
||||
@option_type(
|
||||
[
|
||||
"optiondescription",
|
||||
"option",
|
||||
"symlink",
|
||||
"with_or_without_index",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def name(
|
||||
self,
|
||||
*,
|
||||
|
|
@ -310,9 +366,19 @@ class _TiramisuOptionOptionDescription:
|
|||
"""Get option name"""
|
||||
if uncalculated:
|
||||
return self._subconfig.option.impl_getname()
|
||||
if self._subconfig.is_dynamic_without_identifiers:
|
||||
raise AttributeOptionError(self._subconfig.path, "option-dynamic")
|
||||
return self._subconfig.true_path.rsplit(".", 1)[-1]
|
||||
|
||||
@option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
|
||||
@option_type(
|
||||
[
|
||||
"optiondescription",
|
||||
"option",
|
||||
"with_or_without_index",
|
||||
"symlink",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def path(
|
||||
self,
|
||||
*,
|
||||
|
|
@ -321,8 +387,19 @@ class _TiramisuOptionOptionDescription:
|
|||
"""Get option path"""
|
||||
if uncalculated:
|
||||
return self._subconfig.option.impl_getpath()
|
||||
if self._subconfig.is_dynamic_without_identifiers:
|
||||
raise AttributeOptionError(self._subconfig.path, "option-dynamic")
|
||||
return self._subconfig.true_path
|
||||
|
||||
def parent(self):
|
||||
parent = self._subconfig.parent
|
||||
return TiramisuOption(
|
||||
parent.path,
|
||||
None,
|
||||
self._config_bag,
|
||||
subconfig=parent,
|
||||
)
|
||||
|
||||
@option_type(["optiondescription", "option", "symlink", "with_or_without_index"])
|
||||
def has_dependency(
|
||||
self,
|
||||
|
|
@ -332,34 +409,118 @@ class _TiramisuOptionOptionDescription:
|
|||
return self._subconfig.option.impl_has_dependency(self_is_dep)
|
||||
|
||||
@option_type(["optiondescription", "option", "symlink", "with_or_without_index"])
|
||||
def dependencies(self):
|
||||
def dependencies(
|
||||
self,
|
||||
*,
|
||||
uncalculated: bool = False,
|
||||
):
|
||||
"""Get dependencies from this option"""
|
||||
options = []
|
||||
for option in self._subconfig.option.get_dependencies(self._config_bag.context):
|
||||
options.append(
|
||||
TiramisuOption(
|
||||
option().impl_getpath(),
|
||||
None,
|
||||
self._config_bag,
|
||||
allow_dynoption=True,
|
||||
)
|
||||
)
|
||||
context = self._config_bag.context
|
||||
index = self._index
|
||||
parent = self._subconfig.parent
|
||||
parent_option = parent.option
|
||||
for is_default, woption in self._subconfig.option.get_dependencies(
|
||||
self._config_bag.context
|
||||
):
|
||||
option = woption()
|
||||
if not uncalculated and option.issubdyn():
|
||||
for subconfig in context.get_dynamic_from_dyn_option(
|
||||
self._subconfig, option
|
||||
):
|
||||
if subconfig.properties is None:
|
||||
subconfig.properties = undefined
|
||||
if is_default and subconfig.config_bag.context.get_owner(subconfig) != owners.default:
|
||||
continue
|
||||
options.append(
|
||||
TiramisuOption(
|
||||
subconfig.path,
|
||||
None,
|
||||
self._config_bag,
|
||||
)
|
||||
)
|
||||
elif not uncalculated and option.impl_is_dynoptiondescription():
|
||||
for subconfig in context.get_dynamic_from_dyn_option(
|
||||
self._subconfig, option
|
||||
):
|
||||
if is_default and subconfig.config_bag.context.get_owner(subconfig) != owners.default:
|
||||
continue
|
||||
options.append(
|
||||
TiramisuOption(
|
||||
subconfig.path,
|
||||
None,
|
||||
self._config_bag,
|
||||
)
|
||||
)
|
||||
else:
|
||||
if (
|
||||
not option.impl_is_optiondescription()
|
||||
and option.impl_is_follower()
|
||||
and parent_option.impl_is_leadership()
|
||||
and parent_option.in_same_leadership(option)
|
||||
):
|
||||
if index is not None:
|
||||
current_indexes = [index]
|
||||
else:
|
||||
current_indexes = range(parent.get_length_leadership())
|
||||
else:
|
||||
current_indexes = [None]
|
||||
for current_index in current_indexes:
|
||||
t_option = TiramisuOption(
|
||||
option.impl_getpath(),
|
||||
current_index,
|
||||
self._config_bag,
|
||||
allow_dynoption=uncalculated,
|
||||
)
|
||||
t_option._set_subconfig()
|
||||
subconfig = t_option._subconfig
|
||||
if is_default and subconfig.config_bag.context.get_owner(subconfig) != owners.default:
|
||||
continue
|
||||
options.append(
|
||||
t_option,
|
||||
)
|
||||
return options
|
||||
|
||||
@option_type(["option", "optiondescription", "symlink", "with_or_without_index"])
|
||||
def type(self):
|
||||
@option_type(
|
||||
[
|
||||
"option",
|
||||
"optiondescription",
|
||||
"symlink",
|
||||
"with_or_without_index",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def type(self, only_self=False, translation=False):
|
||||
"""Get de option type"""
|
||||
option = self._subconfig.option
|
||||
if option.impl_is_optiondescription():
|
||||
return "optiondescription"
|
||||
return option.get_type()
|
||||
if translation:
|
||||
type_ = _("optiondescription")
|
||||
else:
|
||||
type_ = "optiondescription"
|
||||
elif only_self and option.impl_is_symlinkoption():
|
||||
if translation:
|
||||
type_ = _("symlink")
|
||||
else:
|
||||
type_ = "symlink"
|
||||
else:
|
||||
type_ = option.get_type(translation=translation)
|
||||
return type_
|
||||
|
||||
@option_type(["option", "symlink", "with_or_without_index"])
|
||||
@option_type(["option", "symlink", "with_or_without_index", "allow_dynoption"])
|
||||
def extra(self, extra):
|
||||
"""Get de option extra"""
|
||||
return self._subconfig.option.impl_get_extra(extra)
|
||||
|
||||
@option_type(["option", "optiondescription", "symlink", "with_or_without_index"])
|
||||
@option_type(
|
||||
[
|
||||
"option",
|
||||
"optiondescription",
|
||||
"symlink",
|
||||
"with_or_without_index",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def isdynamic(self, *, only_self: bool = False):
|
||||
"""Test if option is a dynamic optiondescription"""
|
||||
if not only_self:
|
||||
|
|
@ -369,7 +530,7 @@ class _TiramisuOptionOptionDescription:
|
|||
and self._subconfig.option.impl_is_dynoptiondescription()
|
||||
)
|
||||
|
||||
@option_type(["option", "leadership"])
|
||||
@option_type(["option", "leadership", "allow_dynoption"])
|
||||
def leader(self):
|
||||
"""Get the leader option for a leadership or a follower option"""
|
||||
option = self._subconfig.option
|
||||
|
|
@ -414,15 +575,31 @@ class _TiramisuOptionOptionDescription:
|
|||
)
|
||||
return ret
|
||||
|
||||
@option_type(["dynamic", "with_or_without_index"])
|
||||
@option_type(["dynamic", "with_or_without_index", "allow_dynoption"])
|
||||
def identifiers(
|
||||
self,
|
||||
only_self: bool = False,
|
||||
uncalculated: bool = False,
|
||||
convert: bool = False,
|
||||
):
|
||||
"""Get identifiers for dynamic option"""
|
||||
if not only_self:
|
||||
return self._subconfig.identifiers
|
||||
if self._subconfig.is_dynamic_without_identifiers and not uncalculated:
|
||||
raise AttributeOptionError(self._subconfig.path, "option-dynamic")
|
||||
if not convert:
|
||||
return self._subconfig.identifiers
|
||||
identifiers = []
|
||||
dynconfig = None
|
||||
subconfig = self._subconfig
|
||||
while not dynconfig:
|
||||
if subconfig.option.impl_is_optiondescription() and subconfig.option.impl_is_dynoptiondescription():
|
||||
dynconfig = subconfig
|
||||
subconfig = subconfig.parent
|
||||
for identifier in self._subconfig.identifiers:
|
||||
if identifier is None:
|
||||
continue
|
||||
identifiers.append(dynconfig.option.convert_identifier_to_path(identifier))
|
||||
return identifiers
|
||||
if (
|
||||
not self._subconfig.option.impl_is_optiondescription()
|
||||
or not self._subconfig.option.impl_is_dynoptiondescription()
|
||||
|
|
@ -435,33 +612,34 @@ class _TiramisuOptionOptionDescription:
|
|||
return self._subconfig.option.get_identifiers(
|
||||
self._subconfig.parent,
|
||||
uncalculated=uncalculated,
|
||||
convert=convert,
|
||||
)
|
||||
|
||||
|
||||
class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
|
||||
"""Manage option"""
|
||||
|
||||
@option_type(["option", "symlink", "with_or_without_index"])
|
||||
@option_type(["option", "symlink", "with_or_without_index", "allow_dynoption"])
|
||||
def ismulti(self):
|
||||
"""Test if option could have multi value"""
|
||||
return self._subconfig.option.impl_is_multi()
|
||||
|
||||
@option_type(["option", "symlink", "with_or_without_index"])
|
||||
@option_type(["option", "symlink", "with_or_without_index", "allow_dynoption"])
|
||||
def issubmulti(self):
|
||||
"""Test if option could have submulti value"""
|
||||
return self._subconfig.option.impl_is_submulti()
|
||||
|
||||
@option_type(["option", "with_or_without_index", "symlink"])
|
||||
@option_type(["option", "with_or_without_index", "symlink", "allow_dynoption"])
|
||||
def isleader(self):
|
||||
"""Test if option is a leader"""
|
||||
return self._subconfig.option.impl_is_leader()
|
||||
|
||||
@option_type(["option", "with_or_without_index", "symlink"])
|
||||
@option_type(["option", "with_or_without_index", "symlink", "allow_dynoption"])
|
||||
def isfollower(self):
|
||||
"""Test if option is a follower"""
|
||||
return self._subconfig.option.impl_is_follower()
|
||||
|
||||
@option_type(["option", "symlink", "with_or_without_index"])
|
||||
@option_type(["option", "symlink", "with_or_without_index", "allow_dynoption"])
|
||||
def issymlinkoption(self) -> bool:
|
||||
"""Test if option is a symlink option"""
|
||||
return self._subconfig.option.impl_is_symlinkoption()
|
||||
|
|
@ -483,11 +661,13 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
|
|||
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]?)$"
|
||||
|
||||
@option_type(["option", "with_or_without_index", "symlink"])
|
||||
def index(self):
|
||||
def index(self, index=None):
|
||||
"""Get index of option"""
|
||||
return self._subconfig.index
|
||||
if index is None:
|
||||
return self._subconfig.index
|
||||
return TiramisuOption(self._path, index, self._config_bag)
|
||||
|
||||
@option_type(["symlink", "optiondescription"])
|
||||
@option_type(["symlink", "optiondescription", "allow_dynoption"])
|
||||
def option(self, *args, **kwargs):
|
||||
"""For OptionDescription get sub option, for symlinkoption get the linked option"""
|
||||
if self._subconfig.option.impl_is_optiondescription():
|
||||
|
|
@ -531,14 +711,33 @@ class TiramisuOptionOwner(CommonTiramisuOption):
|
|||
_validate_properties = True
|
||||
|
||||
@option_type(["symlink", "option", "with_index"])
|
||||
def get(self):
|
||||
def get(self, only_self=False):
|
||||
"""Get owner for a specified option"""
|
||||
return self._config_bag.context.get_owner(self._subconfig)
|
||||
return self._config_bag.context.get_owner(
|
||||
self._subconfig, validate_meta=not only_self
|
||||
)
|
||||
|
||||
@option_type(["symlink", "option", "with_index"])
|
||||
def default(self):
|
||||
values = self._config_bag.context.get_values()
|
||||
return values.get_default_owner(self._subconfig)
|
||||
|
||||
@option_type(["symlink", "option", "with_index"])
|
||||
def isdefault(self):
|
||||
"""Is option has defaut value"""
|
||||
return self._config_bag.context.get_owner(self._subconfig) == owners.default
|
||||
subconfig = self._subconfig
|
||||
s_properties = subconfig.properties
|
||||
if (
|
||||
"frozen" in s_properties
|
||||
and "force_default_on_freeze" in s_properties
|
||||
):
|
||||
return True
|
||||
context = self._config_bag.context
|
||||
subconfig = context._get(
|
||||
subconfig,
|
||||
True,
|
||||
)
|
||||
return not context.get_values().hasvalue(subconfig.path, index=subconfig.index)
|
||||
|
||||
@option_type(["option", "with_index"])
|
||||
def set(
|
||||
|
|
@ -562,7 +761,9 @@ class TiramisuOptionProperty(CommonTiramisuOption):
|
|||
|
||||
_validate_properties = False
|
||||
|
||||
@option_type(["option", "optiondescription", "with_index", "symlink"])
|
||||
@option_type(
|
||||
["option", "optiondescription", "with_index", "symlink", "allow_dynoption"]
|
||||
)
|
||||
def get(
|
||||
self,
|
||||
*,
|
||||
|
|
@ -571,6 +772,8 @@ class TiramisuOptionProperty(CommonTiramisuOption):
|
|||
uncalculated: bool = False,
|
||||
):
|
||||
"""Get properties for an option"""
|
||||
if self._subconfig.is_dynamic_without_identifiers and not uncalculated:
|
||||
raise AttributeOptionError(self._subconfig.path, "option-dynamic")
|
||||
settings = self._config_bag.context.get_settings()
|
||||
if not only_raises:
|
||||
return settings.getproperties(
|
||||
|
|
@ -582,6 +785,7 @@ class TiramisuOptionProperty(CommonTiramisuOption):
|
|||
self._subconfig,
|
||||
uncalculated=uncalculated,
|
||||
apply_requires=apply_requires,
|
||||
not_unrestraint=True,
|
||||
)
|
||||
|
||||
@option_type(["option", "optiondescription", "with_or_without_index"])
|
||||
|
|
@ -690,9 +894,16 @@ class TiramisuOptionInformation(CommonTiramisuOption):
|
|||
"""Manage option's informations"""
|
||||
|
||||
_validate_properties = False
|
||||
_allow_dynoption = True
|
||||
|
||||
@option_type(["option", "optiondescription", "with_or_without_index", "symlink"])
|
||||
@option_type(
|
||||
[
|
||||
"option",
|
||||
"optiondescription",
|
||||
"with_or_without_index",
|
||||
"symlink",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def get(
|
||||
self,
|
||||
name: str,
|
||||
|
|
@ -705,7 +916,7 @@ class TiramisuOptionInformation(CommonTiramisuOption):
|
|||
default,
|
||||
)
|
||||
|
||||
@option_type(["option", "optiondescription"])
|
||||
@option_type(["option", "optiondescription", "allow_dynoption"])
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
"""Set information"""
|
||||
self._config_bag.context.get_values().set_information(
|
||||
|
|
@ -714,7 +925,7 @@ class TiramisuOptionInformation(CommonTiramisuOption):
|
|||
value,
|
||||
)
|
||||
|
||||
@option_type(["option", "optiondescription"])
|
||||
@option_type(["option", "optiondescription", "allow_dynoption"])
|
||||
def remove(
|
||||
self,
|
||||
key: str,
|
||||
|
|
@ -725,7 +936,15 @@ class TiramisuOptionInformation(CommonTiramisuOption):
|
|||
path=self._path,
|
||||
)
|
||||
|
||||
@option_type(["option", "optiondescription", "with_or_without_index", "symlink"])
|
||||
@option_type(
|
||||
[
|
||||
"option",
|
||||
"optiondescription",
|
||||
"with_or_without_index",
|
||||
"symlink",
|
||||
"allow_dynoption",
|
||||
]
|
||||
)
|
||||
def list(self) -> list:
|
||||
"""List information's keys"""
|
||||
lst1 = set(self._subconfig.option._list_information())
|
||||
|
|
@ -801,14 +1020,11 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
|
|||
option = self._subconfig.option
|
||||
if (
|
||||
not isinstance(value, Calculation)
|
||||
and isinstance(value, list)
|
||||
and option.impl_is_leader()
|
||||
and len(value) < self._subconfig.parent.get_length_leadership()
|
||||
):
|
||||
raise LeadershipError(
|
||||
_("cannot reduce length of the leader {}" "").format(
|
||||
option.impl_get_display_name(self._subconfig, with_quote=True)
|
||||
)
|
||||
)
|
||||
raise LeadershipError(self._subconfig, "leadership-reduce")
|
||||
values = self._config_bag.context.get_values()
|
||||
return values.set_value(self._subconfig, value)
|
||||
|
||||
|
|
@ -875,13 +1091,15 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
|
|||
return False
|
||||
return True
|
||||
|
||||
@option_type(["choice", "with_index"])
|
||||
@option_type(["choice", "with_index", "allow_dynoption"])
|
||||
def list(
|
||||
self,
|
||||
*,
|
||||
uncalculated: bool = False,
|
||||
):
|
||||
"""All values available for a ChoiceOption"""
|
||||
if self._subconfig.is_dynamic_without_identifiers and not uncalculated:
|
||||
raise AttributeOptionError(self._subconfig.path, "option-dynamic")
|
||||
return self._subconfig.option.impl_get_values(
|
||||
self._subconfig,
|
||||
uncalculated,
|
||||
|
|
@ -906,34 +1124,39 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
|
|||
def mandatory(self):
|
||||
"""Return path of options with mandatory property without any value"""
|
||||
subconfig = self._subconfig
|
||||
if subconfig.option.impl_is_optiondescription():
|
||||
ori_config_bag = self._subconfig.config_bag
|
||||
config_bag = ori_config_bag.copy()
|
||||
config_bag.properties -= {"mandatory", "empty", "warnings"}
|
||||
config_bag.set_permissive()
|
||||
self._subconfig.config_bag = config_bag
|
||||
options = []
|
||||
for subconfig in self._config_bag.context.walk(
|
||||
self._subconfig,
|
||||
only_mandatory=True,
|
||||
):
|
||||
options.append(
|
||||
TiramisuOption(
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
ori_config_bag,
|
||||
subconfig=subconfig,
|
||||
)
|
||||
)
|
||||
self._subconfig.config_bag = ori_config_bag
|
||||
return options
|
||||
ori_config_bag = self._subconfig.config_bag
|
||||
config_bag = ori_config_bag.copy()
|
||||
config_bag.properties -= {"mandatory", "empty", "warnings"}
|
||||
config_bag.set_permissive()
|
||||
self._subconfig.config_bag = config_bag
|
||||
try:
|
||||
self._config_bag.context.walk_valid_value(
|
||||
self._subconfig, only_mandatory=True
|
||||
)
|
||||
except PropertiesOptionError as err:
|
||||
return err.proptype == ["mandatory"] or err.proptype == ["empty"]
|
||||
return False
|
||||
if subconfig.option.impl_is_optiondescription():
|
||||
options = []
|
||||
for subconfig in config_bag.context.walk(
|
||||
self._subconfig,
|
||||
only_mandatory=True,
|
||||
):
|
||||
options.append(
|
||||
TiramisuOption(
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
ori_config_bag,
|
||||
subconfig=subconfig,
|
||||
)
|
||||
)
|
||||
self._subconfig.config_bag = ori_config_bag
|
||||
return options
|
||||
try:
|
||||
self._config_bag.context.walk_valid_value(
|
||||
self._subconfig, only_mandatory=True
|
||||
)
|
||||
except PropertiesOptionError as err:
|
||||
return err.proptype == ["mandatory"] or err.proptype == ["empty"]
|
||||
self._subconfig.config_bag = ori_config_bag
|
||||
return False
|
||||
except Exception as err:
|
||||
self._subconfig.config_bag = ori_config_bag
|
||||
raise err from err
|
||||
|
||||
|
||||
def _registers(
|
||||
|
|
@ -997,7 +1220,6 @@ class TiramisuOption(
|
|||
self._path = path
|
||||
self._index = index
|
||||
self._config_bag = config_bag
|
||||
self._allow_dynoption = allow_dynoption
|
||||
self._subconfig = subconfig
|
||||
if not self._registers:
|
||||
_registers(self._registers, "TiramisuOption")
|
||||
|
|
@ -1033,25 +1255,29 @@ class TiramisuOption(
|
|||
subconfig=sub_subconfig,
|
||||
)
|
||||
|
||||
@option_type("optiondescription")
|
||||
@option_type(["optiondescription", "allow_dynoption"])
|
||||
def group_type(self):
|
||||
"""Get type for an optiondescription (only for optiondescription)"""
|
||||
self._set_subconfig()
|
||||
return self._subconfig.option.impl_get_group_type()
|
||||
|
||||
@option_type("optiondescription")
|
||||
@option_type(["optiondescription", "validate_properties", "allow_dynoption"])
|
||||
def list(
|
||||
self,
|
||||
*,
|
||||
validate_properties: bool = True,
|
||||
uncalculated: bool = False,
|
||||
with_index: bool = True,
|
||||
):
|
||||
"""List options inside an option description (by default list only option)"""
|
||||
self._set_subconfig()
|
||||
if self._subconfig.is_dynamic_without_identifiers and not uncalculated:
|
||||
raise AttributeOptionError(self._subconfig.path, "option-dynamic")
|
||||
return self._list(
|
||||
self._subconfig,
|
||||
validate_properties,
|
||||
uncalculated=uncalculated,
|
||||
with_index=with_index,
|
||||
)
|
||||
|
||||
def _load_dict(
|
||||
|
|
@ -1172,6 +1398,8 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet):
|
|||
self,
|
||||
path: str,
|
||||
value: Any,
|
||||
*,
|
||||
index: Optional[int] = None,
|
||||
only_config=undefined,
|
||||
force_default=undefined,
|
||||
force_default_if_same=undefined,
|
||||
|
|
@ -1187,14 +1415,22 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet):
|
|||
kwargs["force_default_if_same"] = force_default_if_same
|
||||
if force_dont_change_value is not undefined:
|
||||
kwargs["force_dont_change_value"] = force_dont_change_value
|
||||
option_bag = OptionBag(
|
||||
None,
|
||||
None,
|
||||
self._config_bag,
|
||||
path=path,
|
||||
)
|
||||
context = self._config_bag.context
|
||||
if isinstance(context, KernelGroupConfig):
|
||||
subconfig = Fake_SubConfig(
|
||||
self._config_bag,
|
||||
path,
|
||||
index,
|
||||
)
|
||||
else:
|
||||
subconfig = context.get_sub_config(
|
||||
self._config_bag,
|
||||
path,
|
||||
index,
|
||||
validate_properties=False,
|
||||
)
|
||||
return self._config_bag.context.set_value(
|
||||
option_bag,
|
||||
subconfig,
|
||||
value,
|
||||
**kwargs,
|
||||
)
|
||||
|
|
@ -1527,7 +1763,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
|
|||
|
||||
def get(self):
|
||||
"""Get Tiramisu option"""
|
||||
return None
|
||||
return self._config_bag.context.get_description()
|
||||
|
||||
def isleadership(self):
|
||||
"""Test if option is a leader or a follower"""
|
||||
|
|
@ -1556,7 +1792,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
|
|||
self,
|
||||
):
|
||||
"""Get option path"""
|
||||
return None
|
||||
return self._config_bag.context.get_config_path()
|
||||
|
||||
def has_dependency(
|
||||
self,
|
||||
|
|
@ -1582,6 +1818,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
|
|||
*,
|
||||
validate_properties: bool = True,
|
||||
uncalculated: bool = False,
|
||||
with_index: bool = True,
|
||||
):
|
||||
"""List options (by default list only option)"""
|
||||
root = self._config_bag.context.get_root(self._config_bag)
|
||||
|
|
@ -1589,6 +1826,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
|
|||
root,
|
||||
validate_properties,
|
||||
uncalculated=uncalculated,
|
||||
with_index=with_index,
|
||||
)
|
||||
|
||||
def _load_dict(self, clearable="all", remotable="minimum"):
|
||||
|
|
@ -1619,6 +1857,9 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
|
|||
self._load_dict()
|
||||
return self._tiramisu_dict.set_updates(body)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{self.__class__.__name__} path="{self.path()}">'
|
||||
|
||||
|
||||
class _TiramisuContextConfigReset:
|
||||
def reset(self):
|
||||
|
|
@ -1808,6 +2049,8 @@ class TiramisuAPI(TiramisuHelp):
|
|||
|
||||
def __getattr__(self, subfunc: str) -> Any:
|
||||
if subfunc in ["forcepermissive", "unrestraint", "nowarnings"]:
|
||||
if subfunc == "unrestraint" and self._config_bag.is_unrestraint:
|
||||
return self
|
||||
if self._orig_config_bags:
|
||||
msg = _(
|
||||
"do not use unrestraint, nowarnings or forcepermissive together"
|
||||
|
|
@ -1852,9 +2095,7 @@ class TiramisuAPI(TiramisuHelp):
|
|||
|
||||
|
||||
class ConfigProp(TiramisuAPI, TiramisuContextOption):
|
||||
def __repr__(self):
|
||||
return f"<Config path=None>"
|
||||
|
||||
pass
|
||||
|
||||
class Config(TiramisuAPI, TiramisuContextOption):
|
||||
"""Root config object that enables us to handle the configuration options"""
|
||||
|
|
@ -1891,11 +2132,8 @@ class Config(TiramisuAPI, TiramisuContextOption):
|
|||
except ConfigError:
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Config path=None>"
|
||||
|
||||
|
||||
class MetaConfig(TiramisuAPI):
|
||||
class MetaConfig(TiramisuAPI, TiramisuContextOption):
|
||||
"""MetaConfig object that enables us to handle the sub configuration's options
|
||||
with common root optiondescription
|
||||
"""
|
||||
|
|
@ -1963,7 +2201,7 @@ class MixConfig(TiramisuAPI):
|
|||
display_name=display_name,
|
||||
)
|
||||
settings = config.get_settings()
|
||||
properties = settings.get_context_properties(config.properties_cache)
|
||||
properties = settings.get_context_properties()
|
||||
permissives = settings.get_context_permissives()
|
||||
config_bag = ConfigBag(
|
||||
config,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,15 @@ from typing import Any, Optional, Union, Callable, Dict, List
|
|||
from itertools import chain
|
||||
import weakref
|
||||
|
||||
from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning
|
||||
from .error import (
|
||||
PropertiesOptionError,
|
||||
ConfigError,
|
||||
LeadershipError,
|
||||
ValueWarning,
|
||||
CancelParam,
|
||||
display_list,
|
||||
errors,
|
||||
)
|
||||
from .i18n import _
|
||||
from .setting import undefined, ConfigBag
|
||||
from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR
|
||||
|
|
@ -49,6 +57,7 @@ def get_calculated_value(
|
|||
has_calculation = True
|
||||
elif isinstance(value, list):
|
||||
# if value is a list, do subcalculation
|
||||
value = value.copy()
|
||||
for idx, val in enumerate(value):
|
||||
value[idx], _has_calculation = get_calculated_value(
|
||||
subconfig,
|
||||
|
|
@ -148,11 +157,15 @@ class ParamDynOption(ParamOption):
|
|||
)
|
||||
if not isinstance(identifiers, list):
|
||||
raise Exception(
|
||||
f"identifiers in ParamDynOption must be a list, not {identifiers}"
|
||||
_("identifiers in ParamDynOption must be a list, not {0}").format(
|
||||
identifiers
|
||||
)
|
||||
)
|
||||
if not isinstance(optional, bool):
|
||||
raise Exception(
|
||||
f"optional in ParamDynOption must be a boolean, not {optional}"
|
||||
_("optional in ParamDynOption must be a boolean, not {0}").format(
|
||||
optional
|
||||
)
|
||||
)
|
||||
self.identifiers = identifiers
|
||||
self.optional = optional
|
||||
|
|
@ -203,9 +216,11 @@ class ParamInformation(Param):
|
|||
|
||||
def set_option(self, option: "Option" = None) -> None:
|
||||
if not hasattr(self, "self_option"):
|
||||
raise ConfigError("cannot add option in information after creating config")
|
||||
raise ConfigError(
|
||||
_("cannot add option in information after creating config")
|
||||
)
|
||||
if self.option:
|
||||
raise ConfigError("cannot redefine option in information")
|
||||
raise ConfigError(_("cannot redefine option in information"))
|
||||
if not option.impl_is_optiondescription():
|
||||
if option.impl_is_symlinkoption():
|
||||
raise ValueError(
|
||||
|
|
@ -445,14 +460,7 @@ def manager_callback(
|
|||
or param.raisepropertyerror
|
||||
):
|
||||
raise err from err
|
||||
display_name = subconfig.option.impl_get_display_name(
|
||||
subconfig, with_quote=True
|
||||
)
|
||||
raise ConfigError(
|
||||
_("unable to carry out a calculation for {}, {}").format(
|
||||
display_name, err
|
||||
)
|
||||
) from err
|
||||
raise ConfigError(err)
|
||||
except ValueError as err:
|
||||
display_name = subconfig.option.impl_get_display_name(
|
||||
subconfig, with_quote=True
|
||||
|
|
@ -464,20 +472,15 @@ def manager_callback(
|
|||
) from err
|
||||
except AttributeError as err:
|
||||
if isinstance(param, ParamDynOption) and param.optional:
|
||||
# cannot acces, simulate a propertyerror
|
||||
# cannot access, simulate a propertyerror
|
||||
raise PropertiesOptionError(
|
||||
subconfig,
|
||||
["configerror"],
|
||||
config_bag.context.get_settings(),
|
||||
)
|
||||
display_name = subconfig.option.impl_get_display_name(
|
||||
subconfig, with_quote=True
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig, _("unable to get value for calculating {0}, {1}"), err
|
||||
)
|
||||
raise ConfigError(
|
||||
_("unable to get value for calculating {0}, {1}").format(
|
||||
display_name, err
|
||||
)
|
||||
) from err
|
||||
return value
|
||||
|
||||
def get_option_bag(
|
||||
|
|
@ -504,17 +507,18 @@ def manager_callback(
|
|||
index_,
|
||||
validate_properties=not self_calc,
|
||||
properties=properties,
|
||||
valid_conflict=False,
|
||||
)
|
||||
except PropertiesOptionError as err:
|
||||
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
|
||||
if param.notraisepropertyerror or param.raisepropertyerror:
|
||||
raise err from err
|
||||
display_name = option.impl_get_display_name(subconfig, with_quote=True)
|
||||
raise ConfigError(
|
||||
_("unable to carry out a calculation for {}, {}").format(
|
||||
display_name, err
|
||||
)
|
||||
) from err
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_("unable to carry out a calculation for {0}, {1}"),
|
||||
err,
|
||||
option=option,
|
||||
)
|
||||
except ValueError as err:
|
||||
display_name = option.impl_get_display_name(subconfig, with_quote=True)
|
||||
raise ValueError(
|
||||
|
|
@ -524,18 +528,18 @@ def manager_callback(
|
|||
) from err
|
||||
except AttributeError as err:
|
||||
if isinstance(param, ParamDynOption) and param.optional:
|
||||
# cannot acces, simulate a propertyerror
|
||||
# cannot access, simulate a propertyerror
|
||||
raise PropertiesOptionError(
|
||||
param,
|
||||
["configerror"],
|
||||
config_bag.context.get_settings(),
|
||||
)
|
||||
display_name = option.impl_get_display_name(subconfig, with_quote=True)
|
||||
raise ConfigError(
|
||||
_("unable to get value for calculating {0}, {1}").format(
|
||||
display_name, err
|
||||
)
|
||||
) from err
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_("unable to get value for calculating {0}, {1}"),
|
||||
err,
|
||||
option=option,
|
||||
)
|
||||
return subsubconfig
|
||||
|
||||
if isinstance(param, ParamValue):
|
||||
|
|
@ -552,14 +556,15 @@ def manager_callback(
|
|||
true_path=subconfig.path,
|
||||
)
|
||||
if isinstance(isubconfig, list):
|
||||
display_name = option.impl_get_display_name(
|
||||
subconfig, with_quote=True
|
||||
)
|
||||
search_name = search_option.impl_get_display_name(
|
||||
None, with_quote=True
|
||||
)
|
||||
raise ConfigError(
|
||||
f"cannot find information for {display_name}, {search_name} is a dynamic option"
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_("cannot find information for {0}, {1} is a dynamic option"),
|
||||
None,
|
||||
option=option,
|
||||
extra_keys=[search_name],
|
||||
)
|
||||
else:
|
||||
isubconfig = get_option_bag(
|
||||
|
|
@ -579,12 +584,12 @@ def manager_callback(
|
|||
param.default_value,
|
||||
)
|
||||
except ValueError as err:
|
||||
display_name = option.impl_get_display_name(subconfig, with_quote=True)
|
||||
raise ConfigError(
|
||||
_("unable to get value for calculating {0}, {1}").format(
|
||||
display_name, err
|
||||
)
|
||||
) from err
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_("unable to get value for calculating {0}, {1}"),
|
||||
err,
|
||||
option=option,
|
||||
)
|
||||
|
||||
if isinstance(param, ParamIndex):
|
||||
return index
|
||||
|
|
@ -594,14 +599,17 @@ def manager_callback(
|
|||
not option.impl_is_optiondescription()
|
||||
or not option.impl_is_dynoptiondescription()
|
||||
):
|
||||
display_name = subconfig.option.impl_get_display_name(
|
||||
subconfig, with_quote=True
|
||||
)
|
||||
raise ConfigError(
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_(
|
||||
"option {0} is not a dynoptiondescription or in a dynoptiondescription"
|
||||
).format(display_name)
|
||||
),
|
||||
None,
|
||||
option=option,
|
||||
)
|
||||
if subconfig.identifiers is None:
|
||||
# if uncalculated
|
||||
return
|
||||
return subconfig.identifiers[param.identifier_index]
|
||||
|
||||
if isinstance(param, ParamSelfOption):
|
||||
|
|
@ -673,7 +681,37 @@ def manager_callback(
|
|||
parent,
|
||||
)
|
||||
except AttributeError as err:
|
||||
raise ConfigError(err) from err
|
||||
if parent.path:
|
||||
child_path = parent.path + "." + name
|
||||
else:
|
||||
child_path = name
|
||||
if param.optional:
|
||||
raise CancelParam(
|
||||
callbk_option.impl_getpath(), child_path
|
||||
)
|
||||
|
||||
identifiers = doption.get_identifiers(parent)
|
||||
if not identifiers:
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_(
|
||||
'cannot calculate arguments for {0}, {1} with identifier "{2}", there is no identifiers'
|
||||
),
|
||||
err,
|
||||
extra_keys=[identifier],
|
||||
)
|
||||
else:
|
||||
identifiers_list = display_list(
|
||||
identifiers, add_quote=True
|
||||
)
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_(
|
||||
'cannot calculate arguments for {0}, {1} with identifier "{2}", list of valid identifiers: {3}'
|
||||
),
|
||||
err,
|
||||
extra_keys=[identifier, identifiers_list],
|
||||
)
|
||||
new_parents.append(
|
||||
parent.get_child(
|
||||
doption,
|
||||
|
|
@ -777,8 +815,11 @@ def carry_out_calculation(
|
|||
and option.impl_is_follower()
|
||||
and index is None
|
||||
):
|
||||
raise ConfigError(
|
||||
f"the follower {option.impl_get_display_name(subconfig, with_quote=True)} must have index in carry_out_calculation!"
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig,
|
||||
_("the follower {0} must have index in carry_out_calculation!"),
|
||||
None,
|
||||
option=option,
|
||||
)
|
||||
|
||||
def fake_items(iterator):
|
||||
|
|
@ -828,6 +869,12 @@ def carry_out_calculation(
|
|||
args.append(err)
|
||||
else:
|
||||
kwargs[key] = err
|
||||
except CancelParam as err:
|
||||
if callback.__name__ in FUNCTION_WAITING_FOR_ERROR:
|
||||
if key is None:
|
||||
args.append(err)
|
||||
else:
|
||||
kwargs[key] = err
|
||||
ret = calculate(
|
||||
subconfig,
|
||||
callback,
|
||||
|
|
@ -843,33 +890,14 @@ def carry_out_calculation(
|
|||
and option.impl_is_follower()
|
||||
and not option.impl_is_submulti()
|
||||
):
|
||||
if args or kwargs:
|
||||
raise LeadershipError(
|
||||
_(
|
||||
'the "{}" function with positional arguments "{}" '
|
||||
'and keyword arguments "{}" must not return '
|
||||
'a list ("{}") for the follower option {}'
|
||||
""
|
||||
).format(
|
||||
callback.__name__,
|
||||
args,
|
||||
kwargs,
|
||||
ret,
|
||||
option.impl_get_display_name(subconfig, with_quote=True),
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise LeadershipError(
|
||||
_(
|
||||
'the "{}" function must not return a list ("{}") '
|
||||
"for the follower option {}"
|
||||
""
|
||||
).format(
|
||||
callback.__name__,
|
||||
ret,
|
||||
option.impl_get_display_name(subconfig, with_quote=True),
|
||||
)
|
||||
)
|
||||
raise LeadershipError(
|
||||
subconfig,
|
||||
"leadership-follower-callback-list",
|
||||
callback=callback.__name__,
|
||||
args=args,
|
||||
kwargs=kwargs,
|
||||
ret=ret,
|
||||
)
|
||||
return ret
|
||||
|
||||
|
||||
|
|
@ -893,7 +921,7 @@ def calculate(
|
|||
except (ValueError, ValueWarning) as err:
|
||||
if allow_value_error:
|
||||
if force_value_warning:
|
||||
raise ValueWarning(str(err))
|
||||
raise ValueWarning(msg=str(err))
|
||||
raise err from err
|
||||
error = err
|
||||
except ConfigError as err:
|
||||
|
|
@ -902,19 +930,17 @@ def calculate(
|
|||
error = err
|
||||
if args or kwargs:
|
||||
msg = _(
|
||||
'unexpected error "{0}" in function "{1}" with arguments "{3}" and "{4}" '
|
||||
"for option {2}"
|
||||
).format(
|
||||
str(error),
|
||||
'unexpected error "{1}" in function "{2}" with arguments "{3}" and "{4}" '
|
||||
"for option {0}"
|
||||
)
|
||||
extra_keys = [
|
||||
callback.__name__,
|
||||
subconfig.option.impl_get_display_name(subconfig, with_quote=True),
|
||||
args,
|
||||
kwargs,
|
||||
)
|
||||
]
|
||||
else:
|
||||
msg = _('unexpected error "{0}" in function "{1}" for option {2}' "").format(
|
||||
str(error),
|
||||
callback.__name__,
|
||||
subconfig.option.impl_get_display_name(subconfig, with_quote=True),
|
||||
)
|
||||
raise ConfigError(msg) from error
|
||||
msg = _('unexpected error "{1}" in function "{2}" for option {0}')
|
||||
extra_keys = [callback.__name__]
|
||||
errors.raise_carry_out_calculation_error(
|
||||
subconfig, msg, error, extra_keys=extra_keys
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2012-2024 Team tiramisu (see AUTHORS for all contributors)
|
||||
# Copyright (C) 2012-2025 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
|
||||
|
|
@ -25,7 +25,13 @@ from copy import copy, deepcopy
|
|||
from typing import Optional, List, Any, Union
|
||||
from os.path import commonprefix
|
||||
|
||||
from .error import PropertiesOptionError, ConfigError, ConflictError, LeadershipError
|
||||
from .error import (
|
||||
PropertiesOptionError,
|
||||
ConfigError,
|
||||
ConflictError,
|
||||
LeadershipError,
|
||||
AttributeOptionError,
|
||||
)
|
||||
from .option import DynOptionDescription, Leadership, Option
|
||||
from .setting import ConfigBag, Settings, undefined, groups
|
||||
from .value import Values, owners
|
||||
|
|
@ -41,7 +47,7 @@ def get_common_path(path1, path2):
|
|||
return common_path
|
||||
if common_path.endswith("."):
|
||||
return common_path[:-1]
|
||||
if "." in common_path:
|
||||
elif "." in common_path:
|
||||
return common_path.rsplit(".", 1)[0]
|
||||
return None
|
||||
|
||||
|
|
@ -68,6 +74,7 @@ class CCache:
|
|||
self.reset_one_option_cache(
|
||||
subconfig,
|
||||
resetted_opts,
|
||||
False,
|
||||
)
|
||||
subconfig.config_bag.properties = subconfig.config_bag.properties | {
|
||||
"cache"
|
||||
|
|
@ -80,26 +87,31 @@ class CCache:
|
|||
self,
|
||||
subconfig,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
):
|
||||
"""reset cache for one option"""
|
||||
if subconfig.path in resetted_opts:
|
||||
return
|
||||
resetted_opts.append(subconfig.path)
|
||||
config_bag = subconfig.config_bag
|
||||
for woption in subconfig.option.get_dependencies(subconfig.option):
|
||||
# if is_default and config_bag.context.get_owner(subconfig) != owners.default:
|
||||
# return
|
||||
for is_default, woption in subconfig.option.get_dependencies(subconfig.option):
|
||||
option = woption()
|
||||
if option.issubdyn():
|
||||
# it's an option in dynoptiondescription, remove cache for all generated option
|
||||
self.reset_cache_dyn_option(
|
||||
config_bag,
|
||||
subconfig,
|
||||
option,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
)
|
||||
elif option.impl_is_dynoptiondescription():
|
||||
self._reset_cache_dyn_optiondescription(
|
||||
self.reset_cache_dyn_optiondescription(
|
||||
option,
|
||||
config_bag,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
)
|
||||
else:
|
||||
option_subconfig = self.get_sub_config(
|
||||
|
|
@ -112,6 +124,7 @@ class CCache:
|
|||
self.reset_one_option_cache(
|
||||
option_subconfig,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
)
|
||||
del option
|
||||
subconfig.option.reset_cache(
|
||||
|
|
@ -120,13 +133,7 @@ class CCache:
|
|||
resetted_opts,
|
||||
)
|
||||
|
||||
def _reset_cache_dyn_optiondescription(
|
||||
self,
|
||||
option,
|
||||
config_bag,
|
||||
resetted_opts,
|
||||
):
|
||||
# reset cache for all chidren
|
||||
def get_dynamic_from_dyn_optiondescription(self, config_bag, option):
|
||||
path = option.impl_getpath()
|
||||
if "." in path:
|
||||
parent_path = path.rsplit(".", 1)[0]
|
||||
|
|
@ -139,13 +146,27 @@ class CCache:
|
|||
)
|
||||
else:
|
||||
parent_subconfig = self.get_root(config_bag)
|
||||
for subconfig in parent_subconfig.dyn_to_subconfig(
|
||||
return parent_subconfig.dyn_to_subconfig(
|
||||
option,
|
||||
False,
|
||||
)
|
||||
|
||||
def reset_cache_dyn_optiondescription(
|
||||
self,
|
||||
option,
|
||||
config_bag,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
):
|
||||
# reset cache for all chidren
|
||||
for subconfig in self.get_dynamic_from_dyn_optiondescription(
|
||||
config_bag,
|
||||
option,
|
||||
):
|
||||
self.reset_one_option_cache(
|
||||
subconfig,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
)
|
||||
for walk_subconfig in self.walk(
|
||||
subconfig,
|
||||
|
|
@ -155,17 +176,23 @@ class CCache:
|
|||
self.reset_one_option_cache(
|
||||
walk_subconfig,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
)
|
||||
|
||||
def reset_cache_dyn_option(
|
||||
self,
|
||||
config_bag,
|
||||
option,
|
||||
resetted_opts,
|
||||
):
|
||||
currents = [self.get_root(config_bag)]
|
||||
def get_dynamic_from_dyn_option(self, subconfig, option):
|
||||
config_bag = subconfig.config_bag
|
||||
sub_paths = option.impl_getpath()
|
||||
for sub_path in sub_paths.split("."):
|
||||
current_paths = subconfig.path.split(".")
|
||||
current_paths_max_index = len(current_paths) - 1
|
||||
current_subconfigs = []
|
||||
parent = subconfig
|
||||
while True:
|
||||
current_subconfigs.insert(0, parent)
|
||||
parent = parent.parent
|
||||
if parent.path is None:
|
||||
break
|
||||
currents = [self.get_root(config_bag)]
|
||||
for idx, sub_path in enumerate(sub_paths.split(".")):
|
||||
new_currents = []
|
||||
for current in currents:
|
||||
sub_option = current.option.get_child(
|
||||
|
|
@ -175,15 +202,20 @@ class CCache:
|
|||
allow_dynoption=True,
|
||||
)
|
||||
if sub_option.impl_is_dynoptiondescription():
|
||||
new_currents.extend(
|
||||
list(
|
||||
current.dyn_to_subconfig(
|
||||
sub_option,
|
||||
False,
|
||||
if (
|
||||
idx <= current_paths_max_index
|
||||
and sub_option == current_subconfigs[idx].option
|
||||
):
|
||||
new_currents.append(current_subconfigs[idx])
|
||||
else:
|
||||
new_currents.extend(
|
||||
list(
|
||||
current.dyn_to_subconfig(
|
||||
sub_option,
|
||||
False,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
new_currents.append(
|
||||
current.get_child(
|
||||
|
|
@ -194,10 +226,20 @@ class CCache:
|
|||
),
|
||||
)
|
||||
currents = new_currents
|
||||
for dyn_option_subconfig in currents:
|
||||
return currents
|
||||
|
||||
def reset_cache_dyn_option(
|
||||
self,
|
||||
subconfig,
|
||||
option,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
):
|
||||
for dyn_option_subconfig in self.get_dynamic_from_dyn_option(subconfig, option):
|
||||
self.reset_one_option_cache(
|
||||
dyn_option_subconfig,
|
||||
resetted_opts,
|
||||
is_default,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -213,6 +255,7 @@ class SubConfig:
|
|||
"apply_requires",
|
||||
"transitive_properties",
|
||||
"is_dynamic",
|
||||
"is_dynamic_without_identifiers",
|
||||
"identifiers",
|
||||
"_length",
|
||||
)
|
||||
|
|
@ -227,8 +270,10 @@ class SubConfig:
|
|||
identifiers: Optional[list[str]],
|
||||
*,
|
||||
true_path: Optional[str] = None,
|
||||
properties: Union[list[str], undefined] = undefined,
|
||||
# for python 3.9 properties: Union[list[str], undefined] = undefined,
|
||||
properties=undefined,
|
||||
validate_properties: bool = True,
|
||||
check_dynamic_without_identifiers: bool = True,
|
||||
) -> None:
|
||||
self.index = index
|
||||
self.identifiers = identifiers
|
||||
|
|
@ -244,10 +289,26 @@ class SubConfig:
|
|||
)
|
||||
self.apply_requires = not is_follower or index is not None
|
||||
self.true_path = true_path
|
||||
if parent and parent.is_dynamic or self.option.impl_is_dynoptiondescription():
|
||||
if self.option.impl_is_dynoptiondescription():
|
||||
self.is_dynamic = True
|
||||
self.is_dynamic_without_identifiers = identifiers is None or (
|
||||
parent and identifiers == parent.identifiers
|
||||
)
|
||||
if (
|
||||
check_dynamic_without_identifiers
|
||||
and parent
|
||||
and parent.is_dynamic
|
||||
and parent.is_dynamic_without_identifiers
|
||||
and self.is_dynamic_without_identifiers
|
||||
!= parent.is_dynamic_without_identifiers
|
||||
):
|
||||
raise AttributeOptionError(true_path, "option-dynamic")
|
||||
elif parent:
|
||||
self.is_dynamic = parent.is_dynamic
|
||||
self.is_dynamic_without_identifiers = parent.is_dynamic_without_identifiers
|
||||
else:
|
||||
self.is_dynamic = False
|
||||
self.is_dynamic_without_identifiers = False
|
||||
self._properties = properties
|
||||
if validate_properties:
|
||||
if self.path and self._properties is undefined:
|
||||
|
|
@ -347,26 +408,55 @@ class SubConfig:
|
|||
validate_properties,
|
||||
*,
|
||||
uncalculated: bool = False,
|
||||
with_index: bool = True,
|
||||
check_dynamic_without_identifiers: bool = True,
|
||||
):
|
||||
if self.option.impl_is_leadership() and not uncalculated:
|
||||
if self.option.impl_is_leadership() and not uncalculated and with_index:
|
||||
yield from self.get_leadership_children(validate_properties)
|
||||
else:
|
||||
children_name = []
|
||||
for child in self.option.get_children():
|
||||
if child.impl_is_dynoptiondescription() and not uncalculated:
|
||||
yield from self.dyn_to_subconfig(
|
||||
for dyn_child in self.dyn_to_subconfig(
|
||||
child,
|
||||
validate_properties,
|
||||
)
|
||||
):
|
||||
yield dyn_child
|
||||
if child.could_conflict:
|
||||
name = dyn_child.path
|
||||
if name in children_name:
|
||||
raise ConflictError(
|
||||
_('option name "{0}" is not unique in {1}').format(
|
||||
name,
|
||||
self.option.impl_get_display_name(
|
||||
self, with_quote=True
|
||||
),
|
||||
)
|
||||
)
|
||||
children_name.append(name)
|
||||
else:
|
||||
try:
|
||||
yield self.get_child(
|
||||
child,
|
||||
None,
|
||||
validate_properties,
|
||||
check_dynamic_without_identifiers=check_dynamic_without_identifiers,
|
||||
)
|
||||
except PropertiesOptionError as err:
|
||||
if err.proptype in (["mandatory"], ["empty"]):
|
||||
raise err
|
||||
if child.could_conflict:
|
||||
name = child.impl_getpath()
|
||||
if name in children_name:
|
||||
raise ConflictError(
|
||||
_('option name "{0}" is not unique in {1}').format(
|
||||
name,
|
||||
self.option.impl_get_display_name(
|
||||
self, with_quote=True
|
||||
),
|
||||
)
|
||||
)
|
||||
children_name.append(name)
|
||||
|
||||
def get_child(
|
||||
self,
|
||||
|
|
@ -375,12 +465,12 @@ class SubConfig:
|
|||
validate_properties: bool,
|
||||
*,
|
||||
properties=undefined,
|
||||
allow_dynoption: bool = False,
|
||||
identifier: Optional[str] = None,
|
||||
name: Optional[str] = None,
|
||||
check_index: bool = True,
|
||||
config_bag: ConfigBag = None,
|
||||
true_path: Optional[str] = None,
|
||||
check_dynamic_without_identifiers: bool = True,
|
||||
) -> "SubConfig":
|
||||
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments
|
||||
if config_bag is None:
|
||||
|
|
@ -409,6 +499,7 @@ class SubConfig:
|
|||
properties=properties,
|
||||
validate_properties=validate_properties,
|
||||
true_path=true_path,
|
||||
check_dynamic_without_identifiers=check_dynamic_without_identifiers,
|
||||
)
|
||||
if check_index and index is not None:
|
||||
if option.impl_is_optiondescription() or not option.impl_is_follower():
|
||||
|
|
@ -416,13 +507,7 @@ class SubConfig:
|
|||
length = self.get_length_leadership()
|
||||
if index >= length:
|
||||
raise LeadershipError(
|
||||
_(
|
||||
'index "{0}" is greater than the leadership length "{1}" for option {2}'
|
||||
).format(
|
||||
index,
|
||||
length,
|
||||
option.impl_get_display_name(subsubconfig, with_quote=True),
|
||||
)
|
||||
subsubconfig, "leadership-greater", index=index, length=length
|
||||
)
|
||||
return subsubconfig
|
||||
|
||||
|
|
@ -466,6 +551,7 @@ class SubConfig:
|
|||
search_option: "BaseOption",
|
||||
true_path: Optional[str] = None,
|
||||
validate_properties: bool = True,
|
||||
check_dynamic_without_identifiers: bool = True,
|
||||
):
|
||||
current_option_path = self.option.impl_getpath()
|
||||
search_option_path = search_option.impl_getpath()
|
||||
|
|
@ -525,6 +611,7 @@ class SubConfig:
|
|||
None,
|
||||
validate_properties,
|
||||
true_path=true_path,
|
||||
check_dynamic_without_identifiers=check_dynamic_without_identifiers,
|
||||
)
|
||||
)
|
||||
parents = new_parents
|
||||
|
|
@ -535,12 +622,27 @@ class SubConfig:
|
|||
search_option,
|
||||
index,
|
||||
validate_properties,
|
||||
check_dynamic_without_identifiers=check_dynamic_without_identifiers,
|
||||
)
|
||||
)
|
||||
if subconfigs_is_a_list:
|
||||
return subconfigs
|
||||
return subconfigs[0]
|
||||
|
||||
def change_context(self, context) -> "SubConfig":
|
||||
config_bag = self.config_bag.copy()
|
||||
config_bag.context = context
|
||||
return SubConfig(
|
||||
self.option,
|
||||
self.index,
|
||||
self.path,
|
||||
config_bag,
|
||||
self.parent,
|
||||
self.identifiers,
|
||||
true_path=self.true_path,
|
||||
validate_properties=False,
|
||||
)
|
||||
|
||||
|
||||
class _Config(CCache):
|
||||
"""Sub configuration management entry.
|
||||
|
|
@ -593,80 +695,8 @@ class _Config(CCache):
|
|||
"""get cache for values"""
|
||||
return self._impl_values_cache # pylint: disable=no-member
|
||||
|
||||
# =============================================================================
|
||||
# WALK
|
||||
def find(
|
||||
self,
|
||||
option_bag,
|
||||
bytype,
|
||||
byname,
|
||||
byvalue,
|
||||
raise_if_not_found=True,
|
||||
only_path=undefined,
|
||||
only_option=undefined,
|
||||
with_option=False,
|
||||
):
|
||||
"""
|
||||
convenience method for finding an option that lives only in the subtree
|
||||
|
||||
: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):
|
||||
value = self.get_value(soption_bag)
|
||||
if isinstance(value, list):
|
||||
return byvalue in value
|
||||
return value == byvalue
|
||||
|
||||
found = False
|
||||
if only_path is not undefined:
|
||||
|
||||
def _fake_iter():
|
||||
yield only_option
|
||||
|
||||
options = _fake_iter()
|
||||
else:
|
||||
options = option_bag.option.get_children_recursively(
|
||||
bytype,
|
||||
byname,
|
||||
option_bag.config_bag,
|
||||
)
|
||||
for option in options:
|
||||
path = option.impl_getpath()
|
||||
soption_bag = OptionBag(
|
||||
option,
|
||||
None,
|
||||
option_bag.config_bag,
|
||||
)
|
||||
if byvalue is not undefined and not _filter_by_value(soption_bag):
|
||||
continue
|
||||
if option_bag.config_bag.properties:
|
||||
# remove option with propertyerror, ...
|
||||
try:
|
||||
self.get_sub_config(
|
||||
option_bag.config_bag, # pylint: disable=no-member
|
||||
path,
|
||||
None,
|
||||
validate_properties=True,
|
||||
)
|
||||
except PropertiesOptionError:
|
||||
continue
|
||||
found = True
|
||||
if not with_option:
|
||||
yield path
|
||||
else:
|
||||
yield path, option
|
||||
self._find_return_results(
|
||||
found,
|
||||
raise_if_not_found,
|
||||
)
|
||||
|
||||
def _find_return_results(self, found, raise_if_not_found):
|
||||
if not found and raise_if_not_found:
|
||||
raise AttributeError(_("no option found in config" " with these criteria"))
|
||||
|
||||
# # =============================================================================
|
||||
# # WALK
|
||||
def walk_valid_value(
|
||||
self,
|
||||
subconfig,
|
||||
|
|
@ -711,6 +741,7 @@ class _Config(CCache):
|
|||
properties=undefined,
|
||||
true_path: Optional[str] = None,
|
||||
allow_dynoption: bool = False,
|
||||
valid_conflict: bool = True,
|
||||
):
|
||||
subconfig = self.get_root(config_bag)
|
||||
if path is None:
|
||||
|
|
@ -743,6 +774,32 @@ class _Config(CCache):
|
|||
identifier, option = option
|
||||
else:
|
||||
identifier = None
|
||||
if valid_conflict and option.could_conflict:
|
||||
for ref in option.could_conflict:
|
||||
child = ref()
|
||||
if child.impl_is_dynoptiondescription():
|
||||
for dyn_child in subconfig.dyn_to_subconfig(
|
||||
child,
|
||||
validate_properties,
|
||||
):
|
||||
if path == dyn_child.path:
|
||||
raise ConflictError(
|
||||
_('option name "{0}" is not unique in {1}').format(
|
||||
name,
|
||||
option.impl_get_display_name(
|
||||
subconfig, with_quote=True
|
||||
),
|
||||
)
|
||||
)
|
||||
elif child.impl_getname() == name:
|
||||
raise ConflictError(
|
||||
_('option name "{0}" is not unique in {1}').format(
|
||||
name,
|
||||
self.option.impl_get_display_name(
|
||||
self, with_quote=True
|
||||
),
|
||||
)
|
||||
)
|
||||
subconfig = subconfig.get_child(
|
||||
option,
|
||||
index_,
|
||||
|
|
@ -821,6 +878,15 @@ class _Config(CCache):
|
|||
|
||||
# =============================================================================
|
||||
# Manage value
|
||||
def set_value(
|
||||
self,
|
||||
subconfig,
|
||||
value: Any,
|
||||
) -> Any:
|
||||
"""set value"""
|
||||
self.get_settings().validate_properties(subconfig)
|
||||
return self.get_values().set_value(subconfig, value)
|
||||
|
||||
def get_value(
|
||||
self,
|
||||
subconfig,
|
||||
|
|
@ -868,9 +934,10 @@ class _Config(CCache):
|
|||
subconfig, with_quote=True
|
||||
)
|
||||
raise LeadershipError(
|
||||
_(
|
||||
"the follower option {0} has greater length ({1}) than the leader length ({2})"
|
||||
).format(option_name, follower_len, length)
|
||||
subconfig,
|
||||
"leadership-follower-greater",
|
||||
index=follower_len,
|
||||
length=length,
|
||||
)
|
||||
self.get_settings().validate_mandatory(
|
||||
subconfig,
|
||||
|
|
@ -939,6 +1006,8 @@ class _Config(CCache):
|
|||
def get_owner(
|
||||
self,
|
||||
subconfig: "SubConfig",
|
||||
*,
|
||||
validate_meta=True,
|
||||
):
|
||||
"""get owner"""
|
||||
subconfigs = self._get(
|
||||
|
|
@ -949,13 +1018,14 @@ class _Config(CCache):
|
|||
for sc in subconfigs:
|
||||
owner = self.get_owner(
|
||||
sc,
|
||||
validate_meta=validate_meta,
|
||||
)
|
||||
if owner != owners.default:
|
||||
break
|
||||
else:
|
||||
owner = owners.default
|
||||
else:
|
||||
owner = self.get_values().getowner(subconfigs)
|
||||
owner = self.get_values().getowner(subconfigs, validate_meta=validate_meta)
|
||||
return owner
|
||||
|
||||
|
||||
|
|
@ -1236,6 +1306,10 @@ class KernelGroupConfig(_CommonConfig):
|
|||
# pylint: disable=super-init-not-called
|
||||
names = []
|
||||
for child in children:
|
||||
if not isinstance(child, (KernelConfig, KernelGroupConfig)):
|
||||
raise TypeError(
|
||||
_("child must be a Config, GroupConfig, MixConfig or MetaConfig")
|
||||
)
|
||||
name_ = child._impl_name
|
||||
names.append(name_)
|
||||
if len(names) != len(set(names)):
|
||||
|
|
@ -1263,61 +1337,58 @@ class KernelGroupConfig(_CommonConfig):
|
|||
|
||||
def reset_cache(
|
||||
self,
|
||||
option_bag,
|
||||
subconfig,
|
||||
resetted_opts=None,
|
||||
):
|
||||
if resetted_opts is None:
|
||||
resetted_opts = []
|
||||
if isinstance(self, KernelMixConfig):
|
||||
super().reset_cache(
|
||||
option_bag,
|
||||
subconfig,
|
||||
resetted_opts=copy(resetted_opts),
|
||||
)
|
||||
for child in self._impl_children:
|
||||
if option_bag is not None:
|
||||
coption_bag = option_bag.copy()
|
||||
cconfig_bag = coption_bag.config_bag.copy()
|
||||
cconfig_bag.context = child
|
||||
coption_bag.config_bag = cconfig_bag
|
||||
if subconfig is not None:
|
||||
parent_subconfig = subconfig.change_context(child)
|
||||
else:
|
||||
coption_bag = None
|
||||
parent_subconfig = None
|
||||
child.reset_cache(
|
||||
coption_bag,
|
||||
parent_subconfig,
|
||||
resetted_opts=copy(resetted_opts),
|
||||
)
|
||||
|
||||
def set_value(
|
||||
self,
|
||||
option_bag,
|
||||
subconfig,
|
||||
value,
|
||||
only_config=False,
|
||||
):
|
||||
"""Setattr not in current KernelGroupConfig, but in each children"""
|
||||
ret = []
|
||||
for child in self._impl_children:
|
||||
cconfig_bag = option_bag.config_bag.copy()
|
||||
cconfig_bag = subconfig.config_bag.copy()
|
||||
cconfig_bag.context = child
|
||||
if isinstance(child, KernelGroupConfig):
|
||||
ret.extend(
|
||||
child.set_value(
|
||||
option_bag,
|
||||
subconfig,
|
||||
value,
|
||||
only_config=only_config,
|
||||
)
|
||||
)
|
||||
else:
|
||||
settings = child.get_settings()
|
||||
properties = settings.get_context_properties(child.properties_cache)
|
||||
properties = settings.get_context_properties()
|
||||
permissives = settings.get_context_permissives()
|
||||
cconfig_bag.properties = properties
|
||||
cconfig_bag.permissives = permissives
|
||||
try:
|
||||
# GROUP
|
||||
coption_bag = child.get_sub_option_bag(
|
||||
coption_bag = child.get_sub_config(
|
||||
cconfig_bag,
|
||||
option_bag.path,
|
||||
option_bag.index,
|
||||
False,
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
validate_properties=False,
|
||||
)
|
||||
child.set_value(
|
||||
coption_bag,
|
||||
|
|
@ -1327,7 +1398,7 @@ class KernelGroupConfig(_CommonConfig):
|
|||
# pylint: disable=protected-access
|
||||
ret.append(
|
||||
PropertiesOptionError(
|
||||
err._option_bag,
|
||||
err._subconfig,
|
||||
err.proptype,
|
||||
err._settings,
|
||||
err._opt_type,
|
||||
|
|
@ -1390,7 +1461,7 @@ class KernelGroupConfig(_CommonConfig):
|
|||
cconfig_bag.context = child
|
||||
if cconfig_bag.properties is None:
|
||||
settings = child.get_settings()
|
||||
properties = settings.get_context_properties(child.properties_cache)
|
||||
properties = settings.get_context_properties()
|
||||
permissives = settings.get_context_permissives()
|
||||
cconfig_bag.properties = properties
|
||||
cconfig_bag.permissives = permissives
|
||||
|
|
@ -1424,6 +1495,7 @@ class KernelGroupConfig(_CommonConfig):
|
|||
def reset(
|
||||
self,
|
||||
path: str,
|
||||
only_children: bool,
|
||||
config_bag: ConfigBag,
|
||||
) -> None:
|
||||
"""reset value for specified path"""
|
||||
|
|
@ -1432,19 +1504,19 @@ class KernelGroupConfig(_CommonConfig):
|
|||
cconfig_bag = config_bag.copy()
|
||||
cconfig_bag.context = child
|
||||
settings = child.get_settings()
|
||||
properties = settings.get_context_properties(child.properties_cache)
|
||||
properties = settings.get_context_properties()
|
||||
permissives = settings.get_context_permissives()
|
||||
cconfig_bag.properties = properties
|
||||
cconfig_bag.permissives = permissives
|
||||
cconfig_bag.remove_validation()
|
||||
# GROUP
|
||||
option_bag = child.get_sub_option_bag(
|
||||
subconfig = child.get_sub_config(
|
||||
cconfig_bag,
|
||||
path,
|
||||
None,
|
||||
False,
|
||||
)[-1]
|
||||
child.get_values().reset(option_bag)
|
||||
validate_properties=False,
|
||||
)
|
||||
child.get_values().reset(subconfig)
|
||||
|
||||
def getconfig(
|
||||
self,
|
||||
|
|
@ -1499,7 +1571,7 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
|
||||
def set_value(
|
||||
self,
|
||||
option_bag,
|
||||
subconfig,
|
||||
value,
|
||||
only_config=False,
|
||||
force_default=False,
|
||||
|
|
@ -1530,10 +1602,10 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
)
|
||||
)
|
||||
for child in self._impl_children:
|
||||
cconfig_bag = option_bag.config_bag.copy()
|
||||
cconfig_bag = subconfig.config_bag.copy()
|
||||
cconfig_bag.context = child
|
||||
settings = child.get_settings()
|
||||
properties = settings.get_context_properties(child.properties_cache)
|
||||
properties = settings.get_context_properties()
|
||||
cconfig_bag.properties = properties
|
||||
cconfig_bag.permissives = settings.get_context_permissives()
|
||||
try:
|
||||
|
|
@ -1545,14 +1617,16 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
not force_default and not force_default_if_same
|
||||
)
|
||||
# MIX
|
||||
moption_bag = obj.get_sub_option_bag(
|
||||
moption_bag = obj.get_sub_config(
|
||||
cconfig_bag,
|
||||
option_bag.path,
|
||||
option_bag.index,
|
||||
validate_properties,
|
||||
)[-1]
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
validate_properties=validate_properties,
|
||||
)
|
||||
if force_default_if_same:
|
||||
if not child.get_values().hasvalue(option_bag.path):
|
||||
if not child.get_values().hasvalue(
|
||||
subconfig.path, index=subconfig.index
|
||||
):
|
||||
child_value = undefined
|
||||
else:
|
||||
child_value = child.get_value(moption_bag)
|
||||
|
|
@ -1572,7 +1646,7 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
# pylint: disable=protected-access
|
||||
ret.append(
|
||||
PropertiesOptionError(
|
||||
err._option_bag,
|
||||
err._subconfig,
|
||||
err.proptype,
|
||||
err._settings,
|
||||
err._opt_type,
|
||||
|
|
@ -1585,12 +1659,12 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
|
||||
try:
|
||||
# MIX
|
||||
moption_bag = self.get_sub_option_bag(
|
||||
option_bag.config_bag,
|
||||
option_bag.path,
|
||||
option_bag.index,
|
||||
not only_config,
|
||||
)[-1]
|
||||
moption_bag = self.get_sub_config(
|
||||
subconfig.config_bag,
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
validate_properties=not only_config,
|
||||
)
|
||||
if only_config:
|
||||
ret = super().set_value(
|
||||
moption_bag,
|
||||
|
|
@ -1619,37 +1693,37 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
rconfig_bag.remove_validation()
|
||||
if self.impl_type == "meta":
|
||||
# MIX
|
||||
option_bag = self.get_sub_option_bag(
|
||||
subconfig = self.get_sub_config(
|
||||
config_bag,
|
||||
path,
|
||||
None,
|
||||
True,
|
||||
)[-1]
|
||||
validate_properties=True,
|
||||
)
|
||||
elif not only_children:
|
||||
try:
|
||||
# MIX
|
||||
option_bag = self.get_sub_option_bag(
|
||||
subconfig = self.get_sub_config(
|
||||
rconfig_bag,
|
||||
path,
|
||||
None,
|
||||
True,
|
||||
)[-1]
|
||||
validate_properties=True,
|
||||
)
|
||||
except AttributeError:
|
||||
only_children = True
|
||||
for child in self._impl_children:
|
||||
rconfig_bag.context = child
|
||||
try:
|
||||
if self.impl_type == "meta":
|
||||
moption_bag = option_bag
|
||||
moption_bag = subconfig
|
||||
moption_bag.config_bag = rconfig_bag
|
||||
else:
|
||||
# MIX
|
||||
moption_bag = child.get_sub_option_bag(
|
||||
moption_bag = child.get_sub_config(
|
||||
rconfig_bag,
|
||||
path,
|
||||
None,
|
||||
True,
|
||||
)[-1]
|
||||
validate_properties=True,
|
||||
)
|
||||
child.get_values().reset(moption_bag)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
|
@ -1660,8 +1734,8 @@ class KernelMixConfig(KernelGroupConfig):
|
|||
rconfig_bag,
|
||||
)
|
||||
if not only_children:
|
||||
option_bag.config_bag = config_bag
|
||||
self.get_values().reset(option_bag)
|
||||
subconfig.config_bag = config_bag
|
||||
self.get_values().reset(subconfig)
|
||||
|
||||
def new_config(
|
||||
self,
|
||||
|
|
@ -1807,5 +1881,5 @@ class KernelMetaConfig(KernelMixConfig):
|
|||
config,
|
||||
):
|
||||
if self._impl_descr is not config.get_description():
|
||||
raise ValueError(_("metaconfig must " "have the same optiondescription"))
|
||||
raise ValueError(_("metaconfig must have the same optiondescription"))
|
||||
super().add_config(config)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,23 @@
|
|||
import weakref
|
||||
from .i18n import _
|
||||
|
||||
from typing import Literal, Union
|
||||
|
||||
|
||||
TiramisuErrorCode = Literal[
|
||||
"option-dynamic",
|
||||
"option-not-found",
|
||||
"property-frozen",
|
||||
"property-error",
|
||||
"property-mandatory",
|
||||
"leadership-group_type",
|
||||
"leadership-wrong_property",
|
||||
"leadership-force_default_on_freeze",
|
||||
"leadership-greater",
|
||||
"leadership-follower-greater",
|
||||
"leadership-follower-callback-list",
|
||||
]
|
||||
|
||||
|
||||
def display_list(
|
||||
lst,
|
||||
|
|
@ -69,6 +86,8 @@ class PropertiesOptionError(AttributeError):
|
|||
orig_opt=None,
|
||||
help_properties=None,
|
||||
):
|
||||
if orig_opt:
|
||||
raise Exception("a la")
|
||||
if opt_type:
|
||||
self._opt_type = opt_type
|
||||
self._name = name
|
||||
|
|
@ -87,10 +106,23 @@ class PropertiesOptionError(AttributeError):
|
|||
self.help_properties = help_properties
|
||||
self._settings = settings
|
||||
self.msg = None
|
||||
if not self.help_properties:
|
||||
self.help_properties = self.proptype
|
||||
properties = list(self.help_properties)
|
||||
if properties == ["frozen"]:
|
||||
self.code = "property-frozen"
|
||||
elif properties == ["mandatory"]:
|
||||
self.code = "property-mandatory"
|
||||
else:
|
||||
self.code = "property-error"
|
||||
super().__init__(None)
|
||||
|
||||
def set_orig_opt(self, opt):
|
||||
self._orig_opt = opt
|
||||
def display_properties(self, force_property=False, add_quote=True):
|
||||
if force_property:
|
||||
properties = self.proptype
|
||||
else:
|
||||
properties = self.help_properties
|
||||
return display_list(list(properties), add_quote=add_quote)
|
||||
|
||||
def __str__(self):
|
||||
# this part is a bit slow, so only execute when display
|
||||
|
|
@ -98,42 +130,81 @@ class PropertiesOptionError(AttributeError):
|
|||
return self.msg
|
||||
if self._settings is None:
|
||||
return "error"
|
||||
if self.help_properties:
|
||||
properties = list(self.help_properties)
|
||||
else:
|
||||
properties = list(self.proptype)
|
||||
only_one = len(properties) == 1
|
||||
properties_msg = display_list(properties, add_quote=True)
|
||||
if only_one:
|
||||
prop_msg = _("property")
|
||||
else:
|
||||
prop_msg = _("properties")
|
||||
if properties == ["frozen"]:
|
||||
if self._orig_opt:
|
||||
msg = _('cannot modify the {0} {1} because "{2}" has {3} {4}')
|
||||
else:
|
||||
msg = _("cannot modify the {0} {1} because has {2} {3}")
|
||||
else:
|
||||
if self._orig_opt:
|
||||
msg = _('cannot access to {0} {1} because "{2}" has {3} {4}')
|
||||
else:
|
||||
msg = _("cannot access to {0} {1} because has {2} {3}")
|
||||
arguments = [self._opt_type]
|
||||
if self._orig_opt:
|
||||
# FIXME _orig_opt ?
|
||||
self.msg = msg.format(
|
||||
self._opt_type,
|
||||
self._orig_opt.impl_get_display_name(subconfig, with_quote=True),
|
||||
self._name,
|
||||
prop_msg,
|
||||
properties_msg,
|
||||
arguments.append(
|
||||
self._orig_opt.impl_get_display_name(subconfig, with_quote=True)
|
||||
)
|
||||
arguments.append(self._name)
|
||||
index = self._subconfig.index
|
||||
if index is not None:
|
||||
arguments.append(index)
|
||||
if self.code == "property-frozen":
|
||||
if index is not None:
|
||||
if self._orig_opt:
|
||||
msg = _(
|
||||
'cannot modify the {0} {1} at index "{2}" because {3} is frozen'
|
||||
)
|
||||
else:
|
||||
msg = _(
|
||||
'cannot modify the {0} {1} at index "{2}" because is frozen'
|
||||
)
|
||||
else:
|
||||
if self._orig_opt:
|
||||
msg = _("cannot modify the {0} {1} because {2} is frozen")
|
||||
else:
|
||||
msg = _("cannot modify the {0} {1} because is frozen")
|
||||
elif self.code == "property-mandatory":
|
||||
if index is not None:
|
||||
if self._orig_opt:
|
||||
msg = _(
|
||||
'cannot access to {0} {1} at index "{2}" because {3} hasn\'t value'
|
||||
)
|
||||
else:
|
||||
msg = _('{0} {1} at index "{2}" is mandatory but hasn\'t value')
|
||||
else:
|
||||
if self._orig_opt:
|
||||
msg = _("cannot access to {0} {1} because {2} hasn't value")
|
||||
else:
|
||||
msg = _("{0} {1} is mandatory but hasn't value")
|
||||
else:
|
||||
self.msg = msg.format(self._opt_type, self._name, prop_msg, properties_msg)
|
||||
if index is not None:
|
||||
if self._orig_opt:
|
||||
msg = _(
|
||||
'cannot access to {0} {1} at index "{2}" because {3} has {4} {5}'
|
||||
)
|
||||
else:
|
||||
msg = _(
|
||||
'cannot access to {0} {1} at index "{2}" because has {3} {4}'
|
||||
)
|
||||
else:
|
||||
if self._orig_opt:
|
||||
msg = _("cannot access to {0} {1} because {2} has {3} {4}")
|
||||
else:
|
||||
msg = _("cannot access to {0} {1} because has {2} {3}")
|
||||
only_one = len(self.help_properties) == 1
|
||||
if only_one:
|
||||
arguments.append(_("property"))
|
||||
else:
|
||||
arguments.append(_("properties"))
|
||||
arguments.append(self.display_properties())
|
||||
self.msg = msg.format(*arguments)
|
||||
del self._opt_type, self._name
|
||||
del self._settings, self._orig_opt
|
||||
return self.msg
|
||||
|
||||
|
||||
class AttributeOptionError(AttributeError):
|
||||
def __init__(self, path: str, code: TiramisuErrorCode) -> None:
|
||||
self.path = path
|
||||
self.code = code
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.code == "option-dynamic":
|
||||
return _('cannot access to "{0}" it\'s a dynamic option').format(self.path)
|
||||
return _('"{0}" is not an option').format(self.path)
|
||||
|
||||
|
||||
# ____________________________________________________________
|
||||
# Exceptions for a Config
|
||||
class ConfigError(Exception):
|
||||
|
|
@ -158,8 +229,74 @@ class ConflictError(Exception):
|
|||
# ____________________________________________________________
|
||||
# miscellaneous exceptions
|
||||
class LeadershipError(Exception):
|
||||
"problem with a leadership's value length"
|
||||
pass
|
||||
def __init__(
|
||||
self,
|
||||
subconfig: Union[str, "SubConfig"],
|
||||
code,
|
||||
*,
|
||||
prop=None,
|
||||
index=None,
|
||||
length=None,
|
||||
callback=None,
|
||||
args=None,
|
||||
kwargs=None,
|
||||
ret=None,
|
||||
):
|
||||
if isinstance(subconfig, str):
|
||||
self.path = self.display_name = subconfig
|
||||
else:
|
||||
self.path = subconfig.path
|
||||
option = subconfig.option
|
||||
self.display_name = option.impl_get_display_name(subconfig, with_quote=True)
|
||||
self.code = code
|
||||
if prop is not None:
|
||||
self.prop = prop
|
||||
if index is not None:
|
||||
self.index = index
|
||||
if length is not None:
|
||||
self.length = length
|
||||
if callback is not None:
|
||||
self.callback = callback
|
||||
if args is not None:
|
||||
self.args = args
|
||||
if kwargs is not None:
|
||||
self.kwargs = kwargs
|
||||
if ret is not None:
|
||||
self.ret = ret
|
||||
|
||||
def __str__(self):
|
||||
if self.code == "leadership-group_type":
|
||||
return _('cannot set "group_type" attribute for the Leadership {0}').format(
|
||||
self.display_name
|
||||
)
|
||||
if self.code == "leadership-wrong_property":
|
||||
return _('the leader {0} cannot have "{1}" property').format(
|
||||
self.display_name, self.prop
|
||||
)
|
||||
if self.code == "leadership-force_default_on_freeze":
|
||||
return _(
|
||||
'the leader {0} cannot have "force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
|
||||
).format(self.display_name)
|
||||
if self.code == "leadership-reduce":
|
||||
return _("cannot reduce length of the leader {0}").format(self.display_name)
|
||||
if self.code == "leadership-greater":
|
||||
return _(
|
||||
'index "{0}" is greater than the leadership length "{1}" for option {2}'
|
||||
).format(self.index, self.length, self.display_name)
|
||||
if self.code == "leadership-follower-greater":
|
||||
return _(
|
||||
"the follower option {0} has greater length ({1}) than the leader length ({2})"
|
||||
).format(self.display_name, self.index, self.length)
|
||||
if self.code == "leadership-follower-callback-list":
|
||||
if self.args or self.kwargs:
|
||||
return _(
|
||||
'the "{0}" function with positional arguments "{1}" and keyword arguments "{2}" must not return a list ("{3}") for the follower option {4}'
|
||||
).format(
|
||||
self.callback, self.args, self.kwargs, self.ret, self.display_name
|
||||
)
|
||||
return _(
|
||||
'the "{0}" function must not return a list ("{1}") for the follower option {2}'
|
||||
).format(self.callback, self.ret, self.display_name)
|
||||
|
||||
|
||||
class ConstError(TypeError):
|
||||
|
|
@ -172,7 +309,9 @@ class _CommonError:
|
|||
self.val = val
|
||||
self.display_type = display_type
|
||||
self.opt = weakref.ref(opt)
|
||||
self.name = opt.impl_get_display_name(subconfig)
|
||||
self.name = opt.impl_get_display_name(subconfig, with_quote=True)
|
||||
if subconfig:
|
||||
self.path = subconfig.path
|
||||
self.err_msg = err_msg
|
||||
self.index = index
|
||||
super().__init__(self.err_msg)
|
||||
|
|
@ -181,7 +320,9 @@ class _CommonError:
|
|||
try:
|
||||
msg = self.prefix
|
||||
except AttributeError:
|
||||
self.prefix = self.tmpl.format(self.val, _(self.display_type), self.name)
|
||||
self.prefix = self.tmpl.format(
|
||||
self.val, _(self.display_type), self.name, self.index
|
||||
)
|
||||
msg = self.prefix
|
||||
if self.err_msg:
|
||||
if msg:
|
||||
|
|
@ -196,13 +337,20 @@ class _CommonError:
|
|||
class ValueWarning(_CommonError, UserWarning):
|
||||
tmpl = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, **kwargs):
|
||||
if ValueWarning.tmpl is None:
|
||||
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"')
|
||||
if len(args) == 1 and not kwargs:
|
||||
self.msg = args[0]
|
||||
if kwargs.get("index") is None:
|
||||
ValueWarning.tmpl = _(
|
||||
'attention, "{0}" could be an invalid {1} for {2}'
|
||||
)
|
||||
else:
|
||||
ValueWarning.tmpl = _(
|
||||
'attention, "{0}" could be an invalid {1} for {2} at index "{3}"'
|
||||
)
|
||||
if list(kwargs) == ["msg"]:
|
||||
self.msg = kwargs["msg"]
|
||||
else:
|
||||
super().__init__(*args, **kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.msg = None
|
||||
|
||||
def __str__(self):
|
||||
|
|
@ -214,10 +362,20 @@ class ValueWarning(_CommonError, UserWarning):
|
|||
class ValueOptionError(_CommonError, ValueError):
|
||||
tmpl = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, **kwargs):
|
||||
if ValueOptionError.tmpl is None:
|
||||
ValueOptionError.tmpl = _('"{0}" is an invalid {1} for "{2}"')
|
||||
super().__init__(*args, **kwargs)
|
||||
opt = kwargs.get("opt")
|
||||
if opt and opt._do_not_display_value_in_error:
|
||||
if kwargs.get("index") is None:
|
||||
self.tmpl = _("{2} has an invalid {1}")
|
||||
else:
|
||||
self.tmpl = _('{2} at index "{3}" has an invalid {1}')
|
||||
else:
|
||||
if kwargs.get("index") is None:
|
||||
self.tmpl = _('"{0}" is an invalid {1} for {2}')
|
||||
else:
|
||||
self.tmpl = _('"{0}" is an invalid {1} for {2} at index "{3}"')
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
class ValueErrorWarning(ValueWarning):
|
||||
|
|
@ -225,5 +383,45 @@ class ValueErrorWarning(ValueWarning):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if ValueErrorWarning.tmpl is None:
|
||||
ValueErrorWarning.tmpl = _('"{0}" is an invalid {1} for "{2}"')
|
||||
ValueErrorWarning.tmpl = _('"{0}" is an invalid {1} for {2}')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class CancelParam(Exception):
|
||||
def __init__(self, origin_path, current_path):
|
||||
super().__init__()
|
||||
self.origin_path = origin_path
|
||||
self.current_path = current_path
|
||||
|
||||
def __ne__(self, value):
|
||||
return value is None or value == ""
|
||||
|
||||
def __eq__(self, value):
|
||||
return value is None or value == ""
|
||||
|
||||
def __bool__(self):
|
||||
return False
|
||||
|
||||
|
||||
class ValueErrorIndexes(ValueError):
|
||||
def __init__(self, msg, indexes):
|
||||
super().__init__(msg)
|
||||
self.indexes = indexes
|
||||
|
||||
|
||||
class Errors:
|
||||
@staticmethod
|
||||
def raise_carry_out_calculation_error(
|
||||
subconfig, message, original_error, option=None, extra_keys=[]
|
||||
):
|
||||
if option is None:
|
||||
option = subconfig.option
|
||||
display_name = option.impl_get_display_name(subconfig, with_quote=True)
|
||||
if original_error:
|
||||
raise ConfigError(
|
||||
message.format(display_name, original_error, *extra_keys)
|
||||
) from original_error
|
||||
raise ConfigError(message.format(display_name, extra_keys))
|
||||
|
||||
|
||||
errors = Errors()
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -59,6 +59,7 @@ class Base:
|
|||
"_dependencies",
|
||||
"_dependencies_information",
|
||||
"_identifiers_dependencies",
|
||||
"could_conflict",
|
||||
"__weakref__",
|
||||
)
|
||||
|
||||
|
|
@ -88,9 +89,12 @@ class Base:
|
|||
assert isinstance(properties, frozenset), _(
|
||||
"invalid properties type {0} for {1}," " must be a frozenset"
|
||||
).format(type(properties), name)
|
||||
if not self.impl_is_optiondescription() and "novalidator" not in properties:
|
||||
properties = properties | {"validator"}
|
||||
_setattr = object.__setattr__
|
||||
_setattr(self, "_name", name)
|
||||
_setattr(self, "_informations", {"doc": doc})
|
||||
_setattr(self, "could_conflict", [])
|
||||
for prop in properties:
|
||||
if not isinstance(prop, str):
|
||||
if not isinstance(prop, Calculation):
|
||||
|
|
@ -102,7 +106,7 @@ class Base:
|
|||
)
|
||||
for param in chain(prop.params.args, prop.params.kwargs.values()):
|
||||
if isinstance(param, ParamOption):
|
||||
param.option._add_dependency(self)
|
||||
param.option._add_dependency(self, "property")
|
||||
if properties:
|
||||
_setattr(self, "_properties", properties)
|
||||
self.set_informations(informations)
|
||||
|
|
@ -146,11 +150,12 @@ class Base:
|
|||
def _add_dependency(
|
||||
self,
|
||||
option,
|
||||
type_,
|
||||
is_identifier: bool = False,
|
||||
) -> None:
|
||||
woption = weakref.ref(option)
|
||||
options = self.get_dependencies(None)
|
||||
options.add(woption)
|
||||
options.add((type_ == "default", woption))
|
||||
self._dependencies = tuple(
|
||||
options
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
|
|
@ -186,6 +191,7 @@ class Base:
|
|||
else:
|
||||
dico = tuple([keys, tuple(dico.values())])
|
||||
_setattr(self, "_informations", dico)
|
||||
_setattr(self, "could_conflict", tuple(self.could_conflict))
|
||||
extra = getattr(self, "_extra", None)
|
||||
if extra is not None:
|
||||
_setattr(
|
||||
|
|
@ -394,7 +400,11 @@ class BaseOption(Base):
|
|||
for param in chain(value.params.args, value.params.kwargs.values()):
|
||||
if isinstance(param, ParamOption):
|
||||
# pylint: disable=protected-access
|
||||
param.option._add_dependency(self, is_identifier=is_identifier)
|
||||
if is_identifier:
|
||||
type_ = "identifier"
|
||||
else:
|
||||
type_ = "default"
|
||||
param.option._add_dependency(self, type_, is_identifier=is_identifier)
|
||||
self._has_dependency = True
|
||||
elif isinstance(param, ParamInformation):
|
||||
dest = self
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class BoolOption(Option):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "boolean"
|
||||
_t_type = _("boolean")
|
||||
|
||||
def validate(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -23,27 +23,22 @@
|
|||
from ipaddress import ip_address
|
||||
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from .stroption import StrOption
|
||||
|
||||
|
||||
class BroadcastOption(Option):
|
||||
class BroadcastOption(StrOption):
|
||||
"""represents the choice of a broadcast"""
|
||||
|
||||
__slots__ = tuple()
|
||||
_type = "broadcast address"
|
||||
_t_type = _("broadcast address")
|
||||
|
||||
def validate(
|
||||
self,
|
||||
value: str,
|
||||
) -> None:
|
||||
"""validate"""
|
||||
if not isinstance(value, str):
|
||||
raise ValueError(_("invalid string"))
|
||||
if value.count(".") != 3:
|
||||
raise ValueError()
|
||||
for val in value.split("."):
|
||||
if val.startswith("0") and len(val) > 1:
|
||||
raise ValueError()
|
||||
super().validate(value)
|
||||
try:
|
||||
ip_address(value)
|
||||
except ValueError as err:
|
||||
|
|
|
|||
|
|
@ -21,11 +21,12 @@
|
|||
"""ChoiceOption
|
||||
"""
|
||||
from typing import Any
|
||||
from itertools import chain
|
||||
|
||||
from ..setting import undefined
|
||||
from ..i18n import _
|
||||
from .option import Option
|
||||
from ..autolib import Calculation, get_calculated_value
|
||||
from ..autolib import Calculation, get_calculated_value, ParamOption
|
||||
from ..error import ConfigError, display_list
|
||||
|
||||
|
||||
|
|
@ -37,12 +38,17 @@ class ChoiceOption(Option):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "choice"
|
||||
_t_type = _("choice")
|
||||
|
||||
def __init__(self, name, doc, values, *args, **kwargs):
|
||||
"""
|
||||
:param values: is a list of values the option can possibly take
|
||||
"""
|
||||
if not isinstance(values, (Calculation, tuple)):
|
||||
if isinstance(values, Calculation):
|
||||
for param in chain(values.params.args, values.params.kwargs.values()):
|
||||
if isinstance(param, ParamOption):
|
||||
param.option._add_dependency(self, "choice")
|
||||
elif not isinstance(values, tuple):
|
||||
raise TypeError(
|
||||
_("values must be a tuple or a calculation for {0}").format(name)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class DateOption(StrOption):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "date"
|
||||
_t_type = _("date")
|
||||
|
||||
def validate(self, value: str) -> None:
|
||||
super().validate(value)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2024 Team tiramisu (see AUTHORS for all contributors)
|
||||
# Copyright (C) 2017-2025 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
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
"""DomainnameOption
|
||||
"""
|
||||
import re
|
||||
import socket
|
||||
from ipaddress import ip_interface
|
||||
from typing import Any, Optional, List
|
||||
|
||||
|
|
@ -42,6 +43,7 @@ class DomainnameOption(StrOption):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "domain name"
|
||||
_t_type = _("domain name")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -53,12 +55,18 @@ class DomainnameOption(StrOption):
|
|||
type: str = "domainname",
|
||||
allow_without_dot: bool = False,
|
||||
allow_startswith_dot: bool = False,
|
||||
test_existence: bool = False,
|
||||
_extra: dict = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments
|
||||
if _extra is None:
|
||||
extra = {}
|
||||
else:
|
||||
extra = _extra
|
||||
if type not in ["netbios", "hostname", "domainname"]:
|
||||
raise ValueError(_("unknown type {0} for hostname").format(type))
|
||||
extra = {"_dom_type": type}
|
||||
extra["type"] = type
|
||||
if not isinstance(allow_ip, bool):
|
||||
raise ValueError(_("allow_ip must be a boolean"))
|
||||
if not isinstance(allow_cidr_network, bool):
|
||||
|
|
@ -67,7 +75,8 @@ class DomainnameOption(StrOption):
|
|||
raise ValueError(_("allow_without_dot must be a boolean"))
|
||||
if not isinstance(allow_startswith_dot, bool):
|
||||
raise ValueError(_("allow_startswith_dot must be a boolean"))
|
||||
extra["_allow_without_dot"] = allow_without_dot
|
||||
extra["allow_without_dot"] = allow_without_dot
|
||||
extra["test_existence"] = test_existence
|
||||
if type == "domainname":
|
||||
if allow_without_dot:
|
||||
min_time = 0
|
||||
|
|
@ -76,14 +85,20 @@ class DomainnameOption(StrOption):
|
|||
regexp = r"((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}".format(
|
||||
self._get_len(type), min_time
|
||||
)
|
||||
msg = _(
|
||||
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed'
|
||||
)
|
||||
msg_warning = _(
|
||||
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded'
|
||||
)
|
||||
else:
|
||||
regexp = r"((?!-)[a-z0-9-]{{1,{0}}})".format(self._get_len(type))
|
||||
msg = _(
|
||||
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed'
|
||||
)
|
||||
msg_warning = _(
|
||||
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded'
|
||||
)
|
||||
msg = _(
|
||||
'must start with lowercase characters followed by lowercase characters, number and "-" characters are allowed'
|
||||
)
|
||||
msg_warning = _(
|
||||
'must start with lowercase characters followed by lowercase characters, number and "-" characters are recommanded'
|
||||
)
|
||||
if allow_ip:
|
||||
msg = _("could be a IP, otherwise {}").format(msg)
|
||||
msg_warning = _("could be a IP, otherwise {}").format(msg_warning)
|
||||
|
|
@ -105,15 +120,15 @@ class DomainnameOption(StrOption):
|
|||
name,
|
||||
doc,
|
||||
)
|
||||
extra["_allow_ip"] = allow_ip
|
||||
extra["allow_ip"] = allow_ip
|
||||
if allow_cidr_network:
|
||||
extra["_network"] = NetworkOption(
|
||||
name,
|
||||
doc,
|
||||
cidr=True,
|
||||
)
|
||||
extra["_allow_cidr_network"] = allow_cidr_network
|
||||
extra["_allow_startswith_dot"] = allow_startswith_dot
|
||||
extra["allow_cidr_network"] = allow_cidr_network
|
||||
extra["allow_startswith_dot"] = allow_startswith_dot
|
||||
|
||||
super().__init__(
|
||||
name,
|
||||
|
|
@ -137,13 +152,13 @@ class DomainnameOption(StrOption):
|
|||
_("invalid length (max {0})" "").format(part_name_length)
|
||||
)
|
||||
|
||||
part_name_length = self._get_len(self.impl_get_extra("_dom_type"))
|
||||
if self.impl_get_extra("_dom_type") == "domainname":
|
||||
if not self.impl_get_extra("_allow_without_dot") and not "." in value:
|
||||
part_name_length = self._get_len(self.impl_get_extra("type"))
|
||||
if self.impl_get_extra("type") == "domainname":
|
||||
if not self.impl_get_extra("allow_without_dot") and not "." in value:
|
||||
raise ValueError(_("must have dot"))
|
||||
if len(value) > 255:
|
||||
raise ValueError(_("invalid length (max 255)"))
|
||||
if self.impl_get_extra("_allow_startswith_dot") and value.startswith("."):
|
||||
if self.impl_get_extra("allow_startswith_dot") and value.startswith("."):
|
||||
val = value[1:]
|
||||
else:
|
||||
val = value
|
||||
|
|
@ -155,10 +170,22 @@ class DomainnameOption(StrOption):
|
|||
_valid_length(dom)
|
||||
else:
|
||||
_valid_length(value)
|
||||
self._validate_domain_resolution(value)
|
||||
|
||||
def _validate_domain_resolution(self, value: str) -> None:
|
||||
if not value.startswith(".") and self.impl_get_extra("test_existence") is True:
|
||||
try:
|
||||
socket.gethostbyname(value)
|
||||
except socket.gaierror as err:
|
||||
raise ValueError(_("DNS resolution failed").format(value)) from err
|
||||
except Exception as err:
|
||||
raise ValueError(
|
||||
_("error resolving DNS: {1}").format(value, err)
|
||||
) from err
|
||||
|
||||
def _validate_ip_network(self, value: str) -> None:
|
||||
allow_ip = self.impl_get_extra("_allow_ip")
|
||||
allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
|
||||
allow_ip = self.impl_get_extra("allow_ip")
|
||||
allow_cidr_network = self.impl_get_extra("allow_cidr_network")
|
||||
if allow_ip is False and allow_cidr_network is False:
|
||||
raise ValueError(_("must not be an IP"))
|
||||
if allow_ip is True:
|
||||
|
|
@ -184,7 +211,7 @@ class DomainnameOption(StrOption):
|
|||
def _second_level_validation_domain(self, value: str, warnings_only: bool) -> None:
|
||||
if self.impl_get_extra("_has_upper").search(value):
|
||||
raise ValueError(_("some characters are uppercase"))
|
||||
if self.impl_get_extra("_allow_startswith_dot") and value.startswith("."):
|
||||
if self.impl_get_extra("allow_startswith_dot") and value.startswith("."):
|
||||
val = value[1:]
|
||||
else:
|
||||
val = value
|
||||
|
|
@ -200,8 +227,8 @@ class DomainnameOption(StrOption):
|
|||
def _second_level_validation_ip_network(
|
||||
self, value: str, warnings_only: bool
|
||||
) -> None:
|
||||
allow_ip = self.impl_get_extra("_allow_ip")
|
||||
allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
|
||||
allow_ip = self.impl_get_extra("allow_ip")
|
||||
allow_cidr_network = self.impl_get_extra("allow_cidr_network")
|
||||
# it's an IP so validate with IPOption
|
||||
if allow_ip is True and allow_cidr_network is False:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ class DynOptionDescription(OptionDescription):
|
|||
identifier = identifier.replace(".", "_")
|
||||
return identifier
|
||||
|
||||
def name_could_conflict(self, dynchild, child):
|
||||
return child.impl_getname().startswith(dynchild.impl_getname())
|
||||
|
||||
def impl_is_dynoptiondescription(self) -> bool:
|
||||
return True
|
||||
|
||||
|
|
@ -104,6 +107,8 @@ class DynOptionDescription(OptionDescription):
|
|||
parent: "SubConfig",
|
||||
*,
|
||||
uncalculated: bool = False,
|
||||
from_display_name: bool = False,
|
||||
convert: bool = False,
|
||||
) -> List[str]:
|
||||
"""get dynamic identifiers"""
|
||||
subconfig = parent.get_child(
|
||||
|
|
@ -111,6 +116,7 @@ class DynOptionDescription(OptionDescription):
|
|||
None,
|
||||
False,
|
||||
properties=None,
|
||||
check_dynamic_without_identifiers=False,
|
||||
)
|
||||
identifiers = self._identifiers
|
||||
if isinstance(identifiers, list):
|
||||
|
|
@ -124,16 +130,18 @@ class DynOptionDescription(OptionDescription):
|
|||
)[0]
|
||||
if values is None:
|
||||
values = []
|
||||
values_ = []
|
||||
if __debug__:
|
||||
if not isinstance(values, list):
|
||||
if not from_display_name:
|
||||
name = self.impl_get_display_name(subconfig, with_quote=True)
|
||||
else:
|
||||
name = self.impl_getname()
|
||||
raise ValueError(
|
||||
_(
|
||||
"DynOptionDescription identifiers for option {0}, is not a list ({1})"
|
||||
).format(
|
||||
self.impl_get_display_name(subconfig, with_quote=True), values
|
||||
)
|
||||
).format(name, values)
|
||||
)
|
||||
values_ = []
|
||||
for val in values:
|
||||
cval = self.convert_identifier_to_path(val)
|
||||
if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None:
|
||||
|
|
@ -143,9 +151,15 @@ class DynOptionDescription(OptionDescription):
|
|||
cval, self.impl_get_display_name(subconfig, with_quote=True)
|
||||
)
|
||||
)
|
||||
elif convert:
|
||||
values_.append(cval)
|
||||
else:
|
||||
values_.append(val)
|
||||
if __debug__ and len(values_) > len(set(values_)):
|
||||
if (
|
||||
__debug__
|
||||
and "demoting_error_warning" not in subconfig.config_bag.properties
|
||||
and len(values_) > len(set(values_))
|
||||
):
|
||||
raise ValueError(
|
||||
_(
|
||||
'DynOptionDescription "{0}" identifiers return a list with same values "{1}"'
|
||||
|
|
|
|||
|
|
@ -34,3 +34,4 @@ class EmailOption(RegexpOption):
|
|||
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"
|
||||
)
|
||||
_type = "email address"
|
||||
_t_type = _("email address")
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class FilenameOption(StrOption):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "file name"
|
||||
_t_type = _("file name")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -52,9 +53,9 @@ class FilenameOption(StrOption):
|
|||
if typ not in ["file", "directory"]:
|
||||
raise ValueError(f'unknown type "{typ}" for "{name}"')
|
||||
extra = {
|
||||
"_allow_relative": allow_relative,
|
||||
"_test_existence": test_existence,
|
||||
"_types": types,
|
||||
"allow_relative": allow_relative,
|
||||
"test_existence": test_existence,
|
||||
"types": types,
|
||||
}
|
||||
super().__init__(name, *args, extra=extra, **kwargs)
|
||||
|
||||
|
|
@ -63,10 +64,10 @@ class FilenameOption(StrOption):
|
|||
value: str,
|
||||
) -> None:
|
||||
super().validate(value)
|
||||
if not self.impl_get_extra("_allow_relative") and not value.startswith("/"):
|
||||
if not self.impl_get_extra("allow_relative") and not value.startswith("/"):
|
||||
raise ValueError(_('must starts with "/"'))
|
||||
if value is not None and self.impl_get_extra("_test_existence"):
|
||||
types = self.impl_get_extra("_types")
|
||||
if value is not None and self.impl_get_extra("test_existence"):
|
||||
types = self.impl_get_extra("types")
|
||||
file = Path(value)
|
||||
found = False
|
||||
if "file" in types and file.is_file():
|
||||
|
|
@ -74,8 +75,12 @@ class FilenameOption(StrOption):
|
|||
if not found and "directory" in types and file.is_dir():
|
||||
found = True
|
||||
if not found:
|
||||
translated_types = [
|
||||
{"file": _("file"), "directory": _("directory")}.get(typ)
|
||||
for typ in types
|
||||
]
|
||||
raise ValueError(
|
||||
_('cannot find {0} "{1}"').format(
|
||||
display_list(types, separator="or"), value
|
||||
_("cannot find this {0}").format(
|
||||
display_list(translated_types, separator="or"), value
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class FloatOption(Option):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "float"
|
||||
_t_type = _("float")
|
||||
|
||||
def validate(self, value: float) -> None:
|
||||
if not isinstance(value, float):
|
||||
|
|
|
|||
|
|
@ -29,13 +29,26 @@ class IntOption(Option):
|
|||
"represents a choice of an integer"
|
||||
__slots__ = tuple()
|
||||
_type = "integer"
|
||||
_t_type = _("integer")
|
||||
|
||||
def __init__(self, *args, min_number=None, max_number=None, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
min_number=None,
|
||||
max_number=None,
|
||||
min_integer=None,
|
||||
max_integer=None,
|
||||
**kwargs,
|
||||
):
|
||||
extra = {}
|
||||
if min_number is not None:
|
||||
extra["min_number"] = min_number
|
||||
extra["min_integer"] = min_number
|
||||
if min_integer is not None:
|
||||
extra["min_integer"] = min_integer
|
||||
if max_number is not None:
|
||||
extra["max_number"] = max_number
|
||||
extra["max_integer"] = max_number
|
||||
if max_integer is not None:
|
||||
extra["max_integer"] = max_integer
|
||||
super().__init__(*args, extra=extra, **kwargs)
|
||||
|
||||
def validate(
|
||||
|
|
@ -43,20 +56,20 @@ class IntOption(Option):
|
|||
value: int,
|
||||
) -> None:
|
||||
if not isinstance(value, int):
|
||||
raise ValueError()
|
||||
raise ValueError(_("which is not an integer"))
|
||||
|
||||
def second_level_validation(self, value, warnings_only):
|
||||
min_number = self.impl_get_extra("min_number")
|
||||
if min_number is not None and value < min_number:
|
||||
min_integer = self.impl_get_extra("min_integer")
|
||||
if min_integer is not None and value < min_integer:
|
||||
if warnings_only:
|
||||
msg = _('value should be equal or greater than "{0}"')
|
||||
else:
|
||||
msg = _('value must be equal or greater than "{0}"')
|
||||
raise ValueError(msg.format(min_number))
|
||||
max_number = self.impl_get_extra("max_number")
|
||||
if max_number is not None and value > max_number:
|
||||
raise ValueError(msg.format(min_integer))
|
||||
max_integer = self.impl_get_extra("max_integer")
|
||||
if max_integer is not None and value > max_integer:
|
||||
if warnings_only:
|
||||
msg = _('value should be less than "{0}"')
|
||||
else:
|
||||
msg = _('value must be less than "{0}"')
|
||||
raise ValueError(msg.format(max_number))
|
||||
raise ValueError(msg.format(max_integer))
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class IPOption(StrOption):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "IP"
|
||||
_t_type = _("IP")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -43,9 +44,9 @@ class IPOption(StrOption):
|
|||
):
|
||||
if extra is None:
|
||||
extra = {}
|
||||
extra["_private_only"] = private_only
|
||||
extra["_allow_reserved"] = allow_reserved
|
||||
extra["_cidr"] = cidr
|
||||
extra["private_only"] = private_only
|
||||
extra["allow_reserved"] = allow_reserved
|
||||
extra["cidr"] = cidr
|
||||
super().__init__(*args, extra=extra, **kwargs)
|
||||
|
||||
def _validate_cidr(self, value):
|
||||
|
|
@ -53,6 +54,9 @@ class IPOption(StrOption):
|
|||
ip_obj = ip_interface(value)
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
self._second_level_cidr(ip_obj)
|
||||
|
||||
def _second_level_cidr(self, ip_obj):
|
||||
if ip_obj.ip == ip_obj.network.network_address:
|
||||
raise ValueError(_("it's in fact a network address"))
|
||||
if ip_obj.ip == ip_obj.network.broadcast_address:
|
||||
|
|
@ -66,7 +70,7 @@ class IPOption(StrOption):
|
|||
|
||||
def validate(self, value: str) -> None:
|
||||
super().validate(value)
|
||||
if self.impl_get_extra("_cidr"):
|
||||
if self.impl_get_extra("cidr"):
|
||||
if "/" not in value:
|
||||
raise ValueError(_('CIDR address must have a "/"'))
|
||||
self._validate_cidr(value)
|
||||
|
|
@ -75,13 +79,13 @@ class IPOption(StrOption):
|
|||
|
||||
def second_level_validation(self, value: str, warnings_only: bool) -> None:
|
||||
ip_obj = ip_interface(value)
|
||||
if not self.impl_get_extra("_allow_reserved") and ip_obj.is_reserved:
|
||||
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_obj.is_private:
|
||||
if self.impl_get_extra("private_only") and not ip_obj.is_private:
|
||||
if warnings_only:
|
||||
msg = _("should be private IP")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -51,9 +51,7 @@ class Leadership(OptionDescription):
|
|||
**kwargs,
|
||||
) -> None:
|
||||
if "group_type" in kwargs:
|
||||
raise LeadershipError(
|
||||
_('cannot set "group_type" attribute for a Leadership')
|
||||
)
|
||||
raise LeadershipError(name, "leadership-group_type")
|
||||
super().__init__(
|
||||
name,
|
||||
doc,
|
||||
|
|
@ -77,7 +75,7 @@ class Leadership(OptionDescription):
|
|||
# remove empty property for follower
|
||||
child._properties = frozenset(child._properties - {"empty", "unique"})
|
||||
followers.append(child)
|
||||
child._add_dependency(self)
|
||||
child._add_dependency(self, "leadership")
|
||||
child._leadership = weakref.ref(self)
|
||||
if __debug__:
|
||||
leader = children[0]
|
||||
|
|
@ -85,9 +83,7 @@ class Leadership(OptionDescription):
|
|||
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(
|
||||
prop, Calculation
|
||||
):
|
||||
raise LeadershipError(
|
||||
_('leader cannot have "{}" property').format(prop)
|
||||
)
|
||||
raise LeadershipError(name, "leadership-wrong_property", prop=prop)
|
||||
|
||||
def _check_child_is_valid(
|
||||
self,
|
||||
|
|
@ -112,7 +108,7 @@ class Leadership(OptionDescription):
|
|||
if not child.impl_is_multi():
|
||||
raise ValueError(
|
||||
_(
|
||||
"only multi option allowed in leadership {0} but option "
|
||||
"only multi option are allowed in leadership {0} but option "
|
||||
"{1} is not a multi"
|
||||
""
|
||||
).format(
|
||||
|
|
|
|||
|
|
@ -32,3 +32,4 @@ class MACOption(RegexpOption):
|
|||
__slots__ = tuple()
|
||||
_regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$")
|
||||
_type = "mac address"
|
||||
_t_type = _("mac address")
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class NetmaskOption(StrOption):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "netmask address"
|
||||
_t_type = _("netmask address")
|
||||
|
||||
def validate(self, value: str) -> None:
|
||||
super().validate(value)
|
||||
|
|
|
|||
|
|
@ -23,36 +23,22 @@
|
|||
from ipaddress import ip_network
|
||||
|
||||
from ..i18n import _
|
||||
from .ipoption import IPOption
|
||||
from .stroption import StrOption
|
||||
|
||||
|
||||
class NetworkOption(StrOption):
|
||||
class NetworkOption(IPOption):
|
||||
"represents the choice of a network"
|
||||
__slots__ = tuple()
|
||||
_type = "network address"
|
||||
_t_type = _("network address")
|
||||
|
||||
def __init__(self, *args, cidr=False, **kwargs):
|
||||
extra = {"_cidr": cidr}
|
||||
super().__init__(*args, extra=extra, **kwargs)
|
||||
super().__init__(*args, cidr=cidr, **kwargs)
|
||||
|
||||
def validate(self, value: str) -> None:
|
||||
super().validate(value)
|
||||
if value.count(".") != 3:
|
||||
raise ValueError()
|
||||
cidr = self.impl_get_extra("_cidr")
|
||||
if cidr:
|
||||
if "/" not in value:
|
||||
raise ValueError(_("must use CIDR notation"))
|
||||
value_ = value.split("/")[0]
|
||||
else:
|
||||
value_ = value
|
||||
for val in value_.split("."):
|
||||
if val.startswith("0") and len(val) > 1:
|
||||
raise ValueError()
|
||||
try:
|
||||
ip_network(value)
|
||||
except ValueError as err:
|
||||
raise ValueError() from err
|
||||
def _second_level_cidr(self, ip_obj):
|
||||
if ip_obj.ip != ip_obj.network.network_address:
|
||||
raise ValueError(_("it's not a network address"))
|
||||
|
||||
def second_level_validation(self, value: str, warnings_only: bool) -> None:
|
||||
if ip_network(value).network_address.is_reserved:
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ class Option(BaseOption):
|
|||
"_choice_values_params",
|
||||
)
|
||||
_type = None
|
||||
_t_type = None
|
||||
_do_not_display_value_in_error = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -127,11 +129,6 @@ class Option(BaseOption):
|
|||
def test_multi_value(value):
|
||||
if isinstance(value, Calculation):
|
||||
return
|
||||
# option_bag = OptionBag(self,
|
||||
# None,
|
||||
# undefined,
|
||||
# properties=None,
|
||||
# )
|
||||
try:
|
||||
self.validate(value)
|
||||
self.validate_with_option(
|
||||
|
|
@ -183,16 +180,19 @@ class Option(BaseOption):
|
|||
# undefined,
|
||||
# properties=None,
|
||||
# )
|
||||
self_properties = getattr(self, "_properties", {})
|
||||
self.impl_validate(
|
||||
None,
|
||||
default,
|
||||
loaded=True,
|
||||
self_properties=self_properties,
|
||||
)
|
||||
self.impl_validate(
|
||||
None,
|
||||
default,
|
||||
check_error=False,
|
||||
loaded=True,
|
||||
self_properties=self_properties,
|
||||
)
|
||||
self.value_dependencies(default)
|
||||
if (is_multi and default != []) or (not is_multi and default is not None):
|
||||
|
|
@ -215,8 +215,10 @@ class Option(BaseOption):
|
|||
"""is a dynsymlinkoption?"""
|
||||
return False
|
||||
|
||||
def get_type(self) -> str:
|
||||
def get_type(self, translation=True) -> str:
|
||||
"""get the type of option"""
|
||||
if translation:
|
||||
return self._t_type
|
||||
return self._type
|
||||
|
||||
def impl_getdefault(self) -> Any:
|
||||
|
|
@ -261,33 +263,40 @@ class Option(BaseOption):
|
|||
*,
|
||||
check_error: bool = True,
|
||||
loaded: bool = False,
|
||||
self_properties: frozenset = frozenset(),
|
||||
) -> bool:
|
||||
"""Return True if value is really valid
|
||||
If not validate or invalid return it returns False
|
||||
"""
|
||||
if (
|
||||
check_error
|
||||
and subconfig
|
||||
and not "validator" in subconfig.config_bag.properties
|
||||
):
|
||||
return False
|
||||
if check_error:
|
||||
if subconfig:
|
||||
config_properties = subconfig.config_bag.properties
|
||||
self_properties = subconfig.properties
|
||||
else:
|
||||
config_properties = {"validator"}
|
||||
if (
|
||||
"validator" not in config_properties
|
||||
or "validator" not in self_properties
|
||||
):
|
||||
return False
|
||||
if subconfig:
|
||||
force_index = subconfig.index
|
||||
else:
|
||||
force_index = None
|
||||
is_warnings_only = getattr(self, "_warnings_only", False)
|
||||
|
||||
def _is_not_unique(value):
|
||||
# if set(value) has not same length than value
|
||||
def _is_not_unique(current_value, values):
|
||||
if current_value is None:
|
||||
return
|
||||
if not subconfig or not check_error or "unique" not in subconfig.properties:
|
||||
return
|
||||
lvalue = [val for val in value if val is not None]
|
||||
if len(set(lvalue)) == len(lvalue):
|
||||
return
|
||||
for idx, val in enumerate(value):
|
||||
if val not in value[idx + 1 :]:
|
||||
continue
|
||||
raise ValueError(_('the value "{}" is not unique' "").format(val))
|
||||
indexes = [
|
||||
index for index, value in enumerate(values) if value == current_value
|
||||
]
|
||||
if len(indexes) > 1:
|
||||
raise ValueError(
|
||||
_('the value "{}" is not unique' "").format(current_value)
|
||||
)
|
||||
|
||||
def calculation_validator(
|
||||
val,
|
||||
|
|
@ -329,12 +338,12 @@ class Option(BaseOption):
|
|||
except ValueWarning as warn:
|
||||
warnings.warn_explicit(
|
||||
ValueWarning(
|
||||
subconfig,
|
||||
val,
|
||||
_(self.get_type()),
|
||||
self,
|
||||
str(warn),
|
||||
_index,
|
||||
subconfig=subconfig,
|
||||
val=val,
|
||||
display_type=_(self.get_type()),
|
||||
opt=self,
|
||||
err_msg=str(warn),
|
||||
index=_index,
|
||||
),
|
||||
ValueWarning,
|
||||
self.__class__.__name__,
|
||||
|
|
@ -370,12 +379,12 @@ class Option(BaseOption):
|
|||
if is_warnings_only:
|
||||
warnings.warn_explicit(
|
||||
ValueWarning(
|
||||
subconfig,
|
||||
_value,
|
||||
_(self.get_type()),
|
||||
self,
|
||||
str(err),
|
||||
_index,
|
||||
subconfig=subconfig,
|
||||
val=_value,
|
||||
display_type=_(self.get_type()),
|
||||
opt=self,
|
||||
err_msg=str(err),
|
||||
index=_index,
|
||||
),
|
||||
ValueWarning,
|
||||
self.__class__.__name__,
|
||||
|
|
@ -390,70 +399,100 @@ class Option(BaseOption):
|
|||
_index,
|
||||
)
|
||||
|
||||
val = value
|
||||
err_index = force_index
|
||||
try:
|
||||
if not self.impl_is_multi():
|
||||
ret = True
|
||||
if not self.impl_is_multi():
|
||||
try:
|
||||
do_validation(
|
||||
val,
|
||||
value,
|
||||
None,
|
||||
)
|
||||
elif force_index is not None:
|
||||
if self.impl_is_submulti():
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(_("which must be a list"))
|
||||
for val in value:
|
||||
except ValueError as err:
|
||||
self.validate_parse_error(value, err_index, err, subconfig)
|
||||
ret = False
|
||||
elif force_index is not None:
|
||||
if self.impl_is_submulti():
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(_("which must be a list"))
|
||||
for val in value:
|
||||
try:
|
||||
do_validation(
|
||||
val,
|
||||
force_index,
|
||||
)
|
||||
_is_not_unique(value)
|
||||
else:
|
||||
_is_not_unique(val, value)
|
||||
except ValueError as err:
|
||||
self.validate_parse_error(val, err_index, err, subconfig)
|
||||
ret = False
|
||||
else:
|
||||
try:
|
||||
do_validation(
|
||||
val,
|
||||
value,
|
||||
force_index,
|
||||
)
|
||||
elif isinstance(value, Calculation) and not subconfig:
|
||||
pass
|
||||
elif self.impl_is_submulti():
|
||||
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:
|
||||
except ValueError as err:
|
||||
self.validate_parse_error(value, err_index, err, subconfig)
|
||||
ret = False
|
||||
elif isinstance(value, Calculation) and not subconfig:
|
||||
pass
|
||||
elif self.impl_is_submulti():
|
||||
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:
|
||||
try:
|
||||
do_validation(val, err_index)
|
||||
_is_not_unique(lval)
|
||||
elif not isinstance(value, list):
|
||||
raise ValueError(_("which must be a list"))
|
||||
else:
|
||||
# FIXME suboptimal, not several time for whole=True!
|
||||
for err_index, val in enumerate(value):
|
||||
_is_not_unique(val, lval)
|
||||
except ValueError as err:
|
||||
self.validate_parse_error(val, err_index, err, subconfig)
|
||||
ret = False
|
||||
elif not isinstance(value, list):
|
||||
raise ValueError(_("which must be a list"))
|
||||
else:
|
||||
# FIXME suboptimal, not several time for whole=True!
|
||||
for err_index, val in enumerate(value):
|
||||
try:
|
||||
do_validation(
|
||||
val,
|
||||
err_index,
|
||||
)
|
||||
_is_not_unique(value)
|
||||
except ValueError as err:
|
||||
if (
|
||||
not subconfig
|
||||
or "demoting_error_warning" not in subconfig.config_bag.properties
|
||||
):
|
||||
raise ValueOptionError(
|
||||
subconfig, val, _(self.get_type()), self, str(err), err_index
|
||||
) from err
|
||||
warnings.warn_explicit(
|
||||
ValueErrorWarning(
|
||||
subconfig, val, _(self.get_type()), self, str(err), err_index
|
||||
),
|
||||
ValueErrorWarning,
|
||||
self.__class__.__name__,
|
||||
0,
|
||||
)
|
||||
return False
|
||||
return True
|
||||
_is_not_unique(val, value)
|
||||
except ValueError as err:
|
||||
self.validate_parse_error(val, err_index, err, subconfig)
|
||||
ret = False
|
||||
# return False
|
||||
return ret
|
||||
|
||||
def validate_parse_error(self, val, index, err, subconfig):
|
||||
if (
|
||||
not subconfig
|
||||
or "demoting_error_warning" not in subconfig.config_bag.properties
|
||||
):
|
||||
raise ValueOptionError(
|
||||
subconfig=subconfig,
|
||||
val=val,
|
||||
display_type=_(self.get_type()),
|
||||
opt=self,
|
||||
err_msg=str(err),
|
||||
index=index,
|
||||
) from err
|
||||
warnings.warn_explicit(
|
||||
ValueErrorWarning(
|
||||
subconfig=subconfig,
|
||||
val=val,
|
||||
display_type=_(self.get_type()),
|
||||
opt=self,
|
||||
err_msg=str(err),
|
||||
index=index,
|
||||
),
|
||||
ValueErrorWarning,
|
||||
self.__class__.__name__,
|
||||
0,
|
||||
)
|
||||
|
||||
def validate_with_option(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ from ..setting import ConfigBag, groups, undefined, owners, Undefined
|
|||
from .baseoption import BaseOption
|
||||
|
||||
# from .syndynoption import SubDynOptionDescription, SynDynOptionDescription
|
||||
from ..error import ConfigError, ConflictError
|
||||
from ..error import ConfigError, ConflictError, AttributeOptionError
|
||||
|
||||
|
||||
class CacheOptionDescription(BaseOption):
|
||||
|
|
@ -106,7 +106,24 @@ class CacheOptionDescription(BaseOption):
|
|||
if "force_store_value" in properties:
|
||||
force_store_values.append(option)
|
||||
if option.impl_is_readonly():
|
||||
raise ConflictError(_("duplicate option: {0}").format(option))
|
||||
previous_path = option.impl_getpath()
|
||||
if "." in previous_path:
|
||||
previous_path = _('"{0}" option description').format(
|
||||
previous_path.rsplit(".", 1)[0]
|
||||
)
|
||||
else:
|
||||
previous_path = _("root option description")
|
||||
if currpath:
|
||||
current_path = _('"{0}" option description').format(
|
||||
".".join(currpath)
|
||||
)
|
||||
else:
|
||||
current_path = _("root option description")
|
||||
raise ConflictError(
|
||||
_('option "{0}" is include in {1} but is also in {2}').format(
|
||||
option.impl_get_display_name(None), current_path, previous_path
|
||||
)
|
||||
)
|
||||
if not self.impl_is_readonly() and display_name:
|
||||
option._display_name_function = (
|
||||
display_name # pylint: disable=protected-access
|
||||
|
|
@ -232,19 +249,20 @@ class OptionDescriptionWalk(CacheOptionDescription):
|
|||
|
||||
def get_child_not_dynamic(
|
||||
self,
|
||||
name,
|
||||
allow_dynoption,
|
||||
name: str,
|
||||
allow_dynoption: bool,
|
||||
parent: "SubConfig",
|
||||
):
|
||||
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.impl_is_dynoptiondescription() and not allow_dynoption:
|
||||
raise AttributeError(
|
||||
_(
|
||||
'unknown option "{0}" in root optiondescription (it\'s a dynamic option)'
|
||||
).format(name)
|
||||
)
|
||||
if parent.path:
|
||||
path = parent.path + "." + name
|
||||
else:
|
||||
path = name
|
||||
raise AttributeOptionError(path, "option-dynamic")
|
||||
return option
|
||||
|
||||
def get_child(
|
||||
|
|
@ -261,6 +279,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
|
|||
option = self.get_child_not_dynamic(
|
||||
name,
|
||||
allow_dynoption,
|
||||
parent,
|
||||
)
|
||||
if option:
|
||||
return option
|
||||
|
|
@ -274,45 +293,16 @@ class OptionDescriptionWalk(CacheOptionDescription):
|
|||
if not with_identifier:
|
||||
return child
|
||||
return identifier, child
|
||||
if self.impl_get_group_type() == groups.root: # pylint: disable=no-member
|
||||
raise AttributeError(
|
||||
_('unknown option "{0}" in root optiondescription').format(name)
|
||||
)
|
||||
raise AttributeError(
|
||||
_('unknown option "{0}" in optiondescription {1}').format(
|
||||
name, self.impl_get_display_name(parent, with_quote=True)
|
||||
)
|
||||
)
|
||||
if parent.path is None:
|
||||
path = name
|
||||
else:
|
||||
path = parent.path + "." + name
|
||||
raise AttributeOptionError(path, "option-not-found")
|
||||
|
||||
def get_children(self) -> List[BaseOption]:
|
||||
"""get children"""
|
||||
return self._children[1]
|
||||
|
||||
def get_children_recursively(
|
||||
self,
|
||||
bytype: Optional[BaseOption],
|
||||
byname: Optional[str],
|
||||
config_bag: ConfigBag,
|
||||
self_opt: BaseOption = None,
|
||||
*,
|
||||
option_identifiers: Optional[list] = None,
|
||||
) -> Iterator[Union[BaseOption]]:
|
||||
"""get children recursively"""
|
||||
if self_opt is None:
|
||||
self_opt = self
|
||||
for option in self_opt.get_children():
|
||||
if option.impl_is_optiondescription():
|
||||
for subopt in option.get_children_recursively(
|
||||
bytype,
|
||||
byname,
|
||||
config_bag,
|
||||
):
|
||||
yield subopt
|
||||
elif (byname is None or option.impl_getname() == byname) and (
|
||||
bytype is None or isinstance(option, bytype)
|
||||
):
|
||||
yield option
|
||||
|
||||
|
||||
class OptionDescription(OptionDescriptionWalk):
|
||||
"""Config's schema (organisation, group) and container of Options
|
||||
|
|
@ -345,35 +335,38 @@ class OptionDescription(OptionDescriptionWalk):
|
|||
properties=properties,
|
||||
)
|
||||
child_names = []
|
||||
if __debug__:
|
||||
dynopt_names = []
|
||||
fix_child_names = []
|
||||
dynopts = []
|
||||
for child in children:
|
||||
name = child.impl_getname()
|
||||
child_names.append(name)
|
||||
if __debug__ and child.impl_is_dynoptiondescription():
|
||||
dynopt_names.append(name)
|
||||
if child.impl_is_dynoptiondescription():
|
||||
dynopts.append(child)
|
||||
else:
|
||||
fix_child_names.append(name)
|
||||
|
||||
# before sorting
|
||||
children_ = (tuple(child_names), tuple(children))
|
||||
|
||||
if __debug__:
|
||||
# better performance like this
|
||||
child_names.sort()
|
||||
old = None
|
||||
for child in child_names:
|
||||
if child == old:
|
||||
raise ConflictError(
|
||||
_("duplicate option name: " '"{0}"').format(child)
|
||||
# better performance like this
|
||||
fix_child_names.sort()
|
||||
old = None
|
||||
for child_name in fix_child_names:
|
||||
if child_name == old:
|
||||
raise ConflictError(
|
||||
_('the option name "{0}" is duplicate in "{1}"').format(
|
||||
child_name, self.impl_get_display_name(None)
|
||||
)
|
||||
if dynopt_names:
|
||||
for dynopt in dynopt_names:
|
||||
if child != dynopt and child.startswith(dynopt):
|
||||
raise ConflictError(
|
||||
_(
|
||||
'the option\'s name "{0}" start as the dynoptiondescription\'s name "{1}"'
|
||||
).format(child, dynopt)
|
||||
)
|
||||
old = child
|
||||
)
|
||||
old = child_name
|
||||
for dynopt in dynopts:
|
||||
if dynopt.could_conflict:
|
||||
continue
|
||||
for child in children:
|
||||
if child != dynopt and dynopt.name_could_conflict(dynopt, child):
|
||||
dynopt.could_conflict.append(weakref.ref(child))
|
||||
child.could_conflict.append(weakref.ref(dynopt))
|
||||
break
|
||||
self._children = children_
|
||||
# the group_type is useful for filtering OptionDescriptions in a config
|
||||
self._group_type = None
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ class PasswordOption(StrOption):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "password"
|
||||
_t_type = _("password")
|
||||
_do_not_display_value_in_error = True
|
||||
|
||||
def __init__(self, *args, min_len=None, max_len=None, forbidden_char=[], **kwargs):
|
||||
extra = {}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class PermissionsOption(IntOption):
|
|||
__slots__ = tuple()
|
||||
perm_re = re.compile(r"^[0-7]{3,4}$")
|
||||
_type = "unix file permissions"
|
||||
_t_type = _("unix file permissions")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@
|
|||
# ____________________________________________________________
|
||||
"""PortOption
|
||||
"""
|
||||
import re
|
||||
|
||||
from ..i18n import _
|
||||
from .stroption import StrOption
|
||||
|
||||
|
|
@ -38,8 +36,8 @@ class PortOption(StrOption):
|
|||
"""
|
||||
|
||||
__slots__ = tuple()
|
||||
port_re = re.compile(r"^[0-9]*$")
|
||||
_type = "port"
|
||||
_t_type = _("port")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -50,15 +48,21 @@ class PortOption(StrOption):
|
|||
allow_registred: bool = True,
|
||||
allow_protocol: bool = False,
|
||||
allow_private: bool = False,
|
||||
_extra: dict = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
|
||||
extra = {
|
||||
"_allow_range": allow_range,
|
||||
"_allow_protocol": allow_protocol,
|
||||
"_min_value": None,
|
||||
"_max_value": None,
|
||||
}
|
||||
if _extra is None:
|
||||
extra = {}
|
||||
else:
|
||||
extra = _extra
|
||||
extra["allow_range"] = allow_range
|
||||
extra["allow_protocol"] = allow_protocol
|
||||
extra["allow_zero"] = allow_zero
|
||||
extra["allow_wellknown"] = allow_wellknown
|
||||
extra["allow_registred"] = allow_registred
|
||||
extra["allow_private"] = allow_private
|
||||
extra["_min_value"] = None
|
||||
extra["_max_value"] = None
|
||||
ports_min = [0, 1, 1024, 49152]
|
||||
ports_max = [0, 1023, 49151, 65535]
|
||||
is_finally = False
|
||||
|
|
@ -82,11 +86,11 @@ class PortOption(StrOption):
|
|||
|
||||
def validate(self, value: str) -> None:
|
||||
super().validate(value)
|
||||
if self.impl_get_extra("_allow_protocol") and (
|
||||
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):
|
||||
elif self.impl_get_extra("allow_range") and ":" in str(value):
|
||||
value = value.split(":")
|
||||
if len(value) != 2:
|
||||
raise ValueError(_("range must have two values only"))
|
||||
|
|
@ -98,11 +102,11 @@ class PortOption(StrOption):
|
|||
value = [value]
|
||||
|
||||
for val in value:
|
||||
if not self.port_re.search(val):
|
||||
if not val.isdecimal():
|
||||
raise ValueError()
|
||||
|
||||
def second_level_validation(self, value: str, warnings_only: bool) -> None:
|
||||
if self.impl_get_extra("_allow_protocol") and (
|
||||
if self.impl_get_extra("allow_protocol") and (
|
||||
value.startswith("tcp:") or value.startswith("udp:")
|
||||
):
|
||||
value = [value[4:]]
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class StrOption(Option):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "string"
|
||||
_t_type = _("string")
|
||||
|
||||
def validate(
|
||||
self,
|
||||
|
|
@ -38,7 +39,7 @@ class StrOption(Option):
|
|||
) -> None:
|
||||
"""validation"""
|
||||
if not isinstance(value, str):
|
||||
raise ValueError()
|
||||
raise ValueError(_("which is not a string"))
|
||||
|
||||
|
||||
class RegexpOption(StrOption):
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class SymLinkOption(BaseOption):
|
|||
self._name = name
|
||||
self._opt = opt
|
||||
self._leadership = None
|
||||
opt._add_dependency(self)
|
||||
opt._add_dependency(self, "symlink")
|
||||
|
||||
def __getattr__(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class URLOption(StrOption):
|
|||
__slots__ = tuple()
|
||||
path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
|
||||
_type = "URL"
|
||||
_t_type = _("URL")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -54,24 +55,25 @@ class URLOption(StrOption):
|
|||
**kwargs,
|
||||
) -> None:
|
||||
# pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
|
||||
extra = {
|
||||
"_domainname": DomainnameOption(
|
||||
name,
|
||||
doc,
|
||||
allow_ip=allow_ip,
|
||||
type=type,
|
||||
allow_without_dot=allow_without_dot,
|
||||
),
|
||||
"_port": PortOption(
|
||||
name,
|
||||
doc,
|
||||
allow_range=allow_range,
|
||||
allow_zero=allow_zero,
|
||||
allow_wellknown=allow_wellknown,
|
||||
allow_registred=allow_registred,
|
||||
allow_private=allow_private,
|
||||
),
|
||||
}
|
||||
extra = {}
|
||||
extra["_domainname"] = DomainnameOption(
|
||||
name,
|
||||
doc,
|
||||
allow_ip=allow_ip,
|
||||
type=type,
|
||||
allow_without_dot=allow_without_dot,
|
||||
_extra=extra,
|
||||
)
|
||||
extra["_port"] = PortOption(
|
||||
name,
|
||||
doc,
|
||||
allow_range=allow_range,
|
||||
allow_zero=allow_zero,
|
||||
allow_wellknown=allow_wellknown,
|
||||
allow_registred=allow_registred,
|
||||
allow_private=allow_private,
|
||||
_extra=extra,
|
||||
)
|
||||
super().__init__(
|
||||
name,
|
||||
doc,
|
||||
|
|
@ -110,10 +112,18 @@ class URLOption(StrOption):
|
|||
domain, port, files = self._get_domain_port_files(value)
|
||||
# validate port
|
||||
portoption = self.impl_get_extra("_port")
|
||||
portoption.validate(port)
|
||||
try:
|
||||
portoption.validate(port)
|
||||
except ValueError as err:
|
||||
msg = _('the port "{0}" is invalid: {1}').format(domain, err)
|
||||
raise ValueError(msg) from err
|
||||
# validate domainname
|
||||
domainnameoption = self.impl_get_extra("_domainname")
|
||||
domainnameoption.validate(domain)
|
||||
try:
|
||||
domainnameoption.validate(domain)
|
||||
except ValueError as err:
|
||||
msg = _('the domain "{0}" is invalid: {1}').format(domain, err)
|
||||
raise ValueError(msg) from err
|
||||
# validate files
|
||||
if files is not None and files != "" and not self.path_re.search(files):
|
||||
raise ValueError(_("must ends with a valid resource name"))
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ class UsernameOption(RegexpOption):
|
|||
# regexp build with 'man 8 adduser' informations
|
||||
_regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
|
||||
_type = "unix username"
|
||||
_t_type = _("unix username")
|
||||
|
||||
|
||||
class GroupnameOption(UsernameOption):
|
||||
|
|
@ -40,3 +41,4 @@ class GroupnameOption(UsernameOption):
|
|||
|
||||
__slots__ = tuple()
|
||||
_type = "unix groupname"
|
||||
_t_type = _("unix groupname")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"sets the options of the configuration objects Config object itself"
|
||||
# Copyright (C) 2012-2024 Team tiramisu (see AUTHORS for all contributors)
|
||||
# Copyright (C) 2012-2025 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
|
||||
|
|
@ -94,7 +94,7 @@ EXPIRATION_TIME = 5
|
|||
# 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"}
|
||||
SPECIAL_PROPERTIES = {"frozen", "mandatory", "empty", "force_store_value", "validator"}
|
||||
|
||||
# Config can be in two defaut mode:
|
||||
#
|
||||
|
|
@ -143,12 +143,36 @@ RW_REMOVE = frozenset(
|
|||
)
|
||||
|
||||
|
||||
PROPERTIES_MAKE_SENSE = (
|
||||
"cache"
|
||||
"demoting_error_warning",
|
||||
"disabled",
|
||||
"empty",
|
||||
"everything_frozen",
|
||||
"expire",
|
||||
"force_default_on_freeze",
|
||||
"force_metaconfig_on_freeze",
|
||||
"force_store_value",
|
||||
"frozen",
|
||||
"hidden",
|
||||
"mandatory",
|
||||
"notempty",
|
||||
"notunique",
|
||||
"novalidator",
|
||||
"permissive",
|
||||
"unique",
|
||||
"validator",
|
||||
"warnings",
|
||||
)
|
||||
|
||||
|
||||
FORBIDDEN_SET_PROPERTIES = frozenset(["force_store_value"])
|
||||
FORBIDDEN_SET_PERMISSIVES = frozenset(
|
||||
[
|
||||
"force_default_on_freeze",
|
||||
"force_metaconfig_on_freeze",
|
||||
"force_store_value",
|
||||
"validator",
|
||||
]
|
||||
)
|
||||
ALLOWED_LEADER_PROPERTIES = {
|
||||
|
|
@ -158,6 +182,8 @@ ALLOWED_LEADER_PROPERTIES = {
|
|||
"unique",
|
||||
"force_store_value",
|
||||
"mandatory",
|
||||
"validator",
|
||||
"novalidator",
|
||||
"force_default_on_freeze",
|
||||
"force_metaconfig_on_freeze",
|
||||
"frozen",
|
||||
|
|
@ -473,7 +499,7 @@ class Settings:
|
|||
and new_prop not in ALLOWED_LEADER_PROPERTIES
|
||||
):
|
||||
raise LeadershipError(
|
||||
_('leader cannot have "{new_prop}" property')
|
||||
subconfig, "leadership-wrong_property", prop=new_prop
|
||||
)
|
||||
props.add(new_prop)
|
||||
props -= self.getpermissives(subconfig)
|
||||
|
|
@ -561,19 +587,15 @@ class Settings:
|
|||
not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
|
||||
if not_allowed_properties:
|
||||
raise LeadershipError(
|
||||
_('leader cannot have "{0}" property').format(
|
||||
display_list(not_allowed_properties)
|
||||
)
|
||||
subconfig,
|
||||
"leadership-wrong_property",
|
||||
prop=display_list(not_allowed_properties),
|
||||
)
|
||||
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(subconfig, "leadership-force_default_on_freeze")
|
||||
self._properties.setdefault(subconfig.path, {})[subconfig.index] = properties
|
||||
# values too because of follower values could have a PropertiesOptionError has value
|
||||
subconfig.config_bag.context.reset_cache(subconfig)
|
||||
|
|
|
|||
|
|
@ -646,9 +646,9 @@ class TiramisuDict:
|
|||
if self.remotable == "all" or childapi.has_dependency():
|
||||
obj_form["remote"] = True
|
||||
if childtype == "IPOption" and (
|
||||
child.impl_get_extra("_private_only")
|
||||
or not child.impl_get_extra("_allow_reserved")
|
||||
or child.impl_get_extra("_cidr")
|
||||
child.impl_get_extra("private_only")
|
||||
or not child.impl_get_extra("allow_reserved")
|
||||
or child.impl_get_extra("cidr")
|
||||
):
|
||||
obj_form["remote"] = True
|
||||
if childtype == "DateOption":
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"takes care of the option's values and multi values"
|
||||
# Copyright (C) 2013-2024 Team tiramisu (see AUTHORS for all contributors)
|
||||
# Copyright (C) 2013-2025 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
|
||||
|
|
@ -126,6 +126,18 @@ class Values:
|
|||
)
|
||||
return value, has_calculation
|
||||
|
||||
def get_default_owner(
|
||||
self,
|
||||
subconfig: "SubConfig",
|
||||
) -> Any:
|
||||
msubconfig = self._get_modified_parent(subconfig)
|
||||
if msubconfig is not None:
|
||||
# retrieved value from parent config
|
||||
return msubconfig.config_bag.context.get_values().getowner(
|
||||
msubconfig
|
||||
)
|
||||
return owners.default
|
||||
|
||||
def get_default_value(
|
||||
self,
|
||||
subconfig: "SubConfig",
|
||||
|
|
@ -189,10 +201,9 @@ class Values:
|
|||
if subconfig.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(
|
||||
return "force_metaconfig_on_freeze" in settings.get_personalize_properties(
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
frozenset(),
|
||||
)
|
||||
return False
|
||||
|
||||
|
|
@ -265,9 +276,10 @@ class Values:
|
|||
) -> None:
|
||||
"""set value to option"""
|
||||
owner = self.get_context_owner()
|
||||
self_properties = subconfig.properties
|
||||
setting_properties = subconfig.config_bag.properties
|
||||
ori_value = value
|
||||
if "validator" in setting_properties:
|
||||
if "validator" in setting_properties and "validator" in self_properties:
|
||||
value, has_calculation = self.setvalue_validation(
|
||||
subconfig,
|
||||
value,
|
||||
|
|
@ -295,6 +307,7 @@ class Values:
|
|||
)
|
||||
validator = (
|
||||
"validator" in setting_properties
|
||||
and "validator" in self_properties
|
||||
and "demoting_error_warning" not in setting_properties
|
||||
)
|
||||
if validator and not has_calculation:
|
||||
|
|
@ -304,7 +317,11 @@ class Values:
|
|||
value,
|
||||
validated=validator,
|
||||
)
|
||||
elif "validator" in setting_properties and has_calculation:
|
||||
elif (
|
||||
"validator" in setting_properties
|
||||
and "validator" in self_properties
|
||||
and has_calculation
|
||||
):
|
||||
cache = subconfig.config_bag.context.get_values_cache()
|
||||
cache.delcache(subconfig.path)
|
||||
|
||||
|
|
@ -390,12 +407,15 @@ class Values:
|
|||
woption(),
|
||||
true_path=subconfig.path,
|
||||
validate_properties=False,
|
||||
check_dynamic_without_identifiers=False,
|
||||
)
|
||||
if not isinstance(options, list):
|
||||
options = [options]
|
||||
for option in options:
|
||||
parent = option.parent
|
||||
for identifier in identifier_values:
|
||||
if identifier is None:
|
||||
continue
|
||||
name = option.option.impl_getname(identifier)
|
||||
opt_subconfig = parent.get_child(
|
||||
option.option,
|
||||
|
|
@ -428,36 +448,28 @@ class Values:
|
|||
If not found, return None
|
||||
For follower option, return the Config where leader is modified
|
||||
"""
|
||||
|
||||
def build_option_bag(subconfig, parent):
|
||||
doption_bag = subconfig.copy()
|
||||
config_bag = subconfig.config_bag.copy()
|
||||
config_bag.context = parent
|
||||
config_bag.unrestraint()
|
||||
doption_bag.config_bag = config_bag
|
||||
return doption_bag
|
||||
|
||||
for parent in subconfig.config_bag.context.get_parents():
|
||||
doption_bag = build_option_bag(subconfig, parent)
|
||||
parent_subconfig = subconfig.change_context(parent)
|
||||
parent_subconfig.config_bag.unrestraint()
|
||||
parent_subconfig.properties = subconfig.properties
|
||||
if "force_metaconfig_on_freeze" in subconfig.properties:
|
||||
# remove force_metaconfig_on_freeze only if option in metaconfig
|
||||
# hasn't force_metaconfig_on_freeze properties
|
||||
ori_properties = doption_bag.properties
|
||||
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 - {
|
||||
ori_properties = parent_subconfig.properties
|
||||
settings = parent_subconfig.config_bag.context.get_settings()
|
||||
parent_subconfig.properties = settings.getproperties(parent_subconfig)
|
||||
if not self.check_force_to_metaconfig(parent_subconfig):
|
||||
parent_subconfig.properties = ori_properties - {
|
||||
"force_metaconfig_on_freeze"
|
||||
}
|
||||
else:
|
||||
doption_bag.properties = ori_properties
|
||||
parent_subconfig.properties = ori_properties
|
||||
parent_owner = parent.get_values().getowner(
|
||||
doption_bag,
|
||||
parent,
|
||||
parent_subconfig,
|
||||
only_default=True,
|
||||
)
|
||||
if parent_owner != owners.default:
|
||||
return doption_bag
|
||||
return parent_subconfig
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -511,12 +523,10 @@ class Values:
|
|||
was present
|
||||
:returns: a `setting.owners.Owner` object
|
||||
"""
|
||||
# context = subconfig.config_bag.context
|
||||
# settings = context.get_settings()
|
||||
# settings.validate_properties(subconfig)
|
||||
s_properties = subconfig.properties
|
||||
if (
|
||||
"frozen" in subconfig.properties
|
||||
and "force_default_on_freeze" in subconfig.properties
|
||||
"frozen" in s_properties
|
||||
and "force_default_on_freeze" in s_properties
|
||||
):
|
||||
return owners.default
|
||||
if only_default:
|
||||
|
|
@ -534,19 +544,18 @@ class Values:
|
|||
)[1]
|
||||
if validate_meta is not False and (
|
||||
owner is owners.default
|
||||
or "frozen" in subconfig.properties
|
||||
and "force_metaconfig_on_freeze" in subconfig.properties
|
||||
or "frozen" in s_properties
|
||||
and "force_metaconfig_on_freeze" in s_properties
|
||||
):
|
||||
msubconfig = self._get_modified_parent(subconfig)
|
||||
if msubconfig is not None:
|
||||
values = msubconfig.config_bag.context.get_values()
|
||||
owner = values.getowner(
|
||||
msubconfig,
|
||||
parent,
|
||||
only_default=only_default,
|
||||
)
|
||||
elif "force_metaconfig_on_freeze" in subconfig.properties:
|
||||
return owners.default
|
||||
elif "force_metaconfig_on_freeze" in s_properties:
|
||||
owner = owners.default
|
||||
return owner
|
||||
|
||||
def set_owner(
|
||||
|
|
@ -587,35 +596,39 @@ class Values:
|
|||
"""reset value for an option"""
|
||||
config_bag = subconfig.config_bag
|
||||
hasvalue = self.hasvalue(subconfig.path)
|
||||
self_properties = subconfig.properties
|
||||
context = config_bag.context
|
||||
setting_properties = config_bag.properties
|
||||
if validate:
|
||||
if hasvalue and "validator" in setting_properties:
|
||||
fake_context = context.gen_fake_context()
|
||||
fake_config_bag = config_bag.copy()
|
||||
fake_config_bag.remove_validation()
|
||||
fake_config_bag.context = fake_context
|
||||
fake_subconfig = fake_context.get_sub_config(
|
||||
fake_config_bag,
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
validate_properties=False,
|
||||
)
|
||||
fake_values = fake_context.get_values()
|
||||
fake_values.reset(fake_subconfig)
|
||||
fake_subconfig.config_bag.properties = setting_properties
|
||||
value = fake_values.get_default_value(fake_subconfig)
|
||||
fake_values.setvalue_validation(
|
||||
fake_subconfig,
|
||||
value,
|
||||
)
|
||||
# if hasvalue:
|
||||
if (
|
||||
validate
|
||||
and hasvalue
|
||||
and "validator" in setting_properties
|
||||
and "validator" in self_properties
|
||||
):
|
||||
fake_context = context.gen_fake_context()
|
||||
fake_config_bag = config_bag.copy()
|
||||
fake_config_bag.remove_validation()
|
||||
fake_config_bag.context = fake_context
|
||||
fake_subconfig = fake_context.get_sub_config(
|
||||
fake_config_bag,
|
||||
subconfig.path,
|
||||
subconfig.index,
|
||||
validate_properties=False,
|
||||
)
|
||||
fake_values = fake_context.get_values()
|
||||
fake_values.reset(fake_subconfig)
|
||||
fake_subconfig.config_bag.properties = setting_properties
|
||||
value = fake_values.get_default_value(fake_subconfig)
|
||||
fake_values.setvalue_validation(
|
||||
fake_subconfig,
|
||||
value,
|
||||
)
|
||||
opt = subconfig.option
|
||||
if opt.impl_is_leader():
|
||||
opt.impl_get_leadership().reset(subconfig.parent)
|
||||
if (
|
||||
"force_store_value" in setting_properties
|
||||
and "force_store_value" in subconfig.properties
|
||||
and "force_store_value" in self_properties
|
||||
):
|
||||
value = self.get_default_value(subconfig)
|
||||
|
||||
|
|
@ -662,10 +675,11 @@ class Values:
|
|||
index=subconfig.index,
|
||||
):
|
||||
return
|
||||
self_properties = subconfig.properties
|
||||
config_bag = subconfig.config_bag
|
||||
context = config_bag.context
|
||||
setting_properties = config_bag.properties
|
||||
if "validator" in setting_properties:
|
||||
if "validator" in setting_properties and "validator" in self_properties:
|
||||
fake_context = context.gen_fake_context()
|
||||
fake_config_bag = config_bag.copy()
|
||||
fake_config_bag.remove_validation()
|
||||
|
|
@ -686,7 +700,7 @@ class Values:
|
|||
)
|
||||
if (
|
||||
"force_store_value" in setting_properties
|
||||
and "force_store_value" in subconfig.properties
|
||||
and "force_store_value" in self_properties
|
||||
):
|
||||
value = self.get_default_value(
|
||||
subconfig,
|
||||
|
|
@ -738,8 +752,8 @@ class Values:
|
|||
if index >= length:
|
||||
raise IndexError(
|
||||
_(
|
||||
"index {index} is greater than the length {length} "
|
||||
"for option {subconfig.option.impl_get_display_name(with_quote=True)}"
|
||||
f"index {index} is greater than the length {length} "
|
||||
f"for option {subconfig.option.impl_get_display_name(subconfig, with_quote=True)}"
|
||||
)
|
||||
)
|
||||
current_value.pop(index)
|
||||
|
|
|
|||
Loading…
Reference in a new issue