fix: black

This commit is contained in:
egarette@silique.fr 2025-05-12 08:45:39 +02:00
parent bb1f117ed1
commit 5a4bb343b0
31 changed files with 628 additions and 159 deletions

View file

@ -50,7 +50,9 @@ def tiramisu_display_name(
comment = doc if doc and doc != kls.impl_getname() else ""
if "{{ identifier }}" in comment and subconfig.identifiers:
comment = comment.replace("{{ identifier }}", str(subconfig.identifiers[-1]))
path_in_description = values.get_information(context_subconfig, 'path_in_description', True)
path_in_description = values.get_information(
context_subconfig, "path_in_description", True
)
if path_in_description or not comment:
comment = f" ({comment})" if comment else ""
if path_in_description is False:

View file

@ -140,9 +140,7 @@ class Annotator(Walk):
family.path
)
self.objectspace.informations.add(family.path, "dynamic_variable", path)
self.objectspace.informations.add(
family.path, "ymlfiles", family.xmlfiles
)
self.objectspace.informations.add(family.path, "ymlfiles", family.xmlfiles)
def change_modes(self):
"""change the mode of variables"""

View file

@ -115,7 +115,9 @@ class Annotator(Walk): # pylint: disable=R0903
if isinstance(variable.choices, Calculation):
continue
if variable.choices is None:
msg = _('the variable "{0}" is a "choice" variable but don\'t have any choice').format(variable.path)
msg = _(
'the variable "{0}" is a "choice" variable but don\'t have any choice'
).format(variable.path)
raise DictConsistencyError(msg, 19, variable.xmlfiles)
if not variable.mandatory and not variable.multi:
self.add_choice_nil(variable)
@ -132,5 +134,7 @@ class Annotator(Walk): # pylint: disable=R0903
if variable.type != "regexp":
continue
if variable.regexp is None:
msg = _('the variable "{0}" is a "regexp" variable but don\'t have any regexp').format(variable.path)
msg = _(
'the variable "{0}" is a "regexp" variable but don\'t have any regexp'
).format(variable.path)
raise DictConsistencyError(msg, 66, variable.xmlfiles)

View file

