Compare commits

..

No commits in common. "1.2.3" and "main" have entirely different histories.
1.2.3 ... main

20 changed files with 62 additions and 211 deletions

View file

@ -1,18 +1,3 @@
## 1.2.3 (2026-09-02)
### Fix
- variable and family object are same has type object
- a type could be a dynamic family
- main_namespace is not mandatory (for example for risotto)
- correction in error message
## 1.2.2 (2026-08-21)
### Fix
- control if option is not just an unknown dyn family or dyn option
## 1.2.1 (2026-08-19) ## 1.2.1 (2026-08-19)
### Fix ### Fix

View file

@ -1,6 +1,6 @@
[project] [project]
name = "rougail" name = "rougail"
version = "1.2.3" version = "1.2.1"
[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.3" version = "1.2.1"
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.3" version = "1.2.1"
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.3", "rougail-base == 1.2.1",
] ]
[tool.flit.sdist] [tool.flit.sdist]

View file

@ -1 +1 @@
__version__ = "1.2.3" __version__ = "1.2.1"

View file

@ -42,11 +42,8 @@ class Rougail(UserData):
load_from_tiramisu_cache load_from_tiramisu_cache
and Path(self.rougailconfig["tiramisu_cache"]).is_file() and Path(self.rougailconfig["tiramisu_cache"]).is_file()
) )
types = rougail_type(self.rougailconfig)
if not self.load_from_tiramisu_cache: if not self.load_from_tiramisu_cache:
if rougailconfig["types"]:
types = rougail_type(self.rougailconfig)
else:
types = {}
self.converted = RougailConvert(self.rougailconfig, **types) self.converted = RougailConvert(self.rougailconfig, **types)
self.config = None self.config = None

View file

