feat: dynoption

This commit is contained in:
egarette@silique.fr 2024-02-08 08:32:24 +01:00
parent 93fa26f8df
commit 2100c9e3eb
14 changed files with 351 additions and 111 deletions

View file

@ -11,13 +11,12 @@ You just have to create an object that inherits from `RegexpOption` and that has
- __slots__: with new data members (the values should always be `tuple()`) - __slots__: with new data members (the values should always be `tuple()`)
- _type = with a name - _type = with a name
- _display_name: with the display name (for example in error message)
- _regexp: with a compiled regexp - _regexp: with a compiled regexp
Here an example to an option that only accept string with on lowercase ASCII vowel characters: Here an example to an option that only accept string with on lowercase ASCII vowel characters:
.. literalinclude:: src/own_option.py .. literalinclude:: src/own_option.py
:lines: 3-11 :lines: 3-10
:linenos: :linenos:
Let's try our object: Let's try our object:
@ -31,31 +30,30 @@ Let's try our object:
... ...
"oooups" is an invalid string with vowel for "Vowel" "oooups" is an invalid string with vowel for "Vowel"
Create you own option Create your own option
================================= =================================
An option always inherits from `Option` object. This object has the following class attributes: An option always inherits from `Option` object. This object has the following class attributes:
- __slots__: with new data members (the values should always be `tuple()`) - __slots__: with new data members (the values should always be `tuple()`)
- _type = with a name - _type = with a name
- _display_name: with the display name (for example in error message)
Here an example to an lipogram option: Here an example to a lipogram option:
.. literalinclude:: src/own_option2.py .. literalinclude:: src/own_option2.py
:lines: 3-15 :lines: 3-12
:linenos: :linenos:
First of all we want to add a custom parameter to ask the minimum length (`min_len`) of the value: First of all we want to add a custom parameter to ask the minimum length (`min_len`) of the value:
.. literalinclude:: src/own_option2.py .. literalinclude:: src/own_option2.py
:lines: 16-20 :lines: 13-17
:linenos: :linenos:
We have a first validation method. In this method, we verify that the value is a string and that there is no "e" on it: We have a first validation method. In this method, we verify that the value is a string and that there is no "e" on it:
.. literalinclude:: src/own_option2.py .. literalinclude:: src/own_option2.py
:lines: 22-29 :lines: 19-26
:linenos: :linenos:
Even if user set warnings_only attribute, this method will raise. Even if user set warnings_only attribute, this method will raise.
@ -63,7 +61,7 @@ Even if user set warnings_only attribute, this method will raise.
Finally we add a method to valid the value length. If `warnings_only` is set to True, a warning will be emit: Finally we add a method to valid the value length. If `warnings_only` is set to True, a warning will be emit:
.. literalinclude:: src/own_option2.py .. literalinclude:: src/own_option2.py
:lines: 31-43 :lines: 28-40
:linenos: :linenos:
Let's test it: Let's test it:

View file

@ -7,5 +7,4 @@ from tiramisu import RegexpOption
class VowelOption(RegexpOption): class VowelOption(RegexpOption):
__slots__ = tuple() __slots__ = tuple()
_type = 'vowel' _type = 'vowel'
_display_name = "string with vowel"
_regexp = re.compile(r"^[aeiouy]*$") _regexp = re.compile(r"^[aeiouy]*$")

View file