@ -83,13 +83,21 @@ class Annotator(Walk): # pylint: disable=R0903
continue
path = variable.path
if variable.type not in ["unix_user", "secret"]:
msg = _('only "unix_user" or "secret" variable type can have "secret_manager" attribute, but "{0}" has type "{1}"')
raise DictConsistencyError(msg.format(path, variable.type), 56, variable.xmlfiles)
msg = _(
'only "unix_user" or "secret" variable type can have "secret_manager" attribute, but "{0}" has type "{1}"'
)
raise DictConsistencyError(
msg.format(path, variable.type), 56, variable.xmlfiles
)
if variable.multi and path not in self.objectspace.leaders:
msg = _('the variable "{0}" has attribute "secret_manager" but is a multi variable')
msg = _(
'the variable "{0}" has attribute "secret_manager" but is a multi variable'
)
raise DictConsistencyError(msg.format(path), 57, variable.xmlfiles)
if variable.default is not None:
msg = _('the variable "{0}" has attribute "secret_manager" so must not have default value')
msg = _(
'the variable "{0}" has attribute "secret_manager" so must not have default value'
)
raise DictConsistencyError(msg.format(path), 59, variable.xmlfiles)
def convert_variable(self):
@ -106,9 +114,11 @@ class Annotator(Walk): # pylint: disable=R0903
self.objectspace.informations.add(
variable.path, "ymlfiles", variable.xmlfiles
)
if variable.version != '1.0' and isinstance(variable.default, VariableCalculation):
calculated_variable_path, calculated_variable, identifier = variable.default.get_variable(
self.objectspace
if variable.version != "1.0" and isinstance(
variable.default, VariableCalculation
):
calculated_variable_path, calculated_variable, identifier = (
variable.default.get_variable(self.objectspace)
)
else:
calculated_variable = None
@ -144,15 +154,27 @@ class Annotator(Walk): # pylint: disable=R0903
return
if variable.path in self.objectspace.leaders:
variable.multi = self.objectspace.multis[variable.path] = True
elif variable.version != "1.0" and isinstance(variable.default, VariableCalculation):
calculated_variable_path, calculated_variable, identifier = variable.default.get_variable(
self.objectspace
elif variable.version != "1.0" and isinstance(
variable.default, VariableCalculation
):
calculated_variable_path, calculated_variable, identifier = (
variable.default.get_variable(self.objectspace)
)
if calculated_variable is not None:
if calculated_variable.multi is None:
self._convert_variable_multi(calculated_variable)
variable.multi = calc_multi_for_type_variable(variable, calculated_variable_path, calculated_variable, self.objectspace)[1]
if calculated_variable.path in self.objectspace.followers and variable.mandatory is calculated_variable.mandatory is False and calculated_variable.path.rsplit(".", 1)[0] != variable.path.rsplit(".", 1)[0]:
variable.multi = calc_multi_for_type_variable(
variable,
calculated_variable_path,
calculated_variable,
self.objectspace,
)[1]
if (
calculated_variable.path in self.objectspace.followers
and variable.mandatory is calculated_variable.mandatory is False
and calculated_variable.path.rsplit(".", 1)[0]
!= variable.path.rsplit(".", 1)[0]
):
variable.empty = False
else:
variable.multi = isinstance(variable.default, list)
@ -163,15 +185,18 @@ class Annotator(Walk): # pylint: disable=R0903
calculated_variable,
) -> None:
# if a variable has a variable as default value, that means the type/params or multi should has same value
if not isinstance(variable.default, VariableCalculation) or variable.type is not None:
if (
not isinstance(variable.default, VariableCalculation)
or variable.type is not None
):
return
# copy type and params
variable.type = calculated_variable.type
if variable.params is None and calculated_variable.params is not None:
variable.params = calculated_variable.params
if variable.type == 'choice' and variable.choices is None:
if variable.type == "choice" and variable.choices is None:
variable.choices = calculated_variable.choices
if variable.type == 'regexp' and variable.regexp is None:
if variable.type == "regexp" and variable.regexp is None:
variable.regexp = calculated_variable.regexp
def _convert_variable(

View file

@ -381,8 +381,8 @@ secret_manager:
rougail_process += """
alternative_name: {NAME[0]}
""".format(
NAME=normalize_family(process),
)
NAME=normalize_family(process),
)
rougail_process += """
choices:
"""
@ -409,8 +409,9 @@ secret_manager:
rougail_process += """ {% if _.output is not propertyerror and _.output == 'NAME' %}
Cannot load structural for NAME output
{% endif %}
""".replace("NAME", hidden_output
)
""".replace(
"NAME", hidden_output
)
elif process == "user data":
rougail_process += """ multi: true
mandatory: false"""
@ -475,7 +476,7 @@ default_params:
mandatory: false
default: {value}
"""
# print(rougail_process)
# print(rougail_process)
rougail_options += rougail_process
convert = FakeRougailConvert(add_extra_options)
convert.init()

View file

@ -188,7 +188,7 @@ class ParserVariable:
self.output = None
self.tiramisu_cache = rougailconfig["tiramisu_cache"]
self.load_unexist_redefine = rougailconfig["load_unexist_redefine"]
self.secret_pattern = rougailconfig['secret_manager.pattern']
self.secret_pattern = rougailconfig["secret_manager.pattern"]
# change default initkwargs in CONVERT_OPTION
if hasattr(rougailconfig, "config"):
for sub_od in rougailconfig.config.option("default_params"):
@ -505,7 +505,7 @@ class ParserVariable:
for key, value in obj.items():
if not isinstance(key, str):
raise DictConsistencyError(
f'a key is not in string format: {key}',
f"a key is not in string format: {key}",
103,
[filename],
)
@ -648,7 +648,8 @@ class ParserVariable:
raise DictConsistencyError(
f'"{path}" is not a valid variable, there are additional '
f'attributes: "{", ".join(extra_attrs)}"',
65, [filename]
65,
[filename],
)
self.parse_parameters(
path,
@ -687,9 +688,7 @@ class ParserVariable:
msg = f'cannot redefine the inexisting variable "{path}"'
raise DictConsistencyError(msg, 46, [filename])
obj["path"] = path
self.add_variable(
name, obj, filename, family_is_dynamic, parent_dynamic
)
self.add_variable(name, obj, filename, family_is_dynamic, parent_dynamic)
if family_is_leadership:
if first_variable:
self.leaders.append(path)
@ -803,10 +802,11 @@ class ParserVariable:
64,
[filename],
)
secret_manager = {"type": "jinja",
"jinja": self.secret_pattern,
"params": obj["secret_manager"],
}
secret_manager = {
"type": "jinja",
"jinja": self.secret_pattern,
"params": obj["secret_manager"],
}
self.set_calculation(
obj,
"secret_manager",

View file

@ -151,7 +151,9 @@ class Param(BaseModel):
) -> None:
super().__init__(**kwargs)
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
) -> dict:
return self.model_dump()
@ -167,8 +169,12 @@ class VariableParam(Param):
whole: bool = False
optional: bool = False
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
param = super().to_param(attribute_name, objectspace, path, version, namespace, xmlfiles)
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
) -> dict:
param = super().to_param(
attribute_name, objectspace, path, version, namespace, xmlfiles
)
variable, identifier = objectspace.paths.get_with_dynamic(
param["variable"],
path,
@ -178,14 +184,20 @@ class VariableParam(Param):
)
if not variable:
if not param.get("optional"):
msg = _('cannot find variable "{0}" defined in attribute "{1}" for "{2}"').format(param["variable"], attribute_name, path)
msg = _(
'cannot find variable "{0}" defined in attribute "{1}" for "{2}"'
).format(param["variable"], attribute_name, path)
raise DictConsistencyError(msg, 22, xmlfiles)
return None
if isinstance(variable, objectspace.family):
msg = _('the variable "{0}" is in fact a family in attribute "{1}" for "{2}"').format(variable["name"], attribute_name, path)
msg = _(
'the variable "{0}" is in fact a family in attribute "{1}" for "{2}"'
).format(variable["name"], attribute_name, path)
raise DictConsistencyError(msg, 42, xmlfiles)
if not isinstance(variable, objectspace.variable):
msg = _('unknown object "{0}" in attribute "{1}" for "{2}"').format(variable, attribute_name, path)
msg = _('unknown object "{0}" in attribute "{1}" for "{2}"').format(
variable, attribute_name, path
)
raise DictConsistencyError(msg, 44, xmlfiles)
param["variable"] = variable
if identifier:
@ -202,7 +214,9 @@ class IdentifierParam(Param):
**kwargs,
) -> None:
if not kwargs["family_is_dynamic"]:
msg = _('identifier parameter for "{0}" in "{1}" cannot be set none dynamic family').format(kwargs["attribute"], kwargs["path"])
msg = _(
'identifier parameter for "{0}" in "{1}" cannot be set none dynamic family'
).format(kwargs["attribute"], kwargs["path"])
raise DictConsistencyError(msg, 10, kwargs["xmlfiles"])
super().__init__(**kwargs)
@ -212,8 +226,12 @@ class InformationParam(Param):
information: str
variable: Optional[str] = None
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
param = super().to_param(attribute_name, objectspace, path, version, namespace, xmlfiles)
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
) -> dict:
param = super().to_param(
attribute_name, objectspace, path, version, namespace, xmlfiles
)
if not param["variable"]:
del param["variable"]
return param
@ -225,10 +243,14 @@ class InformationParam(Param):
xmlfiles,
)
if not variable:
msg = _('cannot find variable "{0}" defined in "{1}" for "{2}"').format(param["variable"], attribute_name, path)
msg = _('cannot find variable "{0}" defined in "{1}" for "{2}"').format(
param["variable"], attribute_name, path
)
raise DictConsistencyError(msg, 14, xmlfiles)
if identifier:
msg = _('variable "{0}" defined in "{1}" for "{2}" is a dynamic variable').format(param["variable"], attribute_name, path)
msg = _(
'variable "{0}" defined in "{1}" for "{2}" is a dynamic variable'
).format(param["variable"], attribute_name, path)
raise DictConsistencyError(msg, 15, xmlfiles)
param["variable"] = variable
return param
@ -237,25 +259,37 @@ class InformationParam(Param):
class IndexParam(Param):
type: str
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
if path not in objectspace.followers and (attribute_name != 'validators' or path not in objectspace.multis):
msg = _('the variable "{0}" is not a follower, so cannot have index type for param in "{1}"').format(path, attribute)
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
) -> dict:
if path not in objectspace.followers and (
attribute_name != "validators" or path not in objectspace.multis
):
msg = _(
'the variable "{0}" is not a follower, so cannot have index type for param in "{1}"'
).format(path, attribute)
raise DictConsistencyError(msg, 25, xmlfiles)
return super().to_param(attribute_name, objectspace, path, version, namespace, xmlfiles)
return super().to_param(
attribute_name, objectspace, path, version, namespace, xmlfiles
)
class NamespaceParam(Param):
type: str
namespace: str
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
def to_param(
self, attribute_name, objectspace, path, version, namespace, xmlfiles
) -> dict:
namespace = self.namespace
if namespace:
namespace = objectspace.paths[namespace].description
return {'type': 'any',
'value': namespace,
'key': self.key,
}
return {
"type": "any",
"value": namespace,
"key": self.key,
}
PARAM_TYPES = {
"any": AnyParam,
@ -287,7 +321,14 @@ class Calculation(BaseModel):
path = self.ori_path
params = {}
for param_obj in self.params:
param = param_obj.to_param(self.attribute_name, objectspace, path, self.version, self.namespace, self.xmlfiles)
param = param_obj.to_param(
self.attribute_name,
objectspace,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if param is None:
continue
params[param.pop("key")] = param
@ -454,13 +495,17 @@ class _VariableCalculation(Calculation):
path = self.path
else:
path = self.ori_path
if self.version != "1.0" and objectspace.paths.regexp_relative.search(self.variable):
if self.version != "1.0" and objectspace.paths.regexp_relative.search(
self.variable
):
variable_full_path = objectspace.paths.get_full_path(
self.variable,
path,
)
elif self.version == "1.0" and "{{ suffix }}" in self.variable:
variable_full_path = self.variable.replace("{{ suffix }}", "{{ identifier }}")
variable_full_path = self.variable.replace(
"{{ suffix }}", "{{ identifier }}"
)
else:
variable_full_path = self.variable
variable, identifier = objectspace.paths.get_with_dynamic(
@ -472,10 +517,14 @@ class _VariableCalculation(Calculation):
)
if variable and not isinstance(variable, objectspace.variable):
if isinstance(variable, objectspace.family):
msg = _('a variable "{0}" is needs in attribute "{1}" for "{2}" but it\'s a family').format(variable_full_path, self.attribute_name, self.path)
msg = _(
'a variable "{0}" is needs in attribute "{1}" for "{2}" but it\'s a family'
).format(variable_full_path, self.attribute_name, self.path)
raise DictConsistencyError(msg, 47, self.xmlfiles)
else:
msg = _('unknown object "{0}" in attribute "{1}" for "{2}"').format(variable, self.attribute_name, self.path)
msg = _('unknown object "{0}" in attribute "{1}" for "{2}"').format(
variable, self.attribute_name, self.path
)
raise DictConsistencyError(msg, 48, self.xmlfiles)
return variable_full_path, variable, identifier
@ -492,7 +541,9 @@ class _VariableCalculation(Calculation):
path = self.path
else:
path = self.ori_path
msg = _('variable "{0}" has an attribute "{1}" calculated with the unknown variable "{2}"').format(path, self.attribute_name, self.variable)
msg = _(
'variable "{0}" has an attribute "{1}" calculated with the unknown variable "{2}"'
).format(path, self.attribute_name, self.variable)
raise DictConsistencyError(msg, 88, self.xmlfiles)
return {None: [["example"]]}
param = {
@ -509,7 +560,9 @@ class _VariableCalculation(Calculation):
params["__default_value"] = self.default_values
if self.allow_none:
params["allow_none"] = True
self.check_multi(objectspace, variable_in_calculation_path, variable_in_calculation)
self.check_multi(
objectspace, variable_in_calculation_path, variable_in_calculation
)
if self.path in objectspace.followers:
multi = objectspace.multis[self.path] == "submulti"
else:
@ -518,51 +571,123 @@ class _VariableCalculation(Calculation):
params["__internal_multi"] = True
return params
def check_multi(self, objectspace, variable_in_calculation_path, variable_in_calculation):
def check_multi(
self, objectspace, variable_in_calculation_path, variable_in_calculation
):
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
local_variable = objectspace.paths[path]
local_variable_multi, variable_in_calculation_multi = calc_multi_for_type_variable(local_variable, variable_in_calculation_path, variable_in_calculation, objectspace)
local_variable_multi, variable_in_calculation_multi = (
calc_multi_for_type_variable(
local_variable,
variable_in_calculation_path,
variable_in_calculation,
objectspace,
)
)
if self.attribute_name == "default":
if variable_in_calculation_multi == 'submulti':
if variable_in_calculation_multi == "submulti":
if objectspace.paths.is_dynamic(variable_in_calculation.path):
msg = _('the variable "{0}" has an invalid "{1}" the variable "{2}" is in a sub dynamic option').format(local_variable.path, self.attribute_name, variable_in_calculation.path)
msg = _(
'the variable "{0}" has an invalid "{1}" the variable "{2}" is in a sub dynamic option'
).format(
local_variable.path,
self.attribute_name,
variable_in_calculation.path,
)
raise DictConsistencyError(msg, 69, self.xmlfiles)
else:
msg = _('the leader "{0}" has an invalid "{1}" the follower "{2}" is a multi').format(local_variable.path, self.attribute_name, variable_in_calculation.path)
msg = _(
'the leader "{0}" has an invalid "{1}" the follower "{2}" is a multi'
).format(
local_variable.path,
self.attribute_name,
variable_in_calculation.path,
)
raise DictConsistencyError(msg, 74, self.xmlfiles)
if not self.inside_list:
if local_variable_multi != variable_in_calculation_multi:
if local_variable_multi:
self.check_variable_in_calculation_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
self.check_variable_in_calculation_not_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
self.check_variable_in_calculation_multi(
local_variable.path,
variable_in_calculation_path,
variable_in_calculation_multi,
)
self.check_variable_in_calculation_not_multi(
local_variable.path,
variable_in_calculation_path,
variable_in_calculation_multi,
)
else:
self.check_variable_in_calculation_in_list_not_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
self.check_variable_in_calculation_in_list_not_multi(
local_variable.path,
variable_in_calculation_path,
variable_in_calculation_multi,
)
elif self.attribute_name in ["choices", "dynamic"]:
# calculated variable must be a multi
if not self.inside_list:
self.check_variable_in_calculation_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
self.check_variable_in_calculation_multi(
local_variable.path,
variable_in_calculation_path,
variable_in_calculation_multi,
)
else:
self.check_variable_in_calculation_in_list_not_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
self.check_variable_in_calculation_in_list_not_multi(
local_variable.path,
variable_in_calculation_path,
variable_in_calculation_multi,
)
elif variable_in_calculation_multi is True:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable "{2}" must not be multi').format(local_variable.path, self.attribute_name, variable_in_calculation_path)
msg = _(
'the variable "{0}" has an invalid attribute "{1}", the variable "{2}" must not be multi'
).format(
local_variable.path, self.attribute_name, variable_in_calculation_path
)
raise DictConsistencyError(msg, 23, self.xmlfiles)
def check_variable_in_calculation_multi(self, local_variable_path, variable_in_calculation_path, variable_in_calculation_multi):
def check_variable_in_calculation_multi(
self,
local_variable_path,
variable_in_calculation_path,
variable_in_calculation_multi,
):
if variable_in_calculation_multi is False:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable must not be a multi or the variable "{2}" must be multi').format(local_variable_path, self.attribute_name, variable_in_calculation_path)
msg = _(
'the variable "{0}" has an invalid attribute "{1}", the variable must not be a multi or the variable "{2}" must be multi'
).format(
local_variable_path, self.attribute_name, variable_in_calculation_path
)
raise DictConsistencyError(msg, 20, self.xmlfiles)
def check_variable_in_calculation_not_multi(self, local_variable_path, variable_in_calculation_path, variable_in_calculation_multi):
def check_variable_in_calculation_not_multi(
self,
local_variable_path,
variable_in_calculation_path,
variable_in_calculation_multi,
):
if variable_in_calculation_multi is True:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable must be a multi or the variable "{2}" must not be multi').format(local_variable_path, self.attribute_name, variable_in_calculation_path)
msg = _(
'the variable "{0}" has an invalid attribute "{1}", the variable must be a multi or the variable "{2}" must not be multi'
).format(
local_variable_path, self.attribute_name, variable_in_calculation_path
)
raise DictConsistencyError(msg, 21, self.xmlfiles)
def check_variable_in_calculation_in_list_not_multi(self, local_variable_path, variable_in_calculation_path, variable_in_calculation_multi):
def check_variable_in_calculation_in_list_not_multi(
self,
local_variable_path,
variable_in_calculation_path,
variable_in_calculation_multi,
):
if variable_in_calculation_multi is True:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable "{2}" is multi but is inside a list').format(local_variable_path, self.attribute_name, variable_in_calculation_path)
msg = _(
'the variable "{0}" has an invalid attribute "{1}", the variable "{2}" is multi but is inside a list'
).format(
local_variable_path, self.attribute_name, variable_in_calculation_path
)
raise DictConsistencyError(msg, 18, self.xmlfiles)
@ -576,9 +701,15 @@ class VariableCalculation(_VariableCalculation):
objectspace,
) -> dict:
if self.attribute_name != "default" and self.optional:
msg = _('"{0}" attribut shall not have an "optional" attribute for variable "{1}"').format(self.attribute_name, self.variable)
msg = _(
'"{0}" attribut shall not have an "optional" attribute for variable "{1}"'
).format(self.attribute_name, self.variable)
raise DictConsistencyError(msg, 33, self.xmlfiles)
variable_in_calculation_path, variable_in_calculation, variable_in_calculation_identifier = self.get_variable(objectspace)
(
variable_in_calculation_path,
variable_in_calculation,
variable_in_calculation_identifier,
) = self.get_variable(objectspace)
if (
not variable_in_calculation
and self.optional
@ -587,8 +718,12 @@ class VariableCalculation(_VariableCalculation):
raise VariableCalculationDependencyError()
if variable_in_calculation and self.attribute_name == "default":
local_variable = objectspace.paths[self.path]
if CONVERT_OPTION.get(local_variable.type, {}).get("func", str) != CONVERT_OPTION.get(variable_in_calculation.type, {}).get("func", str):
msg = _('variable "{0}" has a default value calculated with "{1}" which has incompatible type').format(self.path, self.variable)
if CONVERT_OPTION.get(local_variable.type, {}).get(
"func", str
) != CONVERT_OPTION.get(variable_in_calculation.type, {}).get("func", str):
msg = _(
'variable "{0}" has a default value calculated with "{1}" which has incompatible type'
).format(self.path, self.variable)
raise DictConsistencyError(msg, 67, self.xmlfiles)
params = self.get_params(
objectspace,
@ -613,29 +748,41 @@ class VariablePropertyCalculation(_VariableCalculation):
self,
objectspace,
) -> dict:
variable_in_calculation_path, variable_in_calculation, variable_in_calculation_identifier = self.get_variable(objectspace)
(
variable_in_calculation_path,
variable_in_calculation,
variable_in_calculation_identifier,
) = self.get_variable(objectspace)
params = self.get_params(
objectspace,
variable_in_calculation_path,
variable_in_calculation,
variable_in_calculation_identifier,
)
if objectspace.force_optional and (not params[None] or "variable" not in params[None][0]):
if objectspace.force_optional and (
not params[None] or "variable" not in params[None][0]
):
params = {None: [None, None, False]}
else:
variable = params[None][0]["variable"]
if self.when is not undefined:
if self.version == "1.0":
msg = _('"when" is not allowed in format version 1.0 for attribute "{0}" for variable "{1}"').format(self.attribute_name, self.path)
msg = _(
'"when" is not allowed in format version 1.0 for attribute "{0}" for variable "{1}"'
).format(self.attribute_name, self.path)
raise DictConsistencyError(msg, 103, variable.xmlfiles)
if self.when_not is not undefined:
msg = _('the variable "{0}" has an invalid attribute "{1}", "when" and "when_not" cannot set together').format(self.path, self.attribute_name)
msg = _(
'the variable "{0}" has an invalid attribute "{1}", "when" and "when_not" cannot set together'
).format(self.path, self.attribute_name)
raise DictConsistencyError(msg, 31, variable.xmlfiles)
when = self.when
inverse = False
elif self.when_not is not undefined:
if self.version == "1.0":
msg = _('"when_not" is not allowed in format version 1.0 for attribute "{0}" for variable "{1}"').format(self.attribute_name, self.path)
msg = _(
'"when_not" is not allowed in format version 1.0 for attribute "{0}" for variable "{1}"'
).format(self.attribute_name, self.path)
raise DictConsistencyError(msg, 104, variable.xmlfiles)
when = self.when_not
inverse = True
@ -650,7 +797,7 @@ class VariablePropertyCalculation(_VariableCalculation):
params["when"] = when
params["inverse"] = inverse
params[None].insert(0, self.attribute_name)
func = 'variable_to_property'
func = "variable_to_property"
return {
"function": func,
"params": params,
@ -690,10 +837,14 @@ class InformationCalculation(Calculation):
)
if variable is None:
if not objectspace.force_optional:
msg = _('cannot find variable "{0}" for the information "{1}" when calculating "{2}"').format(self.variable, self.information, self.attribute_name)
msg = _(
'cannot find variable "{0}" for the information "{1}" when calculating "{2}"'
).format(self.variable, self.information, self.attribute_name)
raise DictConsistencyError(msg, 40, variable.xmlfiles)
if identifier is not None:
msg = _('identifier not allowed for the information "{0}" when calculating "{1}"').format(self.information, self.attribute_name)
msg = _(
'identifier not allowed for the information "{0}" when calculating "{1}"'
).format(self.information, self.attribute_name)
raise DictConsistencyError(msg, 41, variable.xmlfiles)
if variable:
params[None][0]["variable"] = variable
@ -744,11 +895,15 @@ class IdentifierPropertyCalculation(_IdentifierCalculation):
objectspace,
) -> dict:
if self.version == "1.0":
msg = _('"when" is not allowed in format version 1.0 for attribute "{0}"').format(self.attribute_name)
msg = _(
'"when" is not allowed in format version 1.0 for attribute "{0}"'
).format(self.attribute_name)
raise DictConsistencyError(msg, 105, variable.xmlfiles)
if self.when is not undefined:
if self.when_not is not undefined:
msg = _('the identifier has an invalid attribute "{0}", "when" and "when_not" cannot set together').format(self.attribute_name)
msg = _(
'the identifier has an invalid attribute "{0}", "when" and "when_not" cannot set together'
).format(self.attribute_name)
raise DictConsistencyError(msg, 35, variable.xmlfiles)
when = self.when
inverse = False
@ -756,7 +911,9 @@ class IdentifierPropertyCalculation(_IdentifierCalculation):
when = self.when_not
inverse = True
else:
msg = _('the identifier has an invalid attribute "{0}", "when" and "when_not" cannot set together').format(self.attribute_name)
msg = _(
'the identifier has an invalid attribute "{0}", "when" and "when_not" cannot set together'
).format(self.attribute_name)
raise DictConsistencyError
params = {
None: [self.attribute_name, self.get_identifier()],
@ -779,7 +936,9 @@ class IndexCalculation(Calculation):
objectspace,
) -> dict:
if self.path not in objectspace.followers:
msg = _('the variable "{0}" is not a follower, so cannot have index type for "{1}"').format(self.path, self.attribute_name)
msg = _(
'the variable "{0}" is not a follower, so cannot have index type for "{1}"'
).format(self.path, self.attribute_name)
raise DictConsistencyError(msg, 60, self.xmlfiles)
return {
"function": "calc_value",
@ -809,7 +968,7 @@ CALCULATION_TYPES = {
"information": InformationCalculation,
"variable": VariableCalculation,
"identifier": IdentifierCalculation,
# FOR VERSION 1.0
# FOR VERSION 1.0
"suffix": IdentifierCalculation,
"index": IndexCalculation,
"namespace": NamespaceCalculation,

View file

@ -37,17 +37,29 @@ from jinja2.sandbox import SandboxedEnvironment
from rougail.object_model import CONVERT_OPTION
from rougail.error import display_xmlfiles
from tiramisu import DynOptionDescription, calc_value, function_waiting_for_error
from tiramisu.error import ValueWarning, ConfigError, PropertiesOptionError, CancelParam, errors
from tiramisu.error import (
ValueWarning,
ConfigError,
PropertiesOptionError,
CancelParam,
errors,
)
from .utils import normalize_family
from .i18n import _
ori_raise_carry_out_calculation_error = errors.raise_carry_out_calculation_error
def raise_carry_out_calculation_error(subconfig, *args, **kwargs):
try:
ori_raise_carry_out_calculation_error(subconfig, *args, **kwargs)
except ConfigError as err:
ymlfiles = subconfig.config_bag.context.get_values().get_information(subconfig, 'ymlfiles', [])
raise ConfigError(_('{0} in {1}').format(err, display_xmlfiles(ymlfiles)))
ymlfiles = subconfig.config_bag.context.get_values().get_information(
subconfig, "ymlfiles", []
)
raise ConfigError(_("{0} in {1}").format(err, display_xmlfiles(ymlfiles)))
errors.raise_carry_out_calculation_error = raise_carry_out_calculation_error
@ -124,7 +136,7 @@ def kw_to_string(kw, root=None):
if root is None:
path = name
else:
path = root + '.' + name
path = root + "." + name
if isinstance(data, dict):
yield from kw_to_string(data, root=path)
else:
@ -172,11 +184,21 @@ def jinja_to_function(
except Exception as err:
kw_str = ", ".join(kw_to_string(kw))
raise ConfigError(
_('cannot calculating "{0}" attribute for variable "{1}" in {2} with parameters "{3}": {4}').format(__internal_attribute, __internal_variable, display_xmlfiles(__internal_files), kw_str, err)
_(
'cannot calculating "{0}" attribute for variable "{1}" in {2} with parameters "{3}": {4}'
).format(
__internal_attribute,
__internal_variable,
display_xmlfiles(__internal_files),
kw_str,
err,
)
) from err
convert = CONVERT_OPTION[__internal_type].get("func", str)
if __internal_multi:
values = [convert(val.strip()) for val in values.split("\n") if val.strip() != ""]
values = [
convert(val.strip()) for val in values.split("\n") if val.strip() != ""
]
if not values and __default_value is not None:
return __default_value
return values

View file

@ -290,9 +290,10 @@ class Common:
):
"""Populate variable parameters"""
if not isinstance(param, dict):
param = {"type": "any",
"value": param,
}
param = {
"type": "any",
"value": param,
}
if param["type"] == "value":
return f"ParamValue({param['value']})"
if param["type"] == "information":
@ -331,9 +332,7 @@ class Common:
return f'ParamInformation("{param["information"]}", {default}, option={option_name})'
else:
# if we want to get information from the a parent family
information = (
f'ParamInformation("{param["information"]}", {default})'
)
information = f'ParamInformation("{param["information"]}", {default})'
information_name = self.tiramisu.get_information_name()
self.tiramisu.text["option"].append(
f"{information_name} = {information}"

View file

@ -26,6 +26,7 @@ from rougail.utils import normalize_family, undefined
from tiramisu import Calculation
from tiramisu.error import (
PropertiesOptionError,
AttributeOptionError,
LeadershipError,
ConfigError,
CancelParam,
@ -44,6 +45,7 @@ class UserDatas:
self.values = {}
self.errors = []
self.warnings = []
self.show_secrets = False
self._populate_values(user_datas)
self._auto_configure_dynamics()
self._populate_config()
@ -113,7 +115,9 @@ class UserDatas:
continue
identifier = self._get_identifier(tconfig.name(), name)
if identifier != normalize_family(identifier):
msg = _('cannot load variable path "{0}", the identifier "{1}" is not valid in {2}').format(path, identifier, data["source"])
msg = _(
'cannot load variable path "{0}", the identifier "{1}" is not valid in {2}'
).format(path, identifier, data["source"])
self.warnings.append(msg)
continue
if identifier is None:
@ -155,6 +159,27 @@ class UserDatas:
cache[current_path] = config, identifier
break
def convert_value(self, path, option, options, value):
# converted value
needs_convert = options.get("needs_convert", False)
if option.ismulti():
if options.get("multi_separator") and not isinstance(value, list):
value = value.split(options["multi_separator"])
self.values[path]["values"] = value
if option.issubmulti():
value = [[val] for val in value]
if needs_convert:
if option.issubmulti():
for idx, val in enumerate(value):
value[idx] = [convert_value(option, v) for v in val]
else:
value = [convert_value(option, val) for val in value]
self.values[path]["values"] = value
self.values[path]["options"]["needs_convert"] = False
elif needs_convert:
value = convert_value(option, value)
return value
def _populate_config(self):
while self.values:
value_is_set = False
@ -173,33 +198,25 @@ class UserDatas:
).format(path, self.values[path]["source"])
)
continue
value = self.values[path]["values"]
needs_convert = options.get("needs_convert", False)
value = self.convert_value(
path, option, options, self.values[path]["values"]
)
# converted value
if option.ismulti():
if options.get("multi_separator") and not isinstance(value, list):
value = value.split(options["multi_separator"])
self.values[path]["values"] = value
if option.issubmulti():
value = [[val] for val in value]
if needs_convert:
if option.issubmulti():
for idx, val in enumerate(value):
value[idx] = [convert_value(option, v) for v in val]
else:
value = [convert_value(option, val) for val in value]
self.values[path]["values"] = value
self.values[path]["options"]["needs_convert"] = False
elif needs_convert:
value = convert_value(option, value)
index = option.index()
if index is not None:
if not isinstance(value, list) or index >= len(value):
continue
value = value[index]
if option.isleader():
len_leader = len(option.value.get())
if len_leader:
for idx in range(len_leader - 1, -1, -1):
option.value.pop(idx)
try:
option.value.set(value)
option.information.set(
"loaded_from", _("loaded from {0}").format(options["source"])
)
value_is_set = True
# value is correctly set, remove variable to the set
if index is not None:
@ -224,33 +241,144 @@ class UserDatas:
return None
return finded[0]
def _display_value(self, option, value):
if not self.show_secrets and option.type() == "password":
return "*" * 10
return value
def _populate_error_warnings(self):
# we don't find variable, apply value just to get error or warning messages
for path, data in self.values.items():
for path, options in self.values.items():
value = options["values"]
option = self.config.option(path)
try:
option = self.config.option(path)
value = data["values"]
if option.isoptiondescription():
if value:
self.warnings.append(
_('the variable "{0}" is a family, so cannot set the value "{1}" in {2}').format(
option.path(),
value,
data["source"],
_(
'cannot set the value "{0}" to the family {1}, it will be ignored when loading from {2}'
).format(
self._display_value(option, value),
option.description(with_quote=True),
options["source"],
)
)
continue
if option.isfollower():
for index, val in enumerate(value):
except AttributeOptionError as err:
if err.code == "option-not-found":
self.warnings.append(
_(
'variable or family "{0}" does not exist, it will be ignored when loading from {1}'
).format(err.path, options["source"])
)
elif err.code == "option-dynamic":
self.warnings.append(
_(
'"{0}" is the name of a dynamic family, it will be ignored when loading from {1}'
).format(option.description(with_quote=True), options["source"])
)
else:
self.warnings.append(
_("{0} loaded from {1}").format(err, options["source"])
)
continue
value = self.convert_value(
path, option, self.values[path].get("options", {}), value
)
if option.isfollower():
indexes = range(len(value))
else:
indexes = [None]
for index in indexes:
try:
if option.isfollower():
val = value[index]
if val is undefined or isinstance(val, CancelParam):
continue
self.config.option(path, index).value.set(val)
else:
option.value.set(value)
except AttributeError as err:
self.warnings.append(_('{0} loaded from {1}').format(err, data["source"]))
except (ValueError, LeadershipError, PropertiesOptionError) as err:
self.warnings.append(_('{0} in {1}').format(err, data["source"]))
else:
option.value.set(value)
except PropertiesOptionError as err:
if err.code == "property-error":
properties = err.display_properties(
force_property=True, add_quote=False
)
err_path = err._subconfig.path
display_name = option.description(with_quote=True)
if index is not None:
if path == err_path:
self.warnings.append(
_(
'variable {0} at index "{1}" is {2}, it will be ignored when loading from {3}'
).format(
display_name,
index,
properties,
options["source"],
)
)
else:
self.warnings.append(
_(
'family {0} is {1}, {2} at index "{3}" will be ignored when loading from {4}'
).format(
err._name,
properties,
display_name,
index,
options["source"],
)
)
else:
if path == err_path:
self.warnings.append(
_(
"variable {0} is {1}, it will be ignored when loading from {2}"
).format(
display_name, properties, options["source"]
)
)
else:
self.warnings.append(
_(
"family {0} is {1}, {2} will be ignored when loading from {3}"
).format(
err._name,
properties,
display_name,
options["source"],
)
)
else:
self.warnings.append(
_("{0} in {1}").format(err, options["source"])
)
except LeadershipError as err:
self.warnings.append(_("{0} in {1}").format(err, options["source"]))
except ValueError as err:
err.prefix = ""
if index is not None:
self.warnings.append(
_(
'the value "{0}" is invalid for {1} at index "{2}", {3}, it will be ignored when loading from {4}'
).format(
self._display_value(option, value),
option.description(with_quote=True),
err,
options["source"],
)
)
else:
self.warnings.append(
_(
'the value "{0}" is invalid for {1}, {2}, it will be ignored when loading from {3}'
).format(
self._display_value(option, value),
option.description(with_quote=True),
err,
options["source"],
)
)
def convert_value(option, value):
@ -265,5 +393,8 @@ def convert_value(option, value):
value = int(value)
func = CONVERT_OPTION.get(option_type, {}).get("func")
if func:
return func(value)
try:
return func(value)
except:
pass
return value

View file

@ -162,7 +162,13 @@ def get_jinja_variable_to_param(
for variable_path, data in founded_variables.items():
yield data[1], data[0], variable_path
def calc_multi_for_type_variable(local_variable: 'Variable', variable_in_calculation_path: str, variable_in_calculation: 'Variable', objectspace: 'RougailConvert') -> Union[bool, str]:
def calc_multi_for_type_variable(
local_variable: "Variable",
variable_in_calculation_path: str,
variable_in_calculation: "Variable",
objectspace: "RougailConvert",
) -> Union[bool, str]:
variable_in_calculation_multi = variable_in_calculation.multi
if local_variable.path in objectspace.families:
# it's a family
@ -171,33 +177,39 @@ def calc_multi_for_type_variable(local_variable: 'Variable', variable_in_calcula
local_variable_multi = local_variable.multi
# variable is a leader
if variable_in_calculation.path in objectspace.leaders:
local_variable_parent = local_variable.path.rsplit('.', 1)[0]
variable_in_calculation_parent = variable_in_calculation.path.rsplit('.', 1)[0]
local_variable_parent = local_variable.path.rsplit(".", 1)[0]
variable_in_calculation_parent = variable_in_calculation.path.rsplit(
".", 1
)[0]
if local_variable_parent == variable_in_calculation_parent:
variable_in_calculation_multi = False
# variable is a follower
elif variable_in_calculation.path in objectspace.followers:
local_variable_parent = local_variable.path.rsplit('.', 1)[0]
variable_in_calculation_parent = variable_in_calculation.path.rsplit('.', 1)[0]
local_variable_parent = local_variable.path.rsplit(".", 1)[0]
variable_in_calculation_parent = variable_in_calculation.path.rsplit(
".", 1
)[0]
if local_variable_parent != variable_in_calculation_parent:
if variable_in_calculation_multi:
variable_in_calculation_multi = 'submulti'
variable_in_calculation_multi = "submulti"
else:
variable_in_calculation_multi = True
# variable is in a dynamic family
if objectspace.paths.is_dynamic(variable_in_calculation.path):
common_path = get_common_path(local_variable.path, variable_in_calculation_path)
common_path = get_common_path(
local_variable.path, variable_in_calculation_path
)
common_variable_path = variable_in_calculation_path
if common_path:
common_variable_path = common_variable_path[len(common_path) + 1:]
count_identifiers = common_variable_path.count('{{ identifier }}')
common_variable_path = common_variable_path[len(common_path) + 1 :]
count_identifiers = common_variable_path.count("{{ identifier }}")
if count_identifiers == 1:
if variable_in_calculation_multi is False:
variable_in_calculation_multi = True
else:
variable_in_calculation_multi = 'submulti'
variable_in_calculation_multi = "submulti"
elif count_identifiers > 1:
variable_in_calculation_multi = 'submulti'
variable_in_calculation_multi = "submulti"
return local_variable_multi, variable_in_calculation_multi

View file

@ -0,0 +1,22 @@
{
"rougail.leadership.leader": {
"owner": "default",
"value": [
"value_1",
"value_2",
"value_3"
]
},
"rougail.leadership.follower": {
"owner": [
"default",
"default",
"default"
],
"value": [
null,
null,
null
]
}
}

View file

@ -0,0 +1,16 @@
{
"rougail.leadership.leader": [
{
"rougail.leadership.leader": "value_1",
"rougail.leadership.follower": null
},
{
"rougail.leadership.leader": "value_2",
"rougail.leadership.follower": null
},
{
"rougail.leadership.leader": "value_3",
"rougail.leadership.follower": null
}
]
}

View file

@ -0,0 +1,22 @@
{
"rougail.leadership.leader": {
"owner": "default",
"value": [
"value_1",
"value_2",
"value_3"
]
},
"rougail.leadership.follower": {
"owner": [
"default",
"default",
"default"
],
"value": [
null,
null,
null
]
}
}

View file

@ -0,0 +1 @@
["rougail.leadership.follower", "rougail.leadership.follower", "rougail.leadership.follower"]

View file

@ -0,0 +1,16 @@
{
"rougail.leadership.leader": [
{
"rougail.leadership.leader": "value_1",
"rougail.leadership.follower": null
},
{
"rougail.leadership.leader": "value_2",
"rougail.leadership.follower": null
},
{
"rougail.leadership.leader": "value_3",
"rougail.leadership.follower": null
}
]
}

View file

@ -0,0 +1,17 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="leader", doc="a leader", multi=True, default=["value_1", "value_2", "value_3"], properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/40_0leadership_reduce/rougail/00-base.yml'], 'type': 'string', 'test': ('val1', 'val2')})
option_4 = StrOption(name="follower", doc="a follower", multi=True, properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/40_0leadership_reduce/rougail/00-base.yml'], 'type': 'string'})
optiondescription_2 = Leadership(name="leadership", doc="a leadership", children=[option_3, option_4], properties=frozenset({"basic"}), informations={'ymlfiles': ['../rougail-tests/structures/40_0leadership_reduce/rougail/00-base.yml']})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[optiondescription_2], properties=frozenset({"basic"}), informations={'ymlfiles': ['']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,12 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_2 = StrOption(name="leader", doc="a leader", multi=True, default=["value_1", "value_2", "value_3"], properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/40_0leadership_reduce/rougail/00-base.yml'], 'type': 'string', 'test': ('val1', 'val2')})
option_3 = StrOption(name="follower", doc="a follower", multi=True, properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/40_0leadership_reduce/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = Leadership(name="leadership", doc="a leadership", children=[option_2, option_3], properties=frozenset({"basic"}), informations={'ymlfiles': ['../rougail-tests/structures/40_0leadership_reduce/rougail/00-base.yml']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -12,7 +12,7 @@
"default"
],
"value": [
"cannot access to option \"a follower\" because has property \"disabled\" (the value of \"leader\" is \"a\")",
"cannot access to option \"a follower\" at index \"0\" because has property \"disabled\" (the value of \"leader\" is \"a\")",
"b"
]
}

View file

@ -12,7 +12,7 @@
"default"
],
"value": [
"cannot access to option \"a follower\" because has property \"disabled\" (the value of \"leader\" is \"a\")",
"cannot access to option \"a follower\" at index \"0\" because has property \"disabled\" (the value of \"leader\" is \"a\")",
"b"
]
}

View file

@ -31,7 +31,7 @@ excludes = set([])
# excludes = set(['60_5family_dynamic_variable_outside_sub_suffix'])
test_ok -= excludes
# test_ok = ['04_5validators_multi3']
test_ok = ['44_9calculated_default_leadership_leader']
test_ok = list(test_ok)