Compare commits

..

No commits in common. "5.2.0a13" and "main" have entirely different histories.

60 changed files with 4086 additions and 5125 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
[project] [project]
name = "tiramisu" name = "tiramisu"
version = "5.2.0a13" version = "5.1.0"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}] authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md" readme = "README.md"
description = "an options controller tool" description = "an options controller tool"
@ -18,8 +18,6 @@ classifiers = [
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Natural Language :: English", "Natural Language :: English",
@ -35,9 +33,5 @@ name = "cz_conventional_commits"
tag_format = "$version" tag_format = "$version"
version_scheme = "pep440" version_scheme = "pep440"
version_provider = "pep621" version_provider = "pep621"
version_files = [
"tiramisu/__version__.py",
"pyproject.toml:version"
]
#update_changelog_on_bump = true #update_changelog_on_bump = true
changelog_merge_prerelease = true changelog_merge_prerelease = true

View file

@ -1,4 +1,5 @@
# from json import dumps, loads # from json import dumps, loads
import asyncio
from os import environ from os import environ
try: try:
from tiramisu_api import Config from tiramisu_api import Config
@ -62,14 +63,3 @@ def parse_od_get(dico):
else: else:
ret[k.path()] = v ret[k.path()] = v
return ret 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

View file

@ -69,11 +69,11 @@ def test_cache_importation_property():
cfg = Config(od1) cfg = Config(od1)
cfg.option('u2').property.add('prop') cfg.option('u2').property.add('prop')
export = cfg.property.exportation() export = cfg.property.exportation()
assert cfg.option('u2').property.get() == {'validator', 'prop'} assert cfg.option('u2').property.get() == {'prop'}
cfg.option('u2').property.add('prop2') cfg.option('u2').property.add('prop2')
assert cfg.option('u2').property.get() == {'validator', 'prop', 'prop2'} assert cfg.option('u2').property.get() == {'prop', 'prop2'}
cfg.property.importation(export) cfg.property.importation(export)
assert cfg.option('u2').property.get() == {'validator', 'prop'} assert cfg.option('u2').property.get() == {'prop'}
cfg = Config(od1) cfg = Config(od1)
# assert not list_sessions() # assert not list_sessions()
@ -366,8 +366,8 @@ def test_cache_leader_and_followers():
cfg.value.get() cfg.value.get()
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value'] global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
val1_props = [] val1_props = []
val1_val1_props = ['empty', 'unique', 'validator'] val1_val1_props = ['empty', 'unique']
val1_val2_props = ['validator'] val1_val2_props = []
global_props = frozenset(global_props) global_props = frozenset(global_props)
val1_props = frozenset(val1_props) val1_props = frozenset(val1_props)
val1_val1_props = frozenset(val1_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]) cfg.option('val1.val1').value.set([None])
val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)} val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)}
compare(settings.get_cached(), {'val1.val1': {None: ({'validator', 'empty', 'unique'}, None, True)}}) compare(settings.get_cached(), {'val1.val1': {None: ({'empty', 'unique'}, None, True)}})
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}}) compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.get() cfg.value.get()
#has value #has value
@ -416,8 +416,8 @@ def test_cache_leader_callback():
cfg.value.get() cfg.value.get()
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value'] global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
val1_props = [] val1_props = []
val1_val1_props = ['empty', 'unique', 'validator'] val1_val1_props = ['empty', 'unique']
val1_val2_props = ['validator'] val1_val2_props = []
global_props = frozenset(global_props) global_props = frozenset(global_props)
val1_props = frozenset(val1_props) val1_props = frozenset(val1_props)
val1_val1_props = frozenset(val1_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)}}) compare(values.get_cached(), {'val1.val1': {None: ([], None)}})
cfg.option('val1.val1').value.set([None]) cfg.option('val1.val1').value.set([None])
compare(settings.get_cached(), {'val1.val1': {None: ({'unique', 'empty', 'validator'}, None, True)}}) compare(settings.get_cached(), {'val1.val1': {None: ({'unique', 'empty'}, None, True)}})
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}}) compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.get() cfg.value.get()
@ -451,24 +451,24 @@ def test_cache_requires():
settings = cfg._config_bag.context.properties_cache settings = cfg._config_bag.context.properties_cache
assert values.get_cached() == {} assert values.get_cached() == {}
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({'validator'}, None)}, compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)}, compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
cfg.value.get() cfg.value.get()
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)}, compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
cfg.option('ip_address_service').value.set('1.1.1.1') cfg.option('ip_address_service').value.set('1.1.1.1')
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}}) compare(settings.get_cached(), {'activate_service': {None: (set([]), None)}})
compare(values.get_cached(), {'activate_service': {None: (True, None)}, 'ip_address_service': {None: ('1.1.1.1', None, True)}}) compare(values.get_cached(), {'activate_service': {None: (True, None)}, 'ip_address_service': {None: ('1.1.1.1', None, True)}})
cfg.value.get() cfg.value.get()
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: ('1.1.1.1', None)}, compare(values.get_cached(), {'ip_address_service': {None: ('1.1.1.1', None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
@ -477,8 +477,8 @@ def test_cache_requires():
compare(values.get_cached(), {'activate_service': {None: (False, None)}}) compare(values.get_cached(), {'activate_service': {None: (False, None)}})
cfg.value.get() cfg.value.get()
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: ({'disabled', "validator"}, None)}}) 'ip_address_service': {None: (set(['disabled']), None)}})
compare(values.get_cached(), {'activate_service': {None: (False, None)}}) compare(values.get_cached(), {'activate_service': {None: (False, None)}})
# assert not list_sessions() # assert not list_sessions()
@ -499,19 +499,19 @@ def test_cache_global_properties():
settings = cfg._config_bag.context.properties_cache settings = cfg._config_bag.context.properties_cache
assert values.get_cached() == {} assert values.get_cached() == {}
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)}, compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
cfg.property.remove('disabled') cfg.property.remove('disabled')
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'ip_address_service': {None: (set([]), None)}})
cfg.property.add('test') cfg.property.add('test')
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'ip_address_service': {None: (set([]), None)}})
# assert not list_sessions() # assert not list_sessions()

View file

@ -162,10 +162,6 @@ def test_choiceoption_calc_opt_function(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('choice').owner.isdefault() 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') cfg.option('choice').value.set('val1')
assert cfg.option('choice').owner.get() == owner assert cfg.option('choice').owner.get() == owner
# #
@ -191,6 +187,7 @@ def test_choiceoption_calc_opt_function_propertyerror():
# assert not list_sessions() # assert not list_sessions()
#def test_choiceoption_calc_opt_multi_function(config_type):
def test_choiceoption_calc_opt_multi_function(): def test_choiceoption_calc_opt_multi_function():
# FIXME # FIXME
config_type = 'tiramisu' config_type = 'tiramisu'

View file

@ -332,7 +332,7 @@ def test_duplicated_option():
g1 g1
#in same OptionDescription #in same OptionDescription
with pytest.raises(ConflictError): with pytest.raises(ConflictError):
OptionDescription('od', '', [g1, g1]) d1 = OptionDescription('od', '', [g1, g1])
# assert not list_sessions() # assert not list_sessions()
@ -346,28 +346,6 @@ def test_duplicated_option_diff_od():
Config(d2) 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(): def test_cannot_assign_value_to_option_description():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
@ -402,12 +380,12 @@ def test_prefix_error():
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}", which is not an integer').format('yes', _('integer'), 'test1') assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('yes', _('integer'), 'test1')
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
err.prefix = '' err.prefix = ''
assert str(err) == _('which is not an integer') assert str(err) == _('invalid value')
# assert not list_sessions() # assert not list_sessions()

View file

@ -13,6 +13,12 @@ from tiramisu.error import PropertiesOptionError, ValueWarning, ConfigError
import warnings 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(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -346,8 +352,6 @@ def test_invalid_option():
PortOption('a', '', allow_zero=False, allow_wellknown=False, allow_registred=False, allow_private=False) PortOption('a', '', allow_zero=False, allow_wellknown=False, allow_registred=False, allow_private=False)
with raises(ValueError): with raises(ValueError):
PortOption('a', '', 'tcp:80') PortOption('a', '', 'tcp:80')
with raises(ValueError):
PortOption('a', '', '')
NetworkOption('a', '') NetworkOption('a', '')
with raises(ValueError): with raises(ValueError):
NetworkOption('a', '', 'string') NetworkOption('a', '', 'string')
@ -419,7 +423,7 @@ def test_config_reset():
cfg.owner.set('test') cfg.owner.set('test')
assert cfg.owner.get() == 'test' assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get() assert not cfg.option('gc.gc2.bool').value.get()
assert cfg.option('boolop').property.get() == frozenset(["validator"]) assert not cfg.option('boolop').property.get()
assert not cfg.option('boolop').permissive.get() assert not cfg.option('boolop').permissive.get()
assert not cfg.option('wantref').information.get('info', None) assert not cfg.option('wantref').information.get('info', None)
# #
@ -436,7 +440,7 @@ def test_config_reset():
cfg.config.reset() cfg.config.reset()
assert cfg.owner.get() == 'test' assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get() assert not cfg.option('gc.gc2.bool').value.get()
assert cfg.option('boolop').property.get() == {"validator"} assert not cfg.option('boolop').property.get()
assert not cfg.option('float').permissive.get() assert not cfg.option('float').permissive.get()
assert not cfg.option('wantref').information.get('info', None) assert not cfg.option('wantref').information.get('info', None)
# assert not list_sessions() # assert not list_sessions()

View file

@ -275,9 +275,3 @@ def test_url(config_type):
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('u').value.set('https://FOO.COM:8443') cfg.option('u').value.set('https://FOO.COM:8443')
# assert not list_sessions() # 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)

View file

@ -21,7 +21,6 @@ def test_ip(config_type):
cfg.option('a').value.set('192.168.1.0') cfg.option('a').value.set('192.168.1.0')
cfg.option('a').value.set('88.88.88.88') cfg.option('a').value.set('88.88.88.88')
cfg.option('a').value.set('0.0.0.0') cfg.option('a').value.set('0.0.0.0')
cfg.option('a').value.set('2001:db8::1')
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
# FIXME # FIXME
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -149,7 +148,8 @@ def test_network_cidr(config_type):
cfg.option('a').value.set('192.168.1.1') cfg.option('a').value.set('192.168.1.1')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('a').value.set('192.168.1.1/24') cfg.option('a').value.set('192.168.1.1/24')
cfg.option('a').value.set('2001:db00::0/24') with pytest.raises(ValueError):
cfg.option('a').value.set('2001:db00::0/24')
# assert not list_sessions() # assert not list_sessions()
@ -197,7 +197,8 @@ def test_broadcast(config_type):
cfg.option('a').value.set(1) cfg.option('a').value.set(1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('a').value.set(2) cfg.option('a').value.set(2)
cfg.option('a').value.set('2001:db8::1') with pytest.raises(ValueError):
cfg.option('a').value.set('2001:db8::1')
cfg.option('a').value.set('0.0.0.0') cfg.option('a').value.set('0.0.0.0')
cfg.option('a').value.set('255.255.255.0') cfg.option('a').value.set('255.255.255.0')
# assert not list_sessions() # assert not list_sessions()

View file

@ -80,15 +80,15 @@ def test_copy_force_store_value():
assert conf.value.exportation() == {'creole.general.wantref': {None: [True, 'user']}} assert conf.value.exportation() == {'creole.general.wantref': {None: [True, 'user']}}
assert conf2.value.exportation() == {'creole.general.wantref': {None: [False, 'forced']}} assert conf2.value.exportation() == {'creole.general.wantref': {None: [False, 'forced']}}
# assert not list_sessions() # assert not list_sessions()
#
#
def test_copy_force_store_value_metaconfig(): #def test_copy_force_store_value_metaconfig():
od1 = make_description() # od1 = make_description()
meta = MetaConfig([], optiondescription=od1) # meta = MetaConfig([], optiondescription=od1)
conf = meta.config.new() # conf = meta.config.new()
assert meta.property.get() == conf.property.get() # assert meta.property.get() == conf.property.get()
assert meta.permissive.get() == conf.permissive.get() # assert meta.permissive.get() == conf.permissive.get()
conf.property.read_write() # conf.property.read_write()
assert conf.value.exportation() == {'creole.general.wantref': {None: [False, 'forced']}} # assert conf.value.exportation() == {'creole.general.wantref': {None: [False, 'forced']}}
assert meta.value.exportation() == {} # assert meta.value.exportation() == {}
# assert not list_sessions() ## assert not list_sessions()

View file

@ -1,7 +1,7 @@
# coding: utf-8 # coding: utf-8
from .autopath import do_autopath from .autopath import do_autopath
do_autopath() do_autopath()
from .config import parse_od_get, get_dependencies from .config import parse_od_get
import pytest import pytest
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
@ -300,20 +300,20 @@ def test_prop_dyndescription():
od = OptionDescription('od', '', [dod]) od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
cfg = Config(od2) cfg = Config(od2)
assert set(cfg.option('od.dodval1.st').property.get()) == {'test', "validator"} assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == {'test', "validator"} assert set(cfg.option('od.dodval2.st').property.get()) == set(['test'])
cfg.option('od.dodval2.st').property.add('test2') cfg.option('od.dodval2.st').property.add('test2')
assert set(cfg.option('od.dodval1.st').property.get()) == {'test', "validator"} assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == {'test', 'test2', "validator"} assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
# #
assert set(cfg.option('od.dodval1').property.get()) == set() assert set(cfg.option('od.dodval1').property.get()) == set([])
assert set(cfg.option('od.dodval2').property.get()) == set() assert set(cfg.option('od.dodval2').property.get()) == set([])
cfg.option('od.dodval1').property.add('test1') cfg.option('od.dodval1').property.add('test1')
assert set(cfg.option('od.dodval1').property.get()) == {'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.dodval2').property.get()) == set([])
cfg.option('od.dodval1').property.remove('test1') cfg.option('od.dodval1').property.remove('test1')
assert set(cfg.option('od.dodval1').property.get()) == set() assert set(cfg.option('od.dodval1').property.get()) == set([])
assert set(cfg.option('od.dodval2').property.get()) == set() assert set(cfg.option('od.dodval2').property.get()) == set([])
# assert not list_sessions() # assert not list_sessions()
@ -336,7 +336,6 @@ def test_prop_dyndescription_force_store_value_calculation_prefix():
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
cfg = Config(od2) cfg = Config(od2)
cfg.property.read_write() 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.dodval1.st').owner.isdefault() == False
assert cfg.option('od.dodval2.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'} assert parse_od_get(cfg.value.get()) == {'od.lst': ['val1', 'val2'], 'od.dodval1.st': 'val1', 'od.dodval2.st': 'val2'}
@ -395,8 +394,6 @@ def test_callback_dyndescription_outside1():
od = OptionDescription('od', '', [dod, out]) od = OptionDescription('od', '', [dod, out])
od2 = OptionDescription('od', '', [od, lst]) od2 = OptionDescription('od', '', [od, lst])
cfg = Config(od2) 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']} 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.dodval1.st').value.set('val1')
cfg.option('od.dodval2.st').value.set('val2') cfg.option('od.dodval2.st').value.set('val2')
@ -420,8 +417,6 @@ 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']} 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') 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 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() # assert not list_sessions()
@ -437,20 +432,6 @@ def test_callback_dyndescription_outside3():
assert parse_od_get(cfg.value.get()) == {'od.out': 'val1', 'lst': ['val1', 'val2']} 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_callback_dyndescription_subdyn(): def test_callback_dyndescription_subdyn():
lst = StrOption('lst', '', ['val1', 'val2'], multi=True) lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', 'val1') st = StrOption('st', '', 'val1')
@ -621,14 +602,14 @@ def test_prop_dyndescription_context():
od = OptionDescription('od', '', [dod, val1]) od = OptionDescription('od', '', [dod, val1])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
cfg = Config(od2) cfg = Config(od2)
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator", 'test'} assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test'} assert set(cfg.option('od.dodval2.st').property.get()) == set(['test'])
cfg.option('od.dodval2.st').property.add('test2') cfg.option('od.dodval2.st').property.add('test2')
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator", 'test'} assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'} assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
cfg.option('od.dodval1.st').permissive.add('test') cfg.option('od.dodval1.st').permissive.add('test')
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator"} assert set(cfg.option('od.dodval1.st').property.get()) == set([])
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'} assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
# assert not list_sessions() # assert not list_sessions()
@ -1196,8 +1177,6 @@ def test_leadership_dyndescription():
cfg = Config(od1) cfg = Config(od1)
owner = cfg.owner.get() 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 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.stval1.st1.st1').value.get() == []
assert cfg.option('od.stval2.st1.st1').value.get() == [] assert cfg.option('od.stval2.st1.st1').value.get() == []
@ -1257,9 +1236,6 @@ def test_leadership_dyndescription_force_store_value_leader():
od1 = OptionDescription('od', '', [od]) od1 = OptionDescription('od', '', [od])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() 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.stval1.st1.st1').owner.isdefault() == False
assert cfg.option('od.stval2.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 assert cfg.option('od.stval1.st1.st2', 0).owner.isdefault() == True
@ -1720,6 +1696,16 @@ def test_leadership_callback_samegroup_dyndescription():
# assert not list_sessions() # 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(): def test_leadership_default_multi_dyndescription4():
st1 = StrOption('st1', "", multi=True) st1 = StrOption('st1', "", multi=True)
st2 = StrOption('st2', "", multi=True, default_multi='no') st2 = StrOption('st2', "", multi=True, default_multi='no')
@ -2873,86 +2859,3 @@ def test_callback_list_dyndescription_information_not_list():
cfg.information.set('identifier', 'ival3') cfg.information.set('identifier', 'ival3')
assert cfg.option('od.dodival3.st').value.get() == ['ival3', 'val2'] assert cfg.option('od.dodival3.st').value.get() == ['ival3', 'val2']
# assert not list_sessions() # 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()

View file

@ -6,7 +6,7 @@ import pytest
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \ from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \
StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, ParamOption, calc_value, Params, submulti StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, calc_value, Params
from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError
@ -788,19 +788,6 @@ def test_values_with_leader_and_followers_leader_pop():
# assert not list_sessions() # 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(): def test_follower_unique():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) 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',)) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, properties=('unique',))
@ -809,7 +796,7 @@ def test_follower_unique():
cfg = Config(od1) cfg = Config(od1)
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(["192.168.230.145", "192.168.230.146"]) 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 # unique property is removed for a follower
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == {"validator"} assert not cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get()
# assert not list_sessions() # assert not list_sessions()
@ -1055,7 +1042,7 @@ def test_follower_force_store_value_reset():
# assert not list_sessions() # 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) 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',)) 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]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
@ -1106,25 +1093,3 @@ def test_leader_forbidden_properties_callback(config_type):
cfg = Config(od1) cfg = Config(od1)
with pytest.raises(LeadershipError): with pytest.raises(LeadershipError):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() 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']}]}

View file

@ -13,6 +13,8 @@ from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.setting import groups 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): def is_mandatory(variable):
return True return True
@ -686,25 +688,25 @@ def return_list(val=None, identifier=None):
return ['val1', 'val2'] return ['val1', 'val2']
def test_mandatory_dyndescription(): #def test_mandatory_dyndescription():
st = StrOption('st', '', properties=('mandatory',)) # st = StrOption('st', '', properties=('mandatory',))
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list)) # dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list))
od = OptionDescription('od', '', [dod]) # od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od]) # od2 = OptionDescription('od', '', [od])
cfg = Config(od2) # cfg = Config(od2)
cfg.property.read_only() # cfg.property.read_only()
compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st']) # compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st'])
#
#
def test_mandatory_dyndescription_context(): #def test_mandatory_dyndescription_context():
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True) # val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', properties=('mandatory',)) # st = StrOption('st', '', properties=('mandatory',))
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(val1)))) # dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(val1))))
od = OptionDescription('od', '', [dod, val1]) # od = OptionDescription('od', '', [dod, val1])
od2 = OptionDescription('od', '', [od]) # od2 = OptionDescription('od', '', [od])
cfg = Config(od2) # cfg = Config(od2)
cfg.property.read_only() # cfg.property.read_only()
compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st']) # compare(cfg.value.mandatory(), ['od.dodval1.st', 'od.dodval2.st'])
def test_mandatory_callback_leader_and_followers_leader(): 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