@ -37,7 +37,6 @@ from .object_model import (
) )
from ..i18n import _ from ..i18n import _
from ..error import DictConsistencyError from ..error import DictConsistencyError
from ..tiramisu import CONVERT_OPTION
class CollectFamily: class CollectFamily:
@ -54,7 +53,7 @@ class CollectFamily:
elif "variable" in self.parameters: elif "variable" in self.parameters:
self.name += "{{ identifier }}" self.name += "{{ identifier }}"
self.path += "{{ identifier }}" self.path += "{{ identifier }}"
elif self.raises and not self.is_type: elif self.raises:
msg = f'dynamic family name must have "{{{{ identifier }}}}" in his name for "{self.path}"' msg = f'dynamic family name must have "{{{{ identifier }}}}" in his name for "{self.path}"'
raise DictConsistencyError(msg, 13, self.sources) raise DictConsistencyError(msg, 13, self.sources)
if self.version == "1.0": if self.version == "1.0":
@ -198,14 +197,7 @@ class CollectVariable:
) )
new_params = [] new_params = []
namespace = self.objectspace.namespace namespace = self.objectspace.namespace
params_data = list(CONVERT_OPTION.get(self.user_type, {}).get("params", {}))
for key, val in params.items(): for key, val in params.items():
if params_data and key not in params_data:
raise DictConsistencyError(
_('params "{0}" is not a known parameters with type "{1}" for "{2}" (the list of possible parameters is {3})').format(key, self.user_type, self.path, display_list(params_data, add_quote=True)),
92,
self.sources,
)
try: try:
new_params.append( new_params.append(
AnyParam( AnyParam(
@ -501,7 +493,6 @@ class Collect(CollectType, CollectFamily, CollectVariable):
parent_option: Optional["Collect"], parent_option: Optional["Collect"],
*, *,
raises: bool = True, raises: bool = True,
is_type: bool = False,
test_exists: bool = True, test_exists: bool = True,
) -> None: ) -> None:
self.sources_types = None self.sources_types = None
@ -512,7 +503,6 @@ class Collect(CollectType, CollectFamily, CollectVariable):
else: else:
path = f"{subpath}.{name}" path = f"{subpath}.{name}"
self.raises = raises self.raises = raises
self.is_type = is_type
self.test_exists = test_exists self.test_exists = test_exists
if self.raises and name.startswith("_"): if self.raises and name.startswith("_"):
msg = f'the variable or family "{self.path}" is incorrect, it must not starts with "_" character' msg = f'the variable or family "{self.path}" is incorrect, it must not starts with "_" character'

View file

@ -206,63 +206,48 @@ class ParserVariable:
return return
root = Path(__file__).parent.parent root = Path(__file__).parent.parent
self.walker = None self.walker = None
if self.variable_objects is None: self.variable = Variable
self.variable = Variable self.family = Family
self.family = Family for structural_name in self.structurals:
for structural_name in self.structurals: structural = f"structural_{structural_name}"
structural = f"structural_{structural_name}" module_path = root / structural / "__init__.py"
module_path = root / structural / "__init__.py" if not module_path.is_file():
if not module_path.is_file(): continue
continue module = load_modules(f"rougail.{structural}", str(module_path))
module = load_modules(f"rougail.{structural}", str(module_path)) if "Variable" in module.__all__:
if "Variable" in module.__all__: self.variable = type(
self.variable = type( self.variable.__name__ + "_" + structural,
self.variable.__name__ + "_" + structural, (self.variable, module.Variable),
(self.variable, module.Variable), {},
{}, )
) if "Family" in module.__all__:
if "Family" in module.__all__: self.family = type(
self.family = type( self.family.__name__ + "_" + structural,
self.family.__name__ + "_" + structural, (self.family, module.Family),
(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 self.dynamic = type(Dynamic.__name__, (Dynamic, self.family), {})
self.dynamic = type(Dynamic.__name__, (Dynamic, self.family), {}) self.choices = type(Choices.__name__, (Choices, self.variable), {})
self.choices = type(Choices.__name__, (Choices, self.variable), {}) self.regexp = type(Regexp.__name__, (Regexp, self.variable), {})
self.regexp = type(Regexp.__name__, (Regexp, self.variable), {}) variable_types = self.convert_options.copy()
variable_types = self.convert_options.copy() 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.variable_objects = [ self.get_variable_object(obj, is_variable=True)
self.get_variable_object(obj, is_variable=True) for obj in [
for obj in [ (self.variable, variable_types),
(self.variable, variable_types), SymLink,
SymLink, self.choices,
self.choices, self.regexp,
self.regexp,
]
] ]
self.family_objects = [ ]
self.get_variable_object(obj, is_variable=False) self.family_objects = [
for obj in [self.dynamic, self.family] self.get_variable_object(obj, is_variable=False)
] for obj in [self.dynamic, self.family]
else: ]
self.variable = self.variable_objects[0]
self.choices = self.variable_objects[2]
self.regexp = self.variable_objects[3]
self.family = self.family_objects[-1]
self.dynamic = self.family_objects[0]
for structural_name in self.structurals:
structural = f"structural_{structural_name}"
module_path = root / structural / "__init__.py"
if not module_path.is_file():
continue
module = load_modules(f"rougail.{structural}", str(module_path))
if not self.walker and "Walker" in module.__all__:
self.walker = module.Walker
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:
@ -308,7 +293,6 @@ class ParserVariable:
obj, obj,
comment, comment,
parent_option, parent_option,
is_type=self.loaded_custom_types is not None,
) )
if option.option_type == "family": if option.option_type == "family":
parser = self.parse_family parser = self.parse_family
@ -614,16 +598,12 @@ class RougailConvert(ParserVariable):
*, *,
custom_variable_types: dict = {}, custom_variable_types: dict = {},
custom_family_types: dict = {}, custom_family_types: dict = {},
variable_objects = None,
family_objects = None,
) -> None: ) -> 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
self.custom_family_types = custom_family_types self.custom_family_types = custom_family_types
self.loaded_custom_types = None self.loaded_custom_types = None
self.variable_objects = variable_objects
self.family_objects = family_objects
super().__init__(rougailconfig) super().__init__(rougailconfig)
def get_attributes_types( def get_attributes_types(

View file

@ -64,18 +64,15 @@ class Walker:
main_structures: Optional[List[str]] = None, main_structures: Optional[List[str]] = None,
isolated_namespace: bool = True, isolated_namespace: bool = True,
) -> None: ) -> None:
if not main_namespace: directory_dict = chain(
directory_dict = extra_structures.items() (
else:
directory_dict = chain(
( (
( main_namespace,
main_namespace, main_structures,
main_structures,
),
), ),
extra_structures.items(), ),
) extra_structures.items(),
)
for namespace, directories in directory_dict: for namespace, directories in directory_dict:
self.convert.create_namespace(namespace, isolated_namespace) self.convert.create_namespace(namespace, isolated_namespace)
for filename in self.get_sorted_filenames(directories): for filename in self.get_sorted_filenames(directories):

View file

@ -27,7 +27,6 @@ class TypeRougailConvert(StaticRougailConvert):
main_structural_directories: list[str], main_structural_directories: list[str],
secret_pattern: str, secret_pattern: str,
default_structural_format_version: str, default_structural_format_version: str,
structurals: list[str],
) -> None: ) -> None:
super().__init__( super().__init__(
False, False,
@ -39,21 +38,23 @@ class TypeRougailConvert(StaticRougailConvert):
) )
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.structurals = structurals
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"]
def rougail_type(rougailconfig): def rougail_type(rougailconfig):
types = rougailconfig["types"]
if not types:
return {"custom_variable_types": {}, "custom_family_types": {}}
convert = TypeRougailConvert( convert = TypeRougailConvert(
rougailconfig["types"], types,
rougailconfig["secret_manager.pattern"], rougailconfig["secret_manager.pattern"],
rougailconfig["default_structural_format_version"], rougailconfig["default_structural_format_version"],
rougailconfig["step.structural"],
) )
convert.init() convert.init()
convert.parse_directories() convert.parse_directories()
@ -78,8 +79,6 @@ def rougail_type(rougailconfig):
else: else:
custom_family_types[typ] = data custom_family_types[typ] = data
return { return {
"variable_objects": convert.variable_objects,
"family_objects": convert.family_objects,
"custom_variable_types": custom_variable_types, "custom_variable_types": custom_variable_types,
"custom_family_types": custom_family_types, "custom_family_types": custom_family_types,
} }

