diff --git a/src/rougail/annotator/family.py b/src/rougail/annotator/family.py index d4a54799..6ba7a456 100644 --- a/src/rougail/annotator/family.py +++ b/src/rougail/annotator/family.py @@ -104,52 +104,36 @@ class FamilyAnnotator(Walk): def change_modes(self): """change the mode of variables """ - # default is high level - new_family_mode = modes_level[-1] - for family in self.objectspace.space.variables.values(): - self.change_family_mode(family, - new_family_mode, - first=True, - ) + families = list(self.get_families()) + for family in families: + self._set_default_mode(family) + families.reverse() + for family in families: + self._change_family_mode(family) - def change_family_mode(self, - family: 'self.objectspace.family', - new_family_mode: str, - first: bool=False, - ) -> str: - if hasattr(family, 'mode'): + def _set_default_mode(self, + family: 'self.objectspace.family', + ) -> None: + if hasattr(family, 'mode') and 'mode' in vars(family): family_mode = family.mode else: - family_mode = new_family_mode - if hasattr(family, 'variable'): - # change variable mode, but not if variables are not in a family - for variable in family.variable.values(): - if isinstance(variable, self.objectspace.family): - new_family_mode = self.change_family_mode(variable, - new_family_mode, - ) - continue - if first: - continue - if isinstance(variable, self.objectspace.leadership): - func = self._change_variable_mode_leader - else: - func = self._change_variabe_mode - func(variable, - family_mode, - ) - if modes[new_family_mode] > modes[variable.mode]: - new_family_mode = variable.mode - if not first: - # set the lower variable mode to family - family.mode = new_family_mode - return new_family_mode + family_mode = None + if not hasattr(family, 'variable'): + return + for variable in family.variable.values(): + if isinstance(variable, self.objectspace.family): + if family_mode and 'mode' not in vars(variable): + variable.mode = family_mode + continue + if isinstance(variable, self.objectspace.leadership): + func = self._set_default_mode_leader + else: + func = self._set_default_mode_variable + func(variable) - def _change_variabe_mode(self, - variable, - family_mode: str, - is_follower=False, - ) -> None: + @staticmethod + def _set_default_mode_variable(variable: 'self.objectspace.variable', + ) -> None: # auto_save or auto_freeze variable is set to 'basic' mode # if its mode is not defined by the user if 'mode' not in vars(variable) and \ @@ -158,6 +142,57 @@ class FamilyAnnotator(Walk): # mandatory variable without value is a basic variable if variable.mandatory is True and not hasattr(variable, 'default'): variable.mode = modes_level[0] + + def _set_default_mode_leader(self, + leadership, + ) -> None: + leader_mode = None + for follower in leadership.variable: + if follower.auto_save is True: + msg = _(f'leader/followers "{follower.name}" could not be auto_save') + raise DictConsistencyError(msg, 29, leadership.xmlfiles) + if follower.auto_freeze is True: + msg = f'leader/followers "{follower.name}" could not be auto_freeze' + raise DictConsistencyError(_(msg), 30, leadership.xmlfiles) + self._set_default_mode_variable(follower) + if leader_mode is None: + leader_mode = leadership.variable[0].mode + else: + # leader's mode is minimum level + if modes[leader_mode] > modes[follower.mode]: + follower.mode = leader_mode + leadership.mode = leader_mode + + def _change_family_mode(self, + family: 'self.objectspace.family', + ) -> None: + if hasattr(family, 'mode'): + family_mode = family.mode + else: + family_mode = modes_level[1] + min_variable_mode = modes_level[-1] + # change variable mode, but not if variables are not in a family + if hasattr(family, 'variable'): + for variable in family.variable.values(): + if not isinstance(variable, self.objectspace.family): + if isinstance(variable, self.objectspace.leadership): + func = self._change_variable_mode_leader + else: + func = self._change_variable_mode + func(variable, + family_mode, + ) + if modes[min_variable_mode] > modes[variable.mode]: + min_variable_mode = variable.mode + if hasattr(family, 'mode'): + # set the lower variable mode to family + family.mode = min_variable_mode + + def _change_variable_mode(self, + variable, + family_mode: str, + is_follower=False, + ) -> None: # none basic variable in high level family has to be in high level if modes[variable.mode] < modes[family_mode] and \ (not is_follower or variable.mode != modes_level[0]): @@ -168,27 +203,13 @@ class FamilyAnnotator(Walk): family_mode: str, ) -> None: is_follower = False - leader_mode = None for follower in leadership.variable: - if follower.auto_save is True: - msg = _(f'leader/followers "{follower.name}" could not be auto_save') - raise DictConsistencyError(msg, 29, leadership.xmlfiles) - if follower.auto_freeze is True: - msg = f'leader/followers "{follower.name}" could not be auto_freeze' - raise DictConsistencyError(_(msg), 30, leadership.xmlfiles) - self._change_variabe_mode(follower, + self._change_variable_mode(follower, family_mode, is_follower, ) - if leader_mode is None: - leader_mode = leadership.variable[0].mode - leadership.variable[0].mode = None - else: - # leader's mode is minimum level - if modes[leader_mode] > modes[follower.mode]: - follower.mode = leader_mode is_follower = True - leadership.mode = leader_mode + leadership.variable[0].mode = None def dynamic_families(self): """link dynamic families to object diff --git a/src/rougail/annotator/group.py b/src/rougail/annotator/group.py index 84833c62..d6d13618 100644 --- a/src/rougail/annotator/group.py +++ b/src/rougail/annotator/group.py @@ -53,9 +53,14 @@ class GroupAnnotator: cache_paths[group.leader] = leader_fam_path follower_names = list(group.follower.keys()) leader = self.objectspace.paths.get_variable(group.leader) - ori_leader_family = self.objectspace.paths.get_family(leader_fam_path, - leader.namespace, - ) + if '.' not in leader_fam_path: + # it's a namespace + ori_leader_family = self.objectspace.space.variables[leader_fam_path] + else: + # it's a sub family + ori_leader_family = self.objectspace.paths.get_family(leader_fam_path, + leader.namespace, + ) has_a_leader = False for variable in list(ori_leader_family.variable.values()): if isinstance(variable, self.objectspace.leadership) and \ @@ -121,10 +126,7 @@ class GroupAnnotator: leadership_path, leader_space, ) - leader_family = self.objectspace.paths.get_family(ori_leader_family.path, - ori_leader_family.namespace, - ) - leader_family.variable[variable.name] = leader_space + ori_leader_family.variable[variable.name] = leader_space leader_space.variable.append(variable) self.objectspace.paths.set_leader(variable.namespace, ori_leader_family.path, diff --git a/src/rougail/annotator/param.py b/src/rougail/annotator/param.py index 0b684e84..9fa5404b 100644 --- a/src/rougail/annotator/param.py +++ b/src/rougail/annotator/param.py @@ -48,7 +48,7 @@ class ParamAnnotator: variable_type = self.valid_type_validation(obj) for param_idx, param in enumerate(obj.param): if hasattr(param, 'text'): - if param.type == 'suffix': + if param.type in ['suffix', 'index']: msg = _(f'"{param.type}" parameter must not have a value') raise DictConsistencyError(msg, 28, obj.xmlfiles) elif param.type == 'nil': @@ -81,9 +81,16 @@ class ParamAnnotator: if param.type == 'suffix': for target in obj.target: if not self.objectspace.paths.variable_is_dynamic(target.name.path): - msg = _(f'"suffix" parameter cannot be set with target "{target.name}"' + msg = _(f'"{param.type}" parameter cannot be set with target "{target.name}"' f' which is not a dynamic variable') raise DictConsistencyError(msg, 53, obj.xmlfiles) + elif param.type == 'index': + for target in obj.target: + if not self.objectspace.paths.is_follower(target.name.path): + msg = _(f'"{param.type}" parameter cannot be set with target "{target.name}"' + f' which is not a follower variable') + raise DictConsistencyError(msg, 60, obj.xmlfiles) + pass elif param.type == 'nil': param.text = None elif param.type == 'string': diff --git a/src/rougail/data/rougail.dtd b/src/rougail/data/rougail.dtd index ef773057..30de5621 100644 --- a/src/rougail/data/rougail.dtd +++ b/src/rougail/data/rougail.dtd @@ -72,7 +72,7 @@ - + @@ -125,7 +125,7 @@ - + diff --git a/src/rougail/path.py b/src/rougail/path.py index b2b45eb8..2055fba1 100644 --- a/src/rougail/path.py +++ b/src/rougail/path.py @@ -136,6 +136,15 @@ class Path: leadership = self.get_family(variable['leader'], variable['variableobj'].namespace) return leadership.variable[0].path == path + def is_follower(self, path): + """Is the variable is a follower + """ + variable = self._get_variable(path) + if not variable['leader']: + return False + leadership = self.get_family(variable['leader'], variable['variableobj'].namespace) + return leadership.variable[0].path != path + # Variable def add_variable(self, # pylint: disable=R0913 namespace: str, diff --git a/src/rougail/tiramisureflector.py b/src/rougail/tiramisureflector.py index 295ab0f5..caa33c6e 100644 --- a/src/rougail/tiramisureflector.py +++ b/src/rougail/tiramisureflector.py @@ -253,6 +253,8 @@ class Common: return f'ParamInformation("{param.text}", None)' if param.type == 'suffix': return 'ParamSuffix()' + if param.type == 'index': + return 'ParamIndex()' raise Exception(f'unknown type {param.type}') # pragma: no cover @staticmethod diff --git a/tests/dictionaries/01fill_target_optional/00-base.xml b/tests/dictionaries/01fill_target_optional/00-base.xml new file mode 100644 index 00000000..476aa8ec --- /dev/null +++ b/tests/dictionaries/01fill_target_optional/00-base.xml @@ -0,0 +1,19 @@ + + + + + + non + + + + + + + mode_conteneur_actif1 + mode_conteneur_actif + + + + diff --git a/tests/dictionaries/01fill_target_optional/__init__.py b/tests/dictionaries/01fill_target_optional/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/01fill_target_optional/makedict/base.json b/tests/dictionaries/01fill_target_optional/makedict/base.json new file mode 100644 index 00000000..aa4ad28e --- /dev/null +++ b/tests/dictionaries/01fill_target_optional/makedict/base.json @@ -0,0 +1 @@ +{"rougail.general.mode_conteneur_actif1": "non"} diff --git a/tests/dictionaries/01fill_target_optional/tiramisu/base.py b/tests/dictionaries/01fill_target_optional/tiramisu/base.py new file mode 100644 index 00000000..204d9cb6 --- /dev/null +++ b/tests/dictionaries/01fill_target_optional/tiramisu/base.py @@ -0,0 +1,17 @@ +from importlib.machinery import SourceFileLoader +from importlib.util import spec_from_loader, module_from_spec +loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py') +spec = spec_from_loader(loader.name, loader) +func = module_from_spec(spec) +loader.exec_module(func) +for key, value in dict(locals()).items(): + if key != ['SourceFileLoader', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +option_3 = StrOption(name="mode_conteneur_actif1", doc="No change", default="non", properties=frozenset({"mandatory", "normal"})) +option_2 = OptionDescription(name="general", doc="general", children=[option_3], properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) diff --git a/tests/dictionaries/10check_base_target_optional/00-base.xml b/tests/dictionaries/10check_base_target_optional/00-base.xml new file mode 100644 index 00000000..b065fc3d --- /dev/null +++ b/tests/dictionaries/10check_base_target_optional/00-base.xml @@ -0,0 +1,15 @@ + + + + + + + + 0 + 100 + int + + + + diff --git a/tests/dictionaries/10check_base_target_optional/__init__.py b/tests/dictionaries/10check_base_target_optional/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/10check_base_target_optional/makedict/base.json b/tests/dictionaries/10check_base_target_optional/makedict/base.json new file mode 100644 index 00000000..d3a9c907 --- /dev/null +++ b/tests/dictionaries/10check_base_target_optional/makedict/base.json @@ -0,0 +1 @@ +{"rougail.my_variable": null} diff --git a/tests/dictionaries/10check_base_target_optional/tiramisu/base.py b/tests/dictionaries/10check_base_target_optional/tiramisu/base.py new file mode 100644 index 00000000..0da19d84 --- /dev/null +++ b/tests/dictionaries/10check_base_target_optional/tiramisu/base.py @@ -0,0 +1,16 @@ +from importlib.machinery import SourceFileLoader +from importlib.util import spec_from_loader, module_from_spec +loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py') +spec = spec_from_loader(loader.name, loader) +func = module_from_spec(spec) +loader.exec_module(func) +for key, value in dict(locals()).items(): + if key != ['SourceFileLoader', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +option_2 = StrOption(name="my_variable", doc="my_variable", properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) diff --git a/tests/dictionaries/10leadership_auto_index/00-base.xml b/tests/dictionaries/10leadership_auto_index/00-base.xml new file mode 100644 index 00000000..ed6d0676 --- /dev/null +++ b/tests/dictionaries/10leadership_auto_index/00-base.xml @@ -0,0 +1,21 @@ + + + + + a + b + c + + + + + + + + follower1 + + + follower1 + + + diff --git a/tests/dictionaries/10leadership_auto_index/__init__.py b/tests/dictionaries/10leadership_auto_index/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/10leadership_auto_index/makedict/base.json b/tests/dictionaries/10leadership_auto_index/makedict/base.json new file mode 100644 index 00000000..010ea979 --- /dev/null +++ b/tests/dictionaries/10leadership_auto_index/makedict/base.json @@ -0,0 +1 @@ +{"rougail.leader.leader": [{"rougail.leader.leader": "a", "rougail.leader.follower1": 0}, {"rougail.leader.leader": "b", "rougail.leader.follower1": 1}, {"rougail.leader.leader": "c", "rougail.leader.follower1": 2}]} diff --git a/tests/dictionaries/10leadership_auto_index/tiramisu/base.py b/tests/dictionaries/10leadership_auto_index/tiramisu/base.py new file mode 100644 index 00000000..24c130f3 --- /dev/null +++ b/tests/dictionaries/10leadership_auto_index/tiramisu/base.py @@ -0,0 +1,18 @@ +from importlib.machinery import SourceFileLoader +from importlib.util import spec_from_loader, module_from_spec +loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py') +spec = spec_from_loader(loader.name, loader) +func = module_from_spec(spec) +loader.exec_module(func) +for key, value in dict(locals()).items(): + if key != ['SourceFileLoader', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +option_3 = StrOption(name="leader", doc="leader", multi=True, default=['a', 'b', 'c'], properties=frozenset({"mandatory"})) +option_4 = IntOption(name="follower1", doc="follower1", multi=True, default=Calculation(func.calc_val, Params((ParamIndex()))), properties=frozenset({"normal"})) +option_2 = Leadership(name="leader", doc="leader", children=[option_3, option_4], properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) diff --git a/tests/dictionaries/45multi_family_expert/00-base.xml b/tests/dictionaries/45multi_family_expert/00-base.xml new file mode 100644 index 00000000..d79e58e4 --- /dev/null +++ b/tests/dictionaries/45multi_family_expert/00-base.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/tests/dictionaries/45multi_family_expert/__init__.py b/tests/dictionaries/45multi_family_expert/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/45multi_family_expert/makedict/base.json b/tests/dictionaries/45multi_family_expert/makedict/base.json new file mode 100644 index 00000000..536366a7 --- /dev/null +++ b/tests/dictionaries/45multi_family_expert/makedict/base.json @@ -0,0 +1 @@ +{"rougail.base.subfamily.variable": null} diff --git a/tests/dictionaries/45multi_family_expert/tiramisu/base.py b/tests/dictionaries/45multi_family_expert/tiramisu/base.py new file mode 100644 index 00000000..94d393d7 --- /dev/null +++ b/tests/dictionaries/45multi_family_expert/tiramisu/base.py @@ -0,0 +1,18 @@ +from importlib.machinery import SourceFileLoader +from importlib.util import spec_from_loader, module_from_spec +loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py') +spec = spec_from_loader(loader.name, loader) +func = module_from_spec(spec) +loader.exec_module(func) +for key, value in dict(locals()).items(): + if key != ['SourceFileLoader', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +option_4 = StrOption(name="variable", doc="variable", properties=frozenset({"expert"})) +option_3 = OptionDescription(name="subfamily", doc="subfamily", children=[option_4], properties=frozenset({"expert"})) +option_2 = OptionDescription(name="base", doc="base", children=[option_3], properties=frozenset({"expert"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) diff --git a/tests/eosfunc/test.py b/tests/eosfunc/test.py index ec3de625..13bd0452 100644 --- a/tests/eosfunc/test.py +++ b/tests/eosfunc/test.py @@ -60,3 +60,7 @@ def device_type(*args, **kwargs): def calc_list(*args, **kwargs): return list(args) + + +def test_index(index): + return index