rougail-output-display/src/rougail/output_display/display.py
2025-12-04 21:06:20 +01:00

200 lines
6.5 KiB
Python

"""
Silique (https://www.silique.fr)
Copyright (C) 2022-2025
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
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
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/>.
"""
from typing import Optional
from ruamel.yaml import YAML
from tiramisu.error import PropertiesOptionError, ConfigError
from .config import OutPuts
from .i18n import _
class RougailOutputDisplay:
def __init__(
self,
config: "Config",
metaconfig=None,
*,
rougailconfig: "RougailConfig" = None,
user_data_errors: Optional[list] = None,
user_data_warnings: Optional[list] = None,
config_owner_is_path: bool = False,
layer_datas=None,
**kwargs,
) -> None:
if rougailconfig is None:
from rougail import RougailConfig
rougailconfig = RougailConfig
self.rougailconfig = rougailconfig
self.config = config
self.metaconfig = metaconfig
self.layer_datas = layer_datas
self.config_owner_is_path = config_owner_is_path
self.errors = []
self.warnings = []
if user_data_errors is None:
user_data_errors = []
self.user_data_errors = user_data_errors
if user_data_warnings is None:
user_data_warnings = []
self.user_data_warnings = user_data_warnings
self.out = []
self.root = None
def run(self) -> str:
if self.root is None:
self.root = self.get_root()
self.is_mandatory = self.rougailconfig["display.mandatory"]
self.show_secrets = self.rougailconfig["display.show_secrets"]
return self.output.run()
def print(self) -> None:
return self.output.print()
def get_root(self) -> None:
yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
output_format = self.rougailconfig["display.output_format"]
self.output = OutPuts().get()[output_format](
_("Variables:"),
None,
self,
yaml,
self.out,
no_icon=True,
)
return self.output
def exporter(self) -> bool:
if self.is_mandatory:
ori_properties = self.config.property.exportation()
self.config.property.read_write()
if not self.user_data_errors and not self.errors:
self.mandatories()
self.config.property.importation(ori_properties)
warnings = self.user_data_warnings + self.warnings
if warnings:
self.display_warnings(warnings)
errors = self.user_data_errors + self.errors
if errors:
self.output.display_errors(errors)
return False
self.parse_options(
self.config,
self.root,
)
if self.output.tree and self.output.tree.children:
self.output.header()
self.end()
return True
def mandatories(self) -> None:
try:
mandatories = self.config.value.mandatory()
except (ConfigError, PropertiesOptionError) as err:
self.errors.append(_("Error in config: {0}").format(err))
except ValueError as err:
self.errors.append(str(err))
else:
self.check_mandatories(mandatories)
def check_mandatories(self, mandatories: list) -> None:
options_with_error = []
options = []
for option in mandatories:
try:
option.value.get()
except PropertiesOptionError:
options_with_error.append(option)
else:
options.append(option)
if options:
self.errors.append(
{_("The following variables are mandatory but have no value:"): self.options_to_dict(options)}
)
elif options_with_error:
self.errors.append(
{
_(
"The following variables are inaccessible but are empty and mandatory:"
): self.options_to_dict(options_with_error),
}
)
def options_to_dict(self, mandatories: list):
options = {}
for option in mandatories:
parents = [option]
parent = option
while True:
parent = parent.parent()
if parent.path() is None:
break
parents.insert(0, parent)
current_options = options
for child in parents:
if child.isoptiondescription():
current_options = current_options.setdefault(child.description(), {})
else:
current_options.setdefault(None, []).append(child.description())
return options
def parse_options(
self,
conf,
parent,
):
for child in conf:
if child.isoptiondescription():
if child.isleadership():
func = self.parse_leadership
else:
func = self.parse_options
family = parent.add_family(child)
func(
child,
family,
)
else:
parent.add_variable(child)
def parse_leadership(
self,
leadership,
parent,
) -> None:
leader, *followers = list(leadership)
leader_values = leader.value.get()
followers_values = {idx: [] for idx in range(len(leader_values))}
for follower in followers:
followers_values[follower.index()].append(follower)
for idx in followers_values:
leader_obj = parent.add_family(leader)
leader_obj.add_variable(
leader,
value=leader_values[idx],
leader_index=idx,
)
for follower in followers_values[idx]:
leader_obj.add_variable(follower)
def end(self):
self.out.append(self.output.tree)