View file

@ -15,76 +15,76 @@ def make_metaconfig():
return MetaConfig([], optiondescription=od2, name='metacfg1') return MetaConfig([], optiondescription=od2, name='metacfg1')
def test_multi_parents_path(): #def test_multi_parents_path():
""" # """
metacfg1 (1) --- # metacfg1 (1) ---
| -- cfg1 # | -- cfg1
metacfg2 (2) --- # metacfg2 (2) ---
""" # """
metacfg1 = make_metaconfig() # metacfg1 = make_metaconfig()
cfg1 = metacfg1.config.new(type='config', name="cfg1") # cfg1 = metacfg1.config.new(type='config', name="cfg1")
metacfg2 = MetaConfig([cfg1], name='metacfg2') # metacfg2 = MetaConfig([cfg1], name='metacfg2')
# # #
assert metacfg1.config.path() == 'metacfg1' # assert metacfg1.config.path() == 'metacfg1'
assert metacfg2.config.path() == 'metacfg2' # assert metacfg2.config.path() == 'metacfg2'
assert cfg1.config.path() == 'metacfg2.metacfg1.cfg1' # assert cfg1.config.path() == 'metacfg2.metacfg1.cfg1'
#
#
def test_multi_parents_path_same(): #def test_multi_parents_path_same():
""" # """
--- metacfg2 (1) --- # --- metacfg2 (1) ---
metacfg1 --| | -- cfg1 # metacfg1 --| | -- cfg1
--- metacfg3 (2) --- # --- metacfg3 (2) ---
""" # """
metacfg1 = make_metaconfig() # metacfg1 = make_metaconfig()
metacfg2 = metacfg1.config.new(type='metaconfig', name="metacfg2") # metacfg2 = metacfg1.config.new(type='metaconfig', name="metacfg2")
metacfg3 = metacfg1.config.new(type='metaconfig', name="metacfg3") # metacfg3 = metacfg1.config.new(type='metaconfig', name="metacfg3")
cfg1 = metacfg2.config.new(type='config', name="cfg1") # cfg1 = metacfg2.config.new(type='config', name="cfg1")
metacfg3.config.add(cfg1) # metacfg3.config.add(cfg1)
# # #
assert metacfg2.config.path() == 'metacfg1.metacfg2' # assert metacfg2.config.path() == 'metacfg1.metacfg2'
assert metacfg3.config.path() == 'metacfg1.metacfg3' # assert metacfg3.config.path() == 'metacfg1.metacfg3'
assert cfg1.config.path() == 'metacfg1.metacfg3.metacfg1.metacfg2.cfg1' # assert cfg1.config.path() == 'metacfg1.metacfg3.metacfg1.metacfg2.cfg1'
metacfg1.option('od1.i1').value.set(1) # metacfg1.option('od1.i1').value.set(1)
metacfg3.option('od1.i1').value.set(2) # metacfg3.option('od1.i1').value.set(2)
assert cfg1.option('od1.i1').value.get() == 1 # assert cfg1.option('od1.i1').value.get() == 1
orideep = cfg1.config.deepcopy(metaconfig_prefix="test_", name='test_cfg1') # orideep = cfg1.config.deepcopy(metaconfig_prefix="test_", name='test_cfg1')
deep = orideep # deep = orideep
while True: # while True:
try: # try:
children = list(deep.config.list()) # children = list(deep.config.list())
except: # except:
break # break
assert len(children) < 2 # assert len(children) < 2
deep = children[0] # deep = children[0]
assert deep.config.path() == 'test_metacfg3.test_metacfg1.test_metacfg2.test_cfg1' # assert deep.config.path() == 'test_metacfg3.test_metacfg1.test_metacfg2.test_cfg1'
assert cfg1.option('od1.i1').value.get() == 1 # assert cfg1.option('od1.i1').value.get() == 1
#
#
#
def test_multi_parents_value(): #def test_multi_parents_value():
metacfg1 = make_metaconfig() # metacfg1 = make_metaconfig()
cfg1 = metacfg1.config.new(type='config', name="cfg1") # cfg1 = metacfg1.config.new(type='config', name="cfg1")
metacfg2 = MetaConfig([cfg1], name='metacfg2') # metacfg2 = MetaConfig([cfg1], name='metacfg2')
# # #
assert cfg1.option('od1.i1').value.get() == None # assert cfg1.option('od1.i1').value.get() == None
assert cfg1.option('od1.i2').value.get() == 1 # assert cfg1.option('od1.i2').value.get() == 1
assert cfg1.option('od1.i3').value.get() == None # assert cfg1.option('od1.i3').value.get() == None
# # #
assert metacfg1.option('od1.i1').value.get() == None # assert metacfg1.option('od1.i1').value.get() == None
assert metacfg1.option('od1.i2').value.get() == 1 # assert metacfg1.option('od1.i2').value.get() == 1
assert metacfg1.option('od1.i3').value.get() == None # assert metacfg1.option('od1.i3').value.get() == None
# # #
assert metacfg2.option('od1.i1').value.get() == None # assert metacfg2.option('od1.i1').value.get() == None
assert metacfg2.option('od1.i2').value.get() == 1 # assert metacfg2.option('od1.i2').value.get() == 1
assert metacfg2.option('od1.i3').value.get() == None # assert metacfg2.option('od1.i3').value.get() == None
# # #
metacfg1.option('od1.i3').value.set(3) # metacfg1.option('od1.i3').value.set(3)
assert metacfg1.option('od1.i3').value.get() == 3 # assert metacfg1.option('od1.i3').value.get() == 3
assert cfg1.option('od1.i3').value.get() == 3 # assert cfg1.option('od1.i3').value.get() == 3
assert metacfg2.option('od1.i2').value.get() == 1 # assert metacfg2.option('od1.i2').value.get() == 1
# # #
metacfg2.option('od1.i2').value.set(4) # metacfg2.option('od1.i2').value.set(4)
assert metacfg2.option('od1.i2').value.get() == 4 # assert metacfg2.option('od1.i2').value.get() == 4
assert metacfg1.option('od1.i2').value.get() == 1 # assert metacfg1.option('od1.i2').value.get() == 1
assert cfg1.option('od1.i2').value.get() == 4 # assert cfg1.option('od1.i2').value.get() == 4

View file

@ -13,7 +13,7 @@ from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
valid_ip_netmask, ParamSelfOption, ParamInformation, ParamSelfInformation valid_ip_netmask, ParamSelfOption, ParamInformation, ParamSelfInformation
from tiramisu.error import PropertiesOptionError, ConflictError, LeadershipError, ConfigError from tiramisu.error import PropertiesOptionError, ConflictError, LeadershipError, ConfigError
from tiramisu.i18n import _ from tiramisu.i18n import _
from .config import config_type, get_config, parse_od_get, get_dependencies from .config import config_type, get_config, parse_od_get
def return_val(): def return_val():
@ -693,7 +693,6 @@ def test_callback_leader_and_followers_leader(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg = get_config(cfg, config_type) 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('val1').value.get() == ['val']
assert cfg.option('val2.val2').value.get() == ['val'] assert cfg.option('val2.val2').value.get() == ['val']
# #
@ -784,29 +783,10 @@ def test_callback_leader_and_followers_leader2(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg = get_config(cfg, config_type) 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']) 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.val4', 0).value.get() == 'val2'
assert cfg.option('val1.val3', 0).value.get() == 'val2' assert cfg.option('val1.val3', 0).value.get() == 'val2'
assert cfg.option('val1.val2', 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() # assert not list_sessions()
@ -965,7 +945,7 @@ def test_consistency_leader_and_followers_leader_mandatory_transitive():
try: try:
cfg.option('val1.val2', 0).value.get() cfg.option('val1.val2', 0).value.get()
except PropertiesOptionError as error: except PropertiesOptionError as error:
assert str(error) == str(_('cannot access to {0} {1} at index "{2}" because has {3} {4}').format('option', '"val2"', 0, _('property'), '"disabled"')) assert str(error) == str(_('cannot access to {0} {1} because has {2} {3}').format('option', '"val2"', _('property'), '"disabled"'))
else: else:
raise Exception('must raises') raise Exception('must raises')
assert list(cfg.value.mandatory()) == [] assert list(cfg.value.mandatory()) == []
@ -1519,7 +1499,6 @@ def test_leadership_callback_description(config_type):
cfg.option('od.st.st1.st2', 0).value.set('yes') 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.st1').owner.get() == owner
assert cfg.option('od.st.st1.st2', 0).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() # assert not list_sessions()
@ -1536,8 +1515,6 @@ def test_leadership_callback_outside(config_type):
owner = cfg.owner.get() owner = cfg.owner.get()
cfg.option('od.st.st1.st1').value.set(['yes']) 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 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() ## assert not list_sessions()

View file

@ -154,8 +154,8 @@ def test_force_default_on_freeze_multi():
# with pytest.raises(ConfigError): # with pytest.raises(ConfigError):
# Config(od1) # Config(od1)
# assert not list_sessions() # assert not list_sessions()
#
#
#def test_force_metaconfig_on_freeze_leader(): #def test_force_metaconfig_on_freeze_leader():
# dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',)) # dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',))
# dummy2 = BoolOption('dummy2', 'Test string option', multi=True) # dummy2 = BoolOption('dummy2', 'Test string option', multi=True)

View file

@ -202,10 +202,10 @@ def test_property_get_unique_empty():
od1 = OptionDescription("options", "", [s, s2, s3, s4]) od1 = OptionDescription("options", "", [s, s2, s3, s4])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert cfg.option('string').property.get() == {"validator", 'empty', 'unique'} assert cfg.option('string').property.get() == {'empty', 'unique'}
assert cfg.option('string2').property.get() == {"validator", 'empty', 'notunique'} assert cfg.option('string2').property.get() == {'empty', 'notunique'}
assert cfg.option('string3').property.get() == {"validator", 'unique', 'notempty'} assert cfg.option('string3').property.get() == {'unique', 'notempty'}
assert cfg.option('string4').property.get() == {"validator", 'notunique', 'notempty'} assert cfg.option('string4').property.get() == {'notunique', 'notempty'}
# assert not list_sessions() # assert not list_sessions()
@ -220,7 +220,7 @@ def test_property_only_raises():
od1 = OptionDescription("options", "", [s, intoption, stroption]) od1 = OptionDescription("options", "", [s, intoption, stroption])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert cfg.option('str').property.get() == {'empty', 'unique', 'validator'} assert cfg.option('str').property.get() == {'empty', 'unique'}
assert cfg.option('str').property.get(only_raises=True) == set() assert cfg.option('str').property.get(only_raises=True) == set()
# assert not list_sessions() # assert not list_sessions()
@ -569,23 +569,23 @@ def test_access_by_get_whith_hide():
def test_append_properties(): def test_append_properties():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == {"validator"} assert cfg.option('gc.dummy').property.get() == set()
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.option('gc.dummy').property.get() == {'test', "validator"} assert cfg.option('gc.dummy').property.get() == {'test'}
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
cfg.option('gc.dummy').property.add('force_store_value') cfg.option('gc.dummy').property.add('force_store_value')
assert cfg.option('gc.dummy').property.get() == {'test', "validator"} assert cfg.option('gc.dummy').property.get() == {'test'}
# assert not list_sessions() # assert not list_sessions()
def test_reset_properties(): def test_reset_properties():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == {"validator"} assert cfg.option('gc.dummy').property.get() == set()
cfg.option('gc.dummy').property.add('frozen') cfg.option('gc.dummy').property.add('frozen')
assert cfg.option('gc.dummy').property.get() == {"validator", 'frozen'} assert cfg.option('gc.dummy').property.get() == {'frozen'}
cfg.option('gc.dummy').property.reset() cfg.option('gc.dummy').property.reset()
assert cfg.option('gc.dummy').property.get() == {"validator"} assert cfg.option('gc.dummy').property.get() == set()
# assert not list_sessions() # assert not list_sessions()
@ -594,7 +594,7 @@ def test_properties_cached():
od1 = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])]) od1 = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert cfg.option('sub.b1').property.get() == {'test', "validator"} assert cfg.option('sub.b1').property.get() == {'test'}
# assert not list_sessions() # assert not list_sessions()
@ -603,9 +603,9 @@ def test_append_properties_force_store_value():
gcgroup = OptionDescription('gc', '', [gcdummy]) gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup]) od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == {'force_store_value', "validator"} assert cfg.option('gc.dummy').property.get() == {'force_store_value'}
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test', "validator"} assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test'}
# assert not list_sessions() # assert not list_sessions()
@ -818,27 +818,27 @@ def test_reset_properties_force_store_value():
# assert not list_sessions() # assert not list_sessions()
def test_importation_force_store_value(): #def test_importation_force_store_value():
gcdummy = BoolOption('dummy', 'dummy', default=False, # gcdummy = BoolOption('dummy', 'dummy', default=False,
properties=('force_store_value',)) # properties=('force_store_value',))
gcgroup = OptionDescription('gc', '', [gcdummy]) # gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup]) # od1 = OptionDescription('tiramisu', '', [gcgroup])
config1 = Config(od1) # config1 = Config(od1)
assert config1.value.exportation() == {} # assert config1.value.exportation() == {}
config1.property.add('frozen') # config1.property.add('frozen')
assert config1.value.exportation() == {} # assert config1.value.exportation() == {}
config1.property.add('force_store_value') # config1.property.add('force_store_value')
assert config1.value.exportation() == {'gc.dummy': {None: [False, 'forced']}} # assert config1.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
exportation = config1.property.exportation() # exportation = config1.property.exportation()
config2 = Config(od1) # config2 = Config(od1)
assert config2.value.exportation() == {} # assert config2.value.exportation() == {}
config2.property.importation(exportation) # config2.property.importation(exportation)
assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}} # assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
config2.property.importation(exportation) # config2.property.importation(exportation)
assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}} # assert config2.value.exportation() == {'gc.dummy': {None: [False, 'forced']}}
# assert not list_sessions() ## assert not list_sessions()
#
#
def test_set_modified_value(): def test_set_modified_value():
gcdummy = BoolOption('dummy', 'dummy', default=False, properties=('force_store_value',)) gcdummy = BoolOption('dummy', 'dummy', default=False, properties=('force_store_value',))
gcgroup = OptionDescription('gc', '', [gcdummy]) gcgroup = OptionDescription('gc', '', [gcdummy])
@ -920,18 +920,18 @@ def test_set_modified_value():
# assert not list_sessions() # assert not list_sessions()
def test_none_is_not_modified(): #def test_none_is_not_modified():
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',)) # gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value',)) # gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value',))
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1]) # gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
od1 = OptionDescription('tiramisu', '', [gcgroup]) # od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1) # cfg = Config(od1)
assert cfg.value.exportation() == {} # assert cfg.value.exportation() == {}
cfg.property.read_write() # cfg.property.read_write()
assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}} # assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}}
# assert not list_sessions() ## assert not list_sessions()
#
#
def test_pprint(): def test_pprint():
msg_error = _("cannot access to {0} {1} because has {2} {3}") msg_error = _("cannot access to {0} {1} because has {2} {3}")
msg_is_not = _('the value of "{0}" is not {1}') msg_is_not = _('the value of "{0}" is not {1}')

View file

