fix: black

This commit is contained in:
egarette@silique.fr 2026-06-21 16:52:26 +02:00
parent 29453523b2
commit e77355cbc5
17 changed files with 658 additions and 370 deletions

View file

@ -79,7 +79,7 @@ class Annotator(Walk):
def check_sequence(self) -> None: def check_sequence(self) -> None:
"""No subfamily in a sequence""" """No subfamily in a sequence"""
for family in self.get_families(): for family in self.get_families():
if family.type == 'leadership': if family.type == "leadership":
family.type = "sequence" family.type = "sequence"
if family.type != "sequence": if family.type != "sequence":
continue continue
@ -113,7 +113,9 @@ class Annotator(Walk):
) )
self.objectspace.informations.add(family.path, "dynamic_variable", path) self.objectspace.informations.add(family.path, "dynamic_variable", path)
if family.xmlfiles: if family.xmlfiles:
self.objectspace.informations.add(family.path, "ymlfiles", family.xmlfiles) self.objectspace.informations.add(
family.path, "ymlfiles", family.xmlfiles
)
def set_modes(self): def set_modes(self):
if self.objectspace.modes_level: if self.objectspace.modes_level:
@ -130,8 +132,12 @@ class Annotator(Walk):
modes = self.modes = { modes = self.modes = {
name: Mode(idx) for idx, name in enumerate(self.objectspace.modes_level) name: Mode(idx) for idx, name in enumerate(self.objectspace.modes_level)
} }
default_variable_mode = self.default_variable_mode = self.objectspace.default_variable_mode default_variable_mode = self.default_variable_mode = (
default_family_mode = self.default_family_mode = self.objectspace.default_family_mode self.objectspace.default_variable_mode
)
default_family_mode = self.default_family_mode = (
self.objectspace.default_family_mode
)
list_modes = list(modes) list_modes = list(modes)
if default_variable_mode not in list_modes: if default_variable_mode not in list_modes:
msg = _( msg = _(
@ -177,7 +183,9 @@ class Annotator(Walk):
if self.has_mode(obj): if self.has_mode(obj):
msg = _( msg = _(
'mode "{0}" for "{1}" is not a valid mode, valid modes are {2}' 'mode "{0}" for "{1}" is not a valid mode, valid modes are {2}'
).format(obj.mode, obj.name, display_list(list(self.modes), add_quote=True)) ).format(
obj.mode, obj.name, display_list(list(self.modes), add_quote=True)
)
raise DictConsistencyError(msg, 71, obj.xmlfiles) raise DictConsistencyError(msg, 71, obj.xmlfiles)
elif self.has_mode(obj) and obj.mode not in self.modes: elif self.has_mode(obj) and obj.mode not in self.modes:
msg = _( msg = _(

View file

@ -173,9 +173,7 @@ class Annotator(Walk):
for tag in variable.tags: for tag in variable.tags:
self.check_tag(tag, variable.xmlfiles) self.check_tag(tag, variable.xmlfiles)
self.objectspace.properties.add(variable.path, tag, True) self.objectspace.properties.add(variable.path, tag, True)
self.objectspace.informations.add( self.objectspace.informations.add(variable.path, "tags", tuple(variable.tags))
variable.path, "tags", tuple(variable.tags)
)
def check_tag( def check_tag(
self, self,

View file

@ -106,9 +106,7 @@ class Annotator(Walk): # pylint: disable=R0903
'the variable "{0}" has attribute "secret_manager" so must not have default value' 'the variable "{0}" has attribute "secret_manager" so must not have default value'
) )
raise DictConsistencyError(msg.format(path), 59, variable.xmlfiles) raise DictConsistencyError(msg.format(path), 59, variable.xmlfiles)
self.objectspace.informations.add( self.objectspace.informations.add(path, "secret_manager", True)
path, "secret_manager", True
)
def convert_variable(self): def convert_variable(self):
"""convert variable""" """convert variable"""
@ -214,9 +212,9 @@ class Annotator(Walk): # pylint: disable=R0903
if variable.type is not None: if variable.type is not None:
return return
## choice type inference from the `choices` attribute ## choice type inference from the `choices` attribute
#if variable.choices is not None: # if variable.choices is not None:
# variable.type = "choice" # variable.type = "choice"
#elif variable.regexp is not None: # elif variable.regexp is not None:
# variable.type = "regexp" # variable.type = "regexp"
if variable.default not in [None, []]: if variable.default not in [None, []]:
if isinstance(variable.default, list): if isinstance(variable.default, list):
@ -253,7 +251,9 @@ class Annotator(Walk): # pylint: disable=R0903
self._convert_variable_multi(calculated_variable) self._convert_variable_multi(calculated_variable)
identifier_is_a_calculation = False identifier_is_a_calculation = False
if isinstance(variable.default, Calculation): if isinstance(variable.default, Calculation):
identifier_is_a_calculation = isinstance(variable.default.identifier, Calculation) identifier_is_a_calculation = isinstance(
variable.default.identifier, Calculation
)
variable.multi = calc_multi_for_type_variable( variable.multi = calc_multi_for_type_variable(
variable, variable,
calculated_variable_path, calculated_variable_path,
@ -317,11 +317,11 @@ class Annotator(Walk): # pylint: disable=R0903
self.objectspace.multis[variable.path] = "submulti" self.objectspace.multis[variable.path] = "submulti"
elif variable.multi: elif variable.multi:
self.objectspace.multis[variable.path] = True self.objectspace.multis[variable.path] = True
# if variable.regexp is not None and variable.type != "regexp": # if variable.regexp is not None and variable.type != "regexp":
# msg = _( # msg = _(
# 'the variable "{0}" has regexp attribut but has not the "regexp" type' # 'the variable "{0}" has regexp attribut but has not the "regexp" type'
# ).format(variable.path) # ).format(variable.path)
# raise DictConsistencyError(msg, 37, variable.xmlfiles) # raise DictConsistencyError(msg, 37, variable.xmlfiles)
if variable.mandatory is None: if variable.mandatory is None:
variable.mandatory = True variable.mandatory = True
@ -358,9 +358,9 @@ class Annotator(Walk): # pylint: disable=R0903
def verify_choices(self): def verify_choices(self):
for variable in self.get_variables(): for variable in self.get_variables():
# FIXME # FIXME
# if variable.type is None and variable.choices: # if variable.type is None and variable.choices:
# # choice type inference from the `choices` attribute # # choice type inference from the `choices` attribute
# variable.type = "choice" # variable.type = "choice"
if variable.type != "choice": if variable.type != "choice":
continue continue
if variable.default is None: if variable.default is None:

View file

@ -82,9 +82,7 @@ class _RougailConfig:
self.generate_config() self.generate_config()
config = self.config.config.copy() config = self.config.config.copy()
config.value.importation(self.config.value.exportation()) config.value.importation(self.config.value.exportation())
config.property.importation( config.property.importation(self.config.property.exportation())
self.config.property.exportation()
)
config.property.read_only() config.property.read_only()
if backward_compatibility is None: if backward_compatibility is None:
backward_compatibility = self.backward_compatibility backward_compatibility = self.backward_compatibility
@ -250,7 +248,7 @@ class StaticRougailConvert(RougailConvert):
def __init__( def __init__(
self, self,
add_extra_options: bool, add_extra_options: bool,
rougailconfig: dict={}, rougailconfig: dict = {},
) -> None: ) -> None:
self.add_extra_options = add_extra_options self.add_extra_options = add_extra_options
super().__init__(rougailconfig) super().__init__(rougailconfig)
@ -434,7 +432,8 @@ secret_manager: # {_("The secret manager")}
"user data": [], "user data": [],
"output": [], "output": [],
} }
processes_tr = {"structural": _("structural"), processes_tr = {
"structural": _("structural"),
"user data": _("user data"), "user data": _("user data"),
"output": _("output"), "output": _("output"),
} }
@ -495,7 +494,9 @@ secret_manager: # {_("The secret manager")}
"NAME", hidden_output "NAME", hidden_output
) )
rougail_process += f""" description: {_('outputs {0} did not allow user data')} rougail_process += f""" description: {_('outputs {0} did not allow user data')}
""".format(display_list(hidden_outputs, add_quote=True, separator="or")) """.format(
display_list(hidden_outputs, add_quote=True, separator="or")
)
elif objects: elif objects:
rougail_process += " default: {DEFAULT}".format( rougail_process += " default: {DEFAULT}".format(
DEFAULT=objects[0]["name"] DEFAULT=objects[0]["name"]
@ -557,7 +558,9 @@ def _rougail_config(
backward_compatibility: bool = True, backward_compatibility: bool = True,
add_extra_options: bool = True, add_extra_options: bool = True,
) -> "OptionDescription": ) -> "OptionDescription":
processes, processes_empty, rougail_options = get_common_rougail_config(backward_compatibility=backward_compatibility) processes, processes_empty, rougail_options = get_common_rougail_config(
backward_compatibility=backward_compatibility
)
convert = StaticRougailConvert(add_extra_options) convert = StaticRougailConvert(add_extra_options)
convert.init() convert.init()
convert.namespace = None convert.namespace = None

View file

@ -32,13 +32,16 @@ class Rougail(UserData):
def __init__( def __init__(
self, self,
rougailconfig: Optional[RougailConfig]=None, rougailconfig: Optional[RougailConfig] = None,
load_from_tiramisu_cache: bool=False, load_from_tiramisu_cache: bool = False,
) -> None: ) -> None:
if rougailconfig is None: if rougailconfig is None:
rougailconfig = RougailConfig rougailconfig = RougailConfig
self.rougailconfig = rougailconfig self.rougailconfig = rougailconfig
self.load_from_tiramisu_cache = load_from_tiramisu_cache and Path(self.rougailconfig["tiramisu_cache"]).is_file() self.load_from_tiramisu_cache = (
load_from_tiramisu_cache
and Path(self.rougailconfig["tiramisu_cache"]).is_file()
)
types = rougail_type(self.rougailconfig) types = rougail_type(self.rougailconfig)
if not self.load_from_tiramisu_cache: if not self.load_from_tiramisu_cache:
self.converted = RougailConvert(self.rougailconfig, **types) self.converted = RougailConvert(self.rougailconfig, **types)

View file

@ -17,6 +17,7 @@ details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
import logging import logging
from warnings import warn from warnings import warn
from pydantic import ValidationError from pydantic import ValidationError
@ -104,10 +105,16 @@ class CollectFamily:
new_parameters = {} new_parameters = {}
children = self.parameters children = self.parameters
# when an attribute starts with "_", the variable name without this "_" is a variable # when an attribute starts with "_", the variable name without this "_" is a variable
sub_variables = [key[1:] for key in self.parameters if key.startswith('_') and key[1:] in self.parameters] sub_variables = [
key[1:]
for key in self.parameters
if key.startswith("_") and key[1:] in self.parameters
]
# and known variables # and known variables
if self.test_exists and self.path in self.objectspace.paths: if self.test_exists and self.path in self.objectspace.paths:
sub_variables.extend([p.rsplit('.', 1)[-1] for p in self.objectspace.parents[self.path]]) sub_variables.extend(
[p.rsplit(".", 1)[-1] for p in self.objectspace.parents[self.path]]
)
attributes = self.object["attrs"] attributes = self.object["attrs"]
attributes_types = self.object["attributes_types"] attributes_types = self.object["attributes_types"]
if self.types: if self.types:
@ -127,10 +134,12 @@ class CollectFamily:
children.pop(key) children.pop(key)
else: else:
new_parameters[key[1:]] = children.pop(key) new_parameters[key[1:]] = children.pop(key)
elif key in types_children or \ elif (
key not in attributes or \ key in types_children
key in sub_variables or \ or key not in attributes
not self.parameter_is_an_attributes(key, value, attributes_types): or key in sub_variables
or not self.parameter_is_an_attributes(key, value, attributes_types)
):
if key == "type" and self.types and key not in types_children: if key == "type" and self.types and key not in types_children:
children.pop(key) children.pop(key)
else: else:
@ -227,6 +236,7 @@ class CollectVariable:
secret_manager, secret_manager,
) )
class CollectType: class CollectType:
"""Determine the option type """Determine the option type
1/ self.option_type must be "variable" or "family" option type 1/ self.option_type must be "variable" or "family" option type
@ -250,7 +260,12 @@ class CollectType:
self.variable_in_sequence() self.variable_in_sequence()
self.family_or_variable() self.family_or_variable()
if self.user_type: if self.user_type:
logging.info("family_or_variable: %s is a %s (%s)", self.path, self.option_type, self.user_type) logging.info(
"family_or_variable: %s is a %s (%s)",
self.path,
self.option_type,
self.user_type,
)
else: else:
logging.info("family_or_variable: %s is a %s", self.path, self.option_type) logging.info("family_or_variable: %s is a %s", self.path, self.option_type)
@ -259,18 +274,24 @@ class CollectType:
) -> None: ) -> None:
"""Check 'type' attributes and determine the option type""" """Check 'type' attributes and determine the option type"""
for type_name in ["_type", "type"]: for type_name in ["_type", "type"]:
if type_name not in self.parameters or not isinstance(self.parameters[type_name], str): if type_name not in self.parameters or not isinstance(
self.parameters[type_name], str
):
continue continue
self.user_type = self.parameters[type_name] self.user_type = self.parameters[type_name]
if self.user_type in self.objectspace.custom_family_types: if self.user_type in self.objectspace.custom_family_types:
self.set_custom_type(self.objectspace.custom_family_types[self.user_type]) self.set_custom_type(
self.objectspace.custom_family_types[self.user_type]
)
if self.user_type is None: if self.user_type is None:
self.object = self.objectspace.family_objects[-1] self.object = self.objectspace.family_objects[-1]
else: else:
self.set_object_user_type_family() self.set_object_user_type_family()
break break
elif self.user_type in self.objectspace.custom_variable_types: elif self.user_type in self.objectspace.custom_variable_types:
self.set_custom_type(self.objectspace.custom_variable_types[self.user_type]) self.set_custom_type(
self.objectspace.custom_variable_types[self.user_type]
)
if self.user_type is None: if self.user_type is None:
self.object = self.objectspace.variable_objects[0] self.object = self.objectspace.variable_objects[0]
else: else:
@ -281,11 +302,13 @@ class CollectType:
if not self.object: if not self.object:
self.set_object_user_type_variable() self.set_object_user_type_variable()
if self.raises and not self.object: if self.raises and not self.object:
msg = _('cannot determine the type of the {0} "{1}"').format(self.user_type, self.path) msg = _('cannot determine the type of the {0} "{1}"').format(
self.user_type, self.path
)
raise DictConsistencyError(msg, 43, self.sources) raise DictConsistencyError(msg, 43, self.sources)
break break
def set_custom_type(self, custom: dict, from_parent: bool=False) -> None: def set_custom_type(self, custom: dict, from_parent: bool = False) -> None:
if self.raises and self.test_exists and self.path in self.objectspace.paths: if self.raises and self.test_exists and self.path in self.objectspace.paths:
msg = f'cannot redefine "{self.path}" object to a custom type' msg = f'cannot redefine "{self.path}" object to a custom type'
raise DictConsistencyError(msg, 64, self.sources) raise DictConsistencyError(msg, 64, self.sources)
@ -302,7 +325,11 @@ class CollectType:
self.user_type = self.types.user_type self.user_type = self.types.user_type
self.sources = self.types.sources + self.sources self.sources = self.types.sources + self.sources
self.object = self.types.object self.object = self.types.object
if not from_parent and self.option_type == "variable" and "type" in self.parameters: if (
not from_parent
and self.option_type == "variable"
and "type" in self.parameters
):
self.parameters.pop("type") self.parameters.pop("type")
def set_object_user_type_family(self): def set_object_user_type_family(self):
@ -323,7 +350,9 @@ class CollectType:
if self.user_type: if self.user_type:
return return
for type_name in ["_dynamic", "dynamic"]: for type_name in ["_dynamic", "dynamic"]:
if type_name in self.parameters and isinstance(self.parameters[type_name], (list, dict)): if type_name in self.parameters and isinstance(
self.parameters[type_name], (list, dict)
):
self.user_type = "dynamic" self.user_type = "dynamic"
self.option_type = "family" self.option_type = "family"
break break
@ -333,7 +362,9 @@ class CollectType:
return return
# it's already a variable or a family # it's already a variable or a family
old_option_type = self.option_type if self.user_type else None old_option_type = self.option_type if self.user_type else None
self.option_type = "family" if self.path in self.objectspace.families else "variable" self.option_type = (
"family" if self.path in self.objectspace.families else "variable"
)
if self.raises and old_option_type and self.option_type != old_option_type: if self.raises and old_option_type and self.option_type != old_option_type:
msg = f'the {old_option_type} "{self.path}" is redefine as a {self.option_type}, which is not allowed' msg = f'the {old_option_type} "{self.path}" is redefine as a {self.option_type}, which is not allowed'
raise DictConsistencyError(msg, 11, self.sources) raise DictConsistencyError(msg, 11, self.sources)
@ -382,7 +413,9 @@ class CollectType:
def find_variable_object(self) -> bool: def find_variable_object(self) -> bool:
attrs = set(self.parameters) attrs = set(self.parameters)
for variable in self.objectspace.variable_objects: for variable in self.objectspace.variable_objects:
if not attrs - variable["attrs"] and self.check_variable_parameters(variable): if not attrs - variable["attrs"] and self.check_variable_parameters(
variable
):
self.option_type = "variable" self.option_type = "variable"
self.check_no_extra_keys = True self.check_no_extra_keys = True
self.object = variable self.object = variable
@ -399,22 +432,39 @@ class CollectType:
return False return False
return True return True
def parameter_is_an_attributes(self, key: str, value: Any, attributes_types: dict) -> bool: def parameter_is_an_attributes(
self, key: str, value: Any, attributes_types: dict
) -> bool:
if value is None: if value is None:
return True return True
if key in attributes_types["literals"] and value in attributes_types["literals"][key]: if (
key in attributes_types["literals"]
and value in attributes_types["literals"][key]
):
return True return True
for k, typ in [("strings", str), ("booleans", bool), ("integers", int), ("floats", float)]: for k, typ in [
("strings", str),
("booleans", bool),
("integers", int),
("floats", float),
]:
if key in attributes_types[k] and isinstance(value, typ): if key in attributes_types[k] and isinstance(value, typ):
return True return True
if isinstance(value, dict): if isinstance(value, dict):
if key in attributes_types["calculation"]: if key in attributes_types["calculation"]:
return self.is_calculation(key, value, attributes_types=attributes_types) return self.is_calculation(
key, value, attributes_types=attributes_types
)
if key in attributes_types["params"]: if key in attributes_types["params"]:
return True return True
if isinstance(value, list): if isinstance(value, list):
current_value = value.copy() current_value = value.copy()
for k, typ in [("strings", str), ("booleans", bool), ("integers", int), ("floats", float)]: for k, typ in [
("strings", str),
("booleans", bool),
("integers", int),
("floats", float),
]:
if key in attributes_types["lists"][k]: if key in attributes_types["lists"][k]:
for idx, val in reversed(list(enumerate(current_value))): for idx, val in reversed(list(enumerate(current_value))):
if val is not None and not isinstance(val, typ): if val is not None and not isinstance(val, typ):
@ -424,7 +474,9 @@ class CollectType:
return True return True
if key in attributes_types["lists"]["calculation"]: if key in attributes_types["lists"]["calculation"]:
for val in current_value: for val in current_value:
if isinstance(val, dict) and not self.is_calculation(key, val, inside_list=True, attributes_types=attributes_types): if isinstance(val, dict) and not self.is_calculation(
key, val, inside_list=True, attributes_types=attributes_types
):
return False return False
return True return True
return False return False
@ -440,8 +492,8 @@ class Collect(CollectType, CollectFamily, CollectVariable):
comment: Optional[str], comment: Optional[str],
parent_option: Optional["Collect"], parent_option: Optional["Collect"],
*, *,
raises: bool=True, raises: bool = True,
test_exists: bool=True, test_exists: bool = True,
) -> None: ) -> None:
self.sources_types = None self.sources_types = None
self.types = None self.types = None
@ -464,14 +516,23 @@ class Collect(CollectType, CollectFamily, CollectVariable):
self.sources = objectspace.sources.copy() self.sources = objectspace.sources.copy()
self.user_type = None self.user_type = None
self.option_type = None self.option_type = None
if parent_option is not None and parent_option.types is not None and self.name in parent_option.types.children: if (
self.set_custom_type(parent_option.types.children[self.name], from_parent=True) parent_option is not None
and parent_option.types is not None
and self.name in parent_option.types.children
):
self.set_custom_type(
parent_option.types.children[self.name], from_parent=True
)
self.check_no_extra_keys = False self.check_no_extra_keys = False
if self.test_exists and path in objectspace.paths: if self.test_exists and path in objectspace.paths:
for source in reversed(self.objectspace.paths[path].xmlfiles): for source in reversed(self.objectspace.paths[path].xmlfiles):
self.sources.insert(0, source) self.sources.insert(0, source)
if parent_option: if parent_option:
self.family_is_sequence = parent_option.user_type in ["leadership", "sequence"] self.family_is_sequence = parent_option.user_type in [
"leadership",
"sequence",
]
self.family_is_dynamic = parent_option.family_is_dynamic self.family_is_dynamic = parent_option.family_is_dynamic
self.parent_dynamic = parent_option.parent_dynamic self.parent_dynamic = parent_option.parent_dynamic
else: else:
@ -509,7 +570,9 @@ class Collect(CollectType, CollectFamily, CollectVariable):
except ValidationError as err: except ValidationError as err:
if self.raises: if self.raises:
raise DictConsistencyError( raise DictConsistencyError(
_('the {0} "{1}" has an invalid "{2}": {3}').format(self.option_type, self.path, key, err), _('the {0} "{1}" has an invalid "{2}": {3}').format(
self.option_type, self.path, key, err
),
84, 84,
self.sources, self.sources,
) from err ) from err
@ -536,17 +599,21 @@ class Collect(CollectType, CollectFamily, CollectVariable):
f"at index {idx}: {err}" f"at index {idx}: {err}"
) from err ) from err
def is_dict(self, key: str, value: Any, *, attributes_types: Optional[dict]=None) -> bool: def is_dict(
self, key: str, value: Any, *, attributes_types: Optional[dict] = None
) -> bool:
"""it's a dict, so it's a new variables!""" """it's a dict, so it's a new variables!"""
return isinstance(value, dict) and not self.is_calculation(key, value, attributes_types=attributes_types) return isinstance(value, dict) and not self.is_calculation(
key, value, attributes_types=attributes_types
)
def is_calculation( def is_calculation(
self, self,
attribute: str, attribute: str,
value: dict, value: dict,
*, *,
attributes_types: Optional[dict]=None, attributes_types: Optional[dict] = None,
inside_list: bool=False, inside_list: bool = False,
): ):
"""Check if it's a calculation""" """Check if it's a calculation"""
if not isinstance(value, dict): if not isinstance(value, dict):
@ -603,11 +670,16 @@ class Collect(CollectType, CollectFamily, CollectVariable):
calculation_object["namespace"] = namespace calculation_object["namespace"] = namespace
calculation_object["xmlfiles"] = self.sources calculation_object["xmlfiles"] = self.sources
# #
self.set_identifier_calculation_in_default_attribut(attribute, calculation_object, inside_list, index) self.set_identifier_calculation_in_default_attribut(
attribute, calculation_object, inside_list, index
)
self.set_params_calculation(calculation_object, attribute) self.set_params_calculation(calculation_object, attribute)
# #
return_type = calculation_object.get("return_type") return_type = calculation_object.get("return_type")
if return_type and return_type not in self.objectspace.variable_objects[0]["types"]: if (
return_type
and return_type not in self.objectspace.variable_objects[0]["types"]
):
raise Exception( raise Exception(
f'unknown "return_type" in {attribute} of variable "{self.path}"' f'unknown "return_type" in {attribute} of variable "{self.path}"'
) )
@ -624,7 +696,13 @@ class Collect(CollectType, CollectFamily, CollectVariable):
else: else:
obj[attribute][index] = calc obj[attribute][index] = calc
def set_identifier_calculation_in_default_attribut(self, attribute: str, calculation_object: dict, inside_list: bool, index: Optional[int]) -> None: def set_identifier_calculation_in_default_attribut(
self,
attribute: str,
calculation_object: dict,
inside_list: bool,
index: Optional[int],
) -> None:
if attribute != "default" or "identifier" not in calculation_object: if attribute != "default" or "identifier" not in calculation_object:
return return
identifier = calculation_object["identifier"] identifier = calculation_object["identifier"]
@ -634,7 +712,13 @@ class Collect(CollectType, CollectFamily, CollectVariable):
attributes_types=self.objectspace.variable_objects[0]["attributes_types"], attributes_types=self.objectspace.variable_objects[0]["attributes_types"],
inside_list=inside_list, inside_list=inside_list,
): ):
self.set_calculation("identifier", identifier, obj=calculation_object, inside_list=inside_list, index=index) self.set_calculation(
"identifier",
identifier,
obj=calculation_object,
inside_list=inside_list,
index=index,
)
def set_params_calculation(self, calculation_object: dict, attribute: str) -> None: def set_params_calculation(self, calculation_object: dict, attribute: str) -> None:
if "params" not in calculation_object: if "params" not in calculation_object:
@ -686,12 +770,18 @@ class Collect(CollectType, CollectFamily, CollectVariable):
val["type"] = list(param_typ)[0] val["type"] = list(param_typ)[0]
def is_redefine(self): def is_redefine(self):
if self.path in self.objectspace.paths and self.option_type == "family" and not self.parameters: if (
self.path in self.objectspace.paths
and self.option_type == "family"
and not self.parameters
):
# allow loading family with no attribute loaded # allow loading family with no attribute loaded
return True return True
if self.types: if self.types:
return True return True
return self.parameters.pop("redefine", False) or (self.types and list(self.parameters) in [[], ["default"]]) return self.parameters.pop("redefine", False) or (
self.types and list(self.parameters) in [[], ["default"]]
)
def is_exists(self): def is_exists(self):
if self.version == "1.0" and self.option_type == "family": if self.version == "1.0" and self.option_type == "family":

