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