@ -34,7 +34,7 @@ def return_val(value, param=None):
def return_if_val(value): def return_if_val(value):
if value not in ['val', 'val_not_raise']: if value != 'val':
raise ValueError('test error') raise ValueError('test error')
@ -121,34 +121,6 @@ def test_validator(config_type):
# assert not list_sessions() # 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): def test_validator_not_valid(config_type):
with pytest.raises(ValueError): with pytest.raises(ValueError):
StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val') StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val')
@ -341,13 +313,6 @@ def test_validator_multi(config_type):
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
cfg.option('opt1').value.set(['val', 'val1']) cfg.option('opt1').value.set(['val', 'val1'])
assert len(w) == 1 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() # assert not list_sessions()

View file

@ -22,16 +22,7 @@ def test_forcepermissive_and_unrestraint(config_type):
cfg_ori.property.read_write() cfg_ori.property.read_write()
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
cfg_ori.unrestraint.forcepermissive cfg_ori.forcepermissive.add('disabled')
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): def test_permissive(config_type):

View file

@ -426,9 +426,9 @@ def test_requires_transitive_unrestraint(config_type):
# #
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
assert cfg_ori.option('activate_service_web').property.get() == {'disabled', "validator"} assert cfg_ori.option('activate_service_web').property.get() == {'disabled'}
# FIXME assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'} # FIXME assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'}
assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled', "validator"} assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled'}
# assert not list_sessions() # assert not list_sessions()

View file

@ -146,8 +146,8 @@ def test_slots_option_readonly():
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
q._requires = 'q' q._requires = 'q'
# assert not list_sessions() # assert not list_sessions()
#
#
#def test_slots_description(): #def test_slots_description():
# # __slots__ for OptionDescription should be complete for __getattr__ # # __slots__ for OptionDescription should be complete for __getattr__
# slots = set() # slots = set()

View file

@ -528,43 +528,43 @@ def test_submulti_unique():
# assert not list_sessions() # assert not list_sessions()
def test_multi_submulti_meta(): #def test_multi_submulti_meta():
multi = StrOption('multi', '', multi=submulti) # multi = StrOption('multi', '', multi=submulti)
od1 = OptionDescription('od', '', [multi]) # od1 = OptionDescription('od', '', [multi])
cfg = Config(od1, name='cfg') # cfg = Config(od1, name='cfg')
cfg.property.read_write() # cfg.property.read_write()
cfg2 = Config(od1) # cfg2 = Config(od1)
cfg2.property.read_write() # cfg2.property.read_write()
meta = MetaConfig([cfg, cfg2]) # meta = MetaConfig([cfg, cfg2])
meta.property.read_write() # meta.property.read_write()
meta.option('multi').value.set([['val']]) # meta.option('multi').value.set([['val']])
assert meta.option('multi').value.get() == [['val']] # assert meta.option('multi').value.get() == [['val']]
newcfg = meta.config('cfg') # newcfg = meta.config('cfg')
newcfg.option('multi').value.set([['val', None]]) # newcfg.option('multi').value.set([['val', None]])
assert cfg.option('multi').value.get() == [['val', None]] # assert cfg.option('multi').value.get() == [['val', None]]
newcfg = meta.config('cfg') # newcfg = meta.config('cfg')
assert newcfg.option('multi').value.get() == [['val', None]] # assert newcfg.option('multi').value.get() == [['val', None]]
assert meta.option('multi').value.get() == [['val']] # assert meta.option('multi').value.get() == [['val']]
# assert not list_sessions() ## assert not list_sessions()
#
#
def test_multi_submulti_meta_no_cache(): #def test_multi_submulti_meta_no_cache():
multi = StrOption('multi', '', multi=submulti) # multi = StrOption('multi', '', multi=submulti)
multi = StrOption('multi', '', multi=submulti) # multi = StrOption('multi', '', multi=submulti)
od1 = OptionDescription('od', '', [multi]) # od1 = OptionDescription('od', '', [multi])
cfg = Config(od1, name='cfg') # cfg = Config(od1, name='cfg')
cfg.property.read_write() # cfg.property.read_write()
cfg2 = Config(od1) # cfg2 = Config(od1)
cfg.property.read_write() # cfg.property.read_write()
meta = MetaConfig([cfg, cfg2]) # meta = MetaConfig([cfg, cfg2])
meta.property.read_write() # meta.property.read_write()
meta.property.remove('cache') # meta.property.remove('cache')
meta.option('multi').value.set([['val']]) # meta.option('multi').value.set([['val']])
assert meta.option('multi').value.get() == [['val']] # assert meta.option('multi').value.get() == [['val']]
newcfg = meta.config('cfg') # newcfg = meta.config('cfg')
newcfg.option('multi').value.set([['val', None]]) # newcfg.option('multi').value.set([['val', None]])
assert cfg.option('multi').value.get() == [['val', None]] # assert cfg.option('multi').value.get() == [['val', None]]
newcfg = meta.config('cfg') # newcfg = meta.config('cfg')
assert newcfg.option('multi').value.get() == [['val', None]] # assert newcfg.option('multi').value.get() == [['val', None]]
assert meta.option('multi').value.get() == [['val']] # assert meta.option('multi').value.get() == [['val']]
# assert not list_sessions() ## assert not list_sessions()

View file

@ -28,8 +28,6 @@ def test_symlink_option(config_type):
assert cfg.option('c').issymlinkoption() assert cfg.option('c').issymlinkoption()
assert cfg.option('s1.b').type() == 'boolean' assert cfg.option('s1.b').type() == 'boolean'
assert cfg.option('c').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 assert cfg.option('s1.b').value.get() is False
cfg.option("s1.b").value.set(True) cfg.option("s1.b").value.set(True)
cfg.option("s1.b").value.set(False) cfg.option("s1.b").value.set(False)
@ -159,7 +157,7 @@ def test_symlink_getproperties():
od1 = OptionDescription('opt', '', [boolopt, linkopt]) od1 = OptionDescription('opt', '', [boolopt, linkopt])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test', "validator"} assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test'}
# assert boolopt.impl_has_callback() == linkopt.impl_has_callback() == False # assert boolopt.impl_has_callback() == linkopt.impl_has_callback() == False
# assert not list_sessions() # assert not list_sessions()

View file