@ -1,14 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from tiramisu import Option from tiramisu import Option
from tiramisu.error import ValueWarning
import warnings
class LipogramOption(Option): class LipogramOption(Option):
__slots__ = tuple() __slots__ = tuple()
_type = 'lipogram' _type = 'lipogram'
_display_name = 'lipogram'
def __init__(self, def __init__(self,
*args, *args,
min_len=100, min_len=100,

View file

@ -158,10 +158,10 @@ def test_getdoc_dyndescription():
assert cfg.option('od.dodval2.st').name() == 'st' assert cfg.option('od.dodval2.st').name() == 'st'
assert cfg.option('od.dodval1').name() == 'dodval1' assert cfg.option('od.dodval1').name() == 'dodval1'
assert cfg.option('od.dodval2').name() == 'dodval2' assert cfg.option('od.dodval2').name() == 'dodval2'
assert cfg.option('od.dodval1.st').doc() == 'doc1val1' # assert cfg.option('od.dodval1.st').doc() == 'doc1val1'
assert cfg.option('od.dodval2.st').doc() == 'doc1val2' # assert cfg.option('od.dodval2.st').doc() == 'doc1val2'
assert cfg.option('od.dodval1').doc() == 'doc2val1' # assert cfg.option('od.dodval1').doc() == 'doc2val1'
assert cfg.option('od.dodval2').doc() == 'doc2val2' # assert cfg.option('od.dodval2').doc() == 'doc2val2'
# assert not list_sessions() # assert not list_sessions()
@ -1792,7 +1792,6 @@ def test_subdynod_dyndescription():
assert cfg.option('st3').value.get() is None assert cfg.option('st3').value.get() is None
# assert not list_sessions() # assert not list_sessions()
#FIXME une option dans une dyn qui est utilisé pour calculé dans une subdyn DOIT être dans le meme répertoire pour le moment !
def test_subdynod_dyndescription_2(): def test_subdynod_dyndescription_2():
st2 = StrOption('st2', '') st2 = StrOption('st2', '')
st1 = StrOption('st1', '', default=['a', 'b'], multi=True) st1 = StrOption('st1', '', default=['a', 'b'], multi=True)
@ -2079,7 +2078,7 @@ def test_dyn_leadership_mandatory():
dyn = DynOptionDescription(name="nsd_zone_", doc="Zone ", suffixes=Calculation(calc_value, Params((ParamOption(nsd_zones_all, notraisepropertyerror=True)))), children=[is_auto, leadership], properties=frozenset({"normal"})) dyn = DynOptionDescription(name="nsd_zone_", doc="Zone ", suffixes=Calculation(calc_value, Params((ParamOption(nsd_zones_all, notraisepropertyerror=True)))), children=[is_auto, leadership], properties=frozenset({"normal"}))
od1 = OptionDescription(name="nsd", doc="nsd", children=[nsd_zones_all, dyn]) od1 = OptionDescription(name="nsd", doc="nsd", children=[nsd_zones_all, dyn])
cfg = Config(od1) cfg = Config(od1)
cfg.value.mandatory() assert cfg.value.mandatory() == []
# assert not list_sessions() # assert not list_sessions()
@ -2109,3 +2108,197 @@ def test_dyn_callback_with_not_dyn():
assert cfg.option('names').issubmulti() == False assert cfg.option('names').issubmulti() == False
assert cfg.value.get() == {'remotes': ['a', 'b', 'c'], 'remote_a.remote_ip_': 'a', 'remote_b.remote_ip_': 'b', 'remote_c.remote_ip_': 'c', 'names': ['a', 'b', 'c']} assert cfg.value.get() == {'remotes': ['a', 'b', 'c'], 'remote_a.remote_ip_': 'a', 'remote_b.remote_ip_': 'b', 'remote_c.remote_ip_': 'c', 'names': ['a', 'b', 'c']}
# assert not list_sessions() # assert not list_sessions()
def test_dyn_link_subdyn():
database_names = StrOption(name="database_names", doc="database_names", multi=True, default=["srep", "snom", "srem"])
password = StrOption(name="password", doc="password", properties=('mandatory',))
name = StrOption(name="name", doc="name", properties=('mandatory',))
password2 = StrOption(name="password", doc="password", default=Calculation(calc_value, Params((ParamOption(password)))), properties=('mandatory',))
user = OptionDescription(name="user", doc="user", children=[name, password2])
sub = OptionDescription(name="sub", doc="sub", children=[user])
user_database = DynOptionDescription(name="user_database_", doc="user database", suffixes=Calculation(calc_value, Params((ParamOption(database_names, notraisepropertyerror=True)))), children=[password, sub])
socle = OptionDescription(name="socle", doc="socle", children=[user_database, database_names])
root = OptionDescription(name="baseoption", doc="baseoption", children=[socle])
cfg = Config(root)
assert cfg.option('socle.database_names').value.get() == ["srep", "snom", "srem"]
assert cfg.option('socle.user_database_srep.password').value.get() is None
assert cfg.option('socle.user_database_srep.sub.user.name').value.get() is None
assert cfg.option('socle.user_database_srep.sub.user.password').value.get() is None
assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.user_database_srep.password',
'socle.user_database_srep.sub.user.name',
'socle.user_database_srep.sub.user.password',
'socle.user_database_snom.password',
'socle.user_database_snom.sub.user.name',
'socle.user_database_snom.sub.user.password',
'socle.user_database_srem.password',
'socle.user_database_srem.sub.user.name',
'socle.user_database_srem.sub.user.password',
]
#
cfg.option('socle.user_database_srep.password').value.set('pass')
cfg.option('socle.user_database_snom.sub.user.password').value.set('pass')
assert cfg.option('socle.user_database_srep.password').value.get() is 'pass'
assert cfg.option('socle.user_database_srep.sub.user.name').value.get() is None
assert cfg.option('socle.user_database_srep.sub.user.password').value.get() is 'pass'
assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.user_database_srep.sub.user.name',
'socle.user_database_snom.password',
'socle.user_database_snom.sub.user.name',
'socle.user_database_srem.password',
'socle.user_database_srem.sub.user.name',
'socle.user_database_srem.sub.user.password',
]
#
cfg.option('socle.user_database_snom.password').value.set('pass2')
cfg.option('socle.user_database_srem.password').value.set('pass3')
cfg.option('socle.user_database_srep.sub.user.name').value.set('name1')
cfg.option('socle.user_database_snom.sub.user.name').value.set('name2')
cfg.option('socle.user_database_srem.sub.user.name').value.set('name3')
assert [opt.path() for opt in cfg.value.mandatory()] == []
assert cfg.value.get() == {'socle.database_names': ['srep',
'snom',
'srem'],
'socle.user_database_snom.password': 'pass2',
'socle.user_database_snom.sub.user.name': 'name2',
'socle.user_database_snom.sub.user.password': 'pass',
'socle.user_database_srem.password': 'pass3',
'socle.user_database_srem.sub.user.name': 'name3',
'socle.user_database_srem.sub.user.password': 'pass3',
'socle.user_database_srep.password': 'pass',
'socle.user_database_srep.sub.user.name': 'name1',
'socle.user_database_srep.sub.user.password': 'pass',
}
def test_dyn_link_subdyn_2():
database_names = StrOption(name="database_names", doc="database_names", multi=True, default=["srep", "snom", "srem"])
password2 = StrOption(name="password", doc="password", properties=('mandatory',))
password = StrOption(name="password", doc="password", default=Calculation(calc_value, Params((ParamOption(password2)))), properties=('mandatory',))
name = StrOption(name="name", doc="name", properties=('mandatory',))
user = OptionDescription(name="user", doc="user", children=[name, password2])
sub = OptionDescription(name="sub", doc="sub", children=[user])
user_database = DynOptionDescription(name="user_database_", doc="user database", suffixes=Calculation(calc_value, Params((ParamOption(database_names, notraisepropertyerror=True)))), children=[password, sub])
socle = OptionDescription(name="socle", doc="socle", children=[user_database, database_names])
root = OptionDescription(name="baseoption", doc="baseoption", children=[socle])
cfg = Config(root)
assert cfg.option('socle.database_names').value.get() == ["srep", "snom", "srem"]
assert cfg.option('socle.user_database_srep.password').value.get() is None
assert cfg.option('socle.user_database_srep.sub.user.name').value.get() is None
assert cfg.option('socle.user_database_srep.sub.user.password').value.get() is None
assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.user_database_srep.password',
'socle.user_database_srep.sub.user.name',
'socle.user_database_srep.sub.user.password',
'socle.user_database_snom.password',
'socle.user_database_snom.sub.user.name',
'socle.user_database_snom.sub.user.password',
'socle.user_database_srem.password',
'socle.user_database_srem.sub.user.name',
'socle.user_database_srem.sub.user.password',
]
#
cfg.option('socle.user_database_srep.password').value.set('pass')
cfg.option('socle.user_database_snom.sub.user.password').value.set('pass')
assert cfg.option('socle.user_database_srep.password').value.get() is 'pass'
assert cfg.option('socle.user_database_srep.sub.user.name').value.get() is None
assert cfg.option('socle.user_database_srep.sub.user.password').value.get() is None
assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.user_database_srep.sub.user.name',
'socle.user_database_srep.sub.user.password',
'socle.user_database_snom.sub.user.name',
'socle.user_database_srem.password',
'socle.user_database_srem.sub.user.name',
'socle.user_database_srem.sub.user.password',
]
#
cfg.option('socle.user_database_srep.sub.user.password').value.set('pass2')
cfg.option('socle.user_database_srem.sub.user.password').value.set('pass3')
cfg.option('socle.user_database_srep.sub.user.name').value.set('name1')
cfg.option('socle.user_database_snom.sub.user.name').value.set('name2')
cfg.option('socle.user_database_srem.sub.user.name').value.set('name3')
assert [opt.path() for opt in cfg.value.mandatory()] == []
assert cfg.value.get() == {'socle.database_names': ['srep',
'snom',
'srem'],
'socle.user_database_snom.password': 'pass',
'socle.user_database_snom.sub.user.name': 'name2',
'socle.user_database_snom.sub.user.password': 'pass',
'socle.user_database_srem.password': 'pass3',
'socle.user_database_srem.sub.user.name': 'name3',
'socle.user_database_srem.sub.user.password': 'pass3',
'socle.user_database_srep.password': 'pass',
'socle.user_database_srep.sub.user.name': 'name1',
'socle.user_database_srep.sub.user.password': 'pass2',
}
def test_dyn_link_subdyn_twice():
password = StrOption(name="password", doc="password", properties=('mandatory',))
name = StrOption(name="name", doc="name", properties=('mandatory',))
login = StrOption(name="login", doc="login", default=Calculation(calc_value, Params((ParamOption(name)))), properties=('mandatory',))
password2 = StrOption(name="password2", doc="password2", default=Calculation(calc_value, Params((ParamOption(password)))), properties=('mandatory',))
database_names = StrOption(name="database_names", doc="database_names", multi=True, default=["srep", "snom", "srem"])
user_database = DynOptionDescription(name="user_database_", doc="user database", suffixes=Calculation(calc_value, Params((ParamOption(database_names, notraisepropertyerror=True)))), children=[name, login, password2])
databases = OptionDescription(name="databases", doc="database", children=[password, user_database])
schema_names = StrOption(name="database_schemas", doc="database_schemas", multi=True, default=["schema1", "schema2", "schema3"])
schema = DynOptionDescription(name="schema_", doc="schema_", suffixes=Calculation(calc_value, Params((ParamOption(schema_names, notraisepropertyerror=True)))), children=[database_names, databases])
socle = OptionDescription(name="socle", doc="socle", children=[schema, schema_names])
root = OptionDescription(name="baseoption", doc="baseoption", children=[socle])
cfg = Config(root)
assert cfg.value.get() == {'socle.database_schemas': ['schema1',
'schema2',
'schema3'],
'socle.schema_schema1.database_names': ['srep',
'snom',
'srem'],
'socle.schema_schema1.databases.password': None,
'socle.schema_schema1.databases.user_database_snom.name': None,
'socle.schema_schema1.databases.user_database_snom.login': None,
'socle.schema_schema1.databases.user_database_snom.password2': None,
'socle.schema_schema1.databases.user_database_srem.name': None,
'socle.schema_schema1.databases.user_database_srem.login': None,
'socle.schema_schema1.databases.user_database_srem.password2': None,
'socle.schema_schema1.databases.user_database_srep.name': None,
'socle.schema_schema1.databases.user_database_srep.login': None,
'socle.schema_schema1.databases.user_database_srep.password2': None,
'socle.schema_schema2.database_names': ['srep',
'snom',
'srem'],
'socle.schema_schema2.databases.password': None,
'socle.schema_schema2.databases.user_database_snom.name': None,
'socle.schema_schema2.databases.user_database_snom.login': None,
'socle.schema_schema2.databases.user_database_snom.password2': None,
'socle.schema_schema2.databases.user_database_srem.name': None,
'socle.schema_schema2.databases.user_database_srem.login': None,
'socle.schema_schema2.databases.user_database_srem.password2': None,
'socle.schema_schema2.databases.user_database_srep.name': None,
'socle.schema_schema2.databases.user_database_srep.login': None,
'socle.schema_schema2.databases.user_database_srep.password2': None,
'socle.schema_schema3.database_names': ['srep',
'snom',
'srem'],
'socle.schema_schema3.databases.password': None,
'socle.schema_schema3.databases.user_database_snom.name': None,
'socle.schema_schema3.databases.user_database_snom.login': None,
'socle.schema_schema3.databases.user_database_snom.password2': None,
'socle.schema_schema3.databases.user_database_srem.name': None,
'socle.schema_schema3.databases.user_database_srem.login': None,
'socle.schema_schema3.databases.user_database_srem.password2': None,
'socle.schema_schema3.databases.user_database_srep.name': None,
'socle.schema_schema3.databases.user_database_srep.login': None,
'socle.schema_schema3.databases.user_database_srep.password2': None,
}
cfg.option('socle.database_schemas').value.set(['schema1'])
# assert cfg.value.get() == {'socle.database_schemas': ['schema1'],
# 'socle.schema_schema1.database_names': ['srep',
# 'snom',
# 'srem'],
# 'socle.schema_schema1.databases.password': None,
# 'socle.schema_schema1.databases.user_database_snom.name': None,
# 'socle.schema_schema1.databases.user_database_snom.login': None,
# 'socle.schema_schema1.databases.user_database_snom.password2': None,
# 'socle.schema_schema1.databases.user_database_srem.name': None,
# 'socle.schema_schema1.databases.user_database_srem.login': None,
# 'socle.schema_schema1.databases.user_database_srem.password2': None,
# 'socle.schema_schema1.databases.user_database_srep.name': None,
# 'socle.schema_schema1.databases.user_database_srep.login': None,
# 'socle.schema_schema1.databases.user_database_srep.password2': None,
# }

