fix: better path detection

This commit is contained in:
egarette@silique.fr 2026-04-30 21:49:10 +02:00
parent 5251554b50
commit a1ecc76e25
25 changed files with 328 additions and 36 deletions

View file

@ -1,6 +1,6 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2024-2025
Copyright (C) 2024-2026
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the
@ -119,7 +119,6 @@ class RougailOutputFormatter:
_('the "step.output" is not set to "{0}"').format(self.output_name)
)
# yaml.top_level_colon_align = True
self.main_namespace = normalize_family(self.rougailconfig["main_namespace"])
self.has_default_structural_format_version = (
self.rougailconfig["default_structural_format_version"] is not None
)
@ -135,7 +134,7 @@ class RougailOutputFormatter:
def run(self):
self.upgrade()
self.families = {self.main_namespace: CommentedMap()}
self.families = {self.rougail.namespace: CommentedMap()}
self.parse()
self.yaml.indent(mapping=2, sequence=4, offset=2)
self.yaml.version = "1.2"
@ -143,7 +142,7 @@ class RougailOutputFormatter:
self.yaml.explicit_end = True
self.default_flow_style = False
with BytesIO() as ymlfh:
families = self.families[self.main_namespace]
families = self.families[self.rougail.namespace]
if not families:
self.yaml.dump("", ymlfh)
else:
@ -162,7 +161,14 @@ class RougailOutputFormatter:
return self.attributes[type_name]
def upgrade(self) -> None:
filenames = self.rougailconfig["main_structural_directories"]
namespace = self.rougailconfig["main_namespace"]
if namespace and self.rougailconfig["extra_namespaces.names"]:
namespace = self.rougailconfig["extra_namespaces.names"][0]
filenames = self.rougailconfig["extra_namespaces"]["directories"][0]
extra = True
else:
filenames = self.rougailconfig["main_structural_directories"]
extra = False
if len(filenames) > 1:
raise ExtensionError(_('only one filename is allowed, not "{0}"').format(filenames))
filename = Path(filenames[0])
@ -177,11 +183,11 @@ class RougailOutputFormatter:
self.rougail.load_config()
self.rougail.init()
self.filename_str = str(filename)
if self.main_namespace is None:
if namespace is None:
self.rougail.namespace = None
else:
self.rougail.namespace = normalize_family(self.main_namespace)
self.rougail.create_namespace(self.main_namespace)
self.rougail.namespace = normalize_family(namespace)
self.rougail.create_namespace(namespace)
self.rougail.validate_file_version(
datas,
self.filename_str,
@ -199,26 +205,25 @@ class RougailOutputFormatter:
return ret
def parse(self):
self.families[self.main_namespace][self.version_name] = float(
self.families[self.rougail.namespace][self.version_name] = float(
self.rougail.version
)
self.remaining = len(self.rougail.paths._data)
for path, obj in self.rougail.paths._data.items():
self.remaining -= 1
if path == self.rougail.namespace:
# self.families[path] = self.families[None]
continue
if isinstance(obj, Family):
self.parse_family(path, obj)
elif isinstance(obj, Variable):
self.parse_variable(path, obj)
if list(self.families[self.main_namespace]) != [self.version_name]:
if list(self.families[self.rougail.namespace]) != [self.version_name]:
# just to add an empty line space after "version"
self.families[self.main_namespace].yaml_value_comment_extend(
self.families[self.rougail.namespace].yaml_value_comment_extend(
self.version_name, [CommentToken("\n\n", CommentMark(0)), None]
)
if self.has_default_structural_format_version:
del self.families[self.main_namespace][self.version_name]
del self.families[self.rougail.namespace][self.version_name]
def parse_family(self, path, obj):
children = [p.rsplit(".", 1)[-1] for p in self.rougail.parents[path]]
@ -355,6 +360,8 @@ class RougailOutputFormatter:
if attr not in force_keys and value == default_value:
continue
value = self.object_to_yaml(attr, type_, value, multi, path)
if isinstance(value, dict) and "identifier" in value:
value["identifier"] = self.object_to_yaml("identifier", type_, value["identifier"], True, path)
variable[attr] = value
if variable.get("mandatory") is True and None not in variable.get(
"choices", []
@ -605,7 +612,7 @@ class RougailOutputFormatter:
def calc_variable_path(self, object_path, variable_path):
if not variable_path.startswith("_"):
common_path = get_common_path(object_path, variable_path)
if not self.rougail.namespace or common_path:
if not self.rougail.namespace:
if not common_path:
len_common_path = 0
else:
@ -617,6 +624,15 @@ class RougailOutputFormatter:
+ "."
+ variable_path[len_common_path:]
)
if common_path:
len_common_path = len(common_path) + 1
relative_object_path = object_path[len_common_path:]
final_path = "_" * (relative_object_path.count(".") + 1) + "."
return (
"_" * (relative_object_path.count(".") + 1)
+ "."
+ variable_path[len_common_path:]
)
return variable_path
def get_parent_name(self, path):
@ -636,7 +652,7 @@ class RougailOutputFormatter:
name = search_name
return _yaml(y[name])
if self.main_namespace:
if self.rougail.namespace:
subpath = path.split(".")[1:]
else:
subpath = path.split(".")

View file

@ -1,6 +1,6 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2024-2025
Copyright (C) 2024-2026
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the

View file

@ -1,6 +1,6 @@
"""Internationalisation utilities
Silique (https://www.silique.fr)
Copyright (C) 2025
Copyright (C) 2025-2026
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the

View file

@ -4,7 +4,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2025
Copyright (C) 2022-2026
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the

View file

@ -7,6 +7,6 @@ var1: # a first variable
var2:
description: a second variable
default:
variable: _.var1
description: value of a variable!
variable: _.var1
...

View file

@ -7,12 +7,12 @@ var1: # a first variable
var2:
description: a second variable
default:
variable: _.var1
description: |-
value
of
a
variable!
variable: _.var1
var3: # a new variable
...

View file

@ -10,7 +10,7 @@ var1:
var2:
description: an IP in CIDR format
examples:
- 192.168.0.128/25
- 192.168.0.129/25
type: ip
params:
cidr: true

View file

@ -4,6 +4,8 @@ version: 1.1
secret1:
description: the first variable
examples:
- ALongS4cr4t
type: secret
params:
min_len: 10

View file

@ -0,0 +1,18 @@
%YAML 1.2
---
version: 1.1
var1:
description: A first variable
type: boolean
default:
jinja: >-
true
description: A description
hidden: true
var2:
description: A second variable
disabled:
variable: _.var1
...

View file

@ -0,0 +1,15 @@
%YAML 1.2
---
version: 1.1
condition:
description: a condition
default: true
disabled: true
variable:
description: a variable
disabled:
variable: _.condition
propertyerror: transitive
...

View file

@ -0,0 +1,17 @@
%YAML 1.2
---
version: 1.1
condition: true # a condition
variable1:
description: a variable
disabled:
variable: _.condition
variable2:
description: a second variable
disabled:
variable: _.variable1
propertyerror: transitive
...

View file

@ -0,0 +1,15 @@
%YAML 1.2
---
version: 1.1
condition:
description: a condition
default: true
disabled: true
variable:
description: a variable
disabled:
variable: _.condition
propertyerror: transitive
...

View file

@ -0,0 +1,19 @@
%YAML 1.2
---
version: 1.1
condition: false # a condition
variable1:
description: a variable
default: disabled
disabled:
variable: _.condition
variable2:
description: a second variable
disabled:
variable: _.variable1
propertyerror: transitive
when: disabled
...

View file

@ -0,0 +1,19 @@
%YAML 1.2
---
version: 1.1
condition: true # a condition
variable1:
description: a variable
default: not_disabled
disabled:
variable: _.condition
variable2:
description: a second variable
disabled:
variable: _.variable1
propertyerror: transitive
when: disabled
...

View file

@ -8,7 +8,7 @@ leadership:
leader:
description: a leader
test:
examples:
- val1
- val2
default:

View file

@ -0,0 +1,29 @@
%YAML 1.2
---
version: 1.1
var:
description: a suffix variable
test:
- 1
- 2
type: integer
multi: true
mandatory: false
dyn{{ identifier }}:
description: a dynamic family
dynamic:
variable: _.var
var: val # a variable inside dynamic family from "{{ identifier }}"
var2:
description: a variable
default:
jinja: >-
{% if rougail.dyn1 is defined %}
{{ rougail.dyn1.var }}
{% endif %}
description: get the value of "rougail.dyn1.var"
...

View file

@ -3,21 +3,21 @@
version: 1.1
dyn{{ identifier }}:
description: A dynamic famify for {{ identifier }}
description: a dynamic famify for {{ identifier }}
dynamic:
- val1
- val2
var: # A dynamic variable for {{ identifier }}
var: # a dynamic variable for {{ identifier }}
var1:
description: A new variable
description: a new variable
disabled:
variable: _.dynval1.var
when: val
var2:
description: A new variable
description: a new variable
default:
variable: _.dyn{{ identifier }}.var
unique: false

View file

@ -0,0 +1,26 @@
%YAML 1.2
---
version: 1.1
var1: # A suffix variable
- val1
- val2
var2: val1 # A suffix variable2
dyn{{ identifier }}:
dynamic:
variable: _.var1
var:
description: A dynamic variable
default:
type: identifier
var3:
description: A variable calculated
default:
variable: _.dyn{{ identifier }}.var
identifier:
variable: _.var2
...

View file

@ -0,0 +1,24 @@
%YAML 1.2
---
version: 1.1
var1: # A suffix variable
- val1
- val2
var2: val1 # A suffix variable2
dyn{{ identifier }}:
dynamic:
variable: _.var1
var: # A dynamic variable
- type: identifier
var3:
description: A variable calculated
default:
variable: _.dyn{{ identifier }}.var
identifier:
variable: _.var2
...

View file

@ -7,7 +7,8 @@ var1:
test:
- val1
- val2
multi: true
default:
- val1
mandatory: false
dyn{{ identifier }}:
@ -22,5 +23,5 @@ var2:
description: A variable calculated
default:
variable: _.dynval1.var
optional: true
propertyerror: false
...

View file

@ -0,0 +1,26 @@
%YAML 1.2
---
version: 1.1
var1:
description: A suffix variable
test:
- val1
- val2
multi: true
mandatory: false
dyn{{ identifier }}:
dynamic:
variable: _.var1
var:
description: A dynamic variable
disabled: true
var2:
description: A variable calculated
default:
variable: _.dynval1.var
optional: true
...

View file

@ -4,7 +4,7 @@ version: 1.1
var:
description: a suffix variable
test:
examples:
- val1
- val2
- val3

View file

@ -0,0 +1,33 @@
%YAML 1.2
---
version: 1.1
var: # a suffix variable
- val1
- val2
'{{ identifier }}_dyn':
description: a dynamic family
dynamic:
variable: _.var
var1:
description: value is suffix for {{ identifier }}
default:
type: identifier
var2:
description: value is first variable
default:
variable: _.var1
var3:
description: value is relative first variable
default:
variable: _.var1
var4:
description: value is first variable of val1
default:
variable: __.val1_dyn.var1
...

View file

@ -0,0 +1,24 @@
%YAML 1.2
---
version: 1.1
var: # A suffix variable
- val1
- val2
dyn{{ identifier }}:
dynamic:
variable: _.var
dyn{{ identifier }}:
dynamic:
variable: __.var
var1: # A dynamic variable
var2:
description: A variable calculated
default:
variable: ___.dynval2.dyn{{ identifier }}.var1
unique: false
...

View file

@ -68,18 +68,26 @@ def test_structural_files_formatter_namespace(test_dir):
rougailconfig = get_rougail_config(test_dir, namespace)
if not rougailconfig:
return
if rougailconfig["extra_namespaces.names"]:
for n_idx, namespace in enumerate(rougailconfig['extra_namespaces.names']):
for dir_name in rougailconfig["extra_namespaces"]["directories"][n_idx]:
for file_name in Path(dir_name).iterdir():
if file_name.suffix in [".yml", ".yaml"]:
rougailconfig["extra_namespaces"].set_follower("directories", n_idx, [str(file_name)])
_test_structural_files(file_name, namespace, rougailconfig)
rougailconfig["extra_namespaces"].reset()
for dir_name in rougailconfig['main_structural_directories']:
for file_name in Path(dir_name).iterdir():
if file_name.suffix in [".yml", ".yaml"]:
rougailconfig["main_structural_directories"] = [str(file_name)]
_test_structural_files(file_name, namespace, rougailconfig)
for namespace, dirs_name in rougailconfig['extra_namespaces'].items():
rougailconfig["main_namespace"] = namespace
for dir_name in dirs_name:
for file_name in Path(dir_name).iterdir():
if file_name.suffix in [".yml", ".yaml"]:
rougailconfig["main_structural_directories"] = [str(file_name)]
_test_structural_files(file_name, namespace, rougailconfig)
# for namespace, dirs_name in rougailconfig['extra_namespaces'].items():
# rougailconfig["main_namespace"] = namespace
# for dir_name in dirs_name:
# for file_name in Path(dir_name).iterdir():
# if file_name.suffix in [".yml", ".yaml"]:
# rougailconfig["main_structural_directories"] = [str(file_name)]
# _test_structural_files(file_name, namespace, rougailconfig)
def test_structural_files_formatter_load(test_dir):