Compare commits

...

4 commits

32 changed files with 765 additions and 933 deletions

View file

@ -1,618 +1,162 @@
## 1.2.0rc4 (2026-06-21) ## 1.2.0 (2026-06-21)
### Feat ### Feat
- add secret_manager information - add secret_manager information
- tags for family - tags for family
## 1.2.0rc3 (2026-06-18)
### Fix
- dependencies
## 1.2.0rc2 (2026-06-18)
### Fix
- dependencies
## 1.2.0rc1 (2026-06-16)
### Fix
- python 3.13 support
## 1.2.0rc0 (2026-06-16)
### Fix
- we can redefine type of a types child
## 1.2.0a77 (2026-06-15)
### Fix
- for dynamic family, dynamic and variable paramater can be set together
## 1.2.0a76 (2026-06-11)
### Feat
- leadership => sequence - leadership => sequence
### Fix
- update tutorial tests
### Refactor
- **src/rougail/convert/**: split the file
## 1.2.0a75 (2026-05-06)
### Fix
- use mkdtmp for jinja cache
## 1.2.0a74 (2026-05-04)
### Fix
- default params
## 1.2.0a73 (2026-04-30)
### Feat
- add SUPPORTED_VERSION variable - add SUPPORTED_VERSION variable
## 1.2.0a72 (2026-03-26)
### Fix
- None for identifier without value
- add filename where type is used
## 1.2.0a71 (2026-03-18)
### Fix
- auto redefine in type is only for default
## 1.2.0a70 (2026-03-18)
### Feat
- support leadership inside types - support leadership inside types
### Fix
- better support for description and type variable inside types
## 1.2.0a69 (2026-03-13)
### Fix
- do not update name
## 1.2.0a68 (2026-03-09)
### Fix
- copy object before modify namespace
## 1.2.0a67 (2026-03-06)
### Fix
- types inside extra namespace
- better support for identifier calculation
## 1.2.0a66 (2026-03-02)
### Fix
- allow calculation with identifier
## 1.2.0a65 (2026-03-02)
### Fix
- rougail-pyproject.toml
## 1.2.0a64 (2026-03-02)
### Feat
- identifier could be calculated - identifier could be calculated
- check mandatories is now centralized - check mandatories is now centralized
## 1.2.0a63 (2026-02-11)
### Feat
- cannot use tiramisu_cache directly with commandline - cannot use tiramisu_cache directly with commandline
### Fix
- detect if password is set in a forbidden file even if it also in legitimate file
## 1.2.0a62 (2026-02-11)
### Feat
- structural data in a string - structural data in a string
### Fix
- structurals data in extra could be a simple file
- allow define an addition variable in a type
## 1.2.0a61 (2026-02-06)
### Feat
- type with dynamic family - type with dynamic family
## 1.2.0a60 (2026-01-29)
### Feat
- support transitive in properties variable - support transitive in properties variable
## 1.2.0a59 (2026-01-21)
### Fix
- better support for secret manager
- leadership and frozen
## 1.2.0a58 (2026-01-16)
### Fix
- secret with follower
## 1.2.0a57 (2026-01-16)
### Fix
- add transition
## 1.2.0a56 (2026-01-15)
### Fix
- types with subfamily
## 1.2.0a55 (2026-01-15)
### Fix
- better custom types support
## 1.2.0a54 (2026-01-14)
### Fix
- issymlinkoption is not available for an optiondescription
- better error messages
## 1.2.0a53 (2026-01-05)
### Fix
- new tiramisu version support
## 1.2.0a52 (2026-01-03)
### Feat
- load tiramisu objects from cache - load tiramisu objects from cache
## 1.2.0a51 (2025-12-30)
### Feat
- add types support - add types support
### Fix
- better error message
## 1.2.0a50 (2025-12-23)
### Fix
- improvment
## 1.2.0a49 (2025-12-22)
### Fix
- better error message
## 1.2.0a48 (2025-12-22)
### Fix
- documentation
## 1.2.0a47 (2025-12-22)
### Fix
- description
## 1.2.0a46 (2025-12-22)
### Fix
- duplicate description
## 1.2.0a45 (2025-12-22)
### Feat
- better translation - better translation
- add "do" extension for jinja2 - add "do" extension for jinja2
### Fix
- user_datas => user_data
## 1.2.0a44 (2025-11-28)
### Fix
- disabled choices must not generate error when loaded from .rougailcli.yml
## 1.2.0a43 (2025-11-21)
### Feat
- add boolean return_type in validators - add boolean return_type in validators
### Fix
- better rougailconfig copy
- port is a string
## 1.2.0a42 (2025-11-06)
### Feat
- can add limit length for a variable - can add limit length for a variable
### Fix
- black
## 1.2.0a41 (2025-11-06)
### Feat
- choice if invalid value or unknown variable in user data is a fatal error or not - choice if invalid value or unknown variable in user data is a fatal error or not
## 1.2.0a40 (2025-11-03)
### Feat
- can add name for a tiramisu config - can add name for a tiramisu config
- active warnings for validators - active warnings for validators
## 1.2.0a39 (2025-10-29)
### Feat
- add tags support - add tags support
### Fix
- load config leadership
- do not load secrets if not allowed
- update translation
- add test
## 1.2.0a38 (2025-10-22)
## 1.2.0a37 (2025-10-16)
### Feat
- keep forced_descriptions information (mostly for rougail-output-doc) - keep forced_descriptions information (mostly for rougail-output-doc)
### Fix
- name is uncalculated
## 1.2.0a36 (2025-10-10)
### Feat
- can desactivate isolated namespace feature - can desactivate isolated namespace feature
- add return_type to property - add return_type to property
- remove return_values_not_error and only_default parameter - remove return_values_not_error and only_default parameter
- user_data can load secret manager values - user_data can load secret manager values
### Fix
- translation for property is now in rougail
- error in InformationCalculation errors
- proprerty with unknown variable
## 1.2.0a35 (2025-10-02)
### Feat
- add warning class - add warning class
## 1.2.0a34 (2025-09-30)
### Feat
- for formatter - for formatter
## 1.2.0a33 (2025-09-29)
### Fix
- better doc for calculation with unknown variable
## 1.2.0a32 (2025-09-29)
### Feat
- **#28**: default value for a calculated variable with an unknown optional variable - **#28**: default value for a calculated variable with an unknown optional variable
- **#26**: cidr and network_cidr type is depreciate - **#26**: cidr and network_cidr type is depreciate
- **#25**: add integer type which will replace number type - **#25**: add integer type which will replace number type
## 1.2.0a31 (2025-09-22)
### Fix
- dictionary => structure
## 1.2.0a30 (2025-09-20)
### Feat
- **40**: better conflict error message with dynamic name - **40**: better conflict error message with dynamic name
- add new get_root_option function - add new get_root_option function
### Fix
- update test
## 1.2.0a29 (2025-06-20)
### Fix
- UserDatas, do now set modified option in second round
## 1.2.0a28 (2025-06-18)
### Fix
- conversion
## 1.2.0a27 (2025-06-18)
### Feat
- separate rougail and rougail-base - separate rougail and rougail-base
## 1.2.0a26 (2025-05-26)
### Fix
- user_data better support for follower variable
## 1.2.0a25 (2025-05-14)
### Feat
- can launch UserDatas twice - can launch UserDatas twice
- add ymlfile names in ConfigError message
## 1.2.0a24 (2025-05-12) - reoganise param conversion + better variable validation
- can link regexp variable
- can link choice variable
- add rougail secret_manager
- can change defaut params for an option
- upgrade is not in formatter
- remove prefix
- move test to a new project rougail-tests
- output could have annotator
- add "exists" attribut for a family
- add force_optional option to allow charging structure even if all variables are not available
### Fix ### Fix
- black
- do not copy help and tags from types
- dependencies
- dependencies
- python 3.13 support
- we can redefine type of a types child
- for dynamic family, dynamic and variable paramater can be set together
- update tutorial tests
- use mkdtmp for jinja cache
- default params
- None for identifier without value
- add filename where type is used
- auto redefine in type is only for default
- better support for description and type variable inside types
- do not update name
- copy object before modify namespace
- types inside extra namespace
- better support for identifier calculation
- allow calculation with identifier
- rougail-pyproject.toml
- detect if password is set in a forbidden file even if it also in legitimate file
- structurals data in extra could be a simple file
- allow define an addition variable in a type
- better support for secret manager
- leadership and frozen
- secret with follower
- add transition
- types with subfamily
- better custom types support
- issymlinkoption is not available for an optiondescription
- better error messages
- new tiramisu version support
- better error message
- improvment
- better error message
- documentation
- description
- duplicate description
- user_datas => user_data
- disabled choices must not generate error when loaded from .rougailcli.yml
- better rougailconfig copy
- port is a string
- black
- load config leadership
- do not load secrets if not allowed
- update translation
- add test
- name is uncalculated
- translation for property is now in rougail
- error in InformationCalculation errors
- proprerty with unknown variable
- better doc for calculation with unknown variable
- dictionary => structure
- update test
- UserDatas, do now set modified option in second round
- conversion
- user_data better support for follower variable
- upgrade translation - upgrade translation
- black - black
## 1.2.0a23 (2025-05-09)
### Fix
- add_quotes - add_quotes
- tiramisu_display_name could display only description - tiramisu_display_name could display only description
- use own undefined - use own undefined
- support of default_dictionary_format_version file in tests - support of default_dictionary_format_version file in tests
- simplify version support - simplify version support
## 1.2.0a22 (2025-05-05)
### Fix
- user_datas support empty directory - user_datas support empty directory
## 1.2.0a21 (2025-05-02)
### Fix
- support {{ suffix }} name in 1.1 format version - support {{ suffix }} name in 1.1 format version
- do not force use_data usage - do not force use_data usage
- validators for an index - validators for an index
## 1.2.0a20 (2025-04-30)
### Fix
- remove symlink - remove symlink
## 1.2.0a19 (2025-04-30)
### Feat
- add ymlfile names in ConfigError message
### Fix
- update translation - update translation
- remove negative_description support - remove negative_description support
- redefine family in flatten mode - redefine family in flatten mode
- update tests - update tests
- better multi check - better multi check
## 1.2.0a18 (2025-04-09)
### Fix
- version - version
## 1.2.0a17 (2025-04-09)
### Fix
- better detection of multi variable in default attribute - better detection of multi variable in default attribute
- better error message - better error message
## 1.2.0a16 (2025-04-03)
### Feat
- reoganise param conversion + better variable validation
### Fix
- correction in namespace calculation - correction in namespace calculation
## 1.2.0a15 (2025-04-01)
### Feat
- can link regexp variable
- can link choice variable
### Fix
- update tests - update tests
- do not raise variable in property with force_optional - do not raise variable in property with force_optional
- update translation - update translation
## 1.2.0a14 (2025-03-30)
### Fix
- strutural step should not be available in commandline - strutural step should not be available in commandline
## 1.2.0a13 (2025-03-27)
### Fix
- allow no user_datas installation (for example to generate doc) - allow no user_datas installation (for example to generate doc)
## 1.2.0a12 (2025-03-19)
### Feat
- add rougail secret_manager
## 1.2.0a11 (2025-02-17)
### Fix
- add get remove properties - add get remove properties
## 1.2.0a10 (2025-02-17)
### Fix
- we can define structural plugin when generate documentation - we can define structural plugin when generate documentation
- if a variable in user_data not existe, it's no a warnings - if a variable in user_data not existe, it's no a warnings
## 1.2.0a9 (2025-02-10)
### Feat
- can change defaut params for an option
### Fix
- if no description, generate negative_description too - if no description, generate negative_description too
- error messages - error messages
## 1.2.0a8 (2025-01-04)
### Fix
- better support of not_for_commandline feature - better support of not_for_commandline feature
## 1.2.0a7 (2025-01-02)
### Fix
- add structural_directory - add structural_directory
## 1.2.0a6 (2025-01-02)
### Fix
- add path.py - add path.py
## 1.2.0a5 (2025-01-02)
### Feat
- upgrade is not in formatter
- remove prefix
## 1.2.0a4 (2024-12-11)
### Feat
- move test to a new project rougail-tests
- output could have annotator
### Fix
- remove link - remove link
- reorganise user_datas - reorganise user_datas
- only change prefix if path is relative - only change prefix if path is relative
## 1.2.0a3 (2024-11-28)
### Fix
- add user_datas file - add user_datas file
## 1.2.0a2 (2024-11-27)
### Feat
- add "exists" attribut for a family
### Fix
- separate UserDatas - separate UserDatas
- options could be a list - options could be a list
## 1.2.0a1 (2024-11-25)
### Fix
- dynamic variable could be optional - dynamic variable could be optional
- dynamic variable declare in verion 1.0 has {{ suffix }} - dynamic variable declare in verion 1.0 has {{ suffix }}
- user_data plugins could have annotator function - user_data plugins could have annotator function
- do not modify a dynamic variable if has default value - do not modify a dynamic variable if has default value
## 1.2.0a0 (2024-11-08) ### Refactor
### Feat - **src/rougail/convert/**: split the file
- add force_optional option to allow charging structure even if all variables are not available
## 1.1.1 (2024-11-06) ## 1.1.1 (2024-11-06)

View file

@ -1,16 +1,12 @@
rougail==1.2.0rc3 rougail-output-ansible==1.0.0
rougail-cli==1.0.0rc0 rougail-output-display==1.0.0
rougail-output-ansible==1.0.0rc0 rougail-output-doc==1.0.1
rougail-output-display==1.0.0rc0 rougail-output-formatter==1.0.0
rougail-output-doc==1.0.0rc0 rougail-output-json==1.0.0
rougail-output-formatter==1.0.0rc0 rougail-output-table==0.1.0
rougail-output-json==1.0.0rc0 rougail-user-data-ansible==1.0.0
rougail-output-table==0.1.0rc0 rougail-user-data-bitwarden==1.0.0
rougail-user-data-ansible==1.0.0rc0 rougail-user-data-commandline==1.0.0
rougail-user-data-bitwarden==1.0.0rc1 rougail-user-data-environment==1.0.0
rougail-user-data-commandline==1.0.0rc2 rougail-user-data-questionary==1.0.0
rougail-user-data-environment==1.0.0rc0 rougail-user-data-yaml==1.0.0
rougail-user-data-questionary==1.0.0rc0
rougail-user-data-yaml==1.0.0rc1
tiramisu==5.2.0rc0
tiramisu-cmdline-parser==1.0.0rc0

View file

@ -1,6 +1,6 @@
[project] [project]
name = "rougail" name = "rougail"
version = "1.2.0rc4" version = "1.2.0"
[tool.commitizen] [tool.commitizen]
name = "cz_conventional_commits" name = "cz_conventional_commits"

View file

@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
[project] [project]
name = "rougail-base" name = "rougail-base"
version = "1.2.0rc4" version = "1.2.0"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}] authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md" readme = "README.md"
description = "A consistency handling system that was initially designed in the configuration management" description = "A consistency handling system that was initially designed in the configuration management"

View file

@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
[project] [project]
name = "rougail" name = "rougail"
version = "1.2.0rc4" version = "1.2.0"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}] authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md" readme = "README.md"
description = "A consistency handling system that was initially designed in the configuration management" description = "A consistency handling system that was initially designed in the configuration management"
@ -27,7 +27,7 @@ classifiers = [
dependencies = [ dependencies = [
"ruamel.yaml ~= 0.19.1", # same version as rougail-user-data-yaml "ruamel.yaml ~= 0.19.1", # same version as rougail-user-data-yaml
"pydantic ~= 2.13.4", "pydantic ~= 2.13.4",
"rougail-base == 1.2.0rc4", "rougail-base == 1.2.0",
] ]
[tool.flit.sdist] [tool.flit.sdist]

View file

@ -1 +1 @@
__version__ = "1.2.0rc4" __version__ = "1.2.0"

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,10 +432,11 @@ secret_manager: # {_("The secret manager")}
"user data": [], "user data": [],
"output": [], "output": [],
} }
processes_tr = {"structural": _("structural"), processes_tr = {
"user data": _("user data"), "structural": _("structural"),
"output": _("output"), "user data": _("user data"),
} "output": _("output"),
}
processes_empty = [] processes_empty = []
for module in get_sub_modules().values(): for module in get_sub_modules().values():
data = module.get_rougail_config(backward_compatibility=backward_compatibility) data = module.get_rougail_config(backward_compatibility=backward_compatibility)
@ -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,10 +17,10 @@ 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
from copy import deepcopy
from typing import ( from typing import (
Any, Any,
Iterator, Iterator,
@ -105,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:
@ -128,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:
@ -228,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
@ -251,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)
@ -260,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:
@ -282,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)
@ -294,11 +316,20 @@ class CollectType:
self.types.name = self.name self.types.name = self.name
self.types.path = self.path self.types.path = self.path
self.types.namespace = self.namespace self.types.namespace = self.namespace
self.types.help = None
self.types.tags = []
if not from_parent:
self.types.parameters["help"] = None
self.types.parameters["tags"] = []
self.option_type = self.types.option_type self.option_type = self.types.option_type
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):
@ -319,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
@ -329,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)
@ -378,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
@ -395,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):
@ -420,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
@ -436,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
@ -460,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:
@ -505,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
@ -532,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):
@ -599,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}"'
) )
@ -620,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"]
@ -630,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:
@ -682,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,11 +257,12 @@ 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 {
"types": types, "object": obj,
"attrs": self.get_option_attrs(hint), "types": types,
"attributes_types": self.get_attributes_types(hint, variable=is_variable), "attrs": self.get_option_attrs(hint),
} "attributes_types": self.get_attributes_types(hint, variable=is_variable),
}
def get_types(self, hint): def get_types(self, hint):
return hint["type"].__args__ return hint["type"].__args__
@ -298,12 +315,13 @@ 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(
custom_type=True, types,
) custom_type=True,
)
if path not in self.paths: if path not in self.paths:
if not self.load_unexist_redefine and exists is None and redefine: if not self.load_unexist_redefine and exists is None and redefine:
raise Exception( raise Exception(
@ -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
@ -329,7 +345,7 @@ class ParserVariable:
option, option,
self.paths[path].model_copy(update=objects), self.paths[path].model_copy(update=objects),
force=True, force=True,
) )
force_not_first = types == None force_not_first = types == None
children = option.children children = option.children
for idx, key in enumerate(self.list_children(option, types)): for idx, key in enumerate(self.list_children(option, types)):
@ -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(
**data, name=option.name,
), **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,10 +467,11 @@ 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(
first_variable=first_variable, option.types,
custom_type=True, first_variable=first_variable,
) custom_type=True,
)
if path not in self.paths: if path not in self.paths:
if not self.load_unexist_redefine and exists is True: if not self.load_unexist_redefine and exists is True:
# this variable must exist # this variable must exist
@ -461,19 +481,18 @@ 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(
first_variable, option,
custom_type, first_variable,
) custom_type,
)
else: else:
if not self.load_unexist_redefine: if not self.load_unexist_redefine:
if exists is False: if exists is False:
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,9 +518,10 @@ 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"](
**data, name=option.name,
) **data,
)
except ValidationError as err: except ValidationError as err:
raise Exception( raise Exception(
f'invalid variable "{path}" in "{display_list(option.sources)}": {err}' f'invalid variable "{path}" in "{display_list(option.sources)}": {err}'
@ -572,12 +592,13 @@ class ParserVariable:
class RougailConvert(ParserVariable): class RougailConvert(ParserVariable):
"""Main Rougail conversion""" """Main Rougail conversion"""
def __init__(self, def __init__(
rougailconfig, self,
*, rougailconfig,
custom_variable_types: dict={}, *,
custom_family_types: dict={}, custom_variable_types: dict = {},
) -> None: custom_family_types: dict = {},
) -> None:
self.annotator = False self.annotator = False
self.has_namespace = False self.has_namespace = False
self.custom_variable_types = custom_variable_types self.custom_variable_types = custom_variable_types
@ -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 = {
"booleans": [], "strings": [],
"integers": [], "booleans": [],
"floats": [], "integers": [],
"calculation": []} "floats": [],
"calculation": [],
}
if variable: if variable:
lists["calculation"].append("identifier") lists["calculation"].append("identifier")
calculation = ["identifier"] calculation = ["identifier"]
@ -647,18 +670,21 @@ 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 {
"integers": integers, "strings": strings,
"booleans": booleans, "integers": integers,
"floats": floats, "booleans": booleans,
"literals": literals, "floats": floats,
"calculation": calculation, "literals": literals,
"lists": lists, "calculation": calculation,
"params": params, "lists": lists,
} "params": params,
}
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,90 +87,139 @@ 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": {
"allow_startswith_dot": {"description": _("the domain name can starts by a dot")}, "description": _("type of domainname"),
"allow_without_dot": {"description": _("the domain name can be a hostname")}, "choices": ("domainname", "netbios", "hostname"),
"allow_ip": {"description": _("the domain name can be an IP")}, "doc": _("type {0}"),
"allow_cidr_network": {"description": _("the domain name can be network in CIDR format")}, },
"test_existence": {"description": _("the domain name must exist")}, "allow_startswith_dot": {"description": _("the domain name can starts by a dot")},
} "allow_without_dot": {"description": _("the domain name can be a hostname")},
"allow_ip": {"description": _("the domain name can be an IP")},
"allow_cidr_network": {
"description": _("the domain name can be network in CIDR format")
},
"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(
func=int, opttype="IntOption",
params={ func=int,
"min_number": {"description": _("the minimum value"), 'doc': _("the minimum value is {0}")}, params={
"max_number": {"description": _("the maximum value"), 'doc': _("the maximum value is {0}")}, "min_number": {
}, "description": _("the minimum value"),
example=42), "doc": _("the minimum value is {0}"),
"integer": dict(opttype="IntOption", },
params={ "max_number": {
"min_integer": {"description": _("the minimum value"), 'doc': _("the minimum value is {0}")}, "description": _("the maximum value"),
"max_integer": {"description": _("the maximum value"), 'doc': _("the maximum value is {0}")}, "doc": _("the maximum value is {0}"),
}, },
func=int, },
example=42, example=42,
), ),
"integer": dict(
opttype="IntOption",
params={
"min_integer": {
"description": _("the minimum value"),
"doc": _("the minimum value is {0}"),
},
"max_integer": {
"description": _("the maximum value"),
"doc": _("the maximum value is {0}"),
},
},
func=int,
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(
params={ opttype="PasswordOption",
"min_len": {"description": _("minimum characters length for the secret"), "doc": _("minimum length for the secret is {0} characters")}, params={
"max_len": {"description": _("maximum characters length for the secret"), "doc": _("maximum length for the secret is {0} characters")}, "min_len": {
"forbidden_char": {"description": _("forbidden characters"), "doc": _("forbidden characters: {0}")}, "description": _("minimum characters length for the secret"),
}, "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(
msg="UNIX filename", opttype="FilenameOption",
params={ msg="UNIX filename",
"allow_relative": {"description": _("this filename could be a relative path")}, params={
"test_existence": {"description": _("this file must exist")}, "allow_relative": {
"types": {"description": _("file type allowed"), "doc": _("file type allowed: {0}"), "choices": ("file", "directory"), "multi": True}, "description": _("this filename could be a relative path")
}, },
example="/tmp/myfile.txt"), "test_existence": {"description": _("this file must exist")},
"types": {
"description": _("file type allowed"),
"doc": _("file type allowed: {0}"),
"choices": ("file", "directory"),
"multi": True,
},
},
example="/tmp/myfile.txt",
),
"date": dict(opttype="DateOption", example="2000-01-01"), "date": dict(opttype="DateOption", example="2000-01-01"),
"unix_user": dict(opttype="UsernameOption", example="username", "unix_user": dict(opttype="UsernameOption", example="username", msg="UNIX user"),
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},
params=_ip_params,
example="1.1.1.0/24",
), ),
"cidr": dict(opttype="IPOption", msg="CIDR", initkwargs={"cidr": True},
params=_ip_params,
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",
params=_network_params, initkwargs={"cidr": True},
example="1.1.1.0/24",
params=_network_params,
msg="network CIDR", msg="network CIDR",
), ),
"broadcast": dict(opttype="BroadcastOption", example="1.1.1.255"), "broadcast": dict(opttype="BroadcastOption", example="1.1.1.255"),
@ -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 = {
"leadership": "sequence", "number": "integer",
} "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,12 +514,14 @@ 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 = _(
__internal_attribute, 'the attribute "{0}" in {1} with the parameters "{2}" causes the error: {3}'
display_xmlfiles(__internal_files), ).format(
kw_str, __internal_attribute,
err, display_xmlfiles(__internal_files),
) kw_str,
err,
)
raise ConfigError(msg, prefix=prefix) from err raise ConfigError(msg, prefix=prefix) from err
convert = CONVERT_OPTION[__internal_type].get("func", str) convert = CONVERT_OPTION[__internal_type].get("func", str)
if __internal_multi: if __internal_multi:
@ -468,10 +538,10 @@ def jinja_to_function(
msg = _('"{0}" is an invalid {1}').format(values, __internal_type) msg = _('"{0}" is an invalid {1}').format(values, __internal_type)
if __internal_attribute != "default": if __internal_attribute != "default":
msg = _('the attribute "{0}" in {1} causes the error: {2}').format( msg = _('the attribute "{0}" in {1} causes the error: {2}').format(
__internal_attribute, __internal_attribute,
display_xmlfiles(__internal_files), display_xmlfiles(__internal_files),
msg, msg,
) )
raise ConfigError(msg, prefix=prefix) from err raise ConfigError(msg, prefix=prefix) from err
values = values if values != "" and values != "None" else None values = values if values != "" and values != "None" else None
if values is None and __default_value is not None: if values is None and __default_value is not None:

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__(
"main_namespace": None, False,
"main_structural_directories": main_structural_directories, {
}) "sort_structural_files_all": True,
"main_namespace": None,
"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,10 +51,11 @@ 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(
rougailconfig["secret_manager.pattern"], types,
rougailconfig["default_structural_format_version"], rougailconfig["secret_manager.pattern"],
) rougailconfig["default_structural_format_version"],
)
convert.init() convert.init()
convert.parse_directories() convert.parse_directories()
loaded_custom_types_keys = list(convert.loaded_custom_types) loaded_custom_types_keys = list(convert.loaded_custom_types)
@ -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_family_types": custom_family_types, "custom_variable_types": custom_variable_types,
} "custom_family_types": custom_family_types,
}

View file

@ -82,11 +82,12 @@ class UserData:
source = datas["source"] source = datas["source"]
for name, data in datas.get("values", {}).items(): for name, data in datas.get("values", {}).items():
self.values.setdefault(name, []).append( self.values.setdefault(name, []).append(
{ {
"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}' _(
).format(values["source"]): option._subconfig} "the variable contains secrets and should not be defined in {0}"
).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(
self._display_value(option, value), {
options["source"], msg.format(
): option._subconfig} self._display_value(option, value),
options["source"],
): 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}
@ -385,13 +406,19 @@ class UserData:
elif err.code == "option-dynamic": elif err.code == "option-dynamic":
if self.invalid_user_data_error: if self.invalid_user_data_error:
msg = _( msg = _(
'"{0}" is the name of a dynamic family, it has been loading from {1}' '"{0}" is the name of a dynamic family, it has been loading from {1}'
) )
else: else:
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( {
display_name, msg.format(
index, display_name,
properties, index,
options["source"], properties,
): option._subconfig} options["source"],
): 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( {
err_description, msg.format(
properties, err_description,
display_name, properties,
index, display_name,
options["source"], index,
): option._subconfig} options["source"],
): 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( {
properties, options["source"] msg.format(
): option._subconfig} properties, options["source"]
): 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( {
err_description, msg.format(
properties, err_description,
display_name, properties,
options["source"], display_name,
): option._subconfig} options["source"],
): 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()

View file

@ -9,5 +9,5 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = StrOption(name="my_var", doc="My type", default="a value", properties=frozenset({"mandatory", "one_tag", "standard"}), informations={'ymlfiles': ['tests/types/types/error_variable_redefine/00_type.yml', 'tests/types/structures/error_variable_redefine/00_structure.yml'], 'type': 'string', 'tags': ('one_tag',)}) option_1 = StrOption(name="my_var", doc="My type", default="a value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/error_variable_redefine/00_type.yml', 'tests/types/structures/error_variable_redefine/00_structure.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

View file

@ -10,9 +10,9 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_2 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'}) option_2 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'})
option_3 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'}) option_3 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'})
optiondescription_1 = OptionDescription(name="secret1", doc="Family with secret", children=[option_2, option_3], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']}) optiondescription_1 = OptionDescription(name="secret1", doc="Family with secret", children=[option_2, option_3], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']})
option_5 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'}) option_5 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'})
option_6 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'}) option_6 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'})
optiondescription_4 = OptionDescription(name="secret2", doc="Family with secret", children=[option_5, option_6], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']}) optiondescription_4 = OptionDescription(name="secret2", doc="Family with secret", children=[option_5, option_6], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -10,10 +10,10 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_4 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'}) option_4 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'})
option_5 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'}) option_5 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'})
optiondescription_3 = OptionDescription(name="secret1", doc="Family with secret", children=[option_4, option_5], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']}) optiondescription_3 = OptionDescription(name="secret1", doc="Family with secret", children=[option_4, option_5], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']})
option_7 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'}) option_7 = StrOption(name="a_variable", doc="A variable", default="my_value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'string'})
option_8 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'}) option_8 = PasswordOption(name="secret", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml'], 'type': 'secret'})
optiondescription_6 = OptionDescription(name="secret2", doc="Family with secret", children=[option_7, option_8], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']}) optiondescription_6 = OptionDescription(name="secret2", doc="Family with secret", children=[option_7, option_8], properties=frozenset({"basic"}), informations={'ymlfiles': ['tests/types/types/family_secret_manager/00-base.yml', 'tests/types/structures/family_secret_manager/00-base.yml']})
optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[optiondescription_3, optiondescription_6], properties=frozenset({"basic"})) optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[optiondescription_3, optiondescription_6], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2])

View file

@ -9,6 +9,6 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = PasswordOption(name="secret1", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'}) option_1 = PasswordOption(name="secret1", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'})
option_2 = PasswordOption(name="secret2", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'}) option_2 = PasswordOption(name="secret2", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -9,7 +9,7 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = PasswordOption(name="secret1", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'}) option_3 = PasswordOption(name="secret1", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'})
option_4 = PasswordOption(name="secret2", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'}) option_4 = PasswordOption(name="secret2", doc="the secret", properties=frozenset({"basic", "mandatory", "novalidator"}), informations={'secret_manager': True, 'ymlfiles': ['tests/types/types/secret_manager/00-base.yml', 'tests/types/structures/secret_manager/00-base.yml'], 'type': 'secret'})
optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[option_3, option_4], properties=frozenset({"basic"})) optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[option_3, option_4], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2])

View file

@ -9,5 +9,5 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = StrOption(name="my_var", doc="My type", default="a value", properties=frozenset({"mandatory", "one_tag", "standard"}), informations={'ymlfiles': ['tests/types/types/variable/00_type.yml', 'tests/types/structures/variable/00_structure.yml'], 'type': 'string', 'tags': ('one_tag',)}) option_1 = StrOption(name="my_var", doc="My type", default="a value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variable/00_type.yml', 'tests/types/structures/variable/00_structure.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

View file

@ -9,6 +9,6 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="my_var", doc="My type", default="a value", properties=frozenset({"mandatory", "one_tag", "standard"}), informations={'ymlfiles': ['tests/types/types/variable/00_type.yml', 'tests/types/structures/variable/00_structure.yml'], 'type': 'string', 'tags': ('one_tag',)}) option_3 = StrOption(name="my_var", doc="My type", default="a value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variable/00_type.yml', 'tests/types/structures/variable/00_structure.yml'], 'type': 'string'})
optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[option_3], properties=frozenset({"standard"})) optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[option_3], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2])

View file

@ -10,6 +10,6 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = StrOption(name="my_var1", doc="My type", default="a value", properties=frozenset({"an_other_tag", "mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string', 'tags': ('an_other_tag',)}) option_1 = StrOption(name="my_var1", doc="My type", default="a value", properties=frozenset({"an_other_tag", "mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string', 'tags': ('an_other_tag',)})
option_2 = StrOption(name="my_var2", doc="My type", default="a value", properties=frozenset({"mandatory", "one_tag", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string', 'tags': ('one_tag',)}) option_2 = StrOption(name="my_var2", doc="My type", default="a value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string'})
option_3 = StrOption(name="my_var3", doc="my_var3", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['tests/types/structures/variables/00_structure.yml'], 'type': 'string'}) option_3 = StrOption(name="my_var3", doc="my_var3", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['tests/types/structures/variables/00_structure.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2, option_3]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2, option_3])

View file

@ -10,7 +10,7 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="my_var1", doc="My type", default="a value", properties=frozenset({"an_other_tag", "mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string', 'tags': ('an_other_tag',)}) option_3 = StrOption(name="my_var1", doc="My type", default="a value", properties=frozenset({"an_other_tag", "mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string', 'tags': ('an_other_tag',)})
option_4 = StrOption(name="my_var2", doc="My type", default="a value", properties=frozenset({"mandatory", "one_tag", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string', 'tags': ('one_tag',)}) option_4 = StrOption(name="my_var2", doc="My type", default="a value", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/variables/00_type.yml', 'tests/types/structures/variables/00_structure.yml'], 'type': 'string'})
option_5 = StrOption(name="my_var3", doc="my_var3", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['tests/types/structures/variables/00_structure.yml'], 'type': 'string'}) option_5 = StrOption(name="my_var3", doc="my_var3", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['tests/types/structures/variables/00_structure.yml'], 'type': 'string'})
optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[option_3, option_4, option_5], properties=frozenset({"basic"})) optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[option_3, option_4, option_5], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2])