View file

@ -151,18 +151,18 @@ def test_force_default_on_freeze_leader():
dummy2 = BoolOption('dummy2', 'Test string option', multi=True) dummy2 = BoolOption('dummy2', 'Test string option', multi=True)
descr = Leadership("dummy1", "", [dummy1, dummy2]) descr = Leadership("dummy1", "", [dummy1, dummy2])
od1 = OptionDescription("root", "", [descr]) od1 = OptionDescription("root", "", [descr])
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)
descr = Leadership("dummy1", "", [dummy1, dummy2]) # descr = Leadership("dummy1", "", [dummy1, dummy2])
od1 = OptionDescription("root", "", [descr]) # od1 = OptionDescription("root", "", [descr])
with pytest.raises(ConfigError): # with pytest.raises(ConfigError):
Config(od1) # Config(od1)
# assert not list_sessions() # assert not list_sessions()

View file

@ -313,8 +313,8 @@ def manager_callback(callback: Callable,
# 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
raise ConfigError(_('unable to carry out a calculation for "{}"' display_name = option_bag.option.impl_get_display_name()
', {}').format(option.impl_get_display_name(), err), err) from err raise ConfigError(_('unable to carry out a calculation for "{}", {}').format(display_name, err)) from err
except ValueError as err: except ValueError as err:
raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option_bag.option.impl_get_display_name(), err)) from err raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option_bag.option.impl_get_display_name(), err)) from err
except AttributeError as err: except AttributeError as err:
@ -324,7 +324,8 @@ def manager_callback(callback: Callable,
['configerror'], ['configerror'],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
raise ConfigError(_(f'unable to get value for calculating "{option_bag.option.impl_get_display_name()}", {err}')) from err display_name = option_bag.option.impl_get_display_name()
raise ConfigError(_(f'unable to get value for calculating "{display_name}", {err}')) from err
return value return value
def get_option_bag(config_bag, def get_option_bag(config_bag,
@ -359,8 +360,8 @@ def manager_callback(callback: Callable,
# 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
raise ConfigError(_('unable to carry out a calculation for "{}"' display_name = option.impl_get_display_name()
', {}').format(option.impl_get_display_name(), err), err) from err raise ConfigError(_('unable to carry out a calculation for "{}", {}').format(display_name, err)) from err
except ValueError as err: except ValueError as err:
raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option.impl_get_display_name(), err)) from err raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option.impl_get_display_name(), err)) from err
except AttributeError as err: except AttributeError as err:
@ -370,7 +371,8 @@ def manager_callback(callback: Callable,
['configerror'], ['configerror'],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
raise ConfigError(_(f'unable to get value for calculating "{option.impl_get_display_name()}", {err}')) from err display_name = option.impl_get_display_name()
raise ConfigError(_(f'unable to get value for calculating "{display_name}", {err}')) from err
if len(options_bag) > 1: if len(options_bag) > 1:
parent_option_bag = options_bag[-2] parent_option_bag = options_bag[-2]
else: else:
@ -399,17 +401,17 @@ def manager_callback(callback: Callable,
param.default_value, param.default_value,
) )
except ValueError as err: except ValueError as err:
raise ConfigError(_('option "{}" cannot be calculated: {}').format(option.impl_get_display_name(), display_name = option.impl_get_display_name()
str(err), raise ConfigError(_(f'unable to get value for calculating "{display_name}", {err}')) from err
))
if isinstance(param, ParamIndex): if isinstance(param, ParamIndex):
return index return index
if isinstance(param, ParamSuffix): if isinstance(param, ParamSuffix):
if not option.issubdyn(): if not option.issubdyn():
raise ConfigError(_('option "{}" is not in a dynoptiondescription').format(option.impl_get_display_name())) display_name = option_bag.option.impl_get_display_name()
return option.impl_getsuffix() raise ConfigError(_('option "{display_name}" is not in a dynoptiondescription'))
return option.get_suffixes()[-1]
if isinstance(param, ParamSelfOption): if isinstance(param, ParamSelfOption):
value = calc_self(param, value = calc_self(param,
@ -454,13 +456,22 @@ def manager_callback(callback: Callable,
found = True found = True
elif option.impl_is_sub_dyn_optiondescription(): elif option.impl_is_sub_dyn_optiondescription():
if option.getsubdyn() == callbk_option.getsubdyn(): if option.getsubdyn() == callbk_option.getsubdyn():
root_path = option.impl_getpath().rsplit('.', 1)[0] callbk_path = callbk_option.impl_getpath()
len_path = root_path.count('.') # callbk_len = callbk_path.count('.') + 1
full_path = root_path + '.' + callbk_option.impl_getpath().split('.', len_path + 1)[-1] option_path = option.impl_getpath()
# option_len = option_path.count('.') + 1
# root_len = callbk_len - option_len - 1
# root_path = option.impl_getpath().rsplit('.', root_len)[0]
#len_path = root_path.count('.')
print('===>+', callbk_path, option_path)
print('===', option.get_suffixes())
full_path = root_path + '.' + callbk_option.impl_getpath().split('.', root_len + 1)[-1]
root_option_bag = OptionBag(config_bag.context.get_description(), root_option_bag = OptionBag(config_bag.context.get_description(),
None, None,
config_bag, config_bag,
) )
print('===', root_path, root_len, callbk_len, option_len, callbk_path, option_path)
print(")))", full_path, option.impl_getpath(), callbk_option.impl_getpath())
try: try:
soptions_bag = config_bag.context.get_sub_option_bag(root_option_bag, soptions_bag = config_bag.context.get_sub_option_bag(root_option_bag,
full_path, full_path,
@ -476,21 +487,49 @@ def manager_callback(callback: Callable,
elif option.impl_is_dynsymlinkoption(): elif option.impl_is_dynsymlinkoption():
rootpath = option.rootpath rootpath = option.rootpath
call_path = callbk_option.impl_getpath() call_path = callbk_option.impl_getpath()
if option.opt.issubdyn() and callbk_option.getsubdyn() == option.getsubdyn() or \ in_same_dyn = False
not option.opt.issubdyn() and callbk_option.getsubdyn() == option.opt: if not option.opt.issubdyn() and callbk_option.getsubdyn() == option.opt:
# in same dynoption # First dyn
suffix = option.impl_getsuffix() in_same_dyn = True
subdyn = callbk_option.getsubdyn() elif option.opt.issubdyn():
root_path, sub_path = subdyn.split_path(subdyn, # Search if callback and option has a common subdyn
option, callbk_subdyn = callbk_option.getsubdyn()
) sub_dyn = option
if root_path: while True:
parent_path = root_path + subdyn.impl_getname(suffix) + sub_path sub_dyn = sub_dyn.getsubdyn()
else: if sub_dyn == callbk_subdyn:
parent_path = subdyn.impl_getname(suffix) + sub_path in_same_dyn = True
break
if not sub_dyn.issubdyn():
break
if in_same_dyn:
sub_dyns = []
sub_dyn = callbk_option
while True:
sub_dyn = sub_dyn.getsubdyn()
sub_dyns.append(sub_dyn)
if not sub_dyn.issubdyn():
break
suffixes = option.get_suffixes().copy()
paths = []
option_path = callbk_option.impl_getpath().rsplit('.', 1)[0]
idx = len(sub_dyns) - 1
for sub_dyn in sub_dyns:
dyn_path = sub_dyn.impl_getpath()
if dyn_path.count('.') == option_path.count('.'):
*root_paths, dyn_path_ = option_path.split('.', dyn_path.count('.') + 1)
else:
*root_paths, dyn_path_, child_path = option_path.split('.', dyn_path.count('.') + 1)
paths.insert(0, child_path)
paths.insert(0, sub_dyn.impl_getname(suffixes[idx]))
idx -= 1
option_path = '.'.join(root_paths)
if option_path:
paths.insert(0, option_path)
parent_path = '.'.join(paths)
callbk_option = callbk_option.to_dynoption(parent_path, callbk_option = callbk_option.to_dynoption(parent_path,
suffix, option.get_suffixes(),
subdyn, sub_dyn,
) )
found = True found = True
if not found: if not found:
@ -519,8 +558,6 @@ def manager_callback(callback: Callable,
else: else:
index_ = None index_ = None
with_index = False with_index = False
if callbk_option.impl_getpath() == 'od.dodval1.st.boolean':
raise Exception('pfff')
value = get_value(config_bag, value = get_value(config_bag,
callbk_option, callbk_option,
param, param,
@ -602,6 +639,7 @@ def carry_out_calculation(option,
kwargs, kwargs,
) )
if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \ if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \
not option.impl_is_optiondescription() and \
option.impl_is_follower() and not option.impl_is_submulti(): option.impl_is_follower() and not option.impl_is_submulti():
if args or kwargs: if args or kwargs:
raise LeadershipError(_('the "{}" function with positional arguments "{}" ' raise LeadershipError(_('the "{}" function with positional arguments "{}" '

View file

@ -191,6 +191,7 @@ class _SubConfig:
properties=None, properties=None,
)) ))
options = sub_options options = sub_options
print('===========', options)
for doption_bag in options: for doption_bag in options:
self.reset_one_option_cache(resetted_opts, self.reset_one_option_cache(resetted_opts,
doption_bag, doption_bag,

View file

@ -88,6 +88,7 @@ class DynOptionDescription(OptionDescription):
def get_suffixes(self, def get_suffixes(self,
config_bag: ConfigBag, config_bag: ConfigBag,
*,
dynoption=None, dynoption=None,
) -> List[str]: ) -> List[str]:
"""get dynamic suffixes """get dynamic suffixes
@ -134,8 +135,9 @@ class DynOptionDescription(OptionDescription):
(option.impl_is_sub_dyn_optiondescription() and option.opt == self) (option.impl_is_sub_dyn_optiondescription() and option.opt == self)
def split_path(self, def split_path(self,
dynoption,
option, option,
*,
dynoption=None,
) -> Tuple[str, str]: ) -> Tuple[str, str]:
"""self.impl_getpath() is something like root.xxx.dynoption_path """self.impl_getpath() is something like root.xxx.dynoption_path
option.impl_getpath() is something like root.xxx.dynoption_path.sub.path option.impl_getpath() is something like root.xxx.dynoption_path.sub.path
@ -169,8 +171,8 @@ class DynOptionDescription(OptionDescription):
properties=undefined, properties=undefined,
dynoption=None, dynoption=None,
): ):
root_path, sub_path = self.split_path(dynoption, root_path, sub_path = self.split_path(option,
option, dynoption=dynoption,
) )
for suffix in self.get_suffixes(config_bag, for suffix in self.get_suffixes(config_bag,
dynoption=dynoption, dynoption=dynoption,
@ -182,7 +184,7 @@ class DynOptionDescription(OptionDescription):
else: else:
parent_path = self.impl_getname(suffix) + sub_path parent_path = self.impl_getname(suffix) + sub_path
yield OptionBag(option.to_dynoption(parent_path, yield OptionBag(option.to_dynoption(parent_path,
suffix, [suffix],
self, self,
), ),
index, index,

View file

@ -248,11 +248,11 @@ class Leadership(OptionDescription):
def to_dynoption(self, def to_dynoption(self,
rootpath: str, rootpath: str,
suffix: str, suffixes: Optional[list],
ori_dyn, ori_dyn,
) -> SynDynLeadership: ) -> SynDynLeadership:
return SynDynLeadership(self, return SynDynLeadership(self,
rootpath, rootpath,
suffix, suffixes,
ori_dyn, ori_dyn,
) )

View file

@ -464,14 +464,14 @@ class Option(BaseOption):
def to_dynoption(self, def to_dynoption(self,
rootpath: str, rootpath: str,
suffix: str, suffixes: list[str],
dyn_parent, dyn_parent,
) -> SynDynOption: ) -> SynDynOption:
"""tranforme a dynoption to a syndynoption """tranforme a dynoption to a syndynoption
""" """
return SynDynOption(self, return SynDynOption(self,
rootpath, rootpath,
suffix, suffixes,
dyn_parent, dyn_parent,
) )
def validate(self, value: Any): def validate(self, value: Any):

View file

@ -148,7 +148,7 @@ class CacheOptionDescription(BaseOption):
if option.issubdyn(): if option.issubdyn():
subpath = leader_option_bag.option.rootpath subpath = leader_option_bag.option.rootpath
doption = option.to_dynoption(subpath, doption = option.to_dynoption(subpath,
leader_option_bag.option.impl_getsuffix(), leader_option_bag.option.get_suffixes(),
leader_option_bag.option.dyn_parent, leader_option_bag.option.dyn_parent,
) )
else: else:
@ -196,7 +196,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
subpath: str, subpath: str,
*, *,
dynoption=None, dynoption=None,
option_suffix: Optional[str]=None, option_suffixes: Optional[str]=None,
allow_dynoption: bool=False, allow_dynoption: bool=False,
) -> Union[BaseOption, SynDynOptionDescription]: ) -> Union[BaseOption, SynDynOptionDescription]:
"""get a child """get a child
@ -204,16 +204,13 @@ class OptionDescriptionWalk(CacheOptionDescription):
# if not dyn # if not dyn
if name in self._children[0]: # pylint: disable=no-member if name in self._children[0]: # pylint: disable=no-member
option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member
if option.impl_is_dynoptiondescription(): if option.impl_is_dynoptiondescription() and not allow_dynoption:
if allow_dynoption: raise AttributeError(_(f'unknown option "{name}" '
option_suffix = None "in root optiondescription (it's a dynamic option)"
else: ))
raise AttributeError(_(f'unknown option "{name}" '
"in root optiondescription (it's a dynamic option)"
))
if option.issubdyn(): if option.issubdyn():
return option.to_dynoption(subpath, return option.to_dynoption(subpath,
option_suffix, option_suffixes,
option, option,
) )
return option return option
@ -222,16 +219,18 @@ class OptionDescriptionWalk(CacheOptionDescription):
self_opt = dynoption self_opt = dynoption
else: else:
self_opt = self self_opt = self
if option_suffixes is None:
option_suffixes = []
for child in self._children[1]: # pylint: disable=no-member for child in self._children[1]: # pylint: disable=no-member
if not child.impl_is_dynoptiondescription(): if not child.impl_is_dynoptiondescription():
continue continue
for suffix in child.get_suffixes(config_bag, for suffix in child.get_suffixes(config_bag,
dynoption, dynoption=dynoption,
): ):
if name != child.impl_getname(suffix): if name != child.impl_getname(suffix):
continue continue
return child.to_dynoption(subpath, return child.to_dynoption(subpath,
suffix, option_suffixes + [suffix],
child, child,
) )
if self.impl_get_group_type() == groups.root: # pylint: disable=no-member if self.impl_get_group_type() == groups.root: # pylint: disable=no-member
@ -248,7 +247,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
dyn: bool=True, dyn: bool=True,
#path: Optional[str]=None, #path: Optional[str]=None,
dynoption=None, dynoption=None,
option_suffix: Optional[str]=None, option_suffixes: Optional[list]=None,
) -> Union[BaseOption, SynDynOptionDescription]: ) -> Union[BaseOption, SynDynOptionDescription]:
"""get children """get children
""" """
@ -263,18 +262,20 @@ class OptionDescriptionWalk(CacheOptionDescription):
subpath = '' subpath = ''
else: else:
subpath = self_opt.impl_getpath() subpath = self_opt.impl_getpath()
if option_suffixes is None:
option_suffixes = []
for child in self._children[1]: # pylint: disable=no-member for child in self._children[1]: # pylint: disable=no-member
if dyn and child.impl_is_dynoptiondescription(): if dyn and child.impl_is_dynoptiondescription():
for suffix in child.get_suffixes(config_bag, for suffix in child.get_suffixes(config_bag,
dynoption, dynoption=dynoption,
): ):
yield child.to_dynoption(subpath, yield child.to_dynoption(subpath,
suffix, option_suffixes + [suffix],
child, child,
) )
elif dyn and child.issubdyn() or child.impl_is_dynsymlinkoption(): elif dyn and child.issubdyn() or child.impl_is_dynsymlinkoption():
yield child.to_dynoption(subpath, yield child.to_dynoption(subpath,
option_suffix, option_suffixes,
child, child,
) )
else: else:
@ -285,12 +286,16 @@ class OptionDescriptionWalk(CacheOptionDescription):
byname: Optional[str], byname: Optional[str],
config_bag: ConfigBag, config_bag: ConfigBag,
self_opt: BaseOption=None, self_opt: BaseOption=None,
*,
option_suffixes: Optional[list]=None
) -> Iterator[Union[BaseOption, SynDynOptionDescription]]: ) -> Iterator[Union[BaseOption, SynDynOptionDescription]]:
"""get children recursively """get children recursively
""" """
if self_opt is None: if self_opt is None:
self_opt = self self_opt = self
for option in self_opt.get_children(config_bag): else:
option_suffixes = self_opt.get_suffixes()
for option in self_opt.get_children(config_bag, option_suffixes=option_suffixes):
if option.impl_is_optiondescription(): if option.impl_is_optiondescription():
for subopt in option.get_children_recursively(bytype, for subopt in option.get_children_recursively(bytype,
byname, byname,
@ -402,18 +407,19 @@ class OptionDescription(OptionDescriptionWalk):
def to_dynoption(self, def to_dynoption(self,
rootpath: str, rootpath: str,
suffix: str, suffixes: Optional[list],
ori_dyn) -> SynDynOptionDescription: ori_dyn) -> SynDynOptionDescription:
"""get syn dyn option description """get syn dyn option description
""" """
if suffix is None: if suffixes is None:
return SubDynOptionDescription(self, return SubDynOptionDescription(self,
rootpath, rootpath,
suffixes,
ori_dyn, ori_dyn,
) )
return SynDynOptionDescription(self, return SynDynOptionDescription(self,
rootpath, rootpath,
suffix, suffixes,
ori_dyn) ori_dyn)
def impl_is_dynsymlinkoption(self) -> bool: def impl_is_dynsymlinkoption(self) -> bool:

View file

@ -29,19 +29,19 @@ class SynDynOption:
""" """
__slots__ = ('rootpath', __slots__ = ('rootpath',
'opt', 'opt',
'suffix', 'suffixes',
'dyn_parent', 'dyn_parent',
'__weakref__') '__weakref__')
def __init__(self, def __init__(self,
opt: BaseOption, opt: BaseOption,
rootpath: str, rootpath: str,
suffix: str, suffixes: list,
dyn_parent, dyn_parent,
) -> None: ) -> None:
self.opt = opt self.opt = opt
self.rootpath = rootpath self.rootpath = rootpath
self.suffix = suffix self.suffixes = suffixes
self.dyn_parent = dyn_parent self.dyn_parent = dyn_parent
def __getattr__(self, def __getattr__(self,
@ -60,10 +60,10 @@ class SynDynOption:
""" """
return self.opt.impl_get_display_name(self) return self.opt.impl_get_display_name(self)
def impl_getsuffix(self) -> str: def get_suffixes(self) -> str:
"""get suffix """get suffix
""" """
return self.suffix return self.suffixes
def impl_getpath(self) -> str: def impl_getpath(self) -> str:
"""get path """get path
@ -85,6 +85,6 @@ class SynDynOption:
if leadership: if leadership:
rootpath = self.rootpath.rsplit('.', 1)[0] rootpath = self.rootpath.rsplit('.', 1)[0]
return leadership.to_dynoption(rootpath, return leadership.to_dynoption(rootpath,
self.suffix, self.suffixes,
self.dyn_parent, self.dyn_parent,
) )

View file

@ -34,16 +34,21 @@ class SubDynOptionDescription:
__slots__ = ('rootpath', __slots__ = ('rootpath',
'opt', 'opt',
'dyn_parent', 'dyn_parent',
'suffixes',
'__weakref__', '__weakref__',
) )
def __init__(self, def __init__(self,
opt: BaseOption, opt: BaseOption,
rootpath: str, rootpath: str,
suffixes: list,
dyn_parent, dyn_parent,
) -> None: ) -> None:
self.opt = opt self.opt = opt
self.rootpath = rootpath self.rootpath = rootpath
if not suffixes:
raise Exception('connard')
self.suffixes = suffixes
self.dyn_parent = dyn_parent self.dyn_parent = dyn_parent
def impl_getpath(self) -> str: def impl_getpath(self) -> str:
@ -86,6 +91,9 @@ class SubDynOptionDescription:
def impl_is_dynoptiondescription(self) -> bool: def impl_is_dynoptiondescription(self) -> bool:
return True return True
def get_suffixes(self) -> list:
return self.suffixes
def to_dynoption(self, def to_dynoption(self,
rootpath: str, rootpath: str,
suffix: str, suffix: str,
@ -99,17 +107,17 @@ class SynDynOptionDescription:
""" """
__slots__ = ('opt', __slots__ = ('opt',
'rootpath', 'rootpath',
'_suffix', '_suffixes',
'ori_dyn') 'ori_dyn')
def __init__(self, def __init__(self,
opt: BaseOption, opt: BaseOption,
rootpath: str, rootpath: str,
suffix: str, suffixes: str,
ori_dyn) -> None: ori_dyn) -> None:
self.opt = opt self.opt = opt
self.rootpath = rootpath self.rootpath = rootpath
self._suffix = suffix self._suffixes = suffixes
# For a Leadership inside a DynOptionDescription # For a Leadership inside a DynOptionDescription
self.ori_dyn = ori_dyn self.ori_dyn = ori_dyn
@ -125,7 +133,7 @@ class SynDynOptionDescription:
"""get name """get name
""" """
if self.opt.impl_is_dynoptiondescription(): if self.opt.impl_is_dynoptiondescription():
return self.opt.impl_getname(self._suffix) return self.opt.impl_getname(self._suffixes[-1])
return self.opt.impl_getname() return self.opt.impl_getname()
def impl_get_display_name(self) -> str: def impl_get_display_name(self) -> str:
@ -136,13 +144,15 @@ class SynDynOptionDescription:
def get_children(self, def get_children(self,
config_bag: ConfigBag, config_bag: ConfigBag,
dyn: bool=True, dyn: bool=True,
*,
option_suffixes=None
): ):
# pylint: disable=unused-argument # pylint: disable=unused-argument
"""get children """get children
""" """
yield from self.opt.get_children(config_bag, yield from self.opt.get_children(config_bag,
dynoption=self, dynoption=self,
option_suffix=self._suffix, option_suffixes=self._suffixes,
) )
def get_child(self, def get_child(self,
@ -157,7 +167,7 @@ class SynDynOptionDescription:
config_bag, config_bag,
subpath, subpath,
dynoption=self, dynoption=self,
option_suffix=self._suffix, option_suffixes=self._suffixes,
allow_dynoption=allow_dynoption, allow_dynoption=allow_dynoption,
) )
@ -204,10 +214,10 @@ class SynDynOptionDescription:
path = f'{self.rootpath}.{path}' path = f'{self.rootpath}.{path}'
return path return path
def impl_getsuffix(self) -> str: def get_suffixes(self) -> str:
"""get suffix """get suffixes
""" """
return self._suffix return self._suffixes
class SynDynLeadership(SynDynOptionDescription): class SynDynLeadership(SynDynOptionDescription):
@ -217,7 +227,7 @@ class SynDynLeadership(SynDynOptionDescription):
"""get the leader """get the leader
""" """
return self.opt.get_leader().to_dynoption(self.impl_getpath(), return self.opt.get_leader().to_dynoption(self.impl_getpath(),
self._suffix, self._suffixes,
self.ori_dyn, self.ori_dyn,
) )
@ -227,7 +237,7 @@ class SynDynLeadership(SynDynOptionDescription):
subpath = self.impl_getpath() subpath = self.impl_getpath()
for follower in self.opt.get_followers(): for follower in self.opt.get_followers():
yield follower.to_dynoption(subpath, yield follower.to_dynoption(subpath,
self._suffix, self._suffixes,
self.ori_dyn, self.ori_dyn,
) )
@ -271,7 +281,7 @@ class SynDynLeadership(SynDynOptionDescription):
dyn=self, dyn=self,
) )
def impl_getsuffix(self) -> str: def get_suffixes(self) -> str:
"""get suffix """get suffix
""" """
return self._suffix return self._suffixes

View file

@ -152,12 +152,7 @@ class Values:
""" """
has_calculation = False has_calculation = False
if isinstance(value, Calculation): if isinstance(value, Calculation):
try: value = value.execute(option_bag)
value = value.execute(option_bag)
except ConfigError as err:
msg = _(f'error when calculating "{option_bag.option.impl_get_display_name()}": '
f'{err} : {option_bag.path}')
raise ConfigError(msg) from err
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
@ -354,6 +349,7 @@ class Values:
for coption in option.get_children_recursively(None, for coption in option.get_children_recursively(None,
None, None,
option_bag.config_bag, option_bag.config_bag,
option_suffixes=[],
): ):
if 'force_store_value' in coption.impl_getproperties(): if 'force_store_value' in coption.impl_getproperties():
force_store_options.append(coption) force_store_options.append(coption)