View file

@ -188,7 +188,10 @@ class ParserVariable:
self.load_unexist_redefine = rougailconfig["load_unexist_redefine"] 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 # change default initkwargs in CONVERT_OPTION
if hasattr(rougailconfig, "config") and rougailconfig.config.option('define_default_params').value.get(): if (
hasattr(rougailconfig, "config")
and rougailconfig.config.option("define_default_params").value.get()
):
for sub_od in rougailconfig.config.option("default_params"): for sub_od in rougailconfig.config.option("default_params"):
for option in sub_od: for option in sub_od:
if option.owner.isdefault(): if option.owner.isdefault():
@ -219,7 +222,9 @@ class ParserVariable:
) )
if "Family" in module.__all__: if "Family" in module.__all__:
self.family = type( self.family = type(
self.family.__name__ + "_" + structural, (self.family, module.Family), {} self.family.__name__ + "_" + structural,
(self.family, module.Family),
{},
) )
if not self.walker and "Walker" in module.__all__: if not self.walker and "Walker" in module.__all__:
self.walker = module.Walker self.walker = module.Walker
@ -230,8 +235,19 @@ class ParserVariable:
variable_types.remove("choice") variable_types.remove("choice")
variable_types.remove("regexp") variable_types.remove("regexp")
variable_types.remove("symlink") variable_types.remove("symlink")
self.variable_objects = [self.get_variable_object(obj, is_variable=True) for obj in [(self.variable, variable_types), SymLink, self.choices, self.regexp]] self.variable_objects = [
self.family_objects = [self.get_variable_object(obj, is_variable=False) for obj in [self.dynamic, self.family]] self.get_variable_object(obj, is_variable=True)
for obj in [
(self.variable, variable_types),
SymLink,
self.choices,
self.regexp,
]
]
self.family_objects = [
self.get_variable_object(obj, is_variable=False)
for obj in [self.dynamic, self.family]
]
self.is_init = True self.is_init = True
def get_variable_object(self, obj, *, is_variable: bool) -> dict: def get_variable_object(self, obj, *, is_variable: bool) -> dict:
@ -241,7 +257,8 @@ class ParserVariable:
else: else:
hint = get_type_hints(obj) hint = get_type_hints(obj)
types = self.get_types(hint) types = self.get_types(hint)
return {"object": obj, return {
"object": obj,
"types": types, "types": types,
"attrs": self.get_option_attrs(hint), "attrs": self.get_option_attrs(hint),
"attributes_types": self.get_attributes_types(hint, variable=is_variable), "attributes_types": self.get_attributes_types(hint, variable=is_variable),
@ -298,10 +315,11 @@ class ParserVariable:
path = option.path path = option.path
redefine = option.redefine redefine = option.redefine
exists = option.exists exists = option.exists
# if not redefine and not exists and option.user_type in self.custom_family_types: # if not redefine and not exists and option.user_type in self.custom_family_types:
types = option.types types = option.types
if types: if types:
self.add_family(types, self.add_family(
types,
custom_type=True, custom_type=True,
) )
if path not in self.paths: if path not in self.paths:
@ -319,9 +337,7 @@ class ParserVariable:
else: else:
if exists in [None, True] and not redefine: if exists in [None, True] and not redefine:
msg = _('family "{0}" define multiple time').format(path) msg = _('family "{0}" define multiple time').format(path)
raise DictConsistencyError( raise DictConsistencyError(msg, 32, option.sources)
msg, 32, option.sources
)
if self.load_unexist_redefine or exists in [None, True]: if self.load_unexist_redefine or exists in [None, True]:
objects = option.parameters.copy() objects = option.parameters.copy()
objects["xmlfiles"] = option.sources objects["xmlfiles"] = option.sources
@ -394,7 +410,7 @@ class ParserVariable:
self, self,
option: Collect, option: Collect,
*, *,
custom_type: bool=False, custom_type: bool = False,
) -> None: ) -> None:
"""Add a new family""" """Add a new family"""
path = option.path path = option.path
@ -415,12 +431,15 @@ class ParserVariable:
try: try:
self.paths.add( self.paths.add(
option, option,
family_obj(name=option.name, family_obj(
name=option.name,
**data, **data,
), ),
) )
except ValidationError as err: except ValidationError as err:
raise Exception(f'invalid family "{path}" in "{display_list(option.sources)}": {err}') from err raise Exception(
f'invalid family "{path}" in "{display_list(option.sources)}": {err}'
) from err
self.set_name( self.set_name(
self.paths[path], self.paths[path],
"optiondescription_", "optiondescription_",
@ -448,7 +467,8 @@ class ParserVariable:
path = option.path path = option.path
if option.types: if option.types:
redefine = True redefine = True
self.add_variable(option.types, self.add_variable(
option.types,
first_variable=first_variable, first_variable=first_variable,
custom_type=True, custom_type=True,
) )
@ -461,7 +481,8 @@ class ParserVariable:
if not self.load_unexist_redefine and redefine: if not self.load_unexist_redefine and redefine:
msg = f'cannot redefine the inexisting variable "{path}"' msg = f'cannot redefine the inexisting variable "{path}"'
raise DictConsistencyError(msg, 46, option.sources) raise DictConsistencyError(msg, 46, option.sources)
self.add_variable(option, self.add_variable(
option,
first_variable, first_variable,
custom_type, custom_type,
) )
@ -471,9 +492,7 @@ class ParserVariable:
return return
if not redefine: if not redefine:
msg = _('variable "{0}" define multiple time').format(path) msg = _('variable "{0}" define multiple time').format(path)
raise DictConsistencyError( raise DictConsistencyError(msg, 45, option.sources)
msg, 45, option.sources
)
objects = option.parameters.copy() objects = option.parameters.copy()
objects["xmlfiles"] = option.sources objects["xmlfiles"] = option.sources
self.paths.add( self.paths.add(
@ -499,7 +518,8 @@ class ParserVariable:
data["path"] = path data["path"] = path
data["version"] = option.version data["version"] = option.version
try: try:
variable_obj = option.object["object"](name=option.name, variable_obj = option.object["object"](
name=option.name,
**data, **data,
) )
except ValidationError as err: except ValidationError as err:
@ -572,11 +592,12 @@ class ParserVariable:
class RougailConvert(ParserVariable): class RougailConvert(ParserVariable):
"""Main Rougail conversion""" """Main Rougail conversion"""
def __init__(self, def __init__(
self,
rougailconfig, rougailconfig,
*, *,
custom_variable_types: dict={}, custom_variable_types: dict = {},
custom_family_types: dict={}, custom_family_types: dict = {},
) -> None: ) -> None:
self.annotator = False self.annotator = False
self.has_namespace = False self.has_namespace = False
@ -588,14 +609,16 @@ class RougailConvert(ParserVariable):
def get_attributes_types( def get_attributes_types(
self, self,
hint: dict, hint: dict,
variable: bool=False, variable: bool = False,
) -> dict: ) -> dict:
"""attribute is calculated if typing is like: Union[Calculation, xxx]""" """attribute is calculated if typing is like: Union[Calculation, xxx]"""
lists = {"strings": [], lists = {
"strings": [],
"booleans": [], "booleans": [],
"integers": [], "integers": [],
"floats": [], "floats": [],
"calculation": []} "calculation": [],
}
if variable: if variable:
lists["calculation"].append("identifier") lists["calculation"].append("identifier")
calculation = ["identifier"] calculation = ["identifier"]
@ -647,7 +670,8 @@ class RougailConvert(ParserVariable):
booleans.append(key) booleans.append(key)
elif "Literal" in value.__class__.__name__: elif "Literal" in value.__class__.__name__:
literals[key] = value.__args__ literals[key] = value.__args__
return {"strings": strings, return {
"strings": strings,
"integers": integers, "integers": integers,
"booleans": booleans, "booleans": booleans,
"floats": floats, "floats": floats,
@ -658,7 +682,9 @@ class RougailConvert(ParserVariable):
} }
def create_namespace( def create_namespace(
self, namespace_description: str, isolated_namespace: bool=True, self,
namespace_description: str,
isolated_namespace: bool = True,
) -> None: ) -> None:
namespace_path = normalize_family(namespace_description) namespace_path = normalize_family(namespace_description)
self.has_namespace = True self.has_namespace = True
@ -687,9 +713,7 @@ class RougailConvert(ParserVariable):
None, None,
None, None,
) )
self.parse_family( self.parse_family(option)
option
)
def get_comment( def get_comment(
self, self,

View file

@ -35,7 +35,11 @@ from ..utils import (
PROPERTY_ATTRIBUTE, PROPERTY_ATTRIBUTE,
) )
from ..i18n import _ from ..i18n import _
from ..error import DictConsistencyError, VariableCalculationDependencyError, RougailWarning from ..error import (
DictConsistencyError,
VariableCalculationDependencyError,
RougailWarning,
)
from ..tiramisu import CONVERT_OPTION, RENAME_TYPE, display_xmlfiles, convert_boolean from ..tiramisu import CONVERT_OPTION, RENAME_TYPE, display_xmlfiles, convert_boolean
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None] BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
@ -60,7 +64,7 @@ def get_convert_option_types():
if key.startswith("_"): if key.startswith("_"):
continue continue
if "params" in datas and key in datas["params"]: if "params" in datas and key in datas["params"]:
multi = datas["params"][key].get('multi', False) multi = datas["params"][key].get("multi", False)
description = datas["params"][key]["description"] description = datas["params"][key]["description"]
choices = datas["params"][key].get("choices") choices = datas["params"][key].get("choices")
else: else:
@ -111,7 +115,7 @@ class VariableParam(Param):
variable: StrictStr variable: StrictStr
propertyerror: bool = True propertyerror: bool = True
whole: bool = False whole: bool = False
# dynamic: bool = True # dynamic: bool = True
optional: bool = False optional: bool = False
def to_param( def to_param(
@ -324,9 +328,16 @@ class JinjaCalculation(Calculation):
variable = objectspace.paths[path] variable = objectspace.paths[path]
objectspace.jinja[jinja_path] = self.jinja objectspace.jinja[jinja_path] = self.jinja
if return_type in RENAME_TYPE: if return_type in RENAME_TYPE:
warning = _('the variable "{0}" has a depreciated return_type "{1}", please use "{2}" instead in {3}') warning = _(
'the variable "{0}" has a depreciated return_type "{1}", please use "{2}" instead in {3}'
)
warn( warn(
warning.format(path, return_type, RENAME_TYPE[return_type], display_xmlfiles(self.xmlfiles)), warning.format(
path,
return_type,
RENAME_TYPE[return_type],
display_xmlfiles(self.xmlfiles),
),
DeprecationWarning, DeprecationWarning,
stacklevel=2, stacklevel=2,
) )
@ -416,19 +427,26 @@ class JinjaCalculation(Calculation):
'variable "{0}" has a calculating "{1}" with an invalid return_type, should be boolean or string, not "{2}"' 'variable "{0}" has a calculating "{1}" with an invalid return_type, should be boolean or string, not "{2}"'
).format(path, self.attribute_name, return_type) ).format(path, self.attribute_name, return_type)
raise DictConsistencyError(msg, 81, self.xmlfiles) raise DictConsistencyError(msg, 81, self.xmlfiles)
if return_type == 'boolean': if return_type == "boolean":
description = self.description description = self.description
if description is None: if description is None:
if self.ori_path is not None: if self.ori_path is not None:
opath = self.ori_path opath = self.ori_path
else: else:
opath = path opath = path
warning = _('the variable "{0}" has a return_type "{1}", for attribute "{2}" but has not description in {3}') warning = _(
'the variable "{0}" has a return_type "{1}", for attribute "{2}" but has not description in {3}'
)
warn( warn(
warning.format(opath, return_type, self.attribute_name, display_xmlfiles(self.xmlfiles)), warning.format(
opath,
return_type,
self.attribute_name,
display_xmlfiles(self.xmlfiles),
),
RougailWarning, RougailWarning,
) )
self.description = _('value is invalid') self.description = _("value is invalid")
else: else:
description = None description = None
return self._jinja_to_function( return self._jinja_to_function(
@ -437,7 +455,7 @@ class JinjaCalculation(Calculation):
False, False,
objectspace, objectspace,
path, path,
params={'description': description}, params={"description": description},
) )
elif self.attribute_name in PROPERTY_ATTRIBUTE: elif self.attribute_name in PROPERTY_ATTRIBUTE:
return_type = self.return_type return_type = self.return_type
@ -491,7 +509,7 @@ class JinjaCalculation(Calculation):
class _VariableCalculation(Calculation): class _VariableCalculation(Calculation):
variable: StrictStr variable: StrictStr
propertyerror: bool = True, propertyerror: bool = (True,)
allow_none: bool = False allow_none: bool = False
optional: bool = False optional: bool = False
# FIXME identifier is not available for Properties! # FIXME identifier is not available for Properties!
@ -522,7 +540,9 @@ class _VariableCalculation(Calculation):
self.namespace, self.namespace,
self.xmlfiles, self.xmlfiles,
) )
if variable and not isinstance(variable, objectspace.variable_objects[0]["object"]): if variable and not isinstance(
variable, objectspace.variable_objects[0]["object"]
):
if isinstance(variable, objectspace.family): if isinstance(variable, objectspace.family):
msg = _( msg = _(
'a variable "{0}" is needs in attribute "{1}" for "{2}" but it\'s a family' 'a variable "{0}" is needs in attribute "{1}" for "{2}" but it\'s a family'
@ -549,7 +569,13 @@ class _VariableCalculation(Calculation):
if variable_in_calculation_identifier: if variable_in_calculation_identifier:
msg = _( msg = _(
'variable "{0}" has an attribute "{1}" with an identifier "{2}" but the path has also the identifier "{3}"' 'variable "{0}" has an attribute "{1}" with an identifier "{2}" but the path has also the identifier "{3}"'
).format(path, self.attribute_name, self.variable, self.identifier, variable_in_calculation_identifier) ).format(
path,
self.attribute_name,
self.variable,
self.identifier,
variable_in_calculation_identifier,
)
raise DictConsistencyError(msg, 89, self.xmlfiles) raise DictConsistencyError(msg, 89, self.xmlfiles)
variable_in_calculation_identifier = self.identifier variable_in_calculation_identifier = self.identifier
identifier_is_a_calculation = True identifier_is_a_calculation = True
@ -578,7 +604,11 @@ class _VariableCalculation(Calculation):
if self.allow_none: if self.allow_none:
params["allow_none"] = True params["allow_none"] = True
self.check_multi( self.check_multi(
objectspace, path, variable_in_calculation_path, variable_in_calculation, identifier_is_a_calculation, objectspace,
path,
variable_in_calculation_path,
variable_in_calculation,
identifier_is_a_calculation,
) )
if path in objectspace.followers: if path in objectspace.followers:
multi = objectspace.multis[path] == "submulti" multi = objectspace.multis[path] == "submulti"
@ -589,7 +619,12 @@ class _VariableCalculation(Calculation):
return params return params
def check_multi( def check_multi(
self, objectspace, path, variable_in_calculation_path, variable_in_calculation, identifier_is_a_calculation, self,
objectspace,
path,
variable_in_calculation_path,
variable_in_calculation,
identifier_is_a_calculation,
): ):
local_variable = objectspace.paths[path] local_variable = objectspace.paths[path]
local_variable_multi, variable_in_calculation_multi = ( local_variable_multi, variable_in_calculation_multi = (
@ -1075,8 +1110,8 @@ class Family(BaseModel):
class Dynamic(BaseModel): class Dynamic(BaseModel):
type: Literal["dynamic"] = "dynamic" type: Literal["dynamic"] = "dynamic"
# # None only for format 1.0 # # None only for format 1.0
# variable: StrictStr = None # variable: StrictStr = None
dynamic: Union[List[Union[StrictStr, Calculation]], Calculation] dynamic: Union[List[Union[StrictStr, Calculation]], Calculation]

View file

@ -82,7 +82,7 @@ class TiramisuReflector:
continue continue
self.text["header"].append(f"load_functions('{funcs_path}')") self.text["header"].append(f"load_functions('{funcs_path}')")
if self.objectspace.export_with_import: if self.objectspace.export_with_import:
# if self.objectspace.has_namespace: # if self.objectspace.has_namespace:
self.text["header"].extend( self.text["header"].extend(
[ [
"try:", "try:",
@ -343,12 +343,16 @@ class Common:
ret = f"ParamSelfOption(whole={whole}" ret = f"ParamSelfOption(whole={whole}"
if not dynamic: if not dynamic:
ret += ", dynamic=False" ret += ", dynamic=False"
return ret + ')' return ret + ")"
if whole: if whole:
msg = _('variable param "{0}" has whole attribute but it\'s not allowed for external variable') msg = _(
'variable param "{0}" has whole attribute but it\'s not allowed for external variable'
)
raise DictConsistencyError(msg.format(variable.path), 34, self.elt.xmlfiles) raise DictConsistencyError(msg.format(variable.path), 34, self.elt.xmlfiles)
if not dynamic: if not dynamic:
msg = _('variable param "{0}" has dynamic attribute but it\'s not allowed for external variable') msg = _(
'variable param "{0}" has dynamic attribute but it\'s not allowed for external variable'
)
raise DictConsistencyError(msg.format(variable.path), 34, self.elt.xmlfiles) raise DictConsistencyError(msg.format(variable.path), 34, self.elt.xmlfiles)
option_name = self.tiramisu.reflector_objects[variable.path].get( option_name = self.tiramisu.reflector_objects[variable.path].get(
self.calls, self.elt.path self.calls, self.elt.path

View file

@ -68,6 +68,7 @@ class DictConsistencyError(Exception):
class NotFoundError(Exception): class NotFoundError(Exception):
"not found error" "not found error"
pass pass

View file

@ -60,7 +60,9 @@ class Annotator(Walk):
return return
alternative_name = variable.alternative_name alternative_name = variable.alternative_name
variable_path = variable.path variable_path = variable.path
self.objectspace.informations.add(variable_path, "alternative_name", alternative_name) self.objectspace.informations.add(
variable_path, "alternative_name", alternative_name
)
all_letters = "" all_letters = ""
for letter in alternative_name: for letter in alternative_name:
all_letters += letter all_letters += letter

View file

@ -16,15 +16,15 @@ You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from typing import List, Optional from typing import List, Optional
from itertools import chain from itertools import chain
from ruamel.yaml import YAML from ruamel.yaml import YAML
from ..tiramisu import normalize_family from ..tiramisu import normalize_family
from ..convert.path import Paths from ..convert.path import Paths
#from ..error import DictConsistencyError
#from ..i18n import _ # from ..error import DictConsistencyError
# from ..i18n import _
class Walker: class Walker:
@ -85,9 +85,9 @@ class Walker:
return return
path = self.convert.namespace path = self.convert.namespace
if path: if path:
name = f'yaml file for {path}' name = f"yaml file for {path}"
else: else:
name = 'yaml file' name = "yaml file"
version = self.convert.validate_file_version( version = self.convert.validate_file_version(
objects, objects,
name, name,
@ -101,4 +101,3 @@ class Walker:
__all__ = ("Walker",) __all__ = ("Walker",)

View file

@ -66,4 +66,3 @@ extra_namespaces:
__all__ = "get_rougail_config" __all__ = "get_rougail_config"

View file

@ -87,89 +87,138 @@ def convert_boolean(value: str) -> bool:
return None return None
raise Exception(_('unknown boolean value "{0}"').format(value)) raise Exception(_('unknown boolean value "{0}"').format(value))
_ip_params = { _ip_params = {
"cidr": {"description": _("IP must be in CIDR format")}, "cidr": {"description": _("IP must be in CIDR format")},
"private_only": {"description": _("private IP are allowed")}, "private_only": {"description": _("private IP are allowed")},
"allow_reserved": {"description": _("reserved IP are allowed")}, "allow_reserved": {"description": _("reserved IP are allowed")},
} }
_network_params = { _network_params = {
"cidr": {"description": _("network must be in CIDR format")}, "cidr": {"description": _("network must be in CIDR format")},
"private_only": {"description": _("private network are allowed")}, "private_only": {"description": _("private network are allowed")},
"allow_reserved": {"description": _("reserved network are allowed")}, "allow_reserved": {"description": _("reserved network are allowed")},
} }
_port_params = { _port_params = {
"allow_range": {"description": _("can be range of port")}, "allow_range": {"description": _("can be range of port")},
"allow_protocol": {"description": _("can have the protocol")}, "allow_protocol": {"description": _("can have the protocol")},
"allow_zero": {"description": _("port 0 is allowed")}, "allow_zero": {"description": _("port 0 is allowed")},
"allow_wellknown": {"description": _("well-known ports (1 to 1023) are allowed")}, "allow_wellknown": {"description": _("well-known ports (1 to 1023) are allowed")},
"allow_registred": {"description": _("registred ports (1024 to 49151) are allowed")}, "allow_registred": {
"allow_private": {"description": _("private ports (greater than 49152) are allowed")}, "description": _("registred ports (1024 to 49151) are allowed")
} },
"allow_private": {
"description": _("private ports (greater than 49152) are allowed")
},
}
_domain_params = { _domain_params = {
"type": {"description": _("type of domainname"), "choices": ('domainname', 'netbios', 'hostname'), 'doc': _("type {0}")}, "type": {
"description": _("type of domainname"),
"choices": ("domainname", "netbios", "hostname"),
"doc": _("type {0}"),
},
"allow_startswith_dot": {"description": _("the domain name can starts by a dot")}, "allow_startswith_dot": {"description": _("the domain name can starts by a dot")},
"allow_without_dot": {"description": _("the domain name can be a hostname")}, "allow_without_dot": {"description": _("the domain name can be a hostname")},
"allow_ip": {"description": _("the domain name can be an IP")}, "allow_ip": {"description": _("the domain name can be an IP")},
"allow_cidr_network": {"description": _("the domain name can be network in CIDR format")}, "allow_cidr_network": {
"description": _("the domain name can be network in CIDR format")
},
"test_existence": {"description": _("the domain name must exist")}, "test_existence": {"description": _("the domain name must exist")},
} }
_web_params = _port_params | _domain_params _web_params = _port_params | _domain_params
CONVERT_OPTION = { CONVERT_OPTION = {
"string": dict(opttype="StrOption", example="example"), "string": dict(opttype="StrOption", example="example"),
"number": dict(opttype="IntOption", "number": dict(
opttype="IntOption",
func=int, func=int,
params={ params={
"min_number": {"description": _("the minimum value"), 'doc': _("the minimum value is {0}")}, "min_number": {
"max_number": {"description": _("the maximum value"), 'doc': _("the maximum value is {0}")}, "description": _("the minimum value"),
"doc": _("the minimum value is {0}"),
}, },
example=42), "max_number": {
"integer": dict(opttype="IntOption", "description": _("the maximum value"),
"doc": _("the maximum value is {0}"),
},
},
example=42,
),
"integer": dict(
opttype="IntOption",
params={ params={
"min_integer": {"description": _("the minimum value"), 'doc': _("the minimum value is {0}")}, "min_integer": {
"max_integer": {"description": _("the maximum value"), 'doc': _("the maximum value is {0}")}, "description": _("the minimum value"),
"doc": _("the minimum value is {0}"),
},
"max_integer": {
"description": _("the maximum value"),
"doc": _("the maximum value is {0}"),
},
}, },
func=int, func=int,
example=42, example=42,
), ),
"float": dict(opttype="FloatOption", func=float, example=1.42), "float": dict(opttype="FloatOption", func=float, example=1.42),
"boolean": dict(opttype="BoolOption", func=convert_boolean, example=True), "boolean": dict(opttype="BoolOption", func=convert_boolean, example=True),
"secret": dict(opttype="PasswordOption", "secret": dict(
opttype="PasswordOption",
params={ params={
"min_len": {"description": _("minimum characters length for the secret"), "doc": _("minimum length for the secret is {0} characters")}, "min_len": {
"max_len": {"description": _("maximum characters length for the secret"), "doc": _("maximum length for the secret is {0} characters")}, "description": _("minimum characters length for the secret"),
"forbidden_char": {"description": _("forbidden characters"), "doc": _("forbidden characters: {0}")}, "doc": _("minimum length for the secret is {0} characters"),
}, },
example="secrets"), "max_len": {
"description": _("maximum characters length for the secret"),
"doc": _("maximum length for the secret is {0} characters"),
},
"forbidden_char": {
"description": _("forbidden characters"),
"doc": _("forbidden characters: {0}"),
},
},
example="secrets",
),
"mail": dict(opttype="EmailOption", example="user@example.net"), "mail": dict(opttype="EmailOption", example="user@example.net"),
"unix_filename": dict(opttype="FilenameOption", "unix_filename": dict(
opttype="FilenameOption",
msg="UNIX filename", msg="UNIX filename",
params={ params={
"allow_relative": {"description": _("this filename could be a relative path")}, "allow_relative": {
"test_existence": {"description": _("this file must exist")}, "description": _("this filename could be a relative path")
"types": {"description": _("file type allowed"), "doc": _("file type allowed: {0}"), "choices": ("file", "directory"), "multi": True},
}, },
example="/tmp/myfile.txt"), "test_existence": {"description": _("this file must exist")},
"date": dict(opttype="DateOption", example="2000-01-01"), "types": {
"unix_user": dict(opttype="UsernameOption", example="username", "description": _("file type allowed"),
msg="UNIX user" "doc": _("file type allowed: {0}"),
"choices": ("file", "directory"),
"multi": True,
},
},
example="/tmp/myfile.txt",
), ),
"date": dict(opttype="DateOption", example="2000-01-01"),
"unix_user": dict(opttype="UsernameOption", example="username", msg="UNIX user"),
"ip": dict( "ip": dict(
opttype="IPOption", initkwargs={"allow_reserved": True}, opttype="IPOption",
initkwargs={"allow_reserved": True},
msg="IP", msg="IP",
params=_ip_params, params=_ip_params,
example="1.1.1.1" example="1.1.1.1",
), ),
"cidr": dict(opttype="IPOption", msg="CIDR", initkwargs={"cidr": True}, "cidr": dict(
opttype="IPOption",
msg="CIDR",
initkwargs={"cidr": True},
params=_ip_params, params=_ip_params,
example="1.1.1.0/24"), example="1.1.1.0/24",
),
"netmask": dict(opttype="NetmaskOption", example="255.255.255.0"), "netmask": dict(opttype="NetmaskOption", example="255.255.255.0"),
"network": dict(opttype="NetworkOption", "network": dict(opttype="NetworkOption", params=_network_params, example="1.1.1.0"),
params=_network_params,
example="1.1.1.0"),
"network_cidr": dict( "network_cidr": dict(
opttype="NetworkOption", initkwargs={"cidr": True}, example="1.1.1.0/24", opttype="NetworkOption",
initkwargs={"cidr": True},
example="1.1.1.0/24",
params=_network_params, params=_network_params,
msg="network CIDR", msg="network CIDR",
), ),
@ -200,9 +249,11 @@ CONVERT_OPTION = {
example="https://example.net", example="https://example.net",
), ),
"port": dict( "port": dict(
opttype="PortOption", initkwargs={"allow_private": True}, opttype="PortOption",
initkwargs={"allow_private": True},
params=_port_params, params=_port_params,
example="111", func=str, example="111",
func=str,
), ),
"mac": dict(opttype="MACOption", example="00:00:00:00:00"), "mac": dict(opttype="MACOption", example="00:00:00:00:00"),
"unix_permissions": dict( "unix_permissions": dict(
@ -218,9 +269,10 @@ CONVERT_OPTION = {
"symlink": dict(opttype="SymLinkOption"), "symlink": dict(opttype="SymLinkOption"),
} }
# only version 1.1 # only version 1.1
RENAME_TYPE = {"number": "integer", RENAME_TYPE = {
"number": "integer",
"leadership": "sequence", "leadership": "sequence",
} }
def get_identifier_from_dynamic_family(true_name, name) -> str: def get_identifier_from_dynamic_family(true_name, name) -> str:
@ -240,7 +292,9 @@ def raise_carry_out_calculation_error(subconfig, *args, **kwargs):
ymlfiles = subconfig.config_bag.context.get_values().get_information( ymlfiles = subconfig.config_bag.context.get_values().get_information(
subconfig, "ymlfiles", [] subconfig, "ymlfiles", []
) )
raise ConfigError(_("{0} in {1}").format(err, display_xmlfiles(ymlfiles)), subconfig=subconfig) raise ConfigError(
_("{0} in {1}").format(err, display_xmlfiles(ymlfiles)), subconfig=subconfig
)
errors.raise_carry_out_calculation_error = raise_carry_out_calculation_error errors.raise_carry_out_calculation_error = raise_carry_out_calculation_error
@ -249,7 +303,7 @@ errors.raise_carry_out_calculation_error = raise_carry_out_calculation_error
global func global func
dict_env = {} dict_env = {}
ENV = SandboxedEnvironment(loader=DictLoader(dict_env), undefined=StrictUndefined) ENV = SandboxedEnvironment(loader=DictLoader(dict_env), undefined=StrictUndefined)
ENV.add_extension('jinja2.ext.do') ENV.add_extension("jinja2.ext.do")
func = ENV.filters func = ENV.filters
TMP_TEMPLATE = mkdtemp() TMP_TEMPLATE = mkdtemp()
@ -258,6 +312,7 @@ ENV.compile_templates(TMP_TEMPLATE, zip=None)
atexit.register(rmtree, TMP_TEMPLATE) atexit.register(rmtree, TMP_TEMPLATE)
class JinjaError: class JinjaError:
__slot__ = ("_err",) __slot__ = ("_err",)
@ -327,6 +382,7 @@ def tiramisu_display_name(
with_quote: bool = False, with_quote: bool = False,
) -> str: ) -> str:
"""Replace the Tiramisu display_name function to display path + description""" """Replace the Tiramisu display_name function to display path + description"""
def get_path(): def get_path():
if description_type in ["description", "name", "name_and_description"]: if description_type in ["description", "name", "name_and_description"]:
path = kls.impl_getname() path = kls.impl_getname()
@ -337,6 +393,7 @@ def tiramisu_display_name(
"{{ identifier }}", normalize_family(str(subconfig.identifiers[-1])) "{{ identifier }}", normalize_family(str(subconfig.identifiers[-1]))
) )
return path return path
config_bag = subconfig.config_bag config_bag = subconfig.config_bag
context = config_bag.context context = config_bag.context
values = context.get_values() values = context.get_values()
@ -344,12 +401,23 @@ def tiramisu_display_name(
description_type = values.get_information( description_type = values.get_information(
context_subconfig, "description_type", "name_and_description" context_subconfig, "description_type", "name_and_description"
) )
if description_type in ["description", "name_and_description", "path_and_description"]: if description_type in [
"description",
"name_and_description",
"path_and_description",
]:
doc = values.get_information(subconfig, "doc", None) doc = values.get_information(subconfig, "doc", None)
description = doc if doc and doc != kls.impl_getname() else "" description = doc if doc and doc != kls.impl_getname() else ""
if "{{ identifier }}" in description and subconfig.identifiers: if "{{ identifier }}" in description and subconfig.identifiers:
description = description.replace("{{ identifier }}", str(subconfig.identifiers[-1])) description = description.replace(
if description_type in ["name", "path", "name_and_description", "path_and_description"]: "{{ identifier }}", str(subconfig.identifiers[-1])
)
if description_type in [
"name",
"path",
"name_and_description",
"path_and_description",
]:
path = get_path() path = get_path()
if description_type in ["name_and_description", "path_and_description"]: if description_type in ["name_and_description", "path_and_description"]:
if description: if description:
@ -414,15 +482,15 @@ def jinja_to_function(
for v in value: for v in value:
if isinstance(v, PropertiesOptionError): if isinstance(v, PropertiesOptionError):
v = JinjaError(v) v = JinjaError(v)
# if v is None: # if v is None:
# v = '' # v = ''
val.append(v) val.append(v)
value = val value = val
else: else:
if isinstance(value, PropertiesOptionError): if isinstance(value, PropertiesOptionError):
value = JinjaError(value) value = JinjaError(value)
# if value is None: # if value is None:
# value = '' # value = ''
if "." in key: if "." in key:
c_kw = kw c_kw = kw
path, var = key.rsplit(".", 1) path, var = key.rsplit(".", 1)
@ -446,7 +514,9 @@ def jinja_to_function(
except Exception as err: except Exception as err:
kw_str = ", ".join(kw_to_string(kw)) kw_str = ", ".join(kw_to_string(kw))
prefix = _('cannot calculate the variable "{0}"').format(__internal_variable) prefix = _('cannot calculate the variable "{0}"').format(__internal_variable)
msg = _('the attribute "{0}" in {1} with the parameters "{2}" causes the error: {3}').format( msg = _(
'the attribute "{0}" in {1} with the parameters "{2}" causes the error: {3}'
).format(
__internal_attribute, __internal_attribute,
display_xmlfiles(__internal_files), display_xmlfiles(__internal_files),
kw_str, kw_str,

View file

@ -15,6 +15,7 @@ details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from .config import StaticRougailConvert from .config import StaticRougailConvert
from .i18n import _ from .i18n import _
from .error import DictConsistencyError from .error import DictConsistencyError
@ -27,17 +28,21 @@ class TypeRougailConvert(StaticRougailConvert):
secret_pattern: str, secret_pattern: str,
default_structural_format_version: str, default_structural_format_version: str,
) -> None: ) -> None:
super().__init__(False, {"sort_structural_files_all": True, super().__init__(
False,
{
"sort_structural_files_all": True,
"main_namespace": None, "main_namespace": None,
"main_structural_directories": main_structural_directories, "main_structural_directories": main_structural_directories,
}) },
)
self.default_structural_format_version = default_structural_format_version self.default_structural_format_version = default_structural_format_version
self.secret_pattern = secret_pattern self.secret_pattern = secret_pattern
self.loaded_custom_types = {} self.loaded_custom_types = {}
def load_config(self) -> None: def load_config(self) -> None:
super().load_config() super().load_config()
# self.add_extra_options = self.add_extra_options # self.add_extra_options = self.add_extra_options
self.sort_structural_files_all = False self.sort_structural_files_all = False
self.structurals = ["directory"] self.structurals = ["directory"]
@ -46,7 +51,8 @@ def rougail_type(rougailconfig):
types = rougailconfig["types"] types = rougailconfig["types"]
if not types: if not types:
return {"custom_variable_types": {}, "custom_family_types": {}} return {"custom_variable_types": {}, "custom_family_types": {}}
convert = TypeRougailConvert(types, convert = TypeRougailConvert(
types,
rougailconfig["secret_manager.pattern"], rougailconfig["secret_manager.pattern"],
rougailconfig["default_structural_format_version"], rougailconfig["default_structural_format_version"],
) )
@ -68,10 +74,11 @@ def rougail_type(rougailconfig):
custom_variable_types = {} custom_variable_types = {}
custom_family_types = {} custom_family_types = {}
for typ, data in convert.loaded_custom_types.items(): for typ, data in convert.loaded_custom_types.items():
if data.option_type == 'variable': if data.option_type == "variable":
custom_variable_types[typ] = data custom_variable_types[typ] = data
else: else:
custom_family_types[typ] = data custom_family_types[typ] = data
return {"custom_variable_types": custom_variable_types, return {
"custom_variable_types": custom_variable_types,
"custom_family_types": custom_family_types, "custom_family_types": custom_family_types,
} }

View file

@ -86,7 +86,8 @@ class UserData:
"source": source, "source": source,
"values": data, "values": data,
"options": options.copy(), "options": options.copy(),
}) }
)
self.errors.extend(datas.get("errors", [])) self.errors.extend(datas.get("errors", []))
self.warnings.extend(datas.get("warnings", [])) self.warnings.extend(datas.get("warnings", []))
@ -102,7 +103,7 @@ class UserData:
if self.invalid_user_data_error: if self.invalid_user_data_error:
msg = str(err) msg = str(err)
else: else:
msg = _('{0}, it will be ignored').format(err) msg = _("{0}, it will be ignored").format(err)
self.invalids.append({msg: err.subconfig}) self.invalids.append({msg: err.subconfig})
def _auto_configure_dynamics(self): def _auto_configure_dynamics(self):
@ -211,7 +212,9 @@ class UserData:
if option.issubmulti(): if option.issubmulti():
for idx, val in enumerate(value): for idx, val in enumerate(value):
if isinstance(val, list): if isinstance(val, list):
value[idx] = [convert_value(option, v, needs_convert) for v in val] value[idx] = [
convert_value(option, v, needs_convert) for v in val
]
elif isinstance(value, list): elif isinstance(value, list):
value = [convert_value(option, val, needs_convert) for val in value] value = [convert_value(option, val, needs_convert) for val in value]
if needs_convert: if needs_convert:
@ -236,21 +239,26 @@ class UserData:
if option.type() == "password": if option.type() == "password":
one_is_in_error = False one_is_in_error = False
for values in self.values[values_path]: for values in self.values[values_path]:
if values.get("options", {}).get("allow_secrets_variables", True) is False: if (
values.get("options", {}).get(
"allow_secrets_variables", True
)
is False
):
one_is_in_error = True one_is_in_error = True
self.errors.append({ self.errors.append(
{
_( _(
'the variable contains secrets and should not be defined in {0}' "the variable contains secrets and should not be defined in {0}"
).format(values["source"]): option._subconfig} ).format(values["source"]): option._subconfig
}
) )
if one_is_in_error: if one_is_in_error:
self.values.pop(values_path) self.values.pop(values_path)
continue continue
values = self.values[values_path][-1] values = self.values[values_path][-1]
options = values.get("options", {}) options = values.get("options", {})
value = self.convert_value( value = self.convert_value(path, option, options, values["values"])
path, option, options, values["values"]
)
index = option.index() index = option.index()
if index is not None: if index is not None:
if isinstance(value, tuple): if isinstance(value, tuple):
@ -278,8 +286,8 @@ class UserData:
value_is_set = True value_is_set = True
except Exception as err: except Exception as err:
pass pass
# if path != option.path(): # if path != option.path():
# self.values[option.path()] = self.values.pop(values_path) # self.values[option.path()] = self.values.pop(values_path)
else: else:
# value is correctly set, remove variable to the set # value is correctly set, remove variable to the set
if index is not None: if index is not None:
@ -326,34 +334,51 @@ class UserData:
if value: if value:
if self.invalid_user_data_error: if self.invalid_user_data_error:
msg = _( msg = _(
'it\'s a family so we cannot set the value {0}, it has been loading from {1}' "it's a family so we cannot set the value {0}, it has been loading from {1}"
) )
else: else:
msg = _( msg = _(
'it\'s a family so we cannot set the value {0}, it will be ignored when loading from {1}' "it's a family so we cannot set the value {0}, it will be ignored when loading from {1}"
) )
self.invalids.append({msg.format( self.invalids.append(
{
msg.format(
self._display_value(option, value), self._display_value(option, value),
options["source"], options["source"],
): option._subconfig} ): option._subconfig
}
) )
continue continue
if option.issymlinkoption(): if option.issymlinkoption():
err = _('it\'s a symlink option so we cannot set the value {0}').format(self._display_value(option, value)) err = _(
"it's a symlink option so we cannot set the value {0}"
).format(self._display_value(option, value))
if self.invalid_user_data_error: if self.invalid_user_data_error:
msg = _('{0}, it has been loading from {1}').format(err, options["source"]) msg = _("{0}, it has been loading from {1}").format(
err, options["source"]
)
else: else:
msg = _('{0}, it will be ignored when loading from {1}').format(err, options["source"]) msg = _("{0}, it will be ignored when loading from {1}").format(
err, options["source"]
)
self.unknowns.append({msg: option._subconfig}) self.unknowns.append({msg: option._subconfig})
continue continue
except ConfigError as err: except ConfigError as err:
self.invalids.append({ self.invalids.append(
_("{0}, it has been loaded from {1}").format(err, options["source"]): option._subconfig} {
_("{0}, it has been loaded from {1}").format(
err, options["source"]
): option._subconfig
}
) )
continue continue
except PropertiesOptionError as err: except PropertiesOptionError as err:
self.unknowns.append({ self.unknowns.append(
_("{0}, it has been loaded from {1}").format(err, options["source"]): option._subconfig} {
_("{0}, it has been loaded from {1}").format(
err, options["source"]
): option._subconfig
}
) )
continue continue
@ -364,20 +389,16 @@ class UserData:
subconfig = None subconfig = None
child_name = err_path child_name = err_path
else: else:
parent_path, child_name = err_path.rsplit('.', 1) parent_path, child_name = err_path.rsplit(".", 1)
subconfig = self.config.option(parent_path) subconfig = self.config.option(parent_path)
subconfig._set_subconfig() subconfig._set_subconfig()
err_msg = _( err_msg = _(
'variable or family "{0}" does not exist so cannot load "{1}"' 'variable or family "{0}" does not exist so cannot load "{1}"'
).format(child_name, path) ).format(child_name, path)
if self.unknown_user_data_error: if self.unknown_user_data_error:
msg = _( msg = _("{0}, it has been loading from {1}")
'{0}, it has been loading from {1}'
)
else: else:
msg = _( msg = _("{0}, it will be ignored when loading from {1}")
'{0}, it will be ignored when loading from {1}'
)
msg = msg.format(err_msg, options["source"]) msg = msg.format(err_msg, options["source"])
if subconfig is not None: if subconfig is not None:
msg = {msg: subconfig._subconfig} msg = {msg: subconfig._subconfig}
@ -391,7 +412,13 @@ class UserData:
msg = _( msg = _(
'"{0}" is the name of a dynamic family, it will be ignored when loading from {1}' '"{0}" is the name of a dynamic family, it will be ignored when loading from {1}'
) )
self.invalids.append({msg.format(option.description(with_quote=True), options["source"]): option._subconfig}) self.invalids.append(
{
msg.format(
option.description(with_quote=True), options["source"]
): option._subconfig
}
)
else: else:
self.invalids.append( self.invalids.append(
_("{0} loaded from {1}").format(err, options["source"]) _("{0} loaded from {1}").format(err, options["source"])
@ -430,7 +457,9 @@ class UserData:
[_(prop) for prop in err.proptype], add_quote=False [_(prop) for prop in err.proptype], add_quote=False
) )
err_path = err.subconfig.path err_path = err.subconfig.path
err_description = err.subconfig.option.impl_get_display_name(err.subconfig, with_quote=True) err_description = err.subconfig.option.impl_get_display_name(
err.subconfig, with_quote=True
)
display_name = option.description(with_quote=True) display_name = option.description(with_quote=True)
if index is not None: if index is not None:
if path == err_path: if path == err_path:
@ -442,13 +471,15 @@ class UserData:
msg = _( msg = _(
'variable {0} at index "{1}" is {2}, it will be ignored when loading from {3}' 'variable {0} at index "{1}" is {2}, it will be ignored when loading from {3}'
) )
self.unknowns.append({ self.unknowns.append(
{
msg.format( msg.format(
display_name, display_name,
index, index,
properties, properties,
options["source"], options["source"],
): option._subconfig} ): option._subconfig
}
) )
else: else:
if self.unknown_user_data_error: if self.unknown_user_data_error:
@ -459,14 +490,16 @@ class UserData:
msg = _( msg = _(
'family {0} is {1}, {2} at index "{3}", it will be ignored when loading from {4}' 'family {0} is {1}, {2} at index "{3}", it will be ignored when loading from {4}'
) )
self.unknowns.append({ self.unknowns.append(
{
msg.format( msg.format(
err_description, err_description,
properties, properties,
display_name, display_name,
index, index,
options["source"], options["source"],
): option._subconfig} ): option._subconfig
}
) )
else: else:
if path == err_path: if path == err_path:
@ -478,13 +511,17 @@ class UserData:
msg = _( msg = _(
"variable has property {0}, it will be ignored when loading from {1}" "variable has property {0}, it will be ignored when loading from {1}"
) )
self.unknowns.append({ self.unknowns.append(
{
msg.format( msg.format(
properties, options["source"] properties, options["source"]
): option._subconfig} ): option._subconfig
}
) )
else: else:
if not options.get("options", {}).get("secret_manager", False): if not options.get("options", {}).get(
"secret_manager", False
):
if self.unknown_user_data_error: if self.unknown_user_data_error:
msg = _( msg = _(
"family {0} has property {1}, so cannot access to {2}, it has been loading from {3}" "family {0} has property {1}, so cannot access to {2}, it has been loading from {3}"
@ -493,57 +530,59 @@ class UserData:
msg = _( msg = _(
"family {0} has property {1}, so cannot access to {2}, it will be ignored when loading from {3}" "family {0} has property {1}, so cannot access to {2}, it will be ignored when loading from {3}"
) )
self.unknowns.append({ self.unknowns.append(
{
msg.format( msg.format(
err_description, err_description,
properties, properties,
display_name, display_name,
options["source"], options["source"],
): option._subconfig} ): option._subconfig
}
) )
else: else:
if self.unknown_user_data_error: if self.unknown_user_data_error:
msg = _( msg = _("{0}, it has been loading from {1}")
"{0}, it has been loading from {1}"
)
else: else:
msg = _( msg = _("{0}, it will be ignored when loading from {1}")
"{0}, it will be ignored when loading from {1}" self.unknowns.append(
) {msg.format(err, options["source"]): option._subconfig}
self.unknowns.append({
msg.format(err, options["source"]): option._subconfig}
) )
except LeadershipError as err: except LeadershipError as err:
if self.unknown_user_data_error: if self.unknown_user_data_error:
msg = _( msg = _("{0}, it has been loading from {1}")
"{0}, it has been loading from {1}"
)
else: else:
msg = _( msg = _("{0}, it will be ignored when loading from {1}")
"{0}, it will be ignored when loading from {1}" self.unknowns.append(
) {msg.format(err, options["source"]): option._subconfig}
self.unknowns.append({
msg.format(err, options["source"]): option._subconfig}
) )
except ConfigError as err: except ConfigError as err:
err.prefix = "" err.prefix = ""
if self.invalid_user_data_error: if self.invalid_user_data_error:
msg = _('{0}, it has been loading from {1}').format(err, options["source"]) msg = _("{0}, it has been loading from {1}").format(
err, options["source"]
)
else: else:
msg = _('{0}, it will be ignored when loading from {1}').format(err, options["source"]) msg = _("{0}, it will be ignored when loading from {1}").format(
err, options["source"]
)
self.invalids.append({msg: option._subconfig}) self.invalids.append({msg: option._subconfig})
except ValueError as err: except ValueError as err:
err.prefix = "" err.prefix = ""
type_ = option.type(translation=True) type_ = option.type(translation=True)
msg = _('the value {0} is an invalid {1}, {2}').format( msg = _("the value {0} is an invalid {1}, {2}").format(
self._display_value(option, value), self._display_value(option, value),
type_, type_,
err, err,
) )
if self.invalid_user_data_error: if self.invalid_user_data_error:
msg += _(', it has been loading from {0}').format(options["source"]) msg += _(", it has been loading from {0}").format(
options["source"]
)
else: else:
msg += _(', it will be ignored when loading from {0}').format(options["source"]) msg += _(", it will be ignored when loading from {0}").format(
options["source"]
)
self.invalids.append({msg: option._subconfig}) self.invalids.append({msg: option._subconfig})
except AttributeOptionError as err: except AttributeOptionError as err:
err.prefix = "" err.prefix = ""
@ -557,10 +596,12 @@ class UserData:
if is_secret_manager and isinstance(value, tuple): if is_secret_manager and isinstance(value, tuple):
# it's a function # it's a function
params = tuple([ParamValue(val) for val in value[1:]]) params = tuple([ParamValue(val) for val in value[1:]])
option.information.set('secret_manager', True) option.information.set("secret_manager", True)
if index is not None: if index is not None:
option = option.forcepermissive.index(index) option = option.forcepermissive.index(index)
value = Calculation(value[0], Params(params, kwargs={"option": ParamValue(option)})) value = Calculation(
value[0], Params(params, kwargs={"option": ParamValue(option)})
)
option = option.forcepermissive option = option.forcepermissive
add_validation = True add_validation = True
else: else:
@ -586,9 +627,7 @@ class UserData:
key = f"loaded_from_{index}" key = f"loaded_from_{index}"
else: else:
key = "loaded_from" key = "loaded_from"
value = _("loaded from {0}").format( value = _("loaded from {0}").format(self.values[path][-1]["source"])
self.values[path][-1]["source"]
)
if options.get("secret_manager"): if options.get("secret_manager"):
# FIXME (true_config ???) # FIXME (true_config ???)
default = option.value.default() default = option.value.default()
@ -658,12 +697,18 @@ def _populate_mandatory(option, errors: list) -> None:
if index is None: if index is None:
msg = _("mandatory variable but has no value") msg = _("mandatory variable but has no value")
else: else:
msg = _('mandatory variable at index "{0}" but has no value').format(index) msg = _('mandatory variable at index "{0}" but has no value').format(
index
)
else: else:
if index is None: if index is None:
msg = _("mandatory variable but is inaccessible and has no value or has null in value") msg = _(
"mandatory variable but is inaccessible and has no value or has null in value"
)
else: else:
msg = _('mandatory variable at index "{0}" but is inaccessible and has no value or has null in value').format(index) msg = _(
'mandatory variable at index "{0}" but is inaccessible and has no value or has null in value'
).format(index)
else: else:
proptype = option.value.mandatory(return_type=True) proptype = option.value.mandatory(return_type=True)
if proptype == "empty": if proptype == "empty":

View file

@ -85,7 +85,7 @@ def get_jinja_variable_to_param(
): ):
try: try:
env = SandboxedEnvironment(loader=DictLoader({"tmpl": jinja_text})) env = SandboxedEnvironment(loader=DictLoader({"tmpl": jinja_text}))
env.add_extension('jinja2.ext.do') env.add_extension("jinja2.ext.do")
env.filters = functions env.filters = functions
parsed_content = Parser(env, jinja_text, "", "").parse() parsed_content = Parser(env, jinja_text, "", "").parse()