View file

@ -115,7 +115,7 @@ class UserData:
option.name() option.name()
except (ConfigError, PropertiesOptionError): except (ConfigError, PropertiesOptionError):
pass pass
except (AttributeError, AttributeOptionError): except AttributeError:
self._not_found_is_dynamic(self.config, path, cache, added, data[-1]) self._not_found_is_dynamic(self.config, path, cache, added, data[-1])
def _not_found_is_dynamic(self, config, path, cache, added, data): def _not_found_is_dynamic(self, config, path, cache, added, data):
@ -632,7 +632,7 @@ class UserData:
default = option.value.default() default = option.value.default()
if option.isleader(): if option.isleader():
default = default[0] default = default[0]
value = _('{0} (the key being sought is "{1}")').format(value, default) value = _('{0} (the search key is "{1}")').format(value, default)
option_without_index.information.set( option_without_index.information.set(
key, key,
value, value,

View file

@ -129,14 +129,6 @@ def test_type_dynfamily_namespace():
type_variable("family_dynfamily", namespace=True) type_variable("family_dynfamily", namespace=True)
def test_type_root_dynfamily():
type_variable("family_root_dynfamily")
def test_type_root_dynfamily_namespace():
type_variable("family_root_dynfamily", namespace=True)
def test_type_family_subfamily_add(): def test_type_family_subfamily_add():
type_variable("family_subfamily_add") type_variable("family_subfamily_add")

View file

@ -1,15 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = StrOption(name="a_variable", doc="A variable", multi=True, default=["val1", "val2"], default_multi="val1", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/structures/family_root_dynfamily/00_structure.yml'], 'type': 'string'})
option_3 = StrOption(name="a_first_variable", doc="A first variable", default=Calculation(func['calc_value'], Params((ParamIdentifier()))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_root_dynfamily/00_structure.yml', 'tests/types/structures/family_root_dynfamily/00_structure.yml'], 'type': 'string'})
optiondescription_2 = ConvertDynOptionDescription(name="my_family_{{ identifier }}", doc="My family type", identifiers=Calculation(func['calc_value'], Params((ParamOption(option_1)))), children=[option_3], properties=frozenset({"standard"}), informations={'dynamic_variable': 'a_variable', 'ymlfiles': ['tests/types/types/family_root_dynfamily/00_structure.yml', 'tests/types/structures/family_root_dynfamily/00_structure.yml']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, optiondescription_2])

View file

@ -1,8 +0,0 @@
{
"a_variable": [
"val1",
"val2"
],
"my_family_val1.a_first_variable": "val1",
"my_family_val2.a_first_variable": "val2"
}

View file

@ -1,8 +0,0 @@
{
"a_variable": [
"val1",
"val2"
],
"my_family_val1.a_first_variable": "val1",
"my_family_val2.a_first_variable": "val2"
}

View file

@ -1,16 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="a_variable", doc="A variable", multi=True, default=["val1", "val2"], default_multi="val1", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/structures/family_root_dynfamily/00_structure.yml'], 'type': 'string'})
option_5 = StrOption(name="a_first_variable", doc="A first variable", default=Calculation(func['calc_value'], Params((ParamIdentifier()))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['tests/types/types/family_root_dynfamily/00_structure.yml', 'tests/types/structures/family_root_dynfamily/00_structure.yml'], 'type': 'string'})
optiondescription_4 = ConvertDynOptionDescription(name="my_family_{{ identifier }}", doc="My family type", identifiers=Calculation(func['calc_value'], Params((ParamOption(option_3)))), children=[option_5], properties=frozenset({"standard"}), informations={'dynamic_variable': 'ns2.a_variable', 'ymlfiles': ['tests/types/types/family_root_dynfamily/00_structure.yml', 'tests/types/structures/family_root_dynfamily/00_structure.yml']})
optiondescription_2 = OptionDescription(name="ns2", doc="NS2", group_type=groups.namespace, children=[option_3, optiondescription_4], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_2])

View file

@ -1,8 +0,0 @@
{
"ns2.a_variable": [
"val1",
"val2"
],
"ns2.my_family_val1.a_first_variable": "val1",
"ns2.my_family_val2.a_first_variable": "val2"
}

View file

@ -1,8 +0,0 @@
{
"ns2.a_variable": [
"val1",
"val2"
],
"ns2.my_family_val1.a_first_variable": "val1",
"ns2.my_family_val2.a_first_variable": "val2"
}

View file

@ -1,13 +0,0 @@
%YAML 1.2
---
version: 1.1
a_variable: # A variable
- val1
- val2
my_family_{{ identifier }}:
type: my_family_type
dynamic:
variable: _.a_variable
...

View file

@ -1,13 +0,0 @@
%YAML 1.2
---
version: 1.1
my_family_type:
description: My family type
dynamic: []
a_first_variable:
description: A first variable
default:
type: identifier
...