todict for tiramisu 4

This commit is contained in:
egarette@silique.fr 2023-06-19 17:19:07 +02:00
parent 0a4e1445db
commit 50f0f67629
35 changed files with 361 additions and 226 deletions

View file

@ -1,7 +1,7 @@
Authors
--------
Emmanuel Garette <egarette@cadoles.com> lead developer
Emmanuel Garette <egarette@silique.fr> lead developer
Gwenaël Rémond <gremond@cadoles.com> developer
Daniel Dehennin <daniel.dehennin@ac-dijon.fr> contributor

View file

@ -6,7 +6,7 @@ def get_description():
option1 = ChoiceOption('choice', "Choice description", ("hide", "show"), default='hide', properties=('mandatory',))
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option1, todict=True),
kwargs={'condition': ParamOption(option1),
'expected': ParamValue('hide')}))
option2 = StrOption('unicode2', "Unicode 2", properties=(hidden_property,))
descr1 = OptionDescription("options", "Common configuration", [option1, option2])

View file

@ -1 +1,3 @@
{"options.unicode": null}
{
"options.unicode": null
}

View file

@ -1 +1,3 @@
{"options.unicode": "val"}
{
"options.unicode": "val"
}

View file

@ -8,7 +8,7 @@ def get_description():
option2 = StrOption('unicode2', "Values 'test' must show 'Unicode follower 3'", multi=True)
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option2, todict=True),
kwargs={'condition': ParamOption(option2),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option3 = StrOption('unicode3', "Unicode follower 3", properties=(hidden_property,), multi=True)

View file

@ -10,7 +10,7 @@ def get_description():
option3 = StrOption('unicode3', "Unicode follower 2", multi=True)
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option, todict=True),
kwargs={'condition': ParamOption(option),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
descr1 = Leadership("unicode1", "Common configuration",

View file

@ -9,7 +9,7 @@ def get_description():
option2 = StrOption('unicode2', "Unicode follower 2", multi=True)
disabled_property = Calculation(calc_value,
Params(ParamValue('disabled'),
kwargs={'condition': ParamOption(option, todict=True),
kwargs={'condition': ParamOption(option),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option3 = StrOption('unicode3', "Unicode follower 3", properties=(disabled_property,), multi=True)

View file

@ -8,7 +8,7 @@ def get_description():
option2 = StrOption('unicode2', "Unicode follower 1", multi=True)
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option1, todict=True),
kwargs={'condition': ParamOption(option1),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option3 = StrOption('unicode3', "Unicode follower 2", multi=True, properties=(hidden_property,))

View file

@ -8,7 +8,7 @@ def get_description():
option2 = StrOption('unicode2', "Unicode follower 1", multi=True)
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option1, todict=True),
kwargs={'condition': ParamOption(option1),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option3 = StrOption('unicode3', "Unicode follower 2", multi=True, properties=(hidden_property,))

View file

@ -8,7 +8,7 @@ def get_description():
option2 = StrOption('unicode2', "Values 'test' must show 'Unicode follower 2'", multi=True)
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option2, todict=True),
kwargs={'condition': ParamOption(option2),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option3 = StrOption('unicode3', "Unicode follower 2", multi=True, properties=(hidden_property,))

View file

@ -9,7 +9,7 @@ def get_description():
option2 = StrOption('unicode2', "Unicode follower 2", multi=True)
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option, todict=True),
kwargs={'condition': ParamOption(option),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option3 = StrOption('unicode3', "Unicode follower 3", properties=(hidden_property,), multi=True)

View file

@ -1,4 +1,16 @@
{"options.unicode.unicode": ["val3", "val4"],
"options.unicode.unicode1": ["super1", "super2"],
"options.unicode.unicode2": ["pas test", "test"],
"options.unicode.unicode3": [null, "super"]}
{
"options.unicode.unicode": [
{
"options.unicode.unicode": "val3",
"options.unicode.unicode1": "super1",
"options.unicode.unicode2": "pas test",
"options.unicode.unicode3": null
},
{
"options.unicode.unicode": "val4",
"options.unicode.unicode1": "super2",
"options.unicode.unicode2": "test",
"options.unicode.unicode3": "super"
}
]
}

View file

@ -1,4 +1,16 @@
{"options.unicode.unicode": ["val1", "val2"],
"options.unicode.unicode1": [null, null],
"options.unicode.unicode2": ["follower2", "follower2"],
"options.unicode.unicode3": [null, null]}
{
"options.unicode.unicode": [
{
"options.unicode.unicode": "val1",
"options.unicode.unicode1": null,
"options.unicode.unicode2": "follower2",
"options.unicode.unicode3": null
},
{
"options.unicode.unicode": "val2",
"options.unicode.unicode1": null,
"options.unicode.unicode2": "follower2",
"options.unicode.unicode3": null
}
]
}

View file

@ -1,4 +1,10 @@
{"options.unicode.unicode": ["val3"],
"options.unicode.unicode1": ["super1"],
"options.unicode.unicode2": ["pas test"],
"options.unicode.unicode3": [null]}
{
"options.unicode.unicode": [
{
"options.unicode.unicode": "val3",
"options.unicode.unicode1": "super1",
"options.unicode.unicode2": "pas test",
"options.unicode.unicode3": null
}
]
}

View file

@ -1,4 +1,22 @@
{"options.unicode.unicode": ["val3", "val4", "val5"],
"options.unicode.unicode1": ["super1", "super2", null],
"options.unicode.unicode2": ["pas test", "test", "follower2"],
"options.unicode.unicode3": [null, "super", null]}
{
"options.unicode.unicode": [
{
"options.unicode.unicode": "val3",
"options.unicode.unicode1": "super1",
"options.unicode.unicode2": "pas test",
"options.unicode.unicode3": null
},
{
"options.unicode.unicode": "val4",
"options.unicode.unicode1": "super2",
"options.unicode.unicode2": "test",
"options.unicode.unicode3": "super"
},
{
"options.unicode.unicode": "val5",
"options.unicode.unicode1": null,
"options.unicode.unicode2": "follower2",
"options.unicode.unicode3": null
}
]
}

View file

@ -1,4 +1,22 @@
{"options.unicode.unicode": ["val3", "val4", "val5"],
"options.unicode.unicode1": ["super1", "super2", null],
"options.unicode.unicode2": ["pas test", "test", "follower2"],
"options.unicode.unicode3": [null, "super", null]}
{
"options.unicode.unicode": [
{
"options.unicode.unicode": "val3",
"options.unicode.unicode1": "super1",
"options.unicode.unicode2": "pas test",
"options.unicode.unicode3": null
},
{
"options.unicode.unicode": "val4",
"options.unicode.unicode1": "super2",
"options.unicode.unicode2": "test",
"options.unicode.unicode3": "super"
},
{
"options.unicode.unicode": "val5",
"options.unicode.unicode1": null,
"options.unicode.unicode2": "follower2",
"options.unicode.unicode3": null
}
]
}

View file

@ -1,4 +1,16 @@
{"options.unicode.unicode": ["val3", "val4"],
"options.unicode.unicode1": ["super1", "super2"],
"options.unicode.unicode2": ["pas test", "follower2"],
"options.unicode.unicode3": [null, "super"]}
{
"options.unicode.unicode": [
{
"options.unicode.unicode": "val3",
"options.unicode.unicode1": "super1",
"options.unicode.unicode2": "pas test",
"options.unicode.unicode3": null
},
{
"options.unicode.unicode": "val4",
"options.unicode.unicode1": "super2",
"options.unicode.unicode2": "follower2",
"options.unicode.unicode3": "super"
}
]
}

View file

@ -1,4 +1,16 @@
{"options.unicode.unicode": ["val3", "val4"],
"options.unicode.unicode1": ["super1", "super2"],
"options.unicode.unicode2": ["pas test", "test2"],
"options.unicode.unicode3": [null, "super"]}
{
"options.unicode.unicode": [
{
"options.unicode.unicode": "val3",
"options.unicode.unicode1": "super1",
"options.unicode.unicode2": "pas test",
"options.unicode.unicode3": null
},
{
"options.unicode.unicode": "val4",
"options.unicode.unicode1": "super2",
"options.unicode.unicode2": "test2",
"options.unicode.unicode3": "super"
}
]
}

View file

@ -1 +1,3 @@
{"options.unicode": "a"}
{
"options.unicode": "a"
}

View file

@ -1 +1,3 @@
{"options.unicode": "val"}
{
"options.unicode": "val"
}

View file

@ -1 +1,3 @@
{"options.unicode": null}
{
"options.unicode": null
}

View file

@ -1 +1,3 @@
{"options.unicode": []}
{
"options.unicode": []
}

View file

@ -1 +1,5 @@
{"options.unicode": ["val"]}
{
"options.unicode": [
"val"
]
}

View file

@ -1 +1,7 @@
{"options.unicode": ["c", "d", "e"]}
{
"options.unicode": [
"c",
"d",
"e"
]
}

View file

@ -1 +1,6 @@
{"options.unicode": ["a", "b"]}
{
"options.unicode": [
"a",
"b"
]
}

View file

@ -1 +1,7 @@
{"options.unicode": ["c", "f", "e"]}
{
"options.unicode": [
"c",
"f",
"e"
]
}

View file

@ -7,7 +7,7 @@ def get_description():
option1 = StrOption('unicode1', "Value 'test' must show Unicode 2")
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option1, todict=True),
kwargs={'condition': ParamOption(option1),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option2 = StrOption('unicode2', "Unicode 2", properties=(hidden_property,), multi=True)

View file

@ -10,7 +10,7 @@ def get_description():
option3 = StrOption('unicode3', "Unicode 3")
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option1, todict=True),
kwargs={'condition': ParamOption(option1),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
descr2 = OptionDescription("unicode1", "OptionDescription with 2 options", [option2, option3], properties=(hidden_property,))

View file

@ -7,7 +7,7 @@ def get_description():
option1 = StrOption('unicode1', "Value 'test' must show Unicode 2")
hidden_property = Calculation(calc_value,
Params(ParamValue('hidden'),
kwargs={'condition': ParamOption(option1, todict=True),
kwargs={'condition': ParamOption(option1),
'expected': ParamValue('test'),
'reverse_condition': ParamValue(True)}))
option2 = StrOption('unicode2', "Unicode 2", properties=(hidden_property,))

View file

@ -24,7 +24,7 @@ def datapath():
def list_data(ext='.py'):
# if ext == '.py':
# return ['choice1_leadership_hidden.py']
# return ['unicode1_leadership_requires.py']
datadir = datapath()
filenames = listdir(datadir)
filenames.sort()
@ -40,7 +40,8 @@ def load_config(filename,
add_extra_od=False,
remote='minimum',
clearable='minimum',
root=None):
root=None,
):
modulepath = splitext(filename)[0]
mod = __import__(modulepath)
descr = mod.get_description()
@ -169,7 +170,8 @@ def test_jsons(filename):
print(" {} (remote: {}, clearable: {})".format(filename, remote, clearable))
values = load_config(filename,
remote=remote,
clearable=clearable)
clearable=clearable,
)
#
if not isfile(join(datadir, modulepath + '.json')) and \
clearable == 'minimum' and \
@ -216,6 +218,27 @@ def test_jsons(filename):
assert values == expected, "error in file {}".format(filename)
def loads_yml(fh, issub, modulepath):
dico = loads(fh.read())
if issub:
new_dico_ori = {}
for key, value in dico.items():
key = modulepath + '.' + key
if isinstance(value, list):
new_value = []
for val in value:
if isinstance(val, dict):
new_val = {}
for k, v in val.items():
new_val[modulepath + '.' + k] = v
val = new_val
new_value.append(val)
value = new_value
new_dico_ori[key] = value
dico = new_dico_ori
return dico
def test_jsons_subconfig(filename):
debug = False
# debug = True
@ -311,13 +334,7 @@ def test_updates(filename_mod):
dico_ori = None
else:
with open(join(datadir, modulepath + '.dict'), 'r') as fh:
dico_ori = loads(fh.read())
if issub:
new_dico_ori = {}
for key, value in dico_ori.items():
key = modulepath + '.' + key
new_dico_ori[key] = value
dico_ori = new_dico_ori
dico_ori = loads_yml(fh, issub, modulepath)
# modify config
with open(join(datadir, modulepath + '.mod{}'.format(idx)), 'r') as fh:
body = loads(fh.read())['body']
@ -343,13 +360,7 @@ def test_updates(filename_mod):
dico_mod = None
else:
with open(join(datadir, modulepath + '.dict{}'.format(idx)), 'r') as fh:
dico_mod = loads(fh.read())
if issub:
new_dico = {}
for key, value in dico_mod.items():
key = modulepath + '.' + key
new_dico[key] = value
dico_mod = new_dico
dico_mod = loads_yml(fh, issub, modulepath)
if root is None:
root_path = ''
else:

View file

@ -232,8 +232,9 @@ def test_leader_list(config_type):
assert ret[0].name() == 'leadership'
#
ret = cfg.option('leadership').list('all')
assert len(ret) == 1
assert len(ret) == 2
assert ret[0].name() == 'ip_admin_eth0'
assert ret[1].name() == 'netmask_admin_eth0'
#
cfg.option('leadership.ip_admin_eth0').value.set(['a', 'b'])
cfg.option('leadership.netmask_admin_eth0', 0).value.set('c')

View file

@ -58,8 +58,8 @@ def test_mod_read_only_write():
od1 = OptionDescription("options", "", [s])
config = Config(od1)
config2 = Config(od1)
assert config.property.getdefault() == {'cache', 'validator', 'warnings'}
assert config.property.getdefault('read_only', 'append') == {'frozen',
assert config.property.default() == {'cache', 'validator', 'warnings'}
assert config.property.default('read_only', 'append') == {'frozen',
'disabled',
'validator',
'everything_frozen',
@ -67,16 +67,16 @@ def test_mod_read_only_write():
'empty',
'force_store_value',
}
assert config.property.getdefault('read_only', 'remove') == {'permissive',
assert config.property.default('read_only', 'remove') == {'permissive',
'hidden',
}
assert config.property.getdefault('read_write', 'append') == {'frozen',
assert config.property.default('read_write', 'append') == {'frozen',
'disabled',
'validator',
'hidden',
'force_store_value',
}
assert config.property.getdefault('read_write', 'remove') == {'permissive',
assert config.property.default('read_write', 'remove') == {'permissive',
'everything_frozen',
'mandatory',
'empty',
@ -94,12 +94,12 @@ def test_mod_read_only_write():
with pytest.raises(TypeError):
config.property.setdefault(type='read_only', when='append', properties=['disabled'])
assert config.property.getdefault() == {'cache'}
assert config.property.getdefault('read_only', 'append') == {'disabled'}
assert config.property.getdefault('read_only', 'remove') == {'hidden'}
assert config.property.getdefault('read_write', 'append') == {'disabled',
assert config.property.default() == {'cache'}
assert config.property.default('read_only', 'append') == {'disabled'}
assert config.property.default('read_only', 'remove') == {'hidden'}
assert config.property.default('read_write', 'append') == {'disabled',
'hidden'}
assert config.property.getdefault('read_write', 'remove') == set([])
assert config.property.default('read_write', 'remove') == set([])
#
config.property.read_only()
assert config.property.get() == {'cache', 'disabled'}
@ -108,29 +108,33 @@ def test_mod_read_only_write():
config.property.read_only()
assert config.property.get() == {'cache', 'disabled'}
#
assert config2.property.getdefault() == {'cache', 'validator', 'warnings'}
assert config2.property.getdefault('read_only', 'append') == {'frozen',
assert config2.property.default() == {'cache', 'validator', 'warnings'}
assert config2.property.default('read_only', 'append') == {'frozen',
'disabled',
'validator',
'everything_frozen',
'mandatory',
'empty',
'force_store_value'}
assert config2.property.getdefault('read_only', 'remove') == {'permissive',
'hidden'}
assert config2.property.getdefault('read_write', 'append') == {'frozen',
'force_store_value',
}
assert config2.property.default('read_only', 'remove') == {'permissive',
'hidden',
}
assert config2.property.default('read_write', 'append') == {'frozen',
'disabled',
'validator',
'hidden',
'force_store_value'}
assert config2.property.getdefault('read_write', 'remove') == {'permissive',
'force_store_value',
}
assert config2.property.default('read_write', 'remove') == {'permissive',
'everything_frozen',
'mandatory',
'empty'}
'empty',
}
with pytest.raises(ValueError):
config2.property.getdefault('unknown', 'remove')
config2.property.default('unknown', 'remove')
with pytest.raises(ValueError):
config2.property.getdefault('read_write', 'unknown')
config2.property.default('read_write', 'unknown')
# assert not list_sessions()

View file

@ -30,7 +30,7 @@ from .option import RegexpOption, OptionDescription, ChoiceOption
from .todict import TiramisuDict
TIRAMISU_VERSION = 3
TIRAMISU_VERSION = 4
class TiramisuHelp:
@ -166,7 +166,7 @@ class CommonTiramisuOption(CommonTiramisu):
self._config_bag = config_bag
def __getattr__(self, subfunc):
raise ConfigError(_('please specify a valid sub function ({})').format(subfunc))
raise ConfigError(_(f'please specify a valid sub function ({self.__class__.__name__}.{subfunc})'))
class _TiramisuOptionWalk:
@ -188,6 +188,7 @@ class _TiramisuOptionWalk:
recursive=recursive,
types=types,
group_type=group_type,
flatten_leadership=True,
):
if isinstance(option_bag, dict):
for opts_bag in option_bag.values():
@ -214,7 +215,7 @@ class _TiramisuOptionOptionDescription:
"""Manage option"""
_validate_properties = False
@option_type(['optiondescription', 'option', 'with_or_without_index'])
@option_type(['optiondescription', 'option', 'with_or_without_index', 'symlink'])
def get(self, options_bag: List[OptionBag]):
"""Get Tiramisu option"""
option_bag = options_bag[-1]
@ -277,7 +278,7 @@ class _TiramisuOptionOptionDescription:
option_bag = options_bag[-1]
return option_bag.option.impl_is_optiondescription()
@option_type(['optiondescription', 'option', 'with_index'])
@option_type(['optiondescription', 'option', 'with_or_without_index', 'symlink'])
def properties(self,
options_bag: List[OptionBag],
only_raises=False,
@ -319,7 +320,7 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
"""Test if option is a leader"""
return options_bag[-1].option.impl_is_leader()
@option_type(['option', 'with_or_without_index'])
@option_type(['option', 'with_or_without_index', 'symlink'])
def isfollower(self, options_bag: List[OptionBag]):
"""Test if option is a follower"""
return options_bag[-1].option.impl_is_follower()
@ -362,12 +363,12 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
type = option.get_type()
if isinstance(option, RegexpOption):
return option._regexp.pattern
if type == 'integer':
if type == _('integer'):
# FIXME negative too!
return r'^[0-9]+$'
if type == 'domainname':
if type == _('domain name'):
return option.impl_get_extra('_domain_re').pattern
if type in ['ip', 'network', 'netmask']:
if type in [_('ip'), _('network'), _('netmask')]:
#FIXME only from 0.0.0.0 to 255.255.255.255
return r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
@ -439,7 +440,7 @@ class TiramisuOptionProperty(CommonTiramisuOption):
"""Manage option's property"""
_validate_properties = False
@option_type(['option', 'optiondescription', 'with_index'])
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def get(self,
options_bag: List[OptionBag],
only_raises=False,
@ -495,11 +496,10 @@ class TiramisuOptionProperty(CommonTiramisuOption):
class TiramisuOptionPermissive(CommonTiramisuOption):
"""Manage option's permissive"""
@option_type(['option', 'optiondescription', 'symlink', 'with_index'])
@option_type(['option', 'optiondescription', 'symlink', 'with_or_without_index'])
def get(self, options_bag: List[OptionBag]):
"""Get permissives value"""
option_bag = options_bag[-1]
return self._config_bag.context.get_settings().getpermissives(option_bag)
return self._config_bag.context.get_settings().getpermissives(options_bag[-1])
@option_type(['option', 'optiondescription', 'with_or_without_index'])
def set(self,
@ -522,7 +522,7 @@ class TiramisuOptionPermissive(CommonTiramisuOption):
class TiramisuOptionInformation(CommonTiramisuOption):
"""Manage option's informations"""
@option_type(['option', 'optiondescription', 'with_or_without_index'])
@option_type(['option', 'optiondescription', 'with_or_without_index', 'symlink'])
def get(self,
options_bag: List[OptionBag],
name: str,
@ -561,7 +561,7 @@ class TiramisuOptionInformation(CommonTiramisuOption):
path=option_bag.path,
)
@option_type(['option', 'optiondescription', 'with_or_without_index'])
@option_type(['option', 'optiondescription', 'with_or_without_index', 'symlink'])
def list(self,
options_bag: List[OptionBag],
) -> list:
@ -662,7 +662,7 @@ class TiramisuOptionValue(CommonTiramisuOption):
return False
return True
@option_type(['choice', 'with_index'])
@option_type(['choice', 'with_or_without_index'])
def list(self, options_bag: List[OptionBag]):
"""All values available for a ChoiceOption"""
option_bag = options_bag[-1]
@ -754,7 +754,7 @@ class TiramisuOption(CommonTiramisu, _TiramisuOptionOption, TiramisuConfig):
self._index,
self._config_bag,
)
raise ConfigError(_('please specify a valid sub function ({})').format(subfunc))
raise ConfigError(_(f'please specify a valid sub function ({self.__class__.__name__}.{subfunc})'))
@option_type('optiondescription')
def find(self,
@ -793,7 +793,7 @@ class TiramisuOption(CommonTiramisu, _TiramisuOptionOption, TiramisuConfig):
recursive=False,
group_type=None,
):
"""List options (by default list only option)"""
"""List options inside an option description (by default list only option)"""
return self._list(options_bag[-1],
type,
group_type,
@ -804,10 +804,9 @@ class TiramisuOption(CommonTiramisu, _TiramisuOptionOption, TiramisuConfig):
clearable: str="all",
remotable: str="minimum",
):
root = self._option_bag.option.impl_getpath()
config = self._option_bag.config_bag.context
config = self._config_bag.context
self._tiramisu_dict = TiramisuDict(self._return_config(config),
root=root,
root=self._path,
clearable=clearable,
remotable=remotable)
@ -1098,14 +1097,15 @@ class TiramisuContextProperty(TiramisuConfig):
else:
raise ValueError(_('unknown type {}').format(type))
def getdefault(self,
def default(self,
type: Optional[str]=None,
when: Optional[str]=None,
) -> Set[str]:
setting = self._config_bag.context.get_settings()
if type is None and when is None:
return setting.default_properties
if type == 'current':
return setting.get_context_properties(self._config_bag.context.properties_cache)
if when not in ['append', 'remove']:
raise ValueError(_('unknown when {} (must be in append or remove)').format(when))
if type == 'read_only':
@ -1448,7 +1448,7 @@ class TiramisuAPI(TiramisuHelp):
# del config_bag.permissives
return self._registers[subfunc](config_bag,
self._orig_config_bags)
raise ConfigError(_('please specify a valid sub function ({})').format(subfunc))
raise ConfigError(_(f'please specify a valid sub function ({self.__class__.__name__}.{subfunc})'))
def __dir__(self):
return list(self._registers.keys()) + \
@ -1568,7 +1568,8 @@ class MixConfig(TiramisuAPI):
permissives = settings.get_context_permissives()
config_bag = ConfigBag(config,
properties=properties,
permissives=permissives)
permissives=permissives,
)
super().__init__(config_bag)

View file

@ -321,6 +321,7 @@ class _SubConfig:
group_type=None,
recursive: bool=True,
walked: bool=False,
flatten_leadership: bool=False,
):
"""walk to tree
"""
@ -334,7 +335,7 @@ class _SubConfig:
if not recursive:
# it's not recursive, so stop to walk
return
if not option_bag.option.impl_is_leadership():
if not option_bag.option.impl_is_leadership() or flatten_leadership:
for opt in option_bag.option.get_children(option_bag.config_bag):
try:
yield from self.walk(self.get_sub_option_bag(option_bag, # pylint: disable=no-member

View file

@ -10,16 +10,9 @@ from . import SynDynOption, RegexpOption, ChoiceOption, ParamOption
from .i18n import _
TYPES = {'SymLinkOption': 'symlink',
'IntOption': 'integer',
'FloatOption': 'integer',
'ChoiceOption': 'choice',
'BoolOption': 'boolean',
'PasswordOption': 'password',
'PortOption': 'integer',
'DateOption': 'date',
'DomainnameOption': 'domainname',
'StrOption': 'string'
CONVERT_WEB_TYPE = {'bool': 'boolean',
'str': 'string',
'int': 'integer',
}
INPUTS = ['string',
'integer',
@ -48,16 +41,16 @@ class Callbacks(object):
childapi,
schema,
force_store_value):
if self.remotable == 'all' or childapi.option.isoptiondescription():
if self.remotable == 'all' or childapi.isoptiondescription():
return
# callback, callback_params = childapi.option.callbacks()
# callback, callback_params = childapi.callbacks()
# if callback is None: # FIXME ? and force_store_value and self.clearable != 'all':
# return
# self.callbacks.append((callback, callback_params, path, childapi, schema, force_store_value))
def process_properties(self, form):
for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
if childapi.option.isfollower():
if childapi.isfollower():
self.tiramisu_web.set_remotable(path, form, childapi)
continue
has_option = False
@ -65,10 +58,10 @@ class Callbacks(object):
for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
if isinstance(callback_param, ParamOption):
has_option = True
if 'expire' in childapi.option.properties():
self.tiramisu_web.set_remotable(callback_param.option.impl_getpath(), form)
if 'expire' in childapi.properties():
self.tiramisu_web.set_remotable(callback_param.impl_getpath(), form)
if not has_option and form.get(path, {}).get('remote', False) == False:
if 'expire' in childapi.option.properties():
if 'expire' in childapi.properties():
self.tiramisu_web.set_remotable(path, form, childapi)
elif childapi.owner.isdefault():
# get calculated value and set clearable
@ -82,7 +75,7 @@ class Callbacks(object):
# if callback_params is not None:
# for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
# if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy':
# opt_path = callback_param.option.impl_getpath()
# opt_path = callback_param.impl_getpath()
# if form.get(opt_path, {}).get('remote') is not True:
# form.setdefault(opt_path, {})
# form[opt_path].setdefault('copy', []).append(path)
@ -100,8 +93,8 @@ class Consistencies(object):
def add(self, path, childapi, form):
return
if not childapi.option.isoptiondescription():
for consistency in childapi.option.consistencies():
if not childapi.isoptiondescription():
for consistency in childapi.consistencies():
cons_id, func, all_cons_opts, params = consistency
if func == '_cons_not_equal' and params.get('transitive', True) is True:
options_path = []
@ -141,15 +134,15 @@ class Requires(object):
self.action_hide = self.tiramisu_web.config._config_bag.properties
def set_master_remote(self, childapi, path, form):
if childapi.option.isoptiondescription():
if childapi.isoptiondescription():
isfollower = False
else:
isfollower = childapi.option.isfollower()
isfollower = childapi.isfollower()
if isfollower:
parent_path = path.rsplit('.', 1)[0]
parent = self.tiramisu_web.config.unrestraint.option(parent_path)
leader = parent.list()[0]
self.tiramisu_web.set_remotable(leader.option.path(), form, leader)
self.tiramisu_web.set_remotable(leader.path(), form, leader)
def manage_requires(self,
childapi,
@ -157,7 +150,7 @@ class Requires(object):
form,
current_action,
):
for requires in childapi.option.properties(uncalculated=True):
for requires in childapi.properties(uncalculated=True):
if not isinstance(requires, str):
option = requires.params.kwargs['condition'].option
expected = [requires.params.kwargs['expected'].value]
@ -210,9 +203,9 @@ class Requires(object):
if isinstance(option, ChoiceOption):
require_option = self.tiramisu_web.config.unrestraint.option(option_path)
values = self.tiramisu_web.get_enum(require_option,
require_option.option.ismulti(),
require_option.ismulti(),
option_path,
require_option.option.properties())
require_option.properties())
for value in values:
if value not in expected:
self.requires.setdefault(path,
@ -238,7 +231,7 @@ class Requires(object):
def add(self, path, childapi, form):
#collect id of all options
child = childapi.option.get()
child = childapi.get()
if isinstance(child, SynDynOption):
child = child.opt
self.options[child] = path
@ -247,7 +240,8 @@ class Requires(object):
self.manage_requires(childapi,
path,
form,
current_action)
current_action,
)
def process(self, form):
dependencies = {}
@ -337,15 +331,15 @@ class TiramisuDict:
obj['help'] = hlp
def get_list(self, root, subchildapi):
ret = []
#ret = []
for childapi in subchildapi.list('all'):
childname = childapi.option.name()
childname = childapi.name()
if root is None:
path = childname
else:
path = root + '.' + childname
ret.append((path, childapi))
return ret
yield (path, childapi)
#return ret
def is_remote(self, path, form):
if self.remotable == 'all':
@ -359,11 +353,11 @@ class TiramisuDict:
form.setdefault(path, {})['remote'] = True
if childapi is None:
childapi = self.config.unrestraint.option(path)
if childapi.option.isfollower():
if childapi.isfollower():
parent_path = path.rsplit('.', 1)[0]
parent = self.config.unrestraint.option(parent_path)
leader = parent.list()[0]
form.setdefault(leader.option.path(), {})['remote'] = True
form.setdefault(leader.path(), {})['remote'] = True
def walk(self,
root,
@ -391,13 +385,16 @@ class TiramisuDict:
subchildapi = self.config.unrestraint.option(root)
isleadership = False
else:
isleadership = subchildapi.option.isleadership()
isleadership = subchildapi.isleadership()
leader_len = None
for path, childapi in self.get_list(root, subchildapi):
if isleadership and leader_len is None:
leader_len = childapi.value.len()
one_is_remote = False
props_no_requires = set(childapi.option.properties())
if not childapi.isoptiondescription() and childapi.isfollower():
props_no_requires = set()
else:
props_no_requires = set(childapi.properties())
if form is not None:
self.requires.add(path,
childapi,
@ -412,8 +409,7 @@ class TiramisuDict:
schema,
'force_store_value' in props_no_requires,
)
childapi_option = childapi.option
if model is not None and childapi.option.isoptiondescription() or not childapi_option.issymlinkoption():
if model is not None and childapi.isoptiondescription() or not childapi.issymlinkoption():
self.gen_model(model,
childapi,
path,
@ -422,9 +418,9 @@ class TiramisuDict:
)
if order is not None:
order.append(path)
if childapi.option.isoptiondescription():
if childapi.isoptiondescription():
web_type = 'optiondescription'
if childapi_option.isleadership():
if childapi.isleadership():
type_ = 'array'
else:
type_ = 'object'
@ -443,33 +439,32 @@ class TiramisuDict:
updates_status,
)
else:
child = childapi_option.get()
child = childapi.get()
childtype = child.__class__.__name__
if childtype == 'SynDynOption':
childtype = child.opt.__class__.__name__
if childapi_option.issymlinkoption():
if childapi.issymlinkoption():
web_type = 'symlink'
value = None
defaultmulti = None
is_multi = False
else:
web_type = childapi_option.type()
value = childapi.option.default()
web_type = childapi.get().__class__.__name__.lower()[:-6]
web_type = CONVERT_WEB_TYPE.get(web_type, web_type)
value = childapi.default()
if value == []:
value = None
is_multi = childapi_option.ismulti()
is_multi = childapi.ismulti()
if is_multi:
defaultmulti = childapi_option.defaultmulti()
defaultmulti = childapi.defaultmulti()
if defaultmulti == []:
defaultmulti = None
else:
defaultmulti = None
if schema is not None:
self.gen_schema(schema,
childapi,
childapi_option,
path,
props_no_requires,
value,
@ -483,12 +478,12 @@ class TiramisuDict:
web_type,
path,
child,
childapi_option,
childapi,
childtype,
)
if schema is not None:
if web_type != 'symlink':
schema[path]['title'] = childapi_option.description()
schema[path]['title'] = childapi.description()
self.add_help(schema[path],
childapi)
except Exception as err:
@ -513,7 +508,6 @@ class TiramisuDict:
def gen_schema(self,
schema,
childapi,
childapi_option,
path,
props_no_requires,
value,
@ -523,8 +517,8 @@ class TiramisuDict:
form,
):
schema[path] = {'type': web_type}
if childapi_option.issymlinkoption():
sym_option = childapi_option.get()
if childapi.issymlinkoption():
sym_option = childapi.get()
schema[path]['opt_path'] = sym_option.impl_getopt().impl_getpath()
else:
if defaultmulti is not None:
@ -533,7 +527,7 @@ class TiramisuDict:
if is_multi:
schema[path]['isMulti'] = is_multi
if childapi_option.issubmulti():
if childapi.issubmulti():
schema[path]['isSubMulti'] = True
if 'auto_freeze' in props_no_requires:
@ -561,7 +555,7 @@ class TiramisuDict:
props_no_requires,
):
values = childapi.value.list()
empty_is_required = not childapi.option.isfollower() and is_multi
empty_is_required = not childapi.isfollower() and is_multi
if '' not in values and ((empty_is_required and not 'empty' in props_no_requires) or \
(not empty_is_required and not 'mandatory' in props_no_requires)):
values = [''] + list(values)
@ -572,13 +566,13 @@ class TiramisuDict:
web_type,
path,
child,
childapi_option,
childapi,
childtype,
):
obj_form = {}
if path in form:
obj_form.update(form[path])
if not childapi_option.issymlinkoption():
if not childapi.issymlinkoption():
#if childapi_option.validator() != (None, None):
# obj_form['remote'] = True
# params = childapi_option.validator()[1]
@ -590,14 +584,14 @@ class TiramisuDict:
obj_form['clearable'] = True
if self.clearable != 'none':
obj_form['clearable'] = True
if self.remotable == 'all' or childapi_option.has_dependency():
if self.remotable == 'all' or childapi.has_dependency():
obj_form['remote'] = True
if childtype == 'IPOption' and (child.impl_get_extra('_private_only') or not child.impl_get_extra('_allow_reserved') or child.impl_get_extra('_cidr')):
obj_form['remote'] = True
if childtype == 'DateOption':
obj_form['remote'] = True
if not obj_form.get('remote', False):
pattern = childapi_option.pattern()
pattern = childapi.pattern()
if pattern is not None:
obj_form['pattern'] = pattern
if childtype == 'PortOption':
@ -619,10 +613,11 @@ class TiramisuDict:
old_properties = childapi._config_bag.properties
config = childapi._config_bag.context
settings = config.get_settings()
childapi._config_bag.properties = self.config.property.get(default=True) # settings.get_context_properties(config._impl_properties_cache)
childapi._config_bag.properties = self.config.property.default('current') # settings.get_context_properties(config._impl_properties_cache)
childapi._config_bag.properties -= {'permissive'}
properties = childapi.property.get(only_raises=True,
uncalculated=True)
uncalculated=True,
)
properties -= childapi.permissive.get()
# 'hidden=True' means cannot access with or without permissive option
# 'display=False' means cannot access only without permissive option
@ -638,11 +633,11 @@ class TiramisuDict:
path,
index,
):
isfollower = childapi.option.isfollower()
isfollower = childapi.isfollower()
props = set(childapi.property.get())
obj = self.gen_properties(props,
isfollower,
childapi.option.ismulti(),
childapi.ismulti(),
index)
self.calc_raises_properties(obj, childapi)
return obj
@ -687,7 +682,7 @@ class TiramisuDict:
leader_len,
updates_status,
):
if childapi.option.isoptiondescription():
if childapi.isoptiondescription():
props = set(childapi.property.get())
obj = {}
self.calc_raises_properties(obj, childapi)
@ -696,7 +691,7 @@ class TiramisuDict:
lprops.sort()
obj['properties'] = lprops
try:
self.config.option(path).option.get()
self.config.option(path).get()
except PropertiesOptionError:
pass
else:
@ -704,7 +699,7 @@ class TiramisuDict:
path,
None,
)
if childapi.option.isfollower():
if childapi.isfollower():
for index in range(leader_len):
follower_childapi = self.config.unrestraint.option(path, index)
sobj = self._gen_model_properties(follower_childapi,
@ -727,7 +722,7 @@ class TiramisuDict:
updates_status,
)
if obj:
if not childapi.option.isoptiondescription() and childapi.option.isfollower():
if not childapi.isoptiondescription() and childapi.isfollower():
model.setdefault(path, {})['null'] = obj
else:
model[path] = obj
@ -829,9 +824,9 @@ class TiramisuDict:
return ret
def del_value(self, childapi, path, index):
if index is not None and childapi.option.isleader():
if index is not None and childapi.isleader():
childapi.value.pop(index)
elif index is None or childapi.option.isfollower():
elif index is None or childapi.isfollower():
childapi.value.reset()
else:
multi = childapi.value.get()
@ -844,7 +839,7 @@ class TiramisuDict:
childapi.value.set(multi)
def mod_value(self, childapi, path, index, value):
if index is None or childapi.option.isfollower():
if index is None or childapi.isfollower():
childapi.value.set(value)
else:
multi = childapi.value.get()
@ -866,8 +861,7 @@ class TiramisuDict:
if oripath is not None and not path.startswith(oripath):
raise ValueError(_('not in current area'))
childapi = self.config.option(path)
childapi_option = childapi.option
if childapi_option.isfollower():
if childapi.isfollower():
childapi = self.config.option(path, index)
with warnings.catch_warnings(record=True) as warns:
try:
@ -883,7 +877,7 @@ class TiramisuDict:
index,
)
elif update['action'] == 'add':
if childapi_option.ismulti():
if childapi.ismulti():
self.add_value(childapi, path, update['value'])
else:
raise ValueError(_('only multi option can have action "add", but "{}" is not a multi').format(path))