From a6eacf471e54c81c7a2ad26fe14a7408a896a0ff Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 8 Feb 2024 08:32:24 +0100 Subject: [PATCH] feat: dynoption --- docs/own_option.rst | 16 +- docs/src/own_option.py | 1 - docs/src/own_option2.py | 3 - tests/test_dereference.py | 4 +- tests/test_dyn_optiondescription.py | 550 ++++++++++++++++++++- tests/test_option_default.py | 28 +- tiramisu/api.py | 20 +- tiramisu/autolib.py | 81 ++- tiramisu/config.py | 99 ++-- tiramisu/option/baseoption.py | 38 +- tiramisu/option/dynoptiondescription.py | 11 +- tiramisu/option/leadership.py | 6 +- tiramisu/option/option.py | 6 +- tiramisu/option/optiondescription.py | 150 +++--- tiramisu/option/symlinkoption.py | 3 +- tiramisu/option/syndynoption.py | 16 +- tiramisu/option/syndynoptiondescription.py | 266 +++++----- tiramisu/setting.py | 15 +- tiramisu/value.py | 8 +- 19 files changed, 913 insertions(+), 408 deletions(-) diff --git a/docs/own_option.rst b/docs/own_option.rst index b7f6c9c..f7a3495 100644 --- a/docs/own_option.rst +++ b/docs/own_option.rst @@ -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()`) - _type = with a name -- _display_name: with the display name (for example in error message) - _regexp: with a compiled regexp Here an example to an option that only accept string with on lowercase ASCII vowel characters: .. literalinclude:: src/own_option.py - :lines: 3-11 + :lines: 3-10 :linenos: Let's try our object: @@ -31,31 +30,30 @@ Let's try our object: ... "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: - __slots__: with new data members (the values should always be `tuple()`) - _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 - :lines: 3-15 + :lines: 3-12 :linenos: 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 - :lines: 16-20 + :lines: 13-17 :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: .. literalinclude:: src/own_option2.py - :lines: 22-29 + :lines: 19-26 :linenos: 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: .. literalinclude:: src/own_option2.py - :lines: 31-43 + :lines: 28-40 :linenos: Let's test it: diff --git a/docs/src/own_option.py b/docs/src/own_option.py index f31d891..28101fe 100644 --- a/docs/src/own_option.py +++ b/docs/src/own_option.py @@ -7,5 +7,4 @@ from tiramisu import RegexpOption class VowelOption(RegexpOption): __slots__ = tuple() _type = 'vowel' - _display_name = "string with vowel" _regexp = re.compile(r"^[aeiouy]*$") diff --git a/docs/src/own_option2.py b/docs/src/own_option2.py index 304d86e..b782a27 100644 --- a/docs/src/own_option2.py +++ b/docs/src/own_option2.py @@ -1,14 +1,11 @@ #!/usr/bin/env python3 from tiramisu import Option -from tiramisu.error import ValueWarning -import warnings class LipogramOption(Option): __slots__ = tuple() _type = 'lipogram' - _display_name = 'lipogram' def __init__(self, *args, min_len=100, diff --git a/tests/test_dereference.py b/tests/test_dereference.py index fad6f78..258a14b 100644 --- a/tests/test_dereference.py +++ b/tests/test_dereference.py @@ -66,7 +66,7 @@ def test_deref_optiondescription(): def test_deref_option_cache(): b = BoolOption('b', '') o = OptionDescription('od', '', [b]) - o._build_cache() + o._build_cache(None) w = weakref.ref(b) del(b) assert w() is not None @@ -77,7 +77,7 @@ def test_deref_option_cache(): def test_deref_optiondescription_cache(): b = BoolOption('b', '') o = OptionDescription('od', '', [b]) - o._build_cache() + o._build_cache(None) w = weakref.ref(o) del(b) assert w() is not None diff --git a/tests/test_dyn_optiondescription.py b/tests/test_dyn_optiondescription.py index 90bb790..1d987a3 100644 --- a/tests/test_dyn_optiondescription.py +++ b/tests/test_dyn_optiondescription.py @@ -158,10 +158,10 @@ def test_getdoc_dyndescription(): assert cfg.option('od.dodval2.st').name() == 'st' assert cfg.option('od.dodval1').name() == 'dodval1' assert cfg.option('od.dodval2').name() == 'dodval2' - assert cfg.option('od.dodval1.st').doc() == 'doc1val1' - assert cfg.option('od.dodval2.st').doc() == 'doc1val2' - assert cfg.option('od.dodval1').doc() == 'doc2val1' - assert cfg.option('od.dodval2').doc() == 'doc2val2' + assert cfg.option('od.dodval1.st').doc() == 'doc1' + assert cfg.option('od.dodval2.st').doc() == 'doc1' + assert cfg.option('od.dodval1').doc() == 'doc2' + assert cfg.option('od.dodval2').doc() == 'doc2' # assert not list_sessions() @@ -1792,7 +1792,6 @@ def test_subdynod_dyndescription(): assert cfg.option('st3').value.get() is None # 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(): st2 = StrOption('st2', '') 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"})) od1 = OptionDescription(name="nsd", doc="nsd", children=[nsd_zones_all, dyn]) cfg = Config(od1) - cfg.value.mandatory() + assert cfg.value.mandatory() == [] # assert not list_sessions() @@ -2109,3 +2108,542 @@ def test_dyn_callback_with_not_dyn(): 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 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', + } + # + assert [opt.path() for opt in cfg.value.mandatory()] == [] + + +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', + } + # + assert [opt.path() for opt in cfg.value.mandatory()] == [] + + +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, + } + # + assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.schema_schema1.databases.password', + 'socle.schema_schema1.databases.user_database_srep.name', + 'socle.schema_schema1.databases.user_database_srep.login', + 'socle.schema_schema1.databases.user_database_srep.password2', + 'socle.schema_schema1.databases.user_database_snom.name', + 'socle.schema_schema1.databases.user_database_snom.login', + 'socle.schema_schema1.databases.user_database_snom.password2', + 'socle.schema_schema1.databases.user_database_srem.name', + 'socle.schema_schema1.databases.user_database_srem.login', + 'socle.schema_schema1.databases.user_database_srem.password2', + 'socle.schema_schema2.databases.password', + 'socle.schema_schema2.databases.user_database_srep.name', + 'socle.schema_schema2.databases.user_database_srep.login', + 'socle.schema_schema2.databases.user_database_srep.password2', + 'socle.schema_schema2.databases.user_database_snom.name', + 'socle.schema_schema2.databases.user_database_snom.login', + 'socle.schema_schema2.databases.user_database_snom.password2', + 'socle.schema_schema2.databases.user_database_srem.name', + 'socle.schema_schema2.databases.user_database_srem.login', + 'socle.schema_schema2.databases.user_database_srem.password2', + 'socle.schema_schema3.databases.password', + 'socle.schema_schema3.databases.user_database_srep.name', + 'socle.schema_schema3.databases.user_database_srep.login', + 'socle.schema_schema3.databases.user_database_srep.password2', + 'socle.schema_schema3.databases.user_database_snom.name', + 'socle.schema_schema3.databases.user_database_snom.login', + 'socle.schema_schema3.databases.user_database_snom.password2', + 'socle.schema_schema3.databases.user_database_srem.name', + 'socle.schema_schema3.databases.user_database_srem.login', + 'socle.schema_schema3.databases.user_database_srem.password2', + ] + # + cfg.option('socle.schema_schema2.database_names').value.set(['another']) + 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': ['another'], + 'socle.schema_schema2.databases.password': None, + 'socle.schema_schema2.databases.user_database_another.name': None, + 'socle.schema_schema2.databases.user_database_another.login': None, + 'socle.schema_schema2.databases.user_database_another.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, + } + # + cfg.option('socle.schema_schema1.databases.password').value.set('password') + cfg.option('socle.schema_schema1.databases.user_database_snom.name').value.set('name1') + cfg.option('socle.schema_schema1.databases.user_database_srem.name').value.set('name2') + cfg.option('socle.schema_schema1.databases.user_database_srep.name').value.set('name3') + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.database_names': ['srep', + 'snom', + 'srem'], + 'socle.schema_schema1.databases.password': 'password', + 'socle.schema_schema1.databases.user_database_snom.login': 'name1', + 'socle.schema_schema1.databases.user_database_snom.name': 'name1', + 'socle.schema_schema1.databases.user_database_snom.password2': 'password', + 'socle.schema_schema1.databases.user_database_srem.login': 'name2', + 'socle.schema_schema1.databases.user_database_srem.name': 'name2', + 'socle.schema_schema1.databases.user_database_srem.password2': 'password', + 'socle.schema_schema1.databases.user_database_srep.login': 'name3', + 'socle.schema_schema1.databases.user_database_srep.name': 'name3', + 'socle.schema_schema1.databases.user_database_srep.password2': 'password', + } + assert [opt.path() for opt in cfg.value.mandatory()] == [] + # + cfg.option('socle.schema_schema1.database_names').value.set(['snom']) + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.database_names': ['snom'], + 'socle.schema_schema1.databases.password': 'password', + 'socle.schema_schema1.databases.user_database_snom.login': 'name1', + 'socle.schema_schema1.databases.user_database_snom.name': 'name1', + 'socle.schema_schema1.databases.user_database_snom.password2': 'password', + } + assert [opt.path() for opt in cfg.value.mandatory()] == [] + + +def test_dyn_link_subdyn_tree(): + 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',)) + user_names = StrOption(name="users", doc="users", multi=True, default=["user1"]) + user_database = DynOptionDescription(name="user_database_", doc="user database", suffixes=Calculation(calc_value, Params((ParamOption(user_names, notraisepropertyerror=True)))), children=[name, login, password2]) + database_names = StrOption(name="database_names", doc="database_names", multi=True, default=["srep"]) + databases = DynOptionDescription(name="db_", doc="database", suffixes=Calculation(calc_value, Params((ParamOption(database_names, notraisepropertyerror=True)))), children=[user_names, password, user_database]) + schema_names = StrOption(name="database_schemas", doc="database_schemas", multi=True, default=["schema1"]) + 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 [opt.path() for opt in cfg.value.mandatory()] == ['socle.schema_schema1.db_srep.password', + 'socle.schema_schema1.db_srep.user_database_user1.name', + 'socle.schema_schema1.db_srep.user_database_user1.login', + 'socle.schema_schema1.db_srep.user_database_user1.password2', + ] + # + cfg.option('socle.schema_schema1.db_srep.user_database_user1.name').value.set('name') + assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.schema_schema1.db_srep.password', + 'socle.schema_schema1.db_srep.user_database_user1.password2', + ] + # + cfg.option('socle.schema_schema1.db_srep.password').value.set('password') + assert [opt.path() for opt in cfg.value.mandatory()] == [] + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.database_names': ['srep'], + 'socle.schema_schema1.db_srep.password': 'password', + 'socle.schema_schema1.db_srep.user_database_user1.login': 'name', + 'socle.schema_schema1.db_srep.user_database_user1.name': 'name', + 'socle.schema_schema1.db_srep.user_database_user1.password2': 'password', + 'socle.schema_schema1.db_srep.users': ['user1'], + } + # + cfg.option('socle.schema_schema1.db_srep.users').value.set(['user1', 'user2']) + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.database_names': ['srep'], + 'socle.schema_schema1.db_srep.password': 'password', + 'socle.schema_schema1.db_srep.user_database_user1.login': 'name', + 'socle.schema_schema1.db_srep.user_database_user1.name': 'name', + 'socle.schema_schema1.db_srep.user_database_user1.password2': 'password', + 'socle.schema_schema1.db_srep.user_database_user2.login': None, + 'socle.schema_schema1.db_srep.user_database_user2.name': None, + 'socle.schema_schema1.db_srep.user_database_user2.password2': 'password', + 'socle.schema_schema1.db_srep.users': ['user1', 'user2'], + } + # + cfg.option('socle.schema_schema1.db_srep.users').value.set([]) + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.database_names': ['srep'], + 'socle.schema_schema1.db_srep.password': 'password', + 'socle.schema_schema1.db_srep.users': [], + } + # + cfg.option('socle.schema_schema1.database_names').value.set([]) + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.database_names': [], + } + # + cfg.option('socle.database_schemas').value.set([]) + assert cfg.value.get() == {'socle.database_schemas': [], + } + # + cfg.option('socle.database_schemas').value.reset() + cfg.option('socle.schema_schema1.database_names').value.reset() + cfg.option('socle.schema_schema1.db_srep.users').value.reset() + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.database_names': ['srep'], + 'socle.schema_schema1.db_srep.password': 'password', + 'socle.schema_schema1.db_srep.user_database_user1.login': 'name', + 'socle.schema_schema1.db_srep.user_database_user1.name': 'name', + 'socle.schema_schema1.db_srep.user_database_user1.password2': 'password', + 'socle.schema_schema1.db_srep.users': ['user1'], + } + + +def test_dyn_link_subdyn_same_variable(): + 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',)) + schema_names = StrOption(name="database_schemas", doc="database_schemas", multi=True, default=["schema1"]) + user_database = DynOptionDescription(name="user_database_", doc="user database", suffixes=Calculation(calc_value, Params((ParamOption(schema_names, notraisepropertyerror=True)))), children=[name, login, password2]) + databases = DynOptionDescription(name="db_", doc="database", suffixes=Calculation(calc_value, Params((ParamOption(schema_names, notraisepropertyerror=True)))), children=[password, user_database]) + schema = DynOptionDescription(name="schema_", doc="schema_", suffixes=Calculation(calc_value, Params((ParamOption(schema_names, notraisepropertyerror=True)))), children=[databases]) + socle = OptionDescription(name="socle", doc="socle", children=[schema, schema_names]) + root = OptionDescription(name="baseoption", doc="baseoption", children=[socle]) + cfg = Config(root) + assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.schema_schema1.db_schema1.password', + 'socle.schema_schema1.db_schema1.user_database_schema1.name', + 'socle.schema_schema1.db_schema1.user_database_schema1.login', + 'socle.schema_schema1.db_schema1.user_database_schema1.password2', + ] + # + cfg.option('socle.schema_schema1.db_schema1.user_database_schema1.name').value.set('name') + assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.schema_schema1.db_schema1.password', + 'socle.schema_schema1.db_schema1.user_database_schema1.password2', + ] + # + cfg.option('socle.schema_schema1.db_schema1.password').value.set('password') + cfg.option('socle.schema_schema1.db_schema1.user_database_schema1.name').value.set('name') + assert [opt.path() for opt in cfg.value.mandatory()] == [] + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.db_schema1.password': 'password', + 'socle.schema_schema1.db_schema1.user_database_schema1.login': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.name': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.password2': 'password', + } + # + cfg.option('socle.database_schemas').value.set(['schema1', 'schema2']) + assert [opt.path() for opt in cfg.value.mandatory()] == ['socle.schema_schema1.db_schema1.user_database_schema2.name', + 'socle.schema_schema1.db_schema1.user_database_schema2.login', + 'socle.schema_schema1.db_schema2.password', + 'socle.schema_schema1.db_schema2.user_database_schema1.name', + 'socle.schema_schema1.db_schema2.user_database_schema1.login', + 'socle.schema_schema1.db_schema2.user_database_schema1.password2', + 'socle.schema_schema1.db_schema2.user_database_schema2.name', + 'socle.schema_schema1.db_schema2.user_database_schema2.login', + 'socle.schema_schema1.db_schema2.user_database_schema2.password2', + 'socle.schema_schema2.db_schema1.password', + 'socle.schema_schema2.db_schema1.user_database_schema1.name', + 'socle.schema_schema2.db_schema1.user_database_schema1.login', + 'socle.schema_schema2.db_schema1.user_database_schema1.password2', + 'socle.schema_schema2.db_schema1.user_database_schema2.name', + 'socle.schema_schema2.db_schema1.user_database_schema2.login', + 'socle.schema_schema2.db_schema1.user_database_schema2.password2', + 'socle.schema_schema2.db_schema2.password', + 'socle.schema_schema2.db_schema2.user_database_schema1.name', + 'socle.schema_schema2.db_schema2.user_database_schema1.login', + 'socle.schema_schema2.db_schema2.user_database_schema1.password2', + + 'socle.schema_schema2.db_schema2.user_database_schema2.name', + 'socle.schema_schema2.db_schema2.user_database_schema2.login', + 'socle.schema_schema2.db_schema2.user_database_schema2.password2', + ] + assert cfg.value.get() == {'socle.database_schemas': ['schema1', 'schema2'], + 'socle.schema_schema1.db_schema1.password': 'password', + 'socle.schema_schema1.db_schema1.user_database_schema1.login': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.name': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.password2': 'password', + 'socle.schema_schema1.db_schema1.user_database_schema2.login': None, + 'socle.schema_schema1.db_schema1.user_database_schema2.name': None, + 'socle.schema_schema1.db_schema1.user_database_schema2.password2': 'password', + 'socle.schema_schema1.db_schema2.password': None, + 'socle.schema_schema1.db_schema2.user_database_schema1.login': None, + 'socle.schema_schema1.db_schema2.user_database_schema1.name': None, + 'socle.schema_schema1.db_schema2.user_database_schema1.password2': None, + 'socle.schema_schema1.db_schema2.user_database_schema2.login': None, + 'socle.schema_schema1.db_schema2.user_database_schema2.name': None, + 'socle.schema_schema1.db_schema2.user_database_schema2.password2': None, + 'socle.schema_schema2.db_schema1.password': None, + 'socle.schema_schema2.db_schema1.user_database_schema1.login': None, + 'socle.schema_schema2.db_schema1.user_database_schema1.name': None, + 'socle.schema_schema2.db_schema1.user_database_schema1.password2': None, + 'socle.schema_schema2.db_schema1.user_database_schema2.login': None, + 'socle.schema_schema2.db_schema1.user_database_schema2.name': None, + 'socle.schema_schema2.db_schema1.user_database_schema2.password2': None, + 'socle.schema_schema2.db_schema2.password': None, + 'socle.schema_schema2.db_schema2.user_database_schema1.login': None, + 'socle.schema_schema2.db_schema2.user_database_schema1.name': None, + 'socle.schema_schema2.db_schema2.user_database_schema1.password2': None, + 'socle.schema_schema2.db_schema2.user_database_schema2.login': None, + 'socle.schema_schema2.db_schema2.user_database_schema2.name': None, + 'socle.schema_schema2.db_schema2.user_database_schema2.password2': None, + } + assert cfg.option('socle.schema_schema1.db_schema1.user_database_schema1').value.get() == { + 'socle.schema_schema1.db_schema1.user_database_schema1.login': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.name': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.password2': 'password', + } + + +def test_dyn_link_subdyn_disabled(): + password = StrOption(name="password", doc="password") + name = StrOption(name="name", doc="name") + login = StrOption(name="login", doc="login", default=Calculation(calc_value, Params((ParamOption(name))))) + disabled_property = Calculation(calc_value, + Params(ParamValue('disabled'), + kwargs={'condition': ParamOption(login), + 'expected': ParamValue('name'), + 'default': ParamValue(None)})) + password2 = StrOption(name="password2", doc="password2", default=Calculation(calc_value, Params((ParamOption(password)))), properties=(disabled_property,)) + schema_names = StrOption(name="database_schemas", doc="database_schemas", multi=True, default=["schema1"]) + user_database = DynOptionDescription(name="user_database_", doc="user database", suffixes=Calculation(calc_value, Params((ParamOption(schema_names, notraisepropertyerror=True)))), children=[name, login, password2]) + databases = DynOptionDescription(name="db_", doc="database", suffixes=Calculation(calc_value, Params((ParamOption(schema_names, notraisepropertyerror=True)))), children=[password, user_database]) + schema = DynOptionDescription(name="schema_", doc="schema_", suffixes=Calculation(calc_value, Params((ParamOption(schema_names, notraisepropertyerror=True)))), children=[databases]) + socle = OptionDescription(name="socle", doc="socle", children=[schema, schema_names]) + root = OptionDescription(name="baseoption", doc="baseoption", children=[socle]) + cfg = Config(root) + cfg.property.read_write() + assert [opt.path() for opt in cfg.value.mandatory()] == [] + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.db_schema1.password': None, + 'socle.schema_schema1.db_schema1.user_database_schema1.login': None, + 'socle.schema_schema1.db_schema1.user_database_schema1.name': None, + 'socle.schema_schema1.db_schema1.user_database_schema1.password2': None, + } + # + cfg.option('socle.schema_schema1.db_schema1.user_database_schema1.name').value.set('name') + assert cfg.value.get() == {'socle.database_schemas': ['schema1'], + 'socle.schema_schema1.db_schema1.password': None, + 'socle.schema_schema1.db_schema1.user_database_schema1.login': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.name': 'name', + } + # + cfg.option('socle.database_schemas').value.set(['schema1', 'schema2']) + cfg.option('socle.schema_schema2.db_schema2.user_database_schema2.name').value.set('name2') + assert cfg.value.get() == {'socle.database_schemas': ['schema1', 'schema2'], + 'socle.schema_schema1.db_schema1.password': None, + 'socle.schema_schema1.db_schema1.user_database_schema1.login': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema1.name': 'name', + 'socle.schema_schema1.db_schema1.user_database_schema2.login': None, + 'socle.schema_schema1.db_schema1.user_database_schema2.name': None, + 'socle.schema_schema1.db_schema1.user_database_schema2.password2': None, + 'socle.schema_schema1.db_schema2.password': None, + 'socle.schema_schema1.db_schema2.user_database_schema1.login': None, + 'socle.schema_schema1.db_schema2.user_database_schema1.name': None, + 'socle.schema_schema1.db_schema2.user_database_schema1.password2': None, + 'socle.schema_schema1.db_schema2.user_database_schema2.login': None, + 'socle.schema_schema1.db_schema2.user_database_schema2.name': None, + 'socle.schema_schema1.db_schema2.user_database_schema2.password2': None, + 'socle.schema_schema2.db_schema1.password': None, + 'socle.schema_schema2.db_schema1.user_database_schema1.login': None, + 'socle.schema_schema2.db_schema1.user_database_schema1.name': None, + 'socle.schema_schema2.db_schema1.user_database_schema1.password2': None, + 'socle.schema_schema2.db_schema1.user_database_schema2.login': None, + 'socle.schema_schema2.db_schema1.user_database_schema2.name': None, + 'socle.schema_schema2.db_schema1.user_database_schema2.password2': None, + 'socle.schema_schema2.db_schema2.password': None, + 'socle.schema_schema2.db_schema2.user_database_schema1.login': None, + 'socle.schema_schema2.db_schema2.user_database_schema1.name': None, + 'socle.schema_schema2.db_schema2.user_database_schema1.password2': None, + 'socle.schema_schema2.db_schema2.user_database_schema2.login': 'name2', + 'socle.schema_schema2.db_schema2.user_database_schema2.name': 'name2', + 'socle.schema_schema2.db_schema2.user_database_schema2.password2': None, + } diff --git a/tests/test_option_default.py b/tests/test_option_default.py index 5ff883b..5c2bee6 100644 --- a/tests/test_option_default.py +++ b/tests/test_option_default.py @@ -146,23 +146,23 @@ def test_force_default_on_freeze_multi(): # assert not list_sessions() -def test_force_default_on_freeze_leader(): - dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_default_on_freeze',)) - dummy2 = BoolOption('dummy2', 'Test string option', multi=True) - descr = Leadership("dummy1", "", [dummy1, dummy2]) - od1 = OptionDescription("root", "", [descr]) - with pytest.raises(ConfigError): - Config(od1) +#def test_force_default_on_freeze_leader(): +# dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_default_on_freeze',)) +# dummy2 = BoolOption('dummy2', 'Test string option', multi=True) +# descr = Leadership("dummy1", "", [dummy1, dummy2]) +# od1 = OptionDescription("root", "", [descr]) +# with pytest.raises(ConfigError): +# Config(od1) # assert not list_sessions() -def test_force_metaconfig_on_freeze_leader(): - dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',)) - dummy2 = BoolOption('dummy2', 'Test string option', multi=True) - descr = Leadership("dummy1", "", [dummy1, dummy2]) - od1 = OptionDescription("root", "", [descr]) - with pytest.raises(ConfigError): - Config(od1) +#def test_force_metaconfig_on_freeze_leader(): +# dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',)) +# dummy2 = BoolOption('dummy2', 'Test string option', multi=True) +# descr = Leadership("dummy1", "", [dummy1, dummy2]) +# od1 = OptionDescription("root", "", [descr]) +# with pytest.raises(ConfigError): +# Config(od1) # assert not list_sessions() diff --git a/tiramisu/api.py b/tiramisu/api.py index 306e888..9e340e6 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -458,13 +458,12 @@ class TiramisuOptionProperty(CommonTiramisuOption): ): """Get properties for an option""" option_bag = options_bag[-1] - if not only_raises: - return option_bag.properties settings = self._config_bag.context.get_settings() - ret = settings.calc_raises_properties(option_bag, - uncalculated=uncalculated, - ) - return ret + if not only_raises: + return settings.getproperties(option_bag) + return settings.calc_raises_properties(option_bag, + uncalculated=uncalculated, + ) @option_type(['option', 'optiondescription', 'with_or_without_index']) def add(self, @@ -491,10 +490,11 @@ class TiramisuOptionProperty(CommonTiramisuOption): ): """Remove new property for an option""" option_bag = options_bag[-1] - props = option_bag.properties - self._config_bag.context.get_settings().setproperties(option_bag, - props - {prop}, - ) + settings = self._config_bag.context.get_settings() + props = settings.getproperties(option_bag) + settings.setproperties(option_bag, + props - {prop}, + ) @option_type(['option', 'optiondescription', 'with_or_without_index']) def reset(self, options_bag: List[OptionBag]): diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 923d5a9..a62a27b 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -313,8 +313,8 @@ def manager_callback(callback: Callable, # raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation if param.notraisepropertyerror or param.raisepropertyerror: raise err from err - raise ConfigError(_('unable to carry out a calculation for "{}"' - ', {}').format(option.impl_get_display_name(), err), err) from err + display_name = option_bag.option.impl_get_display_name() + raise ConfigError(_('unable to carry out a calculation for "{}", {}').format(display_name, err)) from err except ValueError as err: raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option_bag.option.impl_get_display_name(), err)) from err except AttributeError as err: @@ -324,7 +324,8 @@ def manager_callback(callback: Callable, ['configerror'], 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 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 if param.notraisepropertyerror or param.raisepropertyerror: raise err from err - raise ConfigError(_('unable to carry out a calculation for "{}"' - ', {}').format(option.impl_get_display_name(), err), err) from err + display_name = option.impl_get_display_name() + raise ConfigError(_('unable to carry out a calculation for "{}", {}').format(display_name, err)) from err except ValueError as err: raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option.impl_get_display_name(), err)) from err except AttributeError as err: @@ -370,7 +371,8 @@ def manager_callback(callback: Callable, ['configerror'], 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: parent_option_bag = options_bag[-2] else: @@ -399,17 +401,17 @@ def manager_callback(callback: Callable, param.default_value, ) except ValueError as err: - raise ConfigError(_('option "{}" cannot be calculated: {}').format(option.impl_get_display_name(), - str(err), - )) + display_name = option.impl_get_display_name() + raise ConfigError(_(f'unable to get value for calculating "{display_name}", {err}')) from err if isinstance(param, ParamIndex): return index if isinstance(param, ParamSuffix): if not option.issubdyn(): - raise ConfigError(_('option "{}" is not in a dynoptiondescription').format(option.impl_get_display_name())) - return option.impl_getsuffix() + display_name = option_bag.option.impl_get_display_name() + raise ConfigError(_('option "{display_name}" is not in a dynoptiondescription')) + return option.get_suffixes()[-1] if isinstance(param, ParamSelfOption): value = calc_self(param, @@ -452,46 +454,26 @@ def manager_callback(callback: Callable, raise ConfigError(_(f'option "{option.impl_get_display_name()}" is not in a dynoptiondescription: {err}')) callbk_option = soptions_bag[-1].option found = True - elif option.impl_is_sub_dyn_optiondescription(): - if option.getsubdyn() == callbk_option.getsubdyn(): - root_path = option.impl_getpath().rsplit('.', 1)[0] - len_path = root_path.count('.') - full_path = root_path + '.' + callbk_option.impl_getpath().split('.', len_path + 1)[-1] - root_option_bag = OptionBag(config_bag.context.get_description(), - None, - config_bag, - ) - try: - soptions_bag = config_bag.context.get_sub_option_bag(root_option_bag, - full_path, - #FIXME index? - index=None, - validate_properties=True, - properties=None, - ) - except AttributeError as err: - raise ConfigError(_(f'option "{option.impl_get_display_name()}" is not in a dynoptiondescription: {err}')) - callbk_option = soptions_bag[-1].option - found = True elif option.impl_is_dynsymlinkoption(): rootpath = option.rootpath call_path = callbk_option.impl_getpath() - if option.opt.issubdyn() and callbk_option.getsubdyn() == option.getsubdyn() or \ - not option.opt.issubdyn() and callbk_option.getsubdyn() == option.opt: - # in same dynoption - suffix = option.impl_getsuffix() - subdyn = callbk_option.getsubdyn() - root_path, sub_path = subdyn.split_path(subdyn, - option, - ) - if root_path: - parent_path = root_path + subdyn.impl_getname(suffix) + sub_path - else: - parent_path = subdyn.impl_getname(suffix) + sub_path - callbk_option = callbk_option.to_dynoption(parent_path, - suffix, - subdyn, - ) + in_same_dyn = False + if not option.opt.issubdyn() and callbk_option.getsubdyn() == option.opt: + # First dyn + in_same_dyn = True + elif option.opt.issubdyn(): + # Search if callback and option has a common subdyn + callbk_subdyn = callbk_option.getsubdyn() + sub_dyn = option + while True: + sub_dyn = sub_dyn.getsubdyn() + if sub_dyn == callbk_subdyn: + in_same_dyn = True + break + if not sub_dyn.issubdyn(): + break + if in_same_dyn: + callbk_option = callbk_option.to_sub_dyoption(option.get_suffixes()) found = True if not found: callbk_options = [] @@ -519,8 +501,6 @@ def manager_callback(callback: Callable, else: index_ = None with_index = False - if callbk_option.impl_getpath() == 'od.dodval1.st.boolean': - raise Exception('pfff') value = get_value(config_bag, callbk_option, param, @@ -602,6 +582,7 @@ def carry_out_calculation(option, kwargs, ) 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(): if args or kwargs: raise LeadershipError(_('the "{}" function with positional arguments "{}" ' diff --git a/tiramisu/config.py b/tiramisu/config.py index e827fd2..ab19d4a 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -27,7 +27,6 @@ from typing import Optional, List, Any, Union from .error import PropertiesOptionError, ConfigError, ConflictError, \ LeadershipError from .option import DynOptionDescription, Leadership, Option -from .option.syndynoptiondescription import SubDynOptionDescription from .setting import OptionBag, ConfigBag, Settings, undefined, groups from .value import Values, owners from .i18n import _ @@ -164,9 +163,19 @@ class _SubConfig: ) except AttributeError as err: raise ConfigError(_(f'option "{option.impl_get_display_name()}" is not in a dynoptiondescription: {err}')) - self._reset_cache_dyn_optiondescription(options_bag[-1], - resetted_opts, - ) + if options_bag[-1].option.impl_is_dynoptiondescription(): + for suffix in options_bag[-1].option.get_suffixes(config_bag, dynoption=options_bag[-2].option): + dynopt = options_bag[-1].option.to_dynoption(options_bag[-2].path, + options_bag[-2].option._suffixes + [suffix], + ) + doption_bag = OptionBag(dynopt, None, option_bag.config_bag) + self.reset_one_option_cache(resetted_opts, + doption_bag, + ) + else: + self._reset_cache_dyn_optiondescription(options_bag[-1], + resetted_opts, + ) break else: soption_bag = OptionBag(option, @@ -182,19 +191,38 @@ class _SubConfig: # it's an option in dynoptiondescription, remove cache for all generated option config_bag = option_bag.config_bag options = [soption_bag] - for wdynopt in reversed(option.get_sub_dyns()): - dynopt = wdynopt() - sub_options = [] - for sub_option in options: - sub_options.extend(dynopt.get_sub_children(sub_option.option, - config_bag, - properties=None, - )) - options = sub_options - for doption_bag in options: - self.reset_one_option_cache(resetted_opts, - doption_bag, - ) + dynopt = option.get_sub_dyns()[-1]() + dyn_path = dynopt.impl_getpath() + if '.' in dyn_path: + root_path = dyn_path.rsplit('.', 1)[0] + else: + root_path = '' + for suffix in dynopt.get_suffixes(config_bag): + suffix_dynopt = dynopt.to_dynoption(root_path, + [suffix], + ) + soption_bag = OptionBag(suffix_dynopt, None, config_bag) + for data in self.walk(soption_bag, validate_properties=False): + if isinstance(data, dict): + leader = data.pop('leader') + resetted_opts.append(leader.path) + leader.option.reset_cache(leader.path, + config_bag, + resetted_opts, + ) + for followers in data.values(): + for follower in followers: + resetted_opts.append(follower.path) + follower.option.reset_cache(follower.path, + config_bag, + resetted_opts, + ) + else: + resetted_opts.append(data.path) + data.option.reset_cache(data.path, + config_bag, + resetted_opts, + ) else: self.reset_one_option_cache(resetted_opts, soption_bag, @@ -235,16 +263,11 @@ class _SubConfig: option_bag, resetted_opts, ): - option = option_bag.option - if isinstance(option, (DynOptionDescription, SubDynOptionDescription)): - dynoption = option - else: - dynoption = option.getsubdyn() - for doption_bag in dynoption.get_sub_children(option, - option_bag.config_bag, - index=option_bag.index, - properties=None - ): + for doption_bag in option_bag.option.get_sub_children(option_bag.option, + option_bag.config_bag, + index=option_bag.index, + properties=None + ): self.reset_one_option_cache(resetted_opts, doption_bag, ) @@ -338,11 +361,13 @@ class _SubConfig: def walk(self, option_bag: OptionBag, + *, types: List[str]=('option',), group_type=None, recursive: bool=True, walked: bool=False, flatten_leadership: bool=False, + validate_properties: bool=True, ): """walk to tree """ @@ -362,13 +387,14 @@ class _SubConfig: yield from self.walk(self.get_sub_option_bag(option_bag, # pylint: disable=no-member opt.impl_getpath(), None, - True, + validate_properties, follower_not_apply_requires=flatten_leadership, )[-1], types=types, recursive=recursive, group_type=group_type, walked=True, + validate_properties=validate_properties, ) except PropertiesOptionError as err: if err.proptype in (['mandatory'], ['empty']): @@ -380,7 +406,7 @@ class _SubConfig: leader_option_bag = self.get_sub_option_bag(option_bag, # pylint: disable=no-member leader.impl_getpath(), None, - True, + validate_properties, )[-1] followers_dict = {'leader': leader_option_bag} values = self.get_value(leader_option_bag, @@ -403,7 +429,7 @@ class _SubConfig: options_bag = self.get_sub_option_bag(option_bag, # pylint: disable=no-member follower_path, idx, - True, + validate_properties, leadership_length=ls_length, ) for f_follower_bag in self.walk(options_bag[-1], @@ -411,6 +437,7 @@ class _SubConfig: recursive=recursive, group_type=group_type, walked=True, + validate_properties=validate_properties, ): if 'mandatory' in types: yield f_follower_bag @@ -576,7 +603,7 @@ class _CommonConfig(_SubConfig): def _impl_build_all_caches(self, descr): if not descr.impl_already_build_caches(): descr._group_type = groups.root # pylint: disable=protected-access - descr._build_cache(display_name=self._display_name) # pylint: disable=no-member,protected-access + descr._build_cache(self._display_name) # pylint: disable=no-member,protected-access if not hasattr(descr, '_cache_force_store_values'): raise ConfigError(_('option description seems to be part of an other ' 'config')) @@ -727,8 +754,8 @@ class _CommonConfig(_SubConfig): path: str, index: Optional[int], validate_properties: bool, - leadership_length: int=None, *, + leadership_length: int=None, properties=undefined, follower_not_apply_requires: bool=False, allow_dynoption: bool=False, @@ -755,13 +782,8 @@ class _CommonConfig(_SubConfig): if not suboption.impl_is_optiondescription(): raise TypeError(f'{suboption.impl_getpath()} is not an optiondescription') - if not idx and option_bag.option == self.get_description(): - subpath = None - else: - subpath = suboption.impl_getpath() option = suboption.get_child(step, option_bag.config_bag, - subpath, allow_dynoption=allow_dynoption, ) if idx == last_idx: @@ -787,6 +809,9 @@ class _CommonConfig(_SubConfig): option_index = None apply_requires = True option_properties = undefined + if option_properties is undefined and not validate_properties: + # not transitive property error + apply_requires = False sub_option_bag = OptionBag(option, option_index, option_bag.config_bag, diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 5bb3300..e45dd6a 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -40,8 +40,8 @@ def valid_name(name): """ if not isinstance(name, str): return False -# if '.' in name: -# return False + if '.' in name: + return False return True @@ -166,8 +166,7 @@ class Base: def impl_is_readonly(self) -> str: """the option is readonly """ - # _path is None when initialise SymLinkOption - return hasattr(self, '_path') and self._path is not None # pylint: disable=no-member + return hasattr(self, '_path') def impl_getproperties(self) -> FrozenSet[str]: """get properties @@ -322,3 +321,34 @@ class BaseOption(Base): """get dependencies information """ return getattr(self, '_dependencies_information', {}) + + def to_sub_dyoption(self, + suffixes: list[str], + ): + sub_dyn = self + # retrieve all subdyn options + sub_dyns = [] + while True: + sub_dyn = sub_dyn.getsubdyn() + sub_dyns.append(sub_dyn) + if not sub_dyn.issubdyn(): + break + paths = [] + parent_path = self.impl_getpath().rsplit('.', 1)[0] + suffix_idx = len(sub_dyns) - 1 + for sub_dyn in sub_dyns: + dyn_path = sub_dyn.impl_getpath() + if dyn_path.count('.') == parent_path.count('.'): + *root_paths, dyn_path_ = parent_path.split('.', dyn_path.count('.') + 1) + else: + *root_paths, dyn_path_, child_path = parent_path.split('.', dyn_path.count('.') + 1) + paths.insert(0, child_path) + paths.insert(0, sub_dyn.impl_getname(suffixes[suffix_idx])) + suffix_idx -= 1 + parent_path = '.'.join(root_paths) + if parent_path: + paths.insert(0, parent_path) + full_parent_path = '.'.join(paths) + return self.to_dynoption(full_parent_path, + suffixes, + ) diff --git a/tiramisu/option/dynoptiondescription.py b/tiramisu/option/dynoptiondescription.py index 5677fe1..8208969 100644 --- a/tiramisu/option/dynoptiondescription.py +++ b/tiramisu/option/dynoptiondescription.py @@ -88,6 +88,7 @@ class DynOptionDescription(OptionDescription): def get_suffixes(self, config_bag: ConfigBag, + *, dynoption=None, ) -> List[str]: """get dynamic suffixes @@ -134,8 +135,9 @@ class DynOptionDescription(OptionDescription): (option.impl_is_sub_dyn_optiondescription() and option.opt == self) def split_path(self, - dynoption, option, + *, + dynoption=None, ) -> Tuple[str, str]: """self.impl_getpath() is something like root.xxx.dynoption_path option.impl_getpath() is something like root.xxx.dynoption_path.sub.path @@ -169,8 +171,8 @@ class DynOptionDescription(OptionDescription): properties=undefined, dynoption=None, ): - root_path, sub_path = self.split_path(dynoption, - option, + root_path, sub_path = self.split_path(option, + dynoption=dynoption, ) for suffix in self.get_suffixes(config_bag, dynoption=dynoption, @@ -182,8 +184,7 @@ class DynOptionDescription(OptionDescription): else: parent_path = self.impl_getname(suffix) + sub_path yield OptionBag(option.to_dynoption(parent_path, - suffix, - self, + [suffix], ), index, config_bag, diff --git a/tiramisu/option/leadership.py b/tiramisu/option/leadership.py index dc88e75..efa4dc0 100644 --- a/tiramisu/option/leadership.py +++ b/tiramisu/option/leadership.py @@ -248,11 +248,9 @@ class Leadership(OptionDescription): def to_dynoption(self, rootpath: str, - suffix: str, - ori_dyn, + suffixes: Optional[list], ) -> SynDynLeadership: return SynDynLeadership(self, rootpath, - suffix, - ori_dyn, + suffixes, ) diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index c6d3ec4..1985edf 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -464,15 +464,13 @@ class Option(BaseOption): def to_dynoption(self, rootpath: str, - suffix: str, - dyn_parent, + suffixes: list[str], ) -> SynDynOption: """tranforme a dynoption to a syndynoption """ return SynDynOption(self, rootpath, - suffix, - dyn_parent, + suffixes, ) def validate(self, value: Any): """option needs a validate function diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index 6065e65..5dce1e0 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -44,13 +44,13 @@ class CacheOptionDescription(BaseOption): return self.impl_is_readonly() def _build_cache(self, + display_name, _consistencies=None, _consistencies_id=0, currpath: List[str]=None, cache_option=None, force_store_values=None, dependencies_information=None, - display_name=None, ) -> None: """validate options and set option has readonly option """ @@ -80,13 +80,13 @@ class CacheOptionDescription(BaseOption): subpath = '.'.join(sub_currpath) if isinstance(option, OptionDescription): # pylint: disable=protected-access - option._build_cache(_consistencies, + option._build_cache(display_name, + _consistencies, _consistencies_id, sub_currpath, cache_option, force_store_values, dependencies_information, - display_name, ) else: for information, options in option.get_dependencies_information().items(): @@ -96,15 +96,6 @@ class CacheOptionDescription(BaseOption): properties = option.impl_getproperties() if 'force_store_value' in properties: force_store_values.append(option) -# if __debug__ and ('force_default_on_freeze' in properties or \ -# 'force_metaconfig_on_freeze' in properties) and \ -# 'frozen' not in properties and \ -# option.impl_is_leader(): -# raise ConfigError(_('a leader ({0}) cannot have ' -# '"force_default_on_freeze" or ' -# '"force_metaconfig_on_freeze" ' -# 'property without "frozen"' -# '').format(option.impl_get_display_name())) if option.impl_is_readonly(): raise ConflictError(_('duplicate option: {0}').format(option)) if not self.impl_is_readonly() and display_name: @@ -114,7 +105,7 @@ class CacheOptionDescription(BaseOption): if init: self._cache_force_store_values = force_store_values # pylint: disable=attribute-defined-outside-init self._cache_dependencies_information = dependencies_information # pylint: disable=attribute-defined-outside-init - self._path = self._name # pylint: disable=attribute-defined-outside-init,no-member + self._path = None # pylint: disable=attribute-defined-outside-init,no-member self._set_readonly() def impl_build_force_store_values(self, @@ -146,10 +137,8 @@ class CacheOptionDescription(BaseOption): leader_option_bag.properties = frozenset() follower_len = len(values.get_value(leader_option_bag)[0]) if option.issubdyn(): - subpath = leader_option_bag.option.rootpath - doption = option.to_dynoption(subpath, - leader_option_bag.option.impl_getsuffix(), - leader_option_bag.option.dyn_parent, + doption = option.to_dynoption(leader_option_bag.option.rootpath, + leader_option_bag.option.get_suffixes(), ) else: doption = option @@ -190,56 +179,48 @@ class OptionDescriptionWalk(CacheOptionDescription): """ __slots__ = ('_children',) + def get_child_not_dynamic(self, + name, + allow_dynoption, + ): + if name in self._children[0]: # pylint: disable=no-member + option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member + if option.impl_is_dynoptiondescription() and not allow_dynoption: + raise AttributeError(_(f'unknown option "{name}" ' + "in root optiondescription (it's a dynamic option)" + )) + return option + def get_child(self, name: str, config_bag: ConfigBag, - subpath: str, *, - dynoption=None, - option_suffix: Optional[str]=None, allow_dynoption: bool=False, ) -> Union[BaseOption, SynDynOptionDescription]: """get a child """ # if not dyn - if name in self._children[0]: # pylint: disable=no-member - option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member - if option.impl_is_dynoptiondescription(): - if allow_dynoption: - option_suffix = None - else: - raise AttributeError(_(f'unknown option "{name}" ' - "in root optiondescription (it's a dynamic option)" - )) - if option.issubdyn(): - return option.to_dynoption(subpath, - option_suffix, - option, - ) + option = self.get_child_not_dynamic(name, + allow_dynoption, + ) + if option: return option # if dyn - if dynoption: - self_opt = dynoption - else: - self_opt = self for child in self._children[1]: # pylint: disable=no-member if not child.impl_is_dynoptiondescription(): continue - for suffix in child.get_suffixes(config_bag, - dynoption, - ): + for suffix in child.get_suffixes(config_bag): if name != child.impl_getname(suffix): continue - return child.to_dynoption(subpath, - suffix, - child, + return child.to_dynoption(self.impl_getpath(), + [suffix], ) if self.impl_get_group_type() == groups.root: # pylint: disable=no-member raise AttributeError(_(f'unknown option "{name}" ' 'in root optiondescription' )) raise AttributeError(_(f'unknown option "{name}" ' - f'in optiondescription "{self_opt.impl_get_display_name()}"' + f'in optiondescription "{self.impl_get_display_name()}"' )) def get_children(self, @@ -248,43 +229,54 @@ class OptionDescriptionWalk(CacheOptionDescription): dyn: bool=True, #path: Optional[str]=None, dynoption=None, - option_suffix: Optional[str]=None, ) -> Union[BaseOption, SynDynOptionDescription]: """get children """ -# if path: -# subpath = path + for child in self._children[1]: + if dyn and child.impl_is_dynoptiondescription(): + yield from self.get_suffixed_children(dynoption, + [], + config_bag, + child, + ) + else: + yield child + + def get_path(self, + config_bag, + dynoption, + ): if dynoption: self_opt = dynoption else: self_opt = self - if not dyn or config_bag is undefined or \ + if config_bag is undefined or \ config_bag.context.get_description() == self: - subpath = '' - else: - subpath = self_opt.impl_getpath() - for child in self._children[1]: # pylint: disable=no-member - if dyn and child.impl_is_dynoptiondescription(): - for suffix in child.get_suffixes(config_bag, - dynoption, - ): - yield child.to_dynoption(subpath, - suffix, - child, - ) - elif dyn and child.issubdyn() or child.impl_is_dynsymlinkoption(): - yield child.to_dynoption(subpath, - option_suffix, - child, - ) - else: - yield child + return '' + return self_opt.impl_getpath() + + def get_suffixed_children(self, + dynoption, + option_suffixes: list, + config_bag: ConfigBag, + child, + ): + root_path = self.get_path(config_bag, dynoption) + for suffix in child.get_suffixes(config_bag, + dynoption=dynoption, + ): + yield child.to_dynoption(root_path, + option_suffixes + [suffix], + ) + def get_children_recursively(self, bytype: Optional[BaseOption], byname: Optional[str], config_bag: ConfigBag, self_opt: BaseOption=None, + *, + option_suffixes: Optional[list]=None ) -> Iterator[Union[BaseOption, SynDynOptionDescription]]: """get children recursively """ @@ -402,19 +394,19 @@ class OptionDescription(OptionDescriptionWalk): def to_dynoption(self, rootpath: str, - suffix: str, - ori_dyn) -> SynDynOptionDescription: + suffixes: Optional[list], + #ori_dyn, + ) -> Union[SubDynOptionDescription, SynDynOptionDescription]: """get syn dyn option description """ - if suffix is None: - return SubDynOptionDescription(self, - rootpath, - ori_dyn, - ) - return SynDynOptionDescription(self, - rootpath, - suffix, - ori_dyn) + if self.impl_is_dynoptiondescription(): + obj = SynDynOptionDescription + else: + obj = SubDynOptionDescription + return obj(self, + rootpath, + suffixes, + ) def impl_is_dynsymlinkoption(self) -> bool: """option is not a dyn symlink option diff --git a/tiramisu/option/symlinkoption.py b/tiramisu/option/symlinkoption.py index 3526f6b..acfdc04 100644 --- a/tiramisu/option/symlinkoption.py +++ b/tiramisu/option/symlinkoption.py @@ -47,11 +47,12 @@ class SymLinkOption(BaseOption): _setattr(self, '_name', name) _setattr(self, '_opt', opt) opt._add_dependency(self) - self._path = None def __getattr__(self, name: str, ) -> Any: + if name == '_path': + raise AttributeError() return getattr(self._opt, name) def _setsubdyn(self, diff --git a/tiramisu/option/syndynoption.py b/tiramisu/option/syndynoption.py index a14ea31..fcb0c6b 100644 --- a/tiramisu/option/syndynoption.py +++ b/tiramisu/option/syndynoption.py @@ -29,20 +29,17 @@ class SynDynOption: """ __slots__ = ('rootpath', 'opt', - 'suffix', - 'dyn_parent', + 'suffixes', '__weakref__') def __init__(self, opt: BaseOption, rootpath: str, - suffix: str, - dyn_parent, + suffixes: list, ) -> None: self.opt = opt self.rootpath = rootpath - self.suffix = suffix - self.dyn_parent = dyn_parent + self.suffixes = suffixes def __getattr__(self, name: str) -> Any: @@ -60,10 +57,10 @@ class SynDynOption: """ return self.opt.impl_get_display_name(self) - def impl_getsuffix(self) -> str: + def get_suffixes(self) -> str: """get suffix """ - return self.suffix + return self.suffixes def impl_getpath(self) -> str: """get path @@ -85,6 +82,5 @@ class SynDynOption: if leadership: rootpath = self.rootpath.rsplit('.', 1)[0] return leadership.to_dynoption(rootpath, - self.suffix, - self.dyn_parent, + self.suffixes, ) diff --git a/tiramisu/option/syndynoptiondescription.py b/tiramisu/option/syndynoptiondescription.py index d9f14f2..355a7ac 100644 --- a/tiramisu/option/syndynoptiondescription.py +++ b/tiramisu/option/syndynoptiondescription.py @@ -30,155 +30,75 @@ from .baseoption import BaseOption from .syndynoption import SynDynOption -class SubDynOptionDescription: - __slots__ = ('rootpath', - 'opt', - 'dyn_parent', - '__weakref__', +class Syn: + __slots__ = ('opt', + 'rootpath', + '_suffixes', ) def __init__(self, opt: BaseOption, rootpath: str, - dyn_parent, + suffixes: list, ) -> None: self.opt = opt self.rootpath = rootpath - self.dyn_parent = dyn_parent - - def impl_getpath(self) -> str: - """get path - """ - path = self.opt.impl_getname() - if self.rootpath: - path = f'{self.rootpath}.{path}' - return path - - def get_sub_children(self, - option, - config_bag, - *, - index=None, - properties=undefined, - ): - return self.opt.get_sub_children(option, - config_bag, - index=index, - properties=properties, - dynoption=self, - ) - - def getsubdyn(self): - return self.opt.getsubdyn() - - def impl_is_optiondescription(self): - return True - - def impl_is_dynsymlinkoption(self): - return True - - def impl_is_sub_dyn_optiondescription(self): - return True + self._suffixes = suffixes def impl_get_display_name(self) -> str: return self.opt.impl_get_display_name(self) - def impl_is_dynoptiondescription(self) -> bool: - return True - - def to_dynoption(self, - rootpath: str, - suffix: str, - ori_dyn, - ): - return self.opt.to_dynoption(rootpath, suffix, ori_dyn) - - -class SynDynOptionDescription: - """SynDynOptionDescription internal option, it's an instanciate synoptiondescription - """ - __slots__ = ('opt', - 'rootpath', - '_suffix', - 'ori_dyn') - - def __init__(self, - opt: BaseOption, - rootpath: str, - suffix: str, - ori_dyn) -> None: - self.opt = opt - self.rootpath = rootpath - self._suffix = suffix - # For a Leadership inside a DynOptionDescription - self.ori_dyn = ori_dyn - - def __getattr__(self, - name: str, - ) -> Any: - # if not in SynDynOptionDescription, get value in self.opt - return getattr(self.opt, - name, - ) - - def impl_getname(self) -> str: - """get name - """ - if self.opt.impl_is_dynoptiondescription(): - return self.opt.impl_getname(self._suffix) - return self.opt.impl_getname() - - def impl_get_display_name(self) -> str: - """get display name - """ - return self.opt.impl_get_display_name(self) - - def get_children(self, - config_bag: ConfigBag, - dyn: bool=True, - ): - # pylint: disable=unused-argument - """get children - """ - yield from self.opt.get_children(config_bag, - dynoption=self, - option_suffix=self._suffix, - ) - def get_child(self, name: str, config_bag: ConfigBag, - subpath: str, + *, allow_dynoption: bool=False, ): """get children """ - return self.opt.get_child(name, - config_bag, - subpath, - dynoption=self, - option_suffix=self._suffix, - allow_dynoption=allow_dynoption, - ) + # if not dyn + option = self.opt.get_child_not_dynamic(name, + allow_dynoption, + ) + if option: + if allow_dynoption and option.impl_is_dynoptiondescription(): + return option + return option.to_dynoption(self.impl_getpath(), + self._suffixes, + ) + for child in self.opt._children[1]: # pylint: disable=no-member + if not child.impl_is_dynoptiondescription(): + continue + for suffix in child.get_suffixes(config_bag, + dynoption=self, + ): + if name != child.impl_getname(suffix): + continue + return child.to_dynoption(self.impl_getpath(), + self._suffixes + [suffix], + ) + raise AttributeError(_(f'unknown option "{name}" ' + f'in optiondescription "{self.impl_get_display_name()}"' + )) - def get_sub_children(self, - option, - config_bag, - *, - index=None, - properties=undefined, - ): - return self.opt.get_sub_children(option, - config_bag, - index=index, - properties=properties, - dynoption=self, - ) - - def impl_is_dynsymlinkoption(self) -> bool: - """it's a dynsymlinkoption + def get_children(self, + config_bag: ConfigBag, + ): + # pylint: disable=unused-argument + """get children """ - return True + for child in self.opt._children[1]: + if child.impl_is_dynoptiondescription(): + for suffix in child.get_suffixes(config_bag, + dynoption=self, + ): + yield child.to_dynoption(self.impl_getpath(), + self._suffixes + [suffix], + ) + else: + yield child.to_dynoption(self.impl_getpath(), + self._suffixes, + ) def get_children_recursively(self, bytype: Optional[BaseOption], @@ -196,6 +116,64 @@ class SynDynOptionDescription: ): yield option + def get_suffixes(self) -> str: + """get suffixes + """ + return self._suffixes + + def impl_is_dynsymlinkoption(self) -> bool: + """it's a dynsymlinkoption + """ + return True + + +class SubDynOptionDescription(Syn): + + def impl_getpath(self) -> str: + """get path + """ + path = self.opt.impl_getname() + if self.rootpath: + path = f'{self.rootpath}.{path}' + return path + + def getsubdyn(self): + return self.opt.getsubdyn() + + def impl_is_optiondescription(self): + return True + + def impl_is_symlinkoption(self): + return False + + def impl_is_leadership(self): + return False + + def impl_is_dynoptiondescription(self) -> bool: + return True + + def impl_getproperties(self): + return self.opt.impl_getproperties() + + +class SynDynOptionDescription(Syn): + """SynDynOptionDescription internal option, it's an instanciate synoptiondescription + """ + def __getattr__(self, + name: str, + ) -> Any: + # if not in SynDynOptionDescription, get value in self.opt + return getattr(self.opt, + name, + ) + + def impl_getname(self) -> str: + """get name + """ + if self.opt.impl_is_dynoptiondescription(): + return self.opt.impl_getname(self._suffixes[-1]) + return self.opt.impl_getname() + def impl_getpath(self) -> str: """get path """ @@ -204,10 +182,8 @@ class SynDynOptionDescription: path = f'{self.rootpath}.{path}' return path - def impl_getsuffix(self) -> str: - """get suffix - """ - return self._suffix + def getsubdyn(self): + return self.opt class SynDynLeadership(SynDynOptionDescription): @@ -217,8 +193,7 @@ class SynDynLeadership(SynDynOptionDescription): """get the leader """ return self.opt.get_leader().to_dynoption(self.impl_getpath(), - self._suffix, - self.ori_dyn, + self._suffixes, ) def get_followers(self) -> Iterator[SynDynOption]: @@ -227,26 +202,9 @@ class SynDynLeadership(SynDynOptionDescription): subpath = self.impl_getpath() for follower in self.opt.get_followers(): yield follower.to_dynoption(subpath, - self._suffix, - self.ori_dyn, + self._suffixes, ) - def reset_cache(self, - path: str, - config_bag: 'ConfigBag', - resetted_opts: List[str], - ) -> None: - """reset cache - """ - leader = self.get_leader() - followers = self.get_followers() - self._reset_cache(path, - leader, - followers, - config_bag, - resetted_opts, - ) - def pop(self, *args, **kwargs, @@ -271,7 +229,7 @@ class SynDynLeadership(SynDynOptionDescription): dyn=self, ) - def impl_getsuffix(self) -> str: + def get_suffixes(self) -> str: """get suffix """ - return self._suffix + return self._suffixes diff --git a/tiramisu/setting.py b/tiramisu/setting.py index dd00686..dce19f4 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -192,7 +192,7 @@ class OptionBag: elif option: self.path = option.impl_getpath() context = config_bag.context - if '.' not in self.path and option == context.get_description(): + if self.path is None: self.properties = None elif properties is undefined: settings = context.get_settings() @@ -704,20 +704,18 @@ class Settings: uncalculated=uncalculated, transitive_raise=transitive_raise, ) - return self._calc_raises_properties(option_bag.config_bag.properties, - option_bag.config_bag.permissives, + return self._calc_raises_properties(option_bag, option_properties, ) def _calc_raises_properties(self, - context_properties, - context_permissives, + option_bag, option_properties, ): - raises_properties = context_properties - SPECIAL_PROPERTIES + raises_properties = option_bag.config_bag.properties - SPECIAL_PROPERTIES # remove global permissive properties if raises_properties and 'permissive' in raises_properties: - raises_properties -= context_permissives + raises_properties -= option_bag.config_bag.permissives properties = option_properties & raises_properties # at this point it should not remain any property for the option return properties @@ -743,8 +741,7 @@ class Settings: transitive_raise=transitive_raise, )) calc_properties = [] - for property_ in self._calc_raises_properties(option_bag.config_bag.properties, - option_bag.config_bag.permissives, + for property_ in self._calc_raises_properties(option_bag, set(help_properties.keys()), ): calc_properties.append(help_properties[property_]) diff --git a/tiramisu/value.py b/tiramisu/value.py index b452e60..a4aaab8 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -152,12 +152,7 @@ class Values: """ has_calculation = False if isinstance(value, Calculation): - try: - value = value.execute(option_bag) - except ConfigError as err: - msg = _(f'error when calculating "{option_bag.option.impl_get_display_name()}": ' - f'{err} : {option_bag.path}') - raise ConfigError(msg) from err + value = value.execute(option_bag) has_calculation = True elif isinstance(value, list): # if value is a list, do subcalculation @@ -354,6 +349,7 @@ class Values: for coption in option.get_children_recursively(None, None, option_bag.config_bag, + option_suffixes=[], ): if 'force_store_value' in coption.impl_getproperties(): force_store_options.append(coption)