@ -42,7 +42,6 @@ from .error import ConfigError
from .api import Config, MetaConfig, GroupConfig, MixConfig from .api import Config, MetaConfig, GroupConfig, MixConfig
from .option import __all__ as all_options from .option import __all__ as all_options
from .setting import owners, groups, undefined from .setting import owners, groups, undefined
from .__version__ import __version__
allfuncs = [ allfuncs = [
@ -77,3 +76,4 @@ allfuncs.extend(all_options)
del all_options del all_options
__all__ = tuple(allfuncs) __all__ = tuple(allfuncs)
del allfuncs del allfuncs
__version__ = "4.1.0"

View file

@ -1 +0,0 @@
__version__ = "5.2.0a13"

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2025 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2024 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -52,13 +52,6 @@ from .autolib import Calculation
TIRAMISU_VERSION = 5 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: class TiramisuHelp:
_tmpl_help = " {0}\t{1}" _tmpl_help = " {0}\t{1}"
@ -127,16 +120,13 @@ class CommonTiramisu(TiramisuHelp):
def _set_subconfig(self) -> None: def _set_subconfig(self) -> None:
if not self._subconfig: if not self._subconfig:
try: self._subconfig = self._config_bag.context.get_sub_config(
self._subconfig = self._config_bag.context.get_sub_config( self._config_bag,
self._config_bag, self._path,
self._path, self._index,
self._index, validate_properties=False,
validate_properties=False, allow_dynoption=self._allow_dynoption,
allow_dynoption=self._allow_dynoption, )
)
except AssertionError as err:
raise ConfigError(str(err))
def option_type(typ): def option_type(typ):
@ -149,8 +139,6 @@ def option_type(typ):
@wraps(func) @wraps(func)
def wrapped(*args, **kwargs): def wrapped(*args, **kwargs):
self = args[0] self = args[0]
if isinstance(typ, list) and "allow_dynoption" in typ:
self._allow_dynoption = True
config_bag = self._config_bag config_bag = self._config_bag
if self._config_bag.context.impl_type == "group" and "group" in types: if self._config_bag.context.impl_type == "group" and "group" in types:
options_bag = [ options_bag = [
@ -212,9 +200,7 @@ def option_type(typ):
"please specify index with a follower option ({0}.{1})" "please specify index with a follower option ({0}.{1})"
).format(self.__class__.__name__, func.__name__) ).format(self.__class__.__name__, func.__name__)
raise ConfigError(msg) raise ConfigError(msg)
if "validate_properties" in types or ( if self._validate_properties and "dont_validate_property" not in types:
self._validate_properties and "dont_validate_property" not in types
):
settings = self._config_bag.context.get_settings() settings = self._config_bag.context.get_settings()
parent = self._subconfig.parent parent = self._subconfig.parent
if parent and parent.transitive_properties: if parent and parent.transitive_properties:
@ -265,13 +251,10 @@ class _TiramisuOptionWalk:
validate_properties: bool, validate_properties: bool,
*, *,
uncalculated: bool = False, uncalculated: bool = False,
with_index: bool = True,
): ):
options = [] options = []
for sub_subconfig in subconfig.get_children( for sub_subconfig in subconfig.get_children(
validate_properties, validate_properties, uncalculated=uncalculated
uncalculated=uncalculated,
with_index=with_index,
): ):
options.append( options.append(
TiramisuOption( TiramisuOption(
@ -289,28 +272,12 @@ class _TiramisuOptionOptionDescription:
_validate_properties = False _validate_properties = False
@option_type( @option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
[
"optiondescription",
"option",
"with_or_without_index",
"symlink",
"allow_dynoption",
]
)
def get(self): def get(self):
"""Get Tiramisu option""" """Get Tiramisu option"""
return self._subconfig.option return self._subconfig.option
@option_type( @option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
[
"optiondescription",
"option",
"with_or_without_index",
"symlink",
"allow_dynoption",
]
)
def isoptiondescription(self): def isoptiondescription(self):
"""Test if option is an optiondescription""" """Test if option is an optiondescription"""
return self._subconfig.option.impl_is_optiondescription() return self._subconfig.option.impl_is_optiondescription()
@ -323,14 +290,11 @@ class _TiramisuOptionOptionDescription:
@option_type(["optiondescription", "option", "with_or_without_index", "symlink"]) @option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
def description( def description(
self, self,
with_quote: bool = False,
uncalculated: bool = False, uncalculated: bool = False,
): ):
"""Get option description""" """Get option description"""
if not uncalculated: if not uncalculated:
return self._subconfig.option.impl_get_display_name( return self._subconfig.option.impl_get_display_name(self._subconfig)
self._subconfig, with_quote=with_quote
)
return self._subconfig.option._get_information( return self._subconfig.option._get_information(
self._subconfig, self._subconfig,
"doc", "doc",
@ -359,15 +323,6 @@ class _TiramisuOptionOptionDescription:
return self._subconfig.option.impl_getpath() return self._subconfig.option.impl_getpath()
return self._subconfig.true_path 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"]) @option_type(["optiondescription", "option", "symlink", "with_or_without_index"])
def has_dependency( def has_dependency(
self, self,
@ -377,84 +332,27 @@ class _TiramisuOptionOptionDescription:
return self._subconfig.option.impl_has_dependency(self_is_dep) return self._subconfig.option.impl_has_dependency(self_is_dep)
@option_type(["optiondescription", "option", "symlink", "with_or_without_index"]) @option_type(["optiondescription", "option", "symlink", "with_or_without_index"])
def dependencies( def dependencies(self):
self,
*,
uncalculated: bool = False,
):
"""Get dependencies from this option""" """Get dependencies from this option"""
options = [] options = []
context = self._config_bag.context for option in self._subconfig.option.get_dependencies(self._config_bag.context):
index = self._index options.append(
parent = self._subconfig.parent TiramisuOption(
parent_option = parent.option option().impl_getpath(),
for woption in self._subconfig.option.get_dependencies( None,
self._config_bag.context self._config_bag,
): allow_dynoption=True,
option = woption() )
if not uncalculated and option.issubdyn(): )
for subconfig in context.get_dynamic_from_dyn_option(
self._subconfig, option
):
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
):
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:
options.append(
TiramisuOption(
option.impl_getpath(),
current_index,
self._config_bag,
allow_dynoption=uncalculated,
)
)
return options return options
@option_type(["option", "optiondescription", "symlink", "with_or_without_index"]) @option_type(["option", "optiondescription", "symlink", "with_or_without_index"])
def type(self, only_self=False, translation=False): def type(self):
"""Get de option type""" """Get de option type"""
option = self._subconfig.option option = self._subconfig.option
if option.impl_is_optiondescription(): if option.impl_is_optiondescription():
if translation: return "optiondescription"
type_ = _("optiondescription") return option.get_type()
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"])
def extra(self, extra): def extra(self, extra):
@ -543,27 +441,27 @@ class _TiramisuOptionOptionDescription:
class _TiramisuOptionOption(_TiramisuOptionOptionDescription): class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
"""Manage option""" """Manage option"""
@option_type(["option", "symlink", "with_or_without_index", "allow_dynoption"]) @option_type(["option", "symlink", "with_or_without_index"])
def ismulti(self): def ismulti(self):
"""Test if option could have multi value""" """Test if option could have multi value"""
return self._subconfig.option.impl_is_multi() return self._subconfig.option.impl_is_multi()
@option_type(["option", "symlink", "with_or_without_index", "allow_dynoption"]) @option_type(["option", "symlink", "with_or_without_index"])
def issubmulti(self): def issubmulti(self):
"""Test if option could have submulti value""" """Test if option could have submulti value"""
return self._subconfig.option.impl_is_submulti() return self._subconfig.option.impl_is_submulti()
@option_type(["option", "with_or_without_index", "symlink", "allow_dynoption"]) @option_type(["option", "with_or_without_index", "symlink"])
def isleader(self): def isleader(self):
"""Test if option is a leader""" """Test if option is a leader"""
return self._subconfig.option.impl_is_leader() return self._subconfig.option.impl_is_leader()
@option_type(["option", "with_or_without_index", "symlink", "allow_dynoption"]) @option_type(["option", "with_or_without_index", "symlink"])
def isfollower(self): def isfollower(self):
"""Test if option is a follower""" """Test if option is a follower"""
return self._subconfig.option.impl_is_follower() return self._subconfig.option.impl_is_follower()
@option_type(["option", "symlink", "with_or_without_index", "allow_dynoption"]) @option_type(["option", "symlink", "with_or_without_index"])
def issymlinkoption(self) -> bool: def issymlinkoption(self) -> bool:
"""Test if option is a symlink option""" """Test if option is a symlink option"""
return self._subconfig.option.impl_is_symlinkoption() return self._subconfig.option.impl_is_symlinkoption()
@ -585,11 +483,9 @@ 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]?)$" 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"]) @option_type(["option", "with_or_without_index", "symlink"])
def index(self, index=None): def index(self):
"""Get index of option""" """Get index of option"""
if index is None: return self._subconfig.index
return self._subconfig.index
return TiramisuOption(self._path, index, self._config_bag)
@option_type(["symlink", "optiondescription"]) @option_type(["symlink", "optiondescription"])
def option(self, *args, **kwargs): def option(self, *args, **kwargs):
@ -635,11 +531,9 @@ class TiramisuOptionOwner(CommonTiramisuOption):
_validate_properties = True _validate_properties = True
@option_type(["symlink", "option", "with_index"]) @option_type(["symlink", "option", "with_index"])
def get(self, only_self=False): def get(self):
"""Get owner for a specified option""" """Get owner for a specified option"""
return self._config_bag.context.get_owner( return self._config_bag.context.get_owner(self._subconfig)
self._subconfig, validate_meta=not only_self
)
@option_type(["symlink", "option", "with_index"]) @option_type(["symlink", "option", "with_index"])
def isdefault(self): def isdefault(self):
@ -688,7 +582,6 @@ class TiramisuOptionProperty(CommonTiramisuOption):
self._subconfig, self._subconfig,
uncalculated=uncalculated, uncalculated=uncalculated,
apply_requires=apply_requires, apply_requires=apply_requires,
not_unrestraint=True,
) )
@option_type(["option", "optiondescription", "with_or_without_index"]) @option_type(["option", "optiondescription", "with_or_without_index"])
@ -908,11 +801,14 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
option = self._subconfig.option option = self._subconfig.option
if ( if (
not isinstance(value, Calculation) not isinstance(value, Calculation)
and isinstance(value, list)
and option.impl_is_leader() and option.impl_is_leader()
and len(value) < self._subconfig.parent.get_length_leadership() and len(value) < self._subconfig.parent.get_length_leadership()
): ):
raise LeadershipError(self._subconfig, "leadership-reduce") raise LeadershipError(
_("cannot reduce length of the leader {}" "").format(
option.impl_get_display_name(self._subconfig, with_quote=True)
)
)
values = self._config_bag.context.get_values() values = self._config_bag.context.get_values()
return values.set_value(self._subconfig, value) return values.set_value(self._subconfig, value)
@ -1010,39 +906,34 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
def mandatory(self): def mandatory(self):
"""Return path of options with mandatory property without any value""" """Return path of options with mandatory property without any value"""
subconfig = self._subconfig subconfig = self._subconfig
ori_config_bag = self._subconfig.config_bag if subconfig.option.impl_is_optiondescription():
config_bag = ori_config_bag.copy() ori_config_bag = self._subconfig.config_bag
config_bag.properties -= {"mandatory", "empty", "warnings"} config_bag = ori_config_bag.copy()
config_bag.set_permissive() config_bag.properties -= {"mandatory", "empty", "warnings"}
self._subconfig.config_bag = config_bag config_bag.set_permissive()
try: self._subconfig.config_bag = config_bag
if subconfig.option.impl_is_optiondescription(): options = []
options = [] for subconfig in self._config_bag.context.walk(
for subconfig in config_bag.context.walk( self._subconfig,
self._subconfig, only_mandatory=True,
only_mandatory=True, ):
): options.append(
options.append( TiramisuOption(
TiramisuOption( subconfig.path,
subconfig.path, subconfig.index,
subconfig.index, ori_config_bag,
ori_config_bag, subconfig=subconfig,
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 self._subconfig.config_bag = ori_config_bag
return False return options
except Exception as err: try:
self._subconfig.config_bag = ori_config_bag self._config_bag.context.walk_valid_value(
raise err from err self._subconfig, only_mandatory=True
)
except PropertiesOptionError as err:
return err.proptype == ["mandatory"] or err.proptype == ["empty"]
return False
def _registers( def _registers(
@ -1148,13 +1039,12 @@ class TiramisuOption(
self._set_subconfig() self._set_subconfig()
return self._subconfig.option.impl_get_group_type() return self._subconfig.option.impl_get_group_type()
@option_type(["optiondescription", "validate_properties"]) @option_type("optiondescription")
def list( def list(
self, self,
*, *,
validate_properties: bool = True, validate_properties: bool = True,
uncalculated: bool = False, uncalculated: bool = False,
with_index: bool = True,
): ):
"""List options inside an option description (by default list only option)""" """List options inside an option description (by default list only option)"""
self._set_subconfig() self._set_subconfig()
@ -1162,7 +1052,6 @@ class TiramisuOption(
self._subconfig, self._subconfig,
validate_properties, validate_properties,
uncalculated=uncalculated, uncalculated=uncalculated,
with_index=with_index,
) )
def _load_dict( def _load_dict(
@ -1283,8 +1172,6 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet):
self, self,
path: str, path: str,
value: Any, value: Any,
*,
index: Optional[int] = None,
only_config=undefined, only_config=undefined,
force_default=undefined, force_default=undefined,
force_default_if_same=undefined, force_default_if_same=undefined,
@ -1300,22 +1187,14 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet):
kwargs["force_default_if_same"] = force_default_if_same kwargs["force_default_if_same"] = force_default_if_same
if force_dont_change_value is not undefined: if force_dont_change_value is not undefined:
kwargs["force_dont_change_value"] = force_dont_change_value kwargs["force_dont_change_value"] = force_dont_change_value
context = self._config_bag.context option_bag = OptionBag(
if isinstance(context, KernelGroupConfig): None,
subconfig = Fake_SubConfig( None,
self._config_bag, self._config_bag,
path, path=path,
index, )
)
else:
subconfig = context.get_sub_config(
self._config_bag,
path,
index,
validate_properties=False,
)
return self._config_bag.context.set_value( return self._config_bag.context.set_value(
subconfig, option_bag,
value, value,
**kwargs, **kwargs,
) )
@ -1648,7 +1527,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
def get(self): def get(self):
"""Get Tiramisu option""" """Get Tiramisu option"""
return self._config_bag.context.get_description() return None
def isleadership(self): def isleadership(self):
"""Test if option is a leader or a follower""" """Test if option is a leader or a follower"""
@ -1703,7 +1582,6 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
*, *,
validate_properties: bool = True, validate_properties: bool = True,
uncalculated: bool = False, uncalculated: bool = False,
with_index: bool = True,
): ):
"""List options (by default list only option)""" """List options (by default list only option)"""
root = self._config_bag.context.get_root(self._config_bag) root = self._config_bag.context.get_root(self._config_bag)
@ -1711,7 +1589,6 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
root, root,
validate_properties, validate_properties,
uncalculated=uncalculated, uncalculated=uncalculated,
with_index=with_index,
) )
def _load_dict(self, clearable="all", remotable="minimum"): def _load_dict(self, clearable="all", remotable="minimum"):
@ -1931,8 +1808,6 @@ class TiramisuAPI(TiramisuHelp):
def __getattr__(self, subfunc: str) -> Any: def __getattr__(self, subfunc: str) -> Any:
if subfunc in ["forcepermissive", "unrestraint", "nowarnings"]: if subfunc in ["forcepermissive", "unrestraint", "nowarnings"]:
if subfunc == "unrestraint" and self._config_bag.is_unrestraint:
return self
if self._orig_config_bags: if self._orig_config_bags:
msg = _( msg = _(
"do not use unrestraint, nowarnings or forcepermissive together" "do not use unrestraint, nowarnings or forcepermissive together"
@ -2020,7 +1895,7 @@ class Config(TiramisuAPI, TiramisuContextOption):
return f"<Config path=None>" return f"<Config path=None>"
class MetaConfig(TiramisuAPI, TiramisuContextOption): class MetaConfig(TiramisuAPI):
"""MetaConfig object that enables us to handle the sub configuration's options """MetaConfig object that enables us to handle the sub configuration's options
with common root optiondescription with common root optiondescription
""" """
@ -2088,7 +1963,7 @@ class MixConfig(TiramisuAPI):
display_name=display_name, display_name=display_name,
) )
settings = config.get_settings() settings = config.get_settings()
properties = settings.get_context_properties() properties = settings.get_context_properties(config.properties_cache)
permissives = settings.get_context_permissives() permissives = settings.get_context_permissives()
config_bag = ConfigBag( config_bag = ConfigBag(
config, config,

View file

@ -22,15 +22,7 @@ from typing import Any, Optional, Union, Callable, Dict, List
from itertools import chain from itertools import chain
import weakref import weakref
from .error import ( from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning
PropertiesOptionError,
ConfigError,
LeadershipError,
ValueWarning,
CancelParam,
display_list,
errors,
)
from .i18n import _ from .i18n import _
from .setting import undefined, ConfigBag from .setting import undefined, ConfigBag
from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR
@ -57,7 +49,6 @@ def get_calculated_value(
has_calculation = True has_calculation = True
elif isinstance(value, list): elif isinstance(value, list):
# if value is a list, do subcalculation # if value is a list, do subcalculation
value = value.copy()
for idx, val in enumerate(value): for idx, val in enumerate(value):
value[idx], _has_calculation = get_calculated_value( value[idx], _has_calculation = get_calculated_value(
subconfig, subconfig,
@ -157,15 +148,11 @@ class ParamDynOption(ParamOption):
) )
if not isinstance(identifiers, list): if not isinstance(identifiers, list):
raise Exception( raise Exception(
_("identifiers in ParamDynOption must be a list, not {0}").format( f"identifiers in ParamDynOption must be a list, not {identifiers}"
identifiers
)
) )
if not isinstance(optional, bool): if not isinstance(optional, bool):
raise Exception( raise Exception(
_("optional in ParamDynOption must be a boolean, not {0}").format( f"optional in ParamDynOption must be a boolean, not {optional}"
optional
)
) )
self.identifiers = identifiers self.identifiers = identifiers
self.optional = optional self.optional = optional
@ -216,11 +203,9 @@ class ParamInformation(Param):
def set_option(self, option: "Option" = None) -> None: def set_option(self, option: "Option" = None) -> None:
if not hasattr(self, "self_option"): if not hasattr(self, "self_option"):
raise ConfigError( raise ConfigError("cannot add option in information after creating config")
_("cannot add option in information after creating config")
)
if self.option: if self.option:
raise ConfigError(_("cannot redefine option in information")) raise ConfigError("cannot redefine option in information")
if not option.impl_is_optiondescription(): if not option.impl_is_optiondescription():
if option.impl_is_symlinkoption(): if option.impl_is_symlinkoption():
raise ValueError( raise ValueError(
@ -460,7 +445,14 @@ def manager_callback(
or param.raisepropertyerror or param.raisepropertyerror
): ):
raise err from err raise err from err
raise ConfigError(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
except ValueError as err: except ValueError as err:
display_name = subconfig.option.impl_get_display_name( display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True subconfig, with_quote=True
@ -472,15 +464,20 @@ def manager_callback(
) from err ) from err
except AttributeError as err: except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional: if isinstance(param, ParamDynOption) and param.optional:
# cannot access, simulate a propertyerror # cannot acces, simulate a propertyerror
raise PropertiesOptionError( raise PropertiesOptionError(
subconfig, subconfig,
["configerror"], ["configerror"],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
errors.raise_carry_out_calculation_error( display_name = subconfig.option.impl_get_display_name(
subconfig, _("unable to get value for calculating {0}, {1}"), err subconfig, with_quote=True
) )
raise ConfigError(
_("unable to get value for calculating {0}, {1}").format(
display_name, err
)
) from err
return value return value
def get_option_bag( def get_option_bag(
@ -507,18 +504,17 @@ def manager_callback(
index_, index_,
validate_properties=not self_calc, validate_properties=not self_calc,
properties=properties, properties=properties,
valid_conflict=False,
) )
except PropertiesOptionError as err: except PropertiesOptionError as err:
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation # raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
if param.notraisepropertyerror or param.raisepropertyerror: if param.notraisepropertyerror or param.raisepropertyerror:
raise err from err raise err from err
errors.raise_carry_out_calculation_error( display_name = option.impl_get_display_name(subconfig, with_quote=True)
subconfig, raise ConfigError(
_("unable to carry out a calculation for {0}, {1}"), _("unable to carry out a calculation for {}, {}").format(
err, display_name, err
option=option, )
) ) from err
except ValueError as err: except ValueError as err:
display_name = option.impl_get_display_name(subconfig, with_quote=True) display_name = option.impl_get_display_name(subconfig, with_quote=True)
raise ValueError( raise ValueError(
@ -528,18 +524,18 @@ def manager_callback(
) from err ) from err
except AttributeError as err: except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional: if isinstance(param, ParamDynOption) and param.optional:
# cannot access, simulate a propertyerror # cannot acces, simulate a propertyerror
raise PropertiesOptionError( raise PropertiesOptionError(
param, param,
["configerror"], ["configerror"],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
errors.raise_carry_out_calculation_error( display_name = option.impl_get_display_name(subconfig, with_quote=True)
subconfig, raise ConfigError(
_("unable to get value for calculating {0}, {1}"), _("unable to get value for calculating {0}, {1}").format(
err, display_name, err
option=option, )
) ) from err
return subsubconfig return subsubconfig
if isinstance(param, ParamValue): if isinstance(param, ParamValue):
@ -556,15 +552,14 @@ def manager_callback(
true_path=subconfig.path, true_path=subconfig.path,
) )
if isinstance(isubconfig, list): if isinstance(isubconfig, list):
display_name = option.impl_get_display_name(
subconfig, with_quote=True
)
search_name = search_option.impl_get_display_name( search_name = search_option.impl_get_display_name(
None, with_quote=True None, with_quote=True
) )
errors.raise_carry_out_calculation_error( raise ConfigError(
subconfig, f"cannot find information for {display_name}, {search_name} is a dynamic option"
_("cannot find information for {0}, {1} is a dynamic option"),
None,
option=option,
extra_keys=[search_name],
) )
else: else:
isubconfig = get_option_bag( isubconfig = get_option_bag(
@ -584,12 +579,12 @@ def manager_callback(
param.default_value, param.default_value,
) )
except ValueError as err: except ValueError as err:
errors.raise_carry_out_calculation_error( display_name = option.impl_get_display_name(subconfig, with_quote=True)
subconfig, raise ConfigError(
_("unable to get value for calculating {0}, {1}"), _("unable to get value for calculating {0}, {1}").format(
err, display_name, err
option=option, )
) ) from err
if isinstance(param, ParamIndex): if isinstance(param, ParamIndex):
return index return index
@ -599,17 +594,14 @@ def manager_callback(
not option.impl_is_optiondescription() not option.impl_is_optiondescription()
or not option.impl_is_dynoptiondescription() or not option.impl_is_dynoptiondescription()
): ):
errors.raise_carry_out_calculation_error( display_name = subconfig.option.impl_get_display_name(
subconfig, subconfig, with_quote=True
)
raise ConfigError(
_( _(
"option {0} is not a dynoptiondescription or in a dynoptiondescription" "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] return subconfig.identifiers[param.identifier_index]
if isinstance(param, ParamSelfOption): if isinstance(param, ParamSelfOption):
@ -681,37 +673,7 @@ def manager_callback(
parent, parent,
) )
except AttributeError as err: except AttributeError as err:
if parent.path: raise ConfigError(err) from err
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( new_parents.append(
parent.get_child( parent.get_child(
doption, doption,
@ -815,11 +777,8 @@ def carry_out_calculation(
and option.impl_is_follower() and option.impl_is_follower()
and index is None and index is None
): ):
errors.raise_carry_out_calculation_error( raise ConfigError(
subconfig, f"the follower {option.impl_get_display_name(subconfig, with_quote=True)} must have index in carry_out_calculation!"
_("the follower {0} must have index in carry_out_calculation!"),
None,
option=option,
) )
def fake_items(iterator): def fake_items(iterator):
@ -869,12 +828,6 @@ def carry_out_calculation(
args.append(err) args.append(err)
else: else:
kwargs[key] = err 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( ret = calculate(
subconfig, subconfig,
callback, callback,
@ -890,14 +843,33 @@ def carry_out_calculation(
and option.impl_is_follower() and option.impl_is_follower()
and not option.impl_is_submulti() and not option.impl_is_submulti()
): ):
raise LeadershipError( if args or kwargs:
subconfig, raise LeadershipError(
"leadership-follower-callback-list", _(
callback=callback.__name__, 'the "{}" function with positional arguments "{}" '
args=args, 'and keyword arguments "{}" must not return '
kwargs=kwargs, 'a list ("{}") for the follower option {}'
ret=ret, ""
) ).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),
)
)
return ret return ret
@ -921,7 +893,7 @@ def calculate(
except (ValueError, ValueWarning) as err: except (ValueError, ValueWarning) as err:
if allow_value_error: if allow_value_error:
if force_value_warning: if force_value_warning:
raise ValueWarning(msg=str(err)) raise ValueWarning(str(err))
raise err from err raise err from err
error = err error = err
except ConfigError as err: except ConfigError as err:
@ -930,17 +902,19 @@ def calculate(
error = err error = err
if args or kwargs: if args or kwargs:
msg = _( msg = _(
'unexpected error "{1}" in function "{2}" with arguments "{3}" and "{4}" ' 'unexpected error "{0}" in function "{1}" with arguments "{3}" and "{4}" '
"for option {0}" "for option {2}"
) ).format(
extra_keys = [ str(error),
callback.__name__, callback.__name__,
subconfig.option.impl_get_display_name(subconfig, with_quote=True),
args, args,
kwargs, kwargs,
] )
else: else:
msg = _('unexpected error "{1}" in function "{2}" for option {0}') msg = _('unexpected error "{0}" in function "{1}" for option {2}' "").format(
extra_keys = [callback.__name__] str(error),
errors.raise_carry_out_calculation_error( callback.__name__,
subconfig, msg, error, extra_keys=extra_keys subconfig.option.impl_get_display_name(subconfig, with_quote=True),
) )
raise ConfigError(msg) from error

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2012-2025 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2024 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -41,7 +41,7 @@ def get_common_path(path1, path2):
return common_path return common_path
if common_path.endswith("."): if common_path.endswith("."):
return common_path[:-1] return common_path[:-1]
elif "." in common_path: if "." in common_path:
return common_path.rsplit(".", 1)[0] return common_path.rsplit(".", 1)[0]
return None return None
@ -91,12 +91,12 @@ class CCache:
if option.issubdyn(): if option.issubdyn():
# it's an option in dynoptiondescription, remove cache for all generated option # it's an option in dynoptiondescription, remove cache for all generated option
self.reset_cache_dyn_option( self.reset_cache_dyn_option(
subconfig, config_bag,
option, option,
resetted_opts, resetted_opts,
) )
elif option.impl_is_dynoptiondescription(): elif option.impl_is_dynoptiondescription():
self.reset_cache_dyn_optiondescription( self._reset_cache_dyn_optiondescription(
option, option,
config_bag, config_bag,
resetted_opts, resetted_opts,
@ -120,7 +120,13 @@ class CCache:
resetted_opts, resetted_opts,
) )
def get_dynamic_from_dyn_optiondescription(self, config_bag, option): def _reset_cache_dyn_optiondescription(
self,
option,
config_bag,
resetted_opts,
):
# reset cache for all chidren
path = option.impl_getpath() path = option.impl_getpath()
if "." in path: if "." in path:
parent_path = path.rsplit(".", 1)[0] parent_path = path.rsplit(".", 1)[0]
@ -133,21 +139,9 @@ class CCache:
) )
else: else:
parent_subconfig = self.get_root(config_bag) parent_subconfig = self.get_root(config_bag)
return parent_subconfig.dyn_to_subconfig( for subconfig in parent_subconfig.dyn_to_subconfig(
option, option,
False, False,
)
def reset_cache_dyn_optiondescription(
self,
option,
config_bag,
resetted_opts,
):
# reset cache for all chidren
for subconfig in self.get_dynamic_from_dyn_optiondescription(
config_bag,
option,
): ):
self.reset_one_option_cache( self.reset_one_option_cache(
subconfig, subconfig,
@ -163,20 +157,15 @@ class CCache:
resetted_opts, resetted_opts,
) )
def get_dynamic_from_dyn_option(self, subconfig, option): def reset_cache_dyn_option(
config_bag = subconfig.config_bag self,
sub_paths = option.impl_getpath() config_bag,
current_paths = subconfig.path.split(".") option,
current_paths_max_index = len(current_paths) - 1 resetted_opts,
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)] currents = [self.get_root(config_bag)]
for idx, sub_path in enumerate(sub_paths.split(".")): sub_paths = option.impl_getpath()
for sub_path in sub_paths.split("."):
new_currents = [] new_currents = []
for current in currents: for current in currents:
sub_option = current.option.get_child( sub_option = current.option.get_child(
@ -186,20 +175,15 @@ class CCache:
allow_dynoption=True, allow_dynoption=True,
) )
if sub_option.impl_is_dynoptiondescription(): if sub_option.impl_is_dynoptiondescription():
if ( new_currents.extend(
idx <= current_paths_max_index list(
and sub_option == current_subconfigs[idx].option current.dyn_to_subconfig(
): sub_option,
new_currents.append(current_subconfigs[idx]) False,
else:
new_currents.extend(
list(
current.dyn_to_subconfig(
sub_option,
False,
)
) )
) )
)
else: else:
new_currents.append( new_currents.append(
current.get_child( current.get_child(
@ -210,15 +194,7 @@ class CCache:
), ),
) )
currents = new_currents currents = new_currents
return currents for dyn_option_subconfig in currents:
def reset_cache_dyn_option(
self,
subconfig,
option,
resetted_opts,
):
for dyn_option_subconfig in self.get_dynamic_from_dyn_option(subconfig, option):
self.reset_one_option_cache( self.reset_one_option_cache(
dyn_option_subconfig, dyn_option_subconfig,
resetted_opts, resetted_opts,
@ -251,8 +227,7 @@ class SubConfig:
identifiers: Optional[list[str]], identifiers: Optional[list[str]],
*, *,
true_path: Optional[str] = None, true_path: Optional[str] = None,
# for python 3.9 properties: Union[list[str], undefined] = undefined, properties: Union[list[str], undefined] = undefined,
properties=undefined,
validate_properties: bool = True, validate_properties: bool = True,
) -> None: ) -> None:
self.index = index self.index = index
@ -372,31 +347,16 @@ class SubConfig:
validate_properties, validate_properties,
*, *,
uncalculated: bool = False, uncalculated: bool = False,
with_index: bool = True,
): ):
if self.option.impl_is_leadership() and not uncalculated and with_index: if self.option.impl_is_leadership() and not uncalculated:
yield from self.get_leadership_children(validate_properties) yield from self.get_leadership_children(validate_properties)
else: else:
children_name = []
for child in self.option.get_children(): for child in self.option.get_children():
if child.impl_is_dynoptiondescription() and not uncalculated: if child.impl_is_dynoptiondescription() and not uncalculated:
for dyn_child in self.dyn_to_subconfig( yield from self.dyn_to_subconfig(
child, child,
validate_properties, 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: else:
try: try:
yield self.get_child( yield self.get_child(
@ -407,18 +367,6 @@ class SubConfig:
except PropertiesOptionError as err: except PropertiesOptionError as err:
if err.proptype in (["mandatory"], ["empty"]): if err.proptype in (["mandatory"], ["empty"]):
raise err 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( def get_child(
self, self,
@ -468,7 +416,13 @@ class SubConfig:
length = self.get_length_leadership() length = self.get_length_leadership()
if index >= length: if index >= length:
raise LeadershipError( raise LeadershipError(
subsubconfig, "leadership-greater", index=index, length=length _(
'index "{0}" is greater than the leadership length "{1}" for option {2}'
).format(
index,
length,
option.impl_get_display_name(subsubconfig, with_quote=True),
)
) )
return subsubconfig return subsubconfig
@ -587,20 +541,6 @@ class SubConfig:
return subconfigs return subconfigs
return subconfigs[0] 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): class _Config(CCache):
"""Sub configuration management entry. """Sub configuration management entry.
@ -653,8 +593,80 @@ class _Config(CCache):
"""get cache for values""" """get cache for values"""
return self._impl_values_cache # pylint: disable=no-member return self._impl_values_cache # pylint: disable=no-member
# # ============================================================================= # =============================================================================
# # WALK # 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"))
def walk_valid_value( def walk_valid_value(
self, self,
subconfig, subconfig,
@ -699,7 +711,6 @@ class _Config(CCache):
properties=undefined, properties=undefined,
true_path: Optional[str] = None, true_path: Optional[str] = None,
allow_dynoption: bool = False, allow_dynoption: bool = False,
valid_conflict: bool = True,
): ):
subconfig = self.get_root(config_bag) subconfig = self.get_root(config_bag)
if path is None: if path is None:
@ -732,32 +743,6 @@ class _Config(CCache):
identifier, option = option identifier, option = option
else: else:
identifier = None 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( subconfig = subconfig.get_child(
option, option,
index_, index_,
@ -836,15 +821,6 @@ class _Config(CCache):
# ============================================================================= # =============================================================================
# Manage value # 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( def get_value(
self, self,
subconfig, subconfig,
@ -892,10 +868,9 @@ class _Config(CCache):
subconfig, with_quote=True subconfig, with_quote=True
) )
raise LeadershipError( raise LeadershipError(
subconfig, _(
"leadership-follower-greater", "the follower option {0} has greater length ({1}) than the leader length ({2})"
index=follower_len, ).format(option_name, follower_len, length)
length=length,
) )
self.get_settings().validate_mandatory( self.get_settings().validate_mandatory(
subconfig, subconfig,
@ -964,8 +939,6 @@ class _Config(CCache):
def get_owner( def get_owner(
self, self,
subconfig: "SubConfig", subconfig: "SubConfig",
*,
validate_meta=True,
): ):
"""get owner""" """get owner"""
subconfigs = self._get( subconfigs = self._get(
@ -976,14 +949,13 @@ class _Config(CCache):
for sc in subconfigs: for sc in subconfigs:
owner = self.get_owner( owner = self.get_owner(
sc, sc,
validate_meta=validate_meta,
) )
if owner != owners.default: if owner != owners.default:
break break
else: else:
owner = owners.default owner = owners.default
else: else:
owner = self.get_values().getowner(subconfigs, validate_meta=validate_meta) owner = self.get_values().getowner(subconfigs)
return owner return owner
@ -1264,10 +1236,6 @@ class KernelGroupConfig(_CommonConfig):
# pylint: disable=super-init-not-called # pylint: disable=super-init-not-called
names = [] names = []
for child in children: 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 name_ = child._impl_name
names.append(name_) names.append(name_)
if len(names) != len(set(names)): if len(names) != len(set(names)):
@ -1295,58 +1263,61 @@ class KernelGroupConfig(_CommonConfig):
def reset_cache( def reset_cache(
self, self,
subconfig, option_bag,
resetted_opts=None, resetted_opts=None,
): ):
if resetted_opts is None: if resetted_opts is None:
resetted_opts = [] resetted_opts = []
if isinstance(self, KernelMixConfig): if isinstance(self, KernelMixConfig):
super().reset_cache( super().reset_cache(
subconfig, option_bag,
resetted_opts=copy(resetted_opts), resetted_opts=copy(resetted_opts),
) )
for child in self._impl_children: for child in self._impl_children:
if subconfig is not None: if option_bag is not None:
parent_subconfig = subconfig.change_context(child) coption_bag = option_bag.copy()
cconfig_bag = coption_bag.config_bag.copy()
cconfig_bag.context = child
coption_bag.config_bag = cconfig_bag
else: else:
parent_subconfig = None coption_bag = None
child.reset_cache( child.reset_cache(
parent_subconfig, coption_bag,
resetted_opts=copy(resetted_opts), resetted_opts=copy(resetted_opts),
) )
def set_value( def set_value(
self, self,
subconfig, option_bag,
value, value,
only_config=False, only_config=False,
): ):
"""Setattr not in current KernelGroupConfig, but in each children""" """Setattr not in current KernelGroupConfig, but in each children"""
ret = [] ret = []
for child in self._impl_children: for child in self._impl_children:
cconfig_bag = subconfig.config_bag.copy() cconfig_bag = option_bag.config_bag.copy()
cconfig_bag.context = child cconfig_bag.context = child
if isinstance(child, KernelGroupConfig): if isinstance(child, KernelGroupConfig):
ret.extend( ret.extend(
child.set_value( child.set_value(
subconfig, option_bag,
value, value,
only_config=only_config, only_config=only_config,
) )
) )
else: else:
settings = child.get_settings() settings = child.get_settings()
properties = settings.get_context_properties() properties = settings.get_context_properties(child.properties_cache)
permissives = settings.get_context_permissives() permissives = settings.get_context_permissives()
cconfig_bag.properties = properties cconfig_bag.properties = properties
cconfig_bag.permissives = permissives cconfig_bag.permissives = permissives
try: try:
# GROUP # GROUP
coption_bag = child.get_sub_config( coption_bag = child.get_sub_option_bag(
cconfig_bag, cconfig_bag,
subconfig.path, option_bag.path,
subconfig.index, option_bag.index,
validate_properties=False, False,
) )
child.set_value( child.set_value(
coption_bag, coption_bag,
@ -1356,7 +1327,7 @@ class KernelGroupConfig(_CommonConfig):
# pylint: disable=protected-access # pylint: disable=protected-access
ret.append( ret.append(
PropertiesOptionError( PropertiesOptionError(
err._subconfig, err._option_bag,
err.proptype, err.proptype,
err._settings, err._settings,
err._opt_type, err._opt_type,
@ -1419,7 +1390,7 @@ class KernelGroupConfig(_CommonConfig):
cconfig_bag.context = child cconfig_bag.context = child
if cconfig_bag.properties is None: if cconfig_bag.properties is None:
settings = child.get_settings() settings = child.get_settings()
properties = settings.get_context_properties() properties = settings.get_context_properties(child.properties_cache)
permissives = settings.get_context_permissives() permissives = settings.get_context_permissives()
cconfig_bag.properties = properties cconfig_bag.properties = properties
cconfig_bag.permissives = permissives cconfig_bag.permissives = permissives
@ -1453,7 +1424,6 @@ class KernelGroupConfig(_CommonConfig):
def reset( def reset(
self, self,
path: str, path: str,
only_children: bool,
config_bag: ConfigBag, config_bag: ConfigBag,
) -> None: ) -> None:
"""reset value for specified path""" """reset value for specified path"""
@ -1462,19 +1432,19 @@ class KernelGroupConfig(_CommonConfig):
cconfig_bag = config_bag.copy() cconfig_bag = config_bag.copy()
cconfig_bag.context = child cconfig_bag.context = child
settings = child.get_settings() settings = child.get_settings()
properties = settings.get_context_properties() properties = settings.get_context_properties(child.properties_cache)
permissives = settings.get_context_permissives() permissives = settings.get_context_permissives()
cconfig_bag.properties = properties cconfig_bag.properties = properties
cconfig_bag.permissives = permissives cconfig_bag.permissives = permissives
cconfig_bag.remove_validation() cconfig_bag.remove_validation()
# GROUP # GROUP
subconfig = child.get_sub_config( option_bag = child.get_sub_option_bag(
cconfig_bag, cconfig_bag,
path, path,
None, None,
validate_properties=False, False,
) )[-1]
child.get_values().reset(subconfig) child.get_values().reset(option_bag)
def getconfig( def getconfig(
self, self,
@ -1529,7 +1499,7 @@ class KernelMixConfig(KernelGroupConfig):
def set_value( def set_value(
self, self,
subconfig, option_bag,
value, value,
only_config=False, only_config=False,
force_default=False, force_default=False,
@ -1560,10 +1530,10 @@ class KernelMixConfig(KernelGroupConfig):
) )
) )
for child in self._impl_children: for child in self._impl_children:
cconfig_bag = subconfig.config_bag.copy() cconfig_bag = option_bag.config_bag.copy()
cconfig_bag.context = child cconfig_bag.context = child
settings = child.get_settings() settings = child.get_settings()
properties = settings.get_context_properties() properties = settings.get_context_properties(child.properties_cache)
cconfig_bag.properties = properties cconfig_bag.properties = properties
cconfig_bag.permissives = settings.get_context_permissives() cconfig_bag.permissives = settings.get_context_permissives()
try: try:
@ -1575,16 +1545,14 @@ class KernelMixConfig(KernelGroupConfig):
not force_default and not force_default_if_same not force_default and not force_default_if_same
) )
# MIX # MIX
moption_bag = obj.get_sub_config( moption_bag = obj.get_sub_option_bag(
cconfig_bag, cconfig_bag,
subconfig.path, option_bag.path,
subconfig.index, option_bag.index,
validate_properties=validate_properties, validate_properties,
) )[-1]
if force_default_if_same: if force_default_if_same:
if not child.get_values().hasvalue( if not child.get_values().hasvalue(option_bag.path):
subconfig.path, index=subconfig.index
):
child_value = undefined child_value = undefined
else: else:
child_value = child.get_value(moption_bag) child_value = child.get_value(moption_bag)
@ -1604,7 +1572,7 @@ class KernelMixConfig(KernelGroupConfig):
# pylint: disable=protected-access # pylint: disable=protected-access
ret.append( ret.append(
PropertiesOptionError( PropertiesOptionError(
err._subconfig, err._option_bag,
err.proptype, err.proptype,
err._settings, err._settings,
err._opt_type, err._opt_type,
@ -1617,12 +1585,12 @@ class KernelMixConfig(KernelGroupConfig):
try: try:
# MIX # MIX
moption_bag = self.get_sub_config( moption_bag = self.get_sub_option_bag(
subconfig.config_bag, option_bag.config_bag,
subconfig.path, option_bag.path,
subconfig.index, option_bag.index,
validate_properties=not only_config, not only_config,
) )[-1]
if only_config: if only_config:
ret = super().set_value( ret = super().set_value(
moption_bag, moption_bag,
@ -1651,37 +1619,37 @@ class KernelMixConfig(KernelGroupConfig):
rconfig_bag.remove_validation() rconfig_bag.remove_validation()
if self.impl_type == "meta": if self.impl_type == "meta":
# MIX # MIX
subconfig = self.get_sub_config( option_bag = self.get_sub_option_bag(
config_bag, config_bag,
path, path,
None, None,
validate_properties=True, True,
) )[-1]
elif not only_children: elif not only_children:
try: try:
# MIX # MIX
subconfig = self.get_sub_config( option_bag = self.get_sub_option_bag(
rconfig_bag, rconfig_bag,
path, path,
None, None,
validate_properties=True, True,
) )[-1]
except AttributeError: except AttributeError:
only_children = True only_children = True
for child in self._impl_children: for child in self._impl_children:
rconfig_bag.context = child rconfig_bag.context = child
try: try:
if self.impl_type == "meta": if self.impl_type == "meta":
moption_bag = subconfig moption_bag = option_bag
moption_bag.config_bag = rconfig_bag moption_bag.config_bag = rconfig_bag
else: else:
# MIX # MIX
moption_bag = child.get_sub_config( moption_bag = child.get_sub_option_bag(
rconfig_bag, rconfig_bag,
path, path,
None, None,
validate_properties=True, True,
) )[-1]
child.get_values().reset(moption_bag) child.get_values().reset(moption_bag)
except AttributeError: except AttributeError:
pass pass
@ -1692,8 +1660,8 @@ class KernelMixConfig(KernelGroupConfig):
rconfig_bag, rconfig_bag,
) )
if not only_children: if not only_children:
subconfig.config_bag = config_bag option_bag.config_bag = config_bag
self.get_values().reset(subconfig) self.get_values().reset(option_bag)
def new_config( def new_config(
self, self,

View file

@ -18,23 +18,6 @@
import weakref import weakref
from .i18n import _ 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( def display_list(
lst, lst,
@ -86,8 +69,6 @@ class PropertiesOptionError(AttributeError):
orig_opt=None, orig_opt=None,
help_properties=None, help_properties=None,
): ):
if orig_opt:
raise Exception("a la")
if opt_type: if opt_type:
self._opt_type = opt_type self._opt_type = opt_type
self._name = name self._name = name
@ -106,23 +87,10 @@ class PropertiesOptionError(AttributeError):
self.help_properties = help_properties self.help_properties = help_properties
self._settings = settings self._settings = settings
self.msg = None 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) super().__init__(None)
def display_properties(self, force_property=False, add_quote=True): def set_orig_opt(self, opt):
if force_property: self._orig_opt = opt
properties = self.proptype
else:
properties = self.help_properties
return display_list(list(properties), add_quote=add_quote)
def __str__(self): def __str__(self):
# this part is a bit slow, so only execute when display # this part is a bit slow, so only execute when display
@ -130,81 +98,42 @@ class PropertiesOptionError(AttributeError):
return self.msg return self.msg
if self._settings is None: if self._settings is None:
return "error" return "error"
arguments = [self._opt_type] if self.help_properties:
if self._orig_opt: properties = list(self.help_properties)
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: else:
if index is not None: properties = list(self.proptype)
if self._orig_opt: only_one = len(properties) == 1
msg = _( properties_msg = display_list(properties, add_quote=True)
'cannot access to {0} {1} at index "{2}" because {3} has {4} {5}' if only_one:
) prop_msg = _("property")
else: else:
msg = _( prop_msg = _("properties")
'cannot access to {0} {1} at index "{2}" because has {3} {4}' if properties == ["frozen"]:
) if self._orig_opt:
msg = _('cannot modify the {0} {1} because "{2}" has {3} {4}')
else: else:
if self._orig_opt: msg = _("cannot modify the {0} {1} because has {2} {3}")
msg = _("cannot access to {0} {1} because {2} has {3} {4}") else:
else: if self._orig_opt:
msg = _("cannot access to {0} {1} because has {2} {3}") msg = _('cannot access to {0} {1} because "{2}" has {3} {4}')
only_one = len(self.help_properties) == 1
if only_one:
arguments.append(_("property"))
else: else:
arguments.append(_("properties")) msg = _("cannot access to {0} {1} because has {2} {3}")
arguments.append(self.display_properties()) if self._orig_opt:
self.msg = msg.format(*arguments) # 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,
)
else:
self.msg = msg.format(self._opt_type, self._name, prop_msg, properties_msg)
del self._opt_type, self._name del self._opt_type, self._name
del self._settings, self._orig_opt del self._settings, self._orig_opt
return self.msg 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 # Exceptions for a Config
class ConfigError(Exception): class ConfigError(Exception):
@ -229,74 +158,8 @@ class ConflictError(Exception):
# ____________________________________________________________ # ____________________________________________________________
# miscellaneous exceptions # miscellaneous exceptions
class LeadershipError(Exception): class LeadershipError(Exception):
def __init__( "problem with a leadership's value length"
self, pass
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): class ConstError(TypeError):
@ -309,9 +172,7 @@ class _CommonError:
self.val = val self.val = val
self.display_type = display_type self.display_type = display_type
self.opt = weakref.ref(opt) self.opt = weakref.ref(opt)
self.name = opt.impl_get_display_name(subconfig, with_quote=True) self.name = opt.impl_get_display_name(subconfig)
if subconfig:
self.path = subconfig.path
self.err_msg = err_msg self.err_msg = err_msg
self.index = index self.index = index
super().__init__(self.err_msg) super().__init__(self.err_msg)
@ -320,9 +181,7 @@ class _CommonError:
try: try:
msg = self.prefix msg = self.prefix
except AttributeError: except AttributeError:
self.prefix = self.tmpl.format( self.prefix = self.tmpl.format(self.val, _(self.display_type), self.name)
self.val, _(self.display_type), self.name, self.index
)
msg = self.prefix msg = self.prefix
if self.err_msg: if self.err_msg:
if msg: if msg:
@ -337,20 +196,13 @@ class _CommonError:
class ValueWarning(_CommonError, UserWarning): class ValueWarning(_CommonError, UserWarning):
tmpl = None tmpl = None
def __init__(self, **kwargs): def __init__(self, *args, **kwargs):
if ValueWarning.tmpl is None: if ValueWarning.tmpl is None:
if kwargs.get("index") is None: ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"')
ValueWarning.tmpl = _( if len(args) == 1 and not kwargs:
'attention, "{0}" could be an invalid {1} for {2}' self.msg = args[0]
)
else:
ValueWarning.tmpl = _(
'attention, "{0}" could be an invalid {1} for {2} at index "{3}"'
)
if list(kwargs) == ["msg"]:
self.msg = kwargs["msg"]
else: else:
super().__init__(**kwargs) super().__init__(*args, **kwargs)
self.msg = None self.msg = None
def __str__(self): def __str__(self):
@ -362,20 +214,10 @@ class ValueWarning(_CommonError, UserWarning):
class ValueOptionError(_CommonError, ValueError): class ValueOptionError(_CommonError, ValueError):
tmpl = None tmpl = None
def __init__(self, **kwargs): def __init__(self, *args, **kwargs):
if ValueOptionError.tmpl is None: if ValueOptionError.tmpl is None:
opt = kwargs.get('opt') ValueOptionError.tmpl = _('"{0}" is an invalid {1} for "{2}"')
if opt and opt._do_not_display_value_in_error: super().__init__(*args, **kwargs)
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): class ValueErrorWarning(ValueWarning):
@ -383,45 +225,5 @@ class ValueErrorWarning(ValueWarning):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if ValueErrorWarning.tmpl is None: 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) 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()

View file

@ -59,7 +59,6 @@ class Base:
"_dependencies", "_dependencies",
"_dependencies_information", "_dependencies_information",
"_identifiers_dependencies", "_identifiers_dependencies",
"could_conflict",
"__weakref__", "__weakref__",
) )
@ -89,12 +88,9 @@ class Base:
assert isinstance(properties, frozenset), _( assert isinstance(properties, frozenset), _(
"invalid properties type {0} for {1}," " must be a frozenset" "invalid properties type {0} for {1}," " must be a frozenset"
).format(type(properties), name) ).format(type(properties), name)
if not self.impl_is_optiondescription() and "novalidator" not in properties:
properties = properties | {"validator"}
_setattr = object.__setattr__ _setattr = object.__setattr__
_setattr(self, "_name", name) _setattr(self, "_name", name)
_setattr(self, "_informations", {"doc": doc}) _setattr(self, "_informations", {"doc": doc})
_setattr(self, "could_conflict", [])
for prop in properties: for prop in properties:
if not isinstance(prop, str): if not isinstance(prop, str):
if not isinstance(prop, Calculation): if not isinstance(prop, Calculation):
@ -190,7 +186,6 @@ class Base:
else: else:
dico = tuple([keys, tuple(dico.values())]) dico = tuple([keys, tuple(dico.values())])
_setattr(self, "_informations", dico) _setattr(self, "_informations", dico)
_setattr(self, "could_conflict", tuple(self.could_conflict))
extra = getattr(self, "_extra", None) extra = getattr(self, "_extra", None)
if extra is not None: if extra is not None:
_setattr( _setattr(

View file

@ -30,7 +30,6 @@ class BoolOption(Option):
__slots__ = tuple() __slots__ = tuple()
_type = "boolean" _type = "boolean"
_t_type = _("boolean")
def validate( def validate(
self, self,

View file

@ -23,22 +23,27 @@
from ipaddress import ip_address from ipaddress import ip_address
from ..i18n import _ from ..i18n import _
from .stroption import StrOption from .option import Option
class BroadcastOption(StrOption): class BroadcastOption(Option):
"""represents the choice of a broadcast""" """represents the choice of a broadcast"""
__slots__ = tuple() __slots__ = tuple()
_type = "broadcast address" _type = "broadcast address"
_t_type = _("broadcast address")
def validate( def validate(
self, self,
value: str, value: str,
) -> None: ) -> None:
"""validate""" """validate"""
super().validate(value) 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()
try: try:
ip_address(value) ip_address(value)
except ValueError as err: except ValueError as err:

View file

@ -21,12 +21,11 @@
"""ChoiceOption """ChoiceOption
""" """
from typing import Any from typing import Any
from itertools import chain
from ..setting import undefined from ..setting import undefined
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
from ..autolib import Calculation, get_calculated_value, ParamOption from ..autolib import Calculation, get_calculated_value
from ..error import ConfigError, display_list from ..error import ConfigError, display_list
@ -38,17 +37,12 @@ class ChoiceOption(Option):
__slots__ = tuple() __slots__ = tuple()
_type = "choice" _type = "choice"
_t_type = _("choice")
def __init__(self, name, doc, values, *args, **kwargs): def __init__(self, name, doc, values, *args, **kwargs):
""" """
:param values: is a list of values the option can possibly take :param values: is a list of values the option can possibly take
""" """
if isinstance(values, Calculation): if not isinstance(values, (Calculation, tuple)):
for param in chain(values.params.args, values.params.kwargs.values()):
if isinstance(param, ParamOption):
param.option._add_dependency(self)
elif not isinstance(values, tuple):
raise TypeError( raise TypeError(
_("values must be a tuple or a calculation for {0}").format(name) _("values must be a tuple or a calculation for {0}").format(name)
) )

View file

@ -31,7 +31,6 @@ class DateOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = "date" _type = "date"
_t_type = _("date")
def validate(self, value: str) -> None: def validate(self, value: str) -> None:
super().validate(value) super().validate(value)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2025 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2024 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -21,7 +21,6 @@
"""DomainnameOption """DomainnameOption
""" """
import re import re
import socket
from ipaddress import ip_interface from ipaddress import ip_interface
from typing import Any, Optional, List from typing import Any, Optional, List
@ -43,7 +42,6 @@ class DomainnameOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = "domain name" _type = "domain name"
_t_type = _("domain name")
def __init__( def __init__(
self, self,
@ -55,18 +53,12 @@ class DomainnameOption(StrOption):
type: str = "domainname", type: str = "domainname",
allow_without_dot: bool = False, allow_without_dot: bool = False,
allow_startswith_dot: bool = False, allow_startswith_dot: bool = False,
test_existence: bool = False,
_extra: dict = None,
**kwargs, **kwargs,
) -> None: ) -> None:
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments # 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"]: if type not in ["netbios", "hostname", "domainname"]:
raise ValueError(_("unknown type {0} for hostname").format(type)) raise ValueError(_("unknown type {0} for hostname").format(type))
extra["type"] = type extra = {"_dom_type": type}
if not isinstance(allow_ip, bool): if not isinstance(allow_ip, bool):
raise ValueError(_("allow_ip must be a boolean")) raise ValueError(_("allow_ip must be a boolean"))
if not isinstance(allow_cidr_network, bool): if not isinstance(allow_cidr_network, bool):
@ -75,8 +67,7 @@ class DomainnameOption(StrOption):
raise ValueError(_("allow_without_dot must be a boolean")) raise ValueError(_("allow_without_dot must be a boolean"))
if not isinstance(allow_startswith_dot, bool): if not isinstance(allow_startswith_dot, bool):
raise ValueError(_("allow_startswith_dot must be a boolean")) 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 type == "domainname":
if allow_without_dot: if allow_without_dot:
min_time = 0 min_time = 0
@ -85,20 +76,14 @@ class DomainnameOption(StrOption):
regexp = r"((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}".format( regexp = r"((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}".format(
self._get_len(type), min_time 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: else:
regexp = r"((?!-)[a-z0-9-]{{1,{0}}})".format(self._get_len(type)) regexp = r"((?!-)[a-z0-9-]{{1,{0}}})".format(self._get_len(type))
msg = _( msg = _(
'must start with lowercase characters followed by lowercase characters, number and "-" characters are allowed' 'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed'
) )
msg_warning = _( msg_warning = _(
'must start with lowercase characters followed by lowercase characters, number and "-" characters are recommanded' 'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded'
) )
if allow_ip: if allow_ip:
msg = _("could be a IP, otherwise {}").format(msg) msg = _("could be a IP, otherwise {}").format(msg)
msg_warning = _("could be a IP, otherwise {}").format(msg_warning) msg_warning = _("could be a IP, otherwise {}").format(msg_warning)
@ -120,15 +105,15 @@ class DomainnameOption(StrOption):
name, name,
doc, doc,
) )
extra["allow_ip"] = allow_ip extra["_allow_ip"] = allow_ip
if allow_cidr_network: if allow_cidr_network:
extra["_network"] = NetworkOption( extra["_network"] = NetworkOption(
name, name,
doc, doc,
cidr=True, cidr=True,
) )
extra["allow_cidr_network"] = allow_cidr_network extra["_allow_cidr_network"] = allow_cidr_network
extra["allow_startswith_dot"] = allow_startswith_dot extra["_allow_startswith_dot"] = allow_startswith_dot
super().__init__( super().__init__(
name, name,
@ -152,13 +137,13 @@ class DomainnameOption(StrOption):
_("invalid length (max {0})" "").format(part_name_length) _("invalid length (max {0})" "").format(part_name_length)
) )
part_name_length = self._get_len(self.impl_get_extra("type")) part_name_length = self._get_len(self.impl_get_extra("_dom_type"))
if self.impl_get_extra("type") == "domainname": if self.impl_get_extra("_dom_type") == "domainname":
if not self.impl_get_extra("allow_without_dot") and not "." in value: if not self.impl_get_extra("_allow_without_dot") and not "." in value:
raise ValueError(_("must have dot")) raise ValueError(_("must have dot"))
if len(value) > 255: if len(value) > 255:
raise ValueError(_("invalid length (max 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:] val = value[1:]
else: else:
val = value val = value
@ -170,22 +155,10 @@ class DomainnameOption(StrOption):
_valid_length(dom) _valid_length(dom)
else: else:
_valid_length(value) _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: def _validate_ip_network(self, value: str) -> None:
allow_ip = self.impl_get_extra("allow_ip") allow_ip = self.impl_get_extra("_allow_ip")
allow_cidr_network = self.impl_get_extra("allow_cidr_network") allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
if allow_ip is False and allow_cidr_network is False: if allow_ip is False and allow_cidr_network is False:
raise ValueError(_("must not be an IP")) raise ValueError(_("must not be an IP"))
if allow_ip is True: if allow_ip is True:
@ -211,7 +184,7 @@ class DomainnameOption(StrOption):
def _second_level_validation_domain(self, value: str, warnings_only: bool) -> None: def _second_level_validation_domain(self, value: str, warnings_only: bool) -> None:
if self.impl_get_extra("_has_upper").search(value): if self.impl_get_extra("_has_upper").search(value):
raise ValueError(_("some characters are uppercase")) 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:] val = value[1:]
else: else:
val = value val = value
@ -227,8 +200,8 @@ class DomainnameOption(StrOption):
def _second_level_validation_ip_network( def _second_level_validation_ip_network(
self, value: str, warnings_only: bool self, value: str, warnings_only: bool
) -> None: ) -> None:
allow_ip = self.impl_get_extra("allow_ip") allow_ip = self.impl_get_extra("_allow_ip")
allow_cidr_network = self.impl_get_extra("allow_cidr_network") allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
# it's an IP so validate with IPOption # it's an IP so validate with IPOption
if allow_ip is True and allow_cidr_network is False: if allow_ip is True and allow_cidr_network is False:
try: try:

View file

@ -82,9 +82,6 @@ class DynOptionDescription(OptionDescription):
identifier = identifier.replace(".", "_") identifier = identifier.replace(".", "_")
return identifier return identifier
def name_could_conflict(self, dynchild, child):
return child.impl_getname().startswith(dynchild.impl_getname())
def impl_is_dynoptiondescription(self) -> bool: def impl_is_dynoptiondescription(self) -> bool:
return True return True
@ -107,7 +104,6 @@ class DynOptionDescription(OptionDescription):
parent: "SubConfig", parent: "SubConfig",
*, *,
uncalculated: bool = False, uncalculated: bool = False,
from_display_name: bool = False,
) -> List[str]: ) -> List[str]:
"""get dynamic identifiers""" """get dynamic identifiers"""
subconfig = parent.get_child( subconfig = parent.get_child(
@ -128,20 +124,16 @@ class DynOptionDescription(OptionDescription):
)[0] )[0]
if values is None: if values is None:
values = [] values = []
values_ = []
if __debug__: if __debug__:
if not isinstance(values, list): 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( raise ValueError(
_( _(
"DynOptionDescription identifiers for option {0}, is not a list ({1})" "DynOptionDescription identifiers for option {0}, is not a list ({1})"
).format( ).format(
name, values self.impl_get_display_name(subconfig, with_quote=True), values
) )
) )
values_ = []
for val in values: for val in values:
cval = self.convert_identifier_to_path(val) cval = self.convert_identifier_to_path(val)
if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None: if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None:
@ -153,11 +145,7 @@ class DynOptionDescription(OptionDescription):
) )
else: else:
values_.append(val) values_.append(val)
if ( if __debug__ and len(values_) > len(set(values_)):
__debug__
and "demoting_error_warning" not in subconfig.config_bag.properties
and len(values_) > len(set(values_))
):
raise ValueError( raise ValueError(
_( _(
'DynOptionDescription "{0}" identifiers return a list with same values "{1}"' 'DynOptionDescription "{0}" identifiers return a list with same values "{1}"'

View file

@ -34,4 +34,3 @@ class EmailOption(RegexpOption):
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"
) )
_type = "email address" _type = "email address"
_t_type = _("email address")

View file

@ -32,7 +32,6 @@ class FilenameOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = "file name" _type = "file name"
_t_type = _("file name")
def __init__( def __init__(
self, self,
@ -53,9 +52,9 @@ class FilenameOption(StrOption):
if typ not in ["file", "directory"]: if typ not in ["file", "directory"]:
raise ValueError(f'unknown type "{typ}" for "{name}"') raise ValueError(f'unknown type "{typ}" for "{name}"')
extra = { extra = {
"allow_relative": allow_relative, "_allow_relative": allow_relative,
"test_existence": test_existence, "_test_existence": test_existence,
"types": types, "_types": types,
} }
super().__init__(name, *args, extra=extra, **kwargs) super().__init__(name, *args, extra=extra, **kwargs)
@ -64,10 +63,10 @@ class FilenameOption(StrOption):
value: str, value: str,
) -> None: ) -> None:
super().validate(value) 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 "/"')) raise ValueError(_('must starts with "/"'))
if value is not None and self.impl_get_extra("test_existence"): if value is not None and self.impl_get_extra("_test_existence"):
types = self.impl_get_extra("types") types = self.impl_get_extra("_types")
file = Path(value) file = Path(value)
found = False found = False
if "file" in types and file.is_file(): if "file" in types and file.is_file():
@ -75,12 +74,8 @@ class FilenameOption(StrOption):
if not found and "directory" in types and file.is_dir(): if not found and "directory" in types and file.is_dir():
found = True found = True
if not found: if not found:
translated_types = [
{"file": _("file"), "directory": _("directory")}.get(typ)
for typ in types
]
raise ValueError( raise ValueError(
_("cannot find this {0}").format( _('cannot find {0} "{1}"').format(
display_list(translated_types, separator="or"), value display_list(types, separator="or"), value
) )
) )

View file

@ -30,7 +30,6 @@ class FloatOption(Option):
__slots__ = tuple() __slots__ = tuple()
_type = "float" _type = "float"
_t_type = _("float")
def validate(self, value: float) -> None: def validate(self, value: float) -> None:
if not isinstance(value, float): if not isinstance(value, float):

View file

@ -29,18 +29,13 @@ class IntOption(Option):
"represents a choice of an integer" "represents a choice of an integer"
__slots__ = tuple() __slots__ = tuple()
_type = "integer" _type = "integer"
_t_type = _("integer")
def __init__(self, *args, min_number=None, max_number=None, min_integer=None, max_integer=None, **kwargs): def __init__(self, *args, min_number=None, max_number=None, **kwargs):
extra = {} extra = {}
if min_number is not None: if min_number is not None:
extra["min_integer"] = min_number extra["min_number"] = min_number
if min_integer is not None:
extra["min_integer"] = min_integer
if max_number is not None: if max_number is not None:
extra["max_integer"] = max_number extra["max_number"] = max_number
if max_integer is not None:
extra["max_integer"] = max_integer
super().__init__(*args, extra=extra, **kwargs) super().__init__(*args, extra=extra, **kwargs)
def validate( def validate(
@ -48,20 +43,20 @@ class IntOption(Option):
value: int, value: int,
) -> None: ) -> None:
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError(_("which is not an integer")) raise ValueError()
def second_level_validation(self, value, warnings_only): def second_level_validation(self, value, warnings_only):
min_integer = self.impl_get_extra("min_integer") min_number = self.impl_get_extra("min_number")
if min_integer is not None and value < min_integer: if min_number is not None and value < min_number:
if warnings_only: if warnings_only:
msg = _('value should be equal or greater than "{0}"') msg = _('value should be equal or greater than "{0}"')
else: else:
msg = _('value must be equal or greater than "{0}"') msg = _('value must be equal or greater than "{0}"')
raise ValueError(msg.format(min_integer)) raise ValueError(msg.format(min_number))
max_integer = self.impl_get_extra("max_integer") max_number = self.impl_get_extra("max_number")
if max_integer is not None and value > max_integer: if max_number is not None and value > max_number:
if warnings_only: if warnings_only:
msg = _('value should be less than "{0}"') msg = _('value should be less than "{0}"')
else: else:
msg = _('value must be less than "{0}"') msg = _('value must be less than "{0}"')
raise ValueError(msg.format(max_integer)) raise ValueError(msg.format(max_number))

View file

@ -31,7 +31,6 @@ class IPOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = "IP" _type = "IP"
_t_type = _("IP")
def __init__( def __init__(
self, self,
@ -44,9 +43,9 @@ class IPOption(StrOption):
): ):
if extra is None: if extra is None:
extra = {} extra = {}
extra["private_only"] = private_only extra["_private_only"] = private_only
extra["allow_reserved"] = allow_reserved extra["_allow_reserved"] = allow_reserved
extra["cidr"] = cidr extra["_cidr"] = cidr
super().__init__(*args, extra=extra, **kwargs) super().__init__(*args, extra=extra, **kwargs)
def _validate_cidr(self, value): def _validate_cidr(self, value):
@ -54,9 +53,6 @@ class IPOption(StrOption):
ip_obj = ip_interface(value) ip_obj = ip_interface(value)
except ValueError as err: except ValueError as err:
raise ValueError() from 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: if ip_obj.ip == ip_obj.network.network_address:
raise ValueError(_("it's in fact a network address")) raise ValueError(_("it's in fact a network address"))
if ip_obj.ip == ip_obj.network.broadcast_address: if ip_obj.ip == ip_obj.network.broadcast_address:
@ -70,7 +66,7 @@ class IPOption(StrOption):
def validate(self, value: str) -> None: def validate(self, value: str) -> None:
super().validate(value) super().validate(value)
if self.impl_get_extra("cidr"): if self.impl_get_extra("_cidr"):
if "/" not in value: if "/" not in value:
raise ValueError(_('CIDR address must have a "/"')) raise ValueError(_('CIDR address must have a "/"'))
self._validate_cidr(value) self._validate_cidr(value)
@ -79,13 +75,13 @@ class IPOption(StrOption):
def second_level_validation(self, value: str, warnings_only: bool) -> None: def second_level_validation(self, value: str, warnings_only: bool) -> None:
ip_obj = ip_interface(value) 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: if warnings_only:
msg = _("shouldn't be reserved IP") msg = _("shouldn't be reserved IP")
else: else:
msg = _("mustn't be reserved IP") msg = _("mustn't be reserved IP")
raise ValueError(msg) 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: if warnings_only:
msg = _("should be private IP") msg = _("should be private IP")
else: else:

View file

@ -51,7 +51,9 @@ class Leadership(OptionDescription):
**kwargs, **kwargs,
) -> None: ) -> None:
if "group_type" in kwargs: if "group_type" in kwargs:
raise LeadershipError(name, "leadership-group_type") raise LeadershipError(
_('cannot set "group_type" attribute for a Leadership')
)
super().__init__( super().__init__(
name, name,
doc, doc,
@ -83,7 +85,9 @@ class Leadership(OptionDescription):
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance( if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(
prop, Calculation prop, Calculation
): ):
raise LeadershipError(name, "leadership-wrong_property", prop=prop) raise LeadershipError(
_('leader cannot have "{}" property').format(prop)
)
def _check_child_is_valid( def _check_child_is_valid(
self, self,
@ -108,7 +112,7 @@ class Leadership(OptionDescription):
if not child.impl_is_multi(): if not child.impl_is_multi():
raise ValueError( raise ValueError(
_( _(
"only multi option are allowed in leadership {0} but option " "only multi option allowed in leadership {0} but option "
"{1} is not a multi" "{1} is not a multi"
"" ""
).format( ).format(

View file

@ -32,4 +32,3 @@ class MACOption(RegexpOption):
__slots__ = tuple() __slots__ = tuple()
_regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$") _regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$")
_type = "mac address" _type = "mac address"
_t_type = _("mac address")

View file

@ -30,7 +30,6 @@ class NetmaskOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = "netmask address" _type = "netmask address"
_t_type = _("netmask address")
def validate(self, value: str) -> None: def validate(self, value: str) -> None:
super().validate(value) super().validate(value)

View file

@ -23,22 +23,36 @@
from ipaddress import ip_network from ipaddress import ip_network
from ..i18n import _ from ..i18n import _
from .ipoption import IPOption
from .stroption import StrOption from .stroption import StrOption
class NetworkOption(IPOption): class NetworkOption(StrOption):
"represents the choice of a network" "represents the choice of a network"
__slots__ = tuple() __slots__ = tuple()
_type = "network address" _type = "network address"
_t_type = _("network address")
def __init__(self, *args, cidr=False, **kwargs): def __init__(self, *args, cidr=False, **kwargs):
super().__init__(*args, cidr=cidr, **kwargs) extra = {"_cidr": cidr}
super().__init__(*args, extra=extra, **kwargs)
def _second_level_cidr(self, ip_obj): def validate(self, value: str) -> None:
if ip_obj.ip != ip_obj.network.network_address: super().validate(value)
raise ValueError(_("it's not a network address")) 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_validation(self, value: str, warnings_only: bool) -> None: def second_level_validation(self, value: str, warnings_only: bool) -> None:
if ip_network(value).network_address.is_reserved: if ip_network(value).network_address.is_reserved:

View file

@ -53,8 +53,6 @@ class Option(BaseOption):
"_choice_values_params", "_choice_values_params",
) )
_type = None _type = None
_t_type = None
_do_not_display_value_in_error = False
def __init__( def __init__(
self, self,
@ -129,6 +127,11 @@ class Option(BaseOption):
def test_multi_value(value): def test_multi_value(value):
if isinstance(value, Calculation): if isinstance(value, Calculation):
return return
# option_bag = OptionBag(self,
# None,
# undefined,
# properties=None,
# )
try: try:
self.validate(value) self.validate(value)
self.validate_with_option( self.validate_with_option(
@ -180,19 +183,16 @@ class Option(BaseOption):
# undefined, # undefined,
# properties=None, # properties=None,
# ) # )
self_properties = getattr(self, "_properties", {})
self.impl_validate( self.impl_validate(
None, None,
default, default,
loaded=True, loaded=True,
self_properties=self_properties,
) )
self.impl_validate( self.impl_validate(
None, None,
default, default,
check_error=False, check_error=False,
loaded=True, loaded=True,
self_properties=self_properties,
) )
self.value_dependencies(default) self.value_dependencies(default)
if (is_multi and default != []) or (not is_multi and default is not None): if (is_multi and default != []) or (not is_multi and default is not None):
@ -215,10 +215,8 @@ class Option(BaseOption):
"""is a dynsymlinkoption?""" """is a dynsymlinkoption?"""
return False return False
def get_type(self, translation=True) -> str: def get_type(self) -> str:
"""get the type of option""" """get the type of option"""
if translation:
return self._t_type
return self._type return self._type
def impl_getdefault(self) -> Any: def impl_getdefault(self) -> Any:
@ -263,40 +261,33 @@ class Option(BaseOption):
*, *,
check_error: bool = True, check_error: bool = True,
loaded: bool = False, loaded: bool = False,
self_properties: frozenset = frozenset(),
) -> bool: ) -> bool:
"""Return True if value is really valid """Return True if value is really valid
If not validate or invalid return it returns False If not validate or invalid return it returns False
""" """
if check_error: if (
if subconfig: check_error
config_properties = subconfig.config_bag.properties and subconfig
self_properties = subconfig.properties and not "validator" in subconfig.config_bag.properties
else: ):
config_properties = {"validator"} return False
if (
"validator" not in config_properties
or "validator" not in self_properties
):
return False
if subconfig: if subconfig:
force_index = subconfig.index force_index = subconfig.index
else: else:
force_index = None force_index = None
is_warnings_only = getattr(self, "_warnings_only", False) is_warnings_only = getattr(self, "_warnings_only", False)
def _is_not_unique(current_value, values): def _is_not_unique(value):
if current_value is None: # if set(value) has not same length than value
return
if not subconfig or not check_error or "unique" not in subconfig.properties: if not subconfig or not check_error or "unique" not in subconfig.properties:
return return
indexes = [ lvalue = [val for val in value if val is not None]
index for index, value in enumerate(values) if value == current_value if len(set(lvalue)) == len(lvalue):
] return
if len(indexes) > 1: for idx, val in enumerate(value):
raise ValueError( if val not in value[idx + 1 :]:
_('the value "{}" is not unique' "").format(current_value) continue
) raise ValueError(_('the value "{}" is not unique' "").format(val))
def calculation_validator( def calculation_validator(
val, val,
@ -338,12 +329,12 @@ class Option(BaseOption):
except ValueWarning as warn: except ValueWarning as warn:
warnings.warn_explicit( warnings.warn_explicit(
ValueWarning( ValueWarning(
subconfig=subconfig, subconfig,
val=val, val,
display_type=_(self.get_type()), _(self.get_type()),
opt=self, self,
err_msg=str(warn), str(warn),
index=_index, _index,
), ),
ValueWarning, ValueWarning,
self.__class__.__name__, self.__class__.__name__,
@ -379,12 +370,12 @@ class Option(BaseOption):
if is_warnings_only: if is_warnings_only:
warnings.warn_explicit( warnings.warn_explicit(
ValueWarning( ValueWarning(
subconfig=subconfig, subconfig,
val=_value, _value,
display_type=_(self.get_type()), _(self.get_type()),
opt=self, self,
err_msg=str(err), str(err),
index=_index, _index,
), ),
ValueWarning, ValueWarning,
self.__class__.__name__, self.__class__.__name__,
@ -399,100 +390,70 @@ class Option(BaseOption):
_index, _index,
) )
val = value
err_index = force_index err_index = force_index
ret = True try:
if not self.impl_is_multi(): if not self.impl_is_multi():
try:
do_validation( do_validation(
value, val,
None, None,
) )
except ValueError as err: elif force_index is not None:
self.validate_parse_error(value, err_index, err, subconfig) if self.impl_is_submulti():
ret = False if not isinstance(value, list):
elif force_index is not None: raise ValueError(_("which must be a list"))
if self.impl_is_submulti(): for val in value:
if not isinstance(value, list):
raise ValueError(_("which must be a list"))
for val in value:
try:
do_validation( do_validation(
val, val,
force_index, force_index,
) )
_is_not_unique(val, value) _is_not_unique(value)
except ValueError as err: else:
self.validate_parse_error(val, err_index, err, subconfig)
ret = False
else:
try:
do_validation( do_validation(
value, val,
force_index, force_index,
) )
except ValueError as err: elif isinstance(value, Calculation) and not subconfig:
self.validate_parse_error(value, err_index, err, subconfig) pass
ret = False elif self.impl_is_submulti():
elif isinstance(value, Calculation) and not subconfig: for err_index, lval in enumerate(value):
pass if isinstance(lval, Calculation):
elif self.impl_is_submulti(): continue
for err_index, lval in enumerate(value): if not isinstance(lval, list):
if isinstance(lval, Calculation): raise ValueError(
continue _('which "{}" must be a list of list' "").format(lval)
if not isinstance(lval, list): )
raise ValueError( for val in lval:
_('which "{}" must be a list of list' "").format(lval)
)
for val in lval:
try:
do_validation(val, err_index) do_validation(val, err_index)
_is_not_unique(val, lval) _is_not_unique(lval)
except ValueError as err: elif not isinstance(value, list):
self.validate_parse_error(val, err_index, err, subconfig) raise ValueError(_("which must be a list"))
ret = False else:
elif not isinstance(value, list): # FIXME suboptimal, not several time for whole=True!
raise ValueError(_("which must be a list")) for err_index, val in enumerate(value):
else:
# FIXME suboptimal, not several time for whole=True!
for err_index, val in enumerate(value):
try:
do_validation( do_validation(
val, val,
err_index, err_index,
) )
_is_not_unique(val, value) _is_not_unique(value)
except ValueError as err: except ValueError as err:
self.validate_parse_error(val, err_index, err, subconfig) if (
ret = False not subconfig
# return False or "demoting_error_warning" not in subconfig.config_bag.properties
return ret ):
raise ValueOptionError(
def validate_parse_error(self, val, index, err, subconfig): subconfig, val, _(self.get_type()), self, str(err), err_index
if ( ) from err
not subconfig warnings.warn_explicit(
or "demoting_error_warning" not in subconfig.config_bag.properties ValueErrorWarning(
): subconfig, val, _(self.get_type()), self, str(err), err_index
raise ValueOptionError( ),
subconfig=subconfig, ValueErrorWarning,
val=val, self.__class__.__name__,
display_type=_(self.get_type()), 0,
opt=self, )
err_msg=str(err), return False
index=index, return True
) 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( def validate_with_option(
self, self,

View file

@ -29,7 +29,7 @@ from ..setting import ConfigBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption from .baseoption import BaseOption
# from .syndynoption import SubDynOptionDescription, SynDynOptionDescription # from .syndynoption import SubDynOptionDescription, SynDynOptionDescription
from ..error import ConfigError, ConflictError, AttributeOptionError from ..error import ConfigError, ConflictError
class CacheOptionDescription(BaseOption): class CacheOptionDescription(BaseOption):
@ -106,24 +106,7 @@ class CacheOptionDescription(BaseOption):
if "force_store_value" in properties: if "force_store_value" in properties:
force_store_values.append(option) force_store_values.append(option)
if option.impl_is_readonly(): if option.impl_is_readonly():
previous_path = option.impl_getpath() raise ConflictError(_("duplicate option: {0}").format(option))
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: if not self.impl_is_readonly() and display_name:
option._display_name_function = ( option._display_name_function = (
display_name # pylint: disable=protected-access display_name # pylint: disable=protected-access
@ -249,20 +232,19 @@ class OptionDescriptionWalk(CacheOptionDescription):
def get_child_not_dynamic( def get_child_not_dynamic(
self, self,
name: str, name,
allow_dynoption: bool, allow_dynoption,
parent: "SubConfig",
): ):
if name in self._children[0]: # pylint: disable=no-member if name in self._children[0]: # pylint: disable=no-member
option = self._children[1][ option = self._children[1][
self._children[0].index(name) self._children[0].index(name)
] # pylint: disable=no-member ] # pylint: disable=no-member
if option.impl_is_dynoptiondescription() and not allow_dynoption: if option.impl_is_dynoptiondescription() and not allow_dynoption:
if parent.path: raise AttributeError(
path = parent.path + "." + name _(
else: 'unknown option "{0}" in root optiondescription (it\'s a dynamic option)'
path = name ).format(name)
raise AttributeOptionError(path, "option-dynamic") )
return option return option
def get_child( def get_child(
@ -279,7 +261,6 @@ class OptionDescriptionWalk(CacheOptionDescription):
option = self.get_child_not_dynamic( option = self.get_child_not_dynamic(
name, name,
allow_dynoption, allow_dynoption,
parent,
) )
if option: if option:
return option return option
@ -293,16 +274,45 @@ class OptionDescriptionWalk(CacheOptionDescription):
if not with_identifier: if not with_identifier:
return child return child
return identifier, child return identifier, child
if parent.path is None: if self.impl_get_group_type() == groups.root: # pylint: disable=no-member
path = name raise AttributeError(
else: _('unknown option "{0}" in root optiondescription').format(name)
path = parent.path + "." + name )
raise AttributeOptionError(path, "option-not-found") raise AttributeError(
_('unknown option "{0}" in optiondescription {1}').format(
name, self.impl_get_display_name(parent, with_quote=True)
)
)
def get_children(self) -> List[BaseOption]: def get_children(self) -> List[BaseOption]:
"""get children""" """get children"""
return self._children[1] 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): class OptionDescription(OptionDescriptionWalk):
"""Config's schema (organisation, group) and container of Options """Config's schema (organisation, group) and container of Options
@ -335,38 +345,35 @@ class OptionDescription(OptionDescriptionWalk):
properties=properties, properties=properties,
) )
child_names = [] child_names = []
fix_child_names = [] if __debug__:
dynopts = [] dynopt_names = []
for child in children: for child in children:
name = child.impl_getname() name = child.impl_getname()
child_names.append(name) child_names.append(name)
if child.impl_is_dynoptiondescription(): if __debug__ and child.impl_is_dynoptiondescription():
dynopts.append(child) dynopt_names.append(name)
else:
fix_child_names.append(name)
# before sorting # before sorting
children_ = (tuple(child_names), tuple(children)) children_ = (tuple(child_names), tuple(children))
# better performance like this if __debug__:
fix_child_names.sort() # better performance like this
old = None child_names.sort()
for child_name in fix_child_names: old = None
if child_name == old: for child in child_names:
raise ConflictError( if child == old:
_('the option name "{0}" is duplicate in "{1}"').format( raise ConflictError(
child_name, self.impl_get_display_name(None) _("duplicate option name: " '"{0}"').format(child)
) )
) if dynopt_names:
old = child_name for dynopt in dynopt_names:
for dynopt in dynopts: if child != dynopt and child.startswith(dynopt):
if dynopt.could_conflict: raise ConflictError(
continue _(
for child in children: 'the option\'s name "{0}" start as the dynoptiondescription\'s name "{1}"'
if child != dynopt and dynopt.name_could_conflict(dynopt, child): ).format(child, dynopt)
dynopt.could_conflict.append(weakref.ref(child)) )
child.could_conflict.append(weakref.ref(dynopt)) old = child
break
self._children = children_ self._children = children_
# the group_type is useful for filtering OptionDescriptions in a config # the group_type is useful for filtering OptionDescriptions in a config
self._group_type = None self._group_type = None

View file

@ -31,8 +31,6 @@ class PasswordOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = "password" _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): def __init__(self, *args, min_len=None, max_len=None, forbidden_char=[], **kwargs):
extra = {} extra = {}

View file

@ -37,7 +37,6 @@ class PermissionsOption(IntOption):
__slots__ = tuple() __slots__ = tuple()
perm_re = re.compile(r"^[0-7]{3,4}$") perm_re = re.compile(r"^[0-7]{3,4}$")
_type = "unix file permissions" _type = "unix file permissions"
_t_type = _("unix file permissions")
def __init__( def __init__(
self, self,

View file

@ -20,6 +20,8 @@
# ____________________________________________________________ # ____________________________________________________________
"""PortOption """PortOption
""" """
import re
from ..i18n import _ from ..i18n import _
from .stroption import StrOption from .stroption import StrOption
@ -36,8 +38,8 @@ class PortOption(StrOption):
""" """
__slots__ = tuple() __slots__ = tuple()
port_re = re.compile(r"^[0-9]*$")
_type = "port" _type = "port"
_t_type = _("port")
def __init__( def __init__(
self, self,
@ -48,21 +50,15 @@ class PortOption(StrOption):
allow_registred: bool = True, allow_registred: bool = True,
allow_protocol: bool = False, allow_protocol: bool = False,
allow_private: bool = False, allow_private: bool = False,
_extra: dict = None,
**kwargs, **kwargs,
) -> None: ) -> None:
if _extra is None:
extra = {} extra = {
else: "_allow_range": allow_range,
extra = _extra "_allow_protocol": allow_protocol,
extra["allow_range"] = allow_range "_min_value": None,
extra["allow_protocol"] = allow_protocol "_max_value": None,
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_min = [0, 1, 1024, 49152]
ports_max = [0, 1023, 49151, 65535] ports_max = [0, 1023, 49151, 65535]
is_finally = False is_finally = False
@ -86,11 +82,11 @@ class PortOption(StrOption):
def validate(self, value: str) -> None: def validate(self, value: str) -> None:
super().validate(value) 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.startswith("tcp:") or value.startswith("udp:")
): ):
value = [value[4:]] 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(":") value = value.split(":")
if len(value) != 2: if len(value) != 2:
raise ValueError(_("range must have two values only")) raise ValueError(_("range must have two values only"))
@ -102,11 +98,11 @@ class PortOption(StrOption):
value = [value] value = [value]
for val in value: for val in value:
if not val.isdecimal(): if not self.port_re.search(val):
raise ValueError() raise ValueError()
def second_level_validation(self, value: str, warnings_only: bool) -> None: 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.startswith("tcp:") or value.startswith("udp:")
): ):
value = [value[4:]] value = [value[4:]]

View file

@ -31,7 +31,6 @@ class StrOption(Option):
__slots__ = tuple() __slots__ = tuple()
_type = "string" _type = "string"
_t_type = _("string")
def validate( def validate(
self, self,
@ -39,7 +38,7 @@ class StrOption(Option):
) -> None: ) -> None:
"""validation""" """validation"""
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError(_("which is not a string")) raise ValueError()
class RegexpOption(StrOption): class RegexpOption(StrOption):

View file

@ -37,7 +37,6 @@ class URLOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
_type = "URL" _type = "URL"
_t_type = _("URL")
def __init__( def __init__(
self, self,
@ -55,25 +54,24 @@ class URLOption(StrOption):
**kwargs, **kwargs,
) -> None: ) -> None:
# pylint: disable=too-many-arguments,too-many-locals,redefined-builtin # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
extra = {} extra = {
extra["_domainname"] = DomainnameOption( "_domainname": DomainnameOption(
name, name,
doc, doc,
allow_ip=allow_ip, allow_ip=allow_ip,
type=type, type=type,
allow_without_dot=allow_without_dot, allow_without_dot=allow_without_dot,
_extra=extra, ),
) "_port": PortOption(
extra["_port"] = PortOption( name,
name, doc,
doc, allow_range=allow_range,
allow_range=allow_range, allow_zero=allow_zero,
allow_zero=allow_zero, allow_wellknown=allow_wellknown,
allow_wellknown=allow_wellknown, allow_registred=allow_registred,
allow_registred=allow_registred, allow_private=allow_private,
allow_private=allow_private, ),
_extra=extra, }
)
super().__init__( super().__init__(
name, name,
doc, doc,
@ -112,18 +110,10 @@ class URLOption(StrOption):
domain, port, files = self._get_domain_port_files(value) domain, port, files = self._get_domain_port_files(value)
# validate port # validate port
portoption = self.impl_get_extra("_port") portoption = self.impl_get_extra("_port")
try: portoption.validate(port)
portoption.validate(port)
except ValueError as err:
msg = _('the port "{0}" is invalid: {1}').format(domain, err)
raise ValueError(msg) from err
# validate domainname # validate domainname
domainnameoption = self.impl_get_extra("_domainname") domainnameoption = self.impl_get_extra("_domainname")
try: domainnameoption.validate(domain)
domainnameoption.validate(domain)
except ValueError as err:
msg = _('the domain "{0}" is invalid: {1}').format(domain, err)
raise ValueError(msg) from err
# validate files # validate files
if files is not None and files != "" and not self.path_re.search(files): if files is not None and files != "" and not self.path_re.search(files):
raise ValueError(_("must ends with a valid resource name")) raise ValueError(_("must ends with a valid resource name"))

View file

@ -33,7 +33,6 @@ class UsernameOption(RegexpOption):
# regexp build with 'man 8 adduser' informations # regexp build with 'man 8 adduser' informations
_regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$") _regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
_type = "unix username" _type = "unix username"
_t_type = _("unix username")
class GroupnameOption(UsernameOption): class GroupnameOption(UsernameOption):
@ -41,4 +40,3 @@ class GroupnameOption(UsernameOption):
__slots__ = tuple() __slots__ = tuple()
_type = "unix groupname" _type = "unix groupname"
_t_type = _("unix groupname")

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"sets the options of the configuration objects Config object itself" "sets the options of the configuration objects Config object itself"
# Copyright (C) 2012-2025 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2024 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -94,7 +94,7 @@ EXPIRATION_TIME = 5
# demoting_error_warning # demoting_error_warning
# all value errors are convert to warning (ValueErrorWarning) # all value errors are convert to warning (ValueErrorWarning)
DEFAULT_PROPERTIES = frozenset(["cache", "validator", "warnings"]) DEFAULT_PROPERTIES = frozenset(["cache", "validator", "warnings"])
SPECIAL_PROPERTIES = {"frozen", "mandatory", "empty", "force_store_value", "validator"} SPECIAL_PROPERTIES = {"frozen", "mandatory", "empty", "force_store_value"}
# Config can be in two defaut mode: # Config can be in two defaut mode:
# #
@ -149,7 +149,6 @@ FORBIDDEN_SET_PERMISSIVES = frozenset(
"force_default_on_freeze", "force_default_on_freeze",
"force_metaconfig_on_freeze", "force_metaconfig_on_freeze",
"force_store_value", "force_store_value",
"validator",
] ]
) )
ALLOWED_LEADER_PROPERTIES = { ALLOWED_LEADER_PROPERTIES = {
@ -159,8 +158,6 @@ ALLOWED_LEADER_PROPERTIES = {
"unique", "unique",
"force_store_value", "force_store_value",
"mandatory", "mandatory",
"validator",
"novalidator",
"force_default_on_freeze", "force_default_on_freeze",
"force_metaconfig_on_freeze", "force_metaconfig_on_freeze",
"frozen", "frozen",
@ -476,7 +473,7 @@ class Settings:
and new_prop not in ALLOWED_LEADER_PROPERTIES and new_prop not in ALLOWED_LEADER_PROPERTIES
): ):
raise LeadershipError( raise LeadershipError(
subconfig, "leadership-wrong_property", prop=new_prop _('leader cannot have "{new_prop}" property')
) )
props.add(new_prop) props.add(new_prop)
props -= self.getpermissives(subconfig) props -= self.getpermissives(subconfig)
@ -564,15 +561,19 @@ class Settings:
not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
if not_allowed_properties: if not_allowed_properties:
raise LeadershipError( raise LeadershipError(
subconfig, _('leader cannot have "{0}" property').format(
"leadership-wrong_property", display_list(not_allowed_properties)
prop=display_list(not_allowed_properties), )
) )
if ( if (
"force_default_on_freeze" in properties "force_default_on_freeze" in properties
or "force_metaconfig_on_freeze" in properties or "force_metaconfig_on_freeze" in properties
) and "frozen" not in properties: ) and "frozen" not in properties:
raise LeadershipError(subconfig, "leadership-force_default_on_freeze") 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())
)
self._properties.setdefault(subconfig.path, {})[subconfig.index] = properties self._properties.setdefault(subconfig.path, {})[subconfig.index] = properties
# values too because of follower values could have a PropertiesOptionError has value # values too because of follower values could have a PropertiesOptionError has value
subconfig.config_bag.context.reset_cache(subconfig) subconfig.config_bag.context.reset_cache(subconfig)

View file

@ -646,9 +646,9 @@ class TiramisuDict:
if self.remotable == "all" or childapi.has_dependency(): if self.remotable == "all" or childapi.has_dependency():
obj_form["remote"] = True obj_form["remote"] = True
if childtype == "IPOption" and ( if childtype == "IPOption" and (
child.impl_get_extra("private_only") child.impl_get_extra("_private_only")
or not child.impl_get_extra("allow_reserved") or not child.impl_get_extra("_allow_reserved")
or child.impl_get_extra("cidr") or child.impl_get_extra("_cidr")
): ):
obj_form["remote"] = True obj_form["remote"] = True
if childtype == "DateOption": if childtype == "DateOption":

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"takes care of the option's values and multi values" "takes care of the option's values and multi values"
# Copyright (C) 2013-2025 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2024 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -189,9 +189,10 @@ class Values:
if subconfig.config_bag.context.impl_type == "config": if subconfig.config_bag.context.impl_type == "config":
return True return True
# it's a not a config, force to metaconfig only in *explicitly* set # it's a not a config, force to metaconfig only in *explicitly* set
return "force_metaconfig_on_freeze" in settings.get_personalize_properties( return "force_metaconfig_on_freeze" in settings.get_stored_properties(
subconfig.path, subconfig.path,
subconfig.index, subconfig.index,
frozenset(),
) )
return False return False
@ -264,10 +265,9 @@ class Values:
) -> None: ) -> None:
"""set value to option""" """set value to option"""
owner = self.get_context_owner() owner = self.get_context_owner()
self_properties = subconfig.properties
setting_properties = subconfig.config_bag.properties setting_properties = subconfig.config_bag.properties
ori_value = value ori_value = value
if "validator" in setting_properties and "validator" in self_properties: if "validator" in setting_properties:
value, has_calculation = self.setvalue_validation( value, has_calculation = self.setvalue_validation(
subconfig, subconfig,
value, value,
@ -295,7 +295,6 @@ class Values:
) )
validator = ( validator = (
"validator" in setting_properties "validator" in setting_properties
and "validator" in self_properties
and "demoting_error_warning" not in setting_properties and "demoting_error_warning" not in setting_properties
) )
if validator and not has_calculation: if validator and not has_calculation:
@ -305,11 +304,7 @@ class Values:
value, value,
validated=validator, validated=validator,
) )
elif ( elif "validator" in setting_properties and has_calculation:
"validator" in setting_properties
and "validator" in self_properties
and has_calculation
):
cache = subconfig.config_bag.context.get_values_cache() cache = subconfig.config_bag.context.get_values_cache()
cache.delcache(subconfig.path) cache.delcache(subconfig.path)
@ -433,28 +428,36 @@ class Values:
If not found, return None If not found, return None
For follower option, return the Config where leader is modified 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(): for parent in subconfig.config_bag.context.get_parents():
parent_subconfig = subconfig.change_context(parent) doption_bag = build_option_bag(subconfig, parent)
parent_subconfig.config_bag.unrestraint()
parent_subconfig.properties = subconfig.properties
if "force_metaconfig_on_freeze" in subconfig.properties: if "force_metaconfig_on_freeze" in subconfig.properties:
# remove force_metaconfig_on_freeze only if option in metaconfig # remove force_metaconfig_on_freeze only if option in metaconfig
# hasn't force_metaconfig_on_freeze properties # hasn't force_metaconfig_on_freeze properties
ori_properties = parent_subconfig.properties ori_properties = doption_bag.properties
settings = parent_subconfig.config_bag.context.get_settings() settings = doption_bag.config_bag.context.get_settings()
parent_subconfig.properties = settings.getproperties(parent_subconfig) doption_bag.properties = settings.getproperties(doption_bag)
if not self.check_force_to_metaconfig(parent_subconfig): if not self.check_force_to_metaconfig(doption_bag):
parent_subconfig.properties = ori_properties - { doption_bag.properties = ori_properties - {
"force_metaconfig_on_freeze" "force_metaconfig_on_freeze"
} }
else: else:
parent_subconfig.properties = ori_properties doption_bag.properties = ori_properties
parent_owner = parent.get_values().getowner( parent_owner = parent.get_values().getowner(
parent_subconfig, doption_bag,
parent,
only_default=True, only_default=True,
) )
if parent_owner != owners.default: if parent_owner != owners.default:
return parent_subconfig return doption_bag
return None return None
@ -508,6 +511,9 @@ class Values:
was present was present
:returns: a `setting.owners.Owner` object :returns: a `setting.owners.Owner` object
""" """
# context = subconfig.config_bag.context
# settings = context.get_settings()
# settings.validate_properties(subconfig)
if ( if (
"frozen" in subconfig.properties "frozen" in subconfig.properties
and "force_default_on_freeze" in subconfig.properties and "force_default_on_freeze" in subconfig.properties
@ -536,10 +542,11 @@ class Values:
values = msubconfig.config_bag.context.get_values() values = msubconfig.config_bag.context.get_values()
owner = values.getowner( owner = values.getowner(
msubconfig, msubconfig,
parent,
only_default=only_default, only_default=only_default,
) )
elif "force_metaconfig_on_freeze" in subconfig.properties: elif "force_metaconfig_on_freeze" in subconfig.properties:
owner = owners.default return owners.default
return owner return owner
def set_owner( def set_owner(
@ -580,39 +587,35 @@ class Values:
"""reset value for an option""" """reset value for an option"""
config_bag = subconfig.config_bag config_bag = subconfig.config_bag
hasvalue = self.hasvalue(subconfig.path) hasvalue = self.hasvalue(subconfig.path)
self_properties = subconfig.properties
context = config_bag.context context = config_bag.context
setting_properties = config_bag.properties setting_properties = config_bag.properties
if ( if validate:
validate if hasvalue and "validator" in setting_properties:
and hasvalue fake_context = context.gen_fake_context()
and "validator" in setting_properties fake_config_bag = config_bag.copy()
and "validator" in self_properties fake_config_bag.remove_validation()
): fake_config_bag.context = fake_context
fake_context = context.gen_fake_context() fake_subconfig = fake_context.get_sub_config(
fake_config_bag = config_bag.copy() fake_config_bag,
fake_config_bag.remove_validation() subconfig.path,
fake_config_bag.context = fake_context subconfig.index,
fake_subconfig = fake_context.get_sub_config( validate_properties=False,
fake_config_bag, )
subconfig.path, fake_values = fake_context.get_values()
subconfig.index, fake_values.reset(fake_subconfig)
validate_properties=False, fake_subconfig.config_bag.properties = setting_properties
) value = fake_values.get_default_value(fake_subconfig)
fake_values = fake_context.get_values() fake_values.setvalue_validation(
fake_values.reset(fake_subconfig) fake_subconfig,
fake_subconfig.config_bag.properties = setting_properties value,
value = fake_values.get_default_value(fake_subconfig) )
fake_values.setvalue_validation( # if hasvalue:
fake_subconfig,
value,
)
opt = subconfig.option opt = subconfig.option
if opt.impl_is_leader(): if opt.impl_is_leader():
opt.impl_get_leadership().reset(subconfig.parent) opt.impl_get_leadership().reset(subconfig.parent)
if ( if (
"force_store_value" in setting_properties "force_store_value" in setting_properties
and "force_store_value" in self_properties and "force_store_value" in subconfig.properties
): ):
value = self.get_default_value(subconfig) value = self.get_default_value(subconfig)
@ -659,11 +662,10 @@ class Values:
index=subconfig.index, index=subconfig.index,
): ):
return return
self_properties = subconfig.properties
config_bag = subconfig.config_bag config_bag = subconfig.config_bag
context = config_bag.context context = config_bag.context
setting_properties = config_bag.properties setting_properties = config_bag.properties
if "validator" in setting_properties and "validator" in self_properties: if "validator" in setting_properties:
fake_context = context.gen_fake_context() fake_context = context.gen_fake_context()
fake_config_bag = config_bag.copy() fake_config_bag = config_bag.copy()
fake_config_bag.remove_validation() fake_config_bag.remove_validation()
@ -684,7 +686,7 @@ class Values:
) )
if ( if (
"force_store_value" in setting_properties "force_store_value" in setting_properties
and "force_store_value" in self_properties and "force_store_value" in subconfig.properties
): ):
value = self.get_default_value( value = self.get_default_value(
subconfig, subconfig,
@ -736,8 +738,8 @@ class Values:
if index >= length: if index >= length:
raise IndexError( raise IndexError(
_( _(
f"index {index} is greater than the length {length} " "index {index} is greater than the length {length} "
f"for option {subconfig.option.impl_get_display_name(subconfig, with_quote=True)}" "for option {subconfig.option.impl_get_display_name(with_quote=True)}"
) )
) )
current_value.pop(index) current_value.pop(index)