rougail-output-display/src/rougail/output_display/util.py

281 lines
9.3 KiB
Python
Raw Normal View History

2025-12-02 13:59:33 +01:00
"""
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 Any, Optional
from io import BytesIO
from tiramisu import owners
from rougail.utils import undefined
class CommonOutput:
def __init__(
self,
family,
parent,
root,
_yaml,
out,
*,
# is_leader: bool = False,
root_family: Optional["OutputFamily"] = None,
no_icon: bool = False,
) -> None:
self.tree = None
self.root = root
self.parent = parent
if isinstance(family, tuple):
raise Exception()
self.family = family
# self.is_leader = is_leader
self.no_icon = no_icon
self._yaml = _yaml
self.out = out
if root_family is None:
root_family = self
self.variable_default_enable = False
self.variable_hidden_enable = False
self.value_modified_enable = False
self.value_unmodified_enable = False
self.value_default_enable = False
self.root_family = root_family
def header(self):
raise NotImplementedError()
def display_errors(
self,
errors,
) -> None:
tree = self.error_header()
for error in errors:
self.parse_error(tree, error)
self.out.append(tree)
def parse_error(self, tree, error):
if isinstance(error, list):
for err in error:
self.parse_error(tree, err)
return tree
elif isinstance(error, dict):
for key, value in error.items():
if key is None:
# it's variables, no more families
self.parse_error(tree, value)
else:
sub_tree = self.parse_error(tree, key)
self.parse_error(sub_tree, value)
else:
return self.display_error(tree, error)
def add_family(
self,
family,
) -> 'OutputFamily':
properties = family.property.get()
if "hidden" in properties:
self.root_family.variable_hidden_enable = True
color = self.variable_hidden_color
else:
self.root_family.variable_default_enable = True
color = None
family_output = self.colorize(
[
{
"value": family.description(),
"color": color,
"loaded_from": None,
}
]
)
return self.__class__(
family_output,
self.get_tree(),
self.root,
self._yaml,
self.out,
root_family = self.root_family,
)
def add_variable(
self, option, value: Any = undefined, leader_index: Optional[int] = None
):
values = []
properties = option.property.get()
if not option.owner.isdefault() and option.information.get(
"default_value_makes_sense", True
):
self.get_modified_value(option, value, values)
if "force_store_value" not in properties or not values:
self.get_default_values(option, values, leader_index)
#
if "hidden" in properties:
self.root_family.variable_hidden_enable = True
variable_color = self.variable_hidden_color
else:
self.root_family.variable_default_enable = True
variable_color = self.variable_normal_color
key = self.colorize(
[
{
"value": option.description(),
"color": variable_color,
"loaded_from": None,
}
]
)
value = self.colorize(
values,
option,
)
return key, value
def get_modified_value(self, option: "Option", value: Any, values: list) -> None:
self.root_family.value_modified_enable = True
follower_index = option.index()
if follower_index is not None:
loaded_from = option.information.get(
f"loaded_from_{follower_index}", None
)
else:
loaded_from = option.information.get("loaded_from", None)
if value is undefined:
value = option.value.get()
values.append(
{
"value": value,
"color": self.value_modified_color,
"loaded_from": loaded_from,
}
)
def get_default_values(self, option: "Option", values: list, leader_index: Optional[int]) -> None:
meta_config = self.root.config
meta_option = option
index = option.index()
while True:
default_value = meta_option.value.default()
if leader_index is not None:
if len(default_value) > leader_index:
default_value = default_value[leader_index]
else:
default_value = None
is_root_metaconfig = False
if (
meta_config is None
or not meta_config.path()
or "." not in meta_config.path()
):
is_root_metaconfig = True
if (
(values and default_value is None) or default_value == []
) and is_root_metaconfig:
break
if not values:
self.root_family.value_unmodified_enable = True
color = self.value_unmodified_color
else:
self.root_family.value_default_enable = True
color = self.value_default_color
meta_config = self.get_subconfig_with_default_value(meta_option)
meta_option = meta_config.option(option.path(), index)
if is_root_metaconfig:
loaded_from = None
else:
if index is not None:
key = f"loaded_from_{index}"
else:
key = "loaded_from"
loaded_from = meta_option.information.get(key, None)
values.append(
{
"value": default_value,
"color": color,
"loaded_from": loaded_from,
}
)
if is_root_metaconfig:
break
#
# def get_default_value(self, option: "Option", values: list, leader_index: Optional[int]) -> None:
# follower_index = option.index()
## if leader_index is None and follower_index is not None:
## if not option.isfollower() or not option.issubmulti():
## default_value = None
## else:
## default_value = []
## else:
## default_value = None
# if not values:
# self.root_family.value_unmodified_enable = True
# color = self.value_unmodified_color
# else:
# self.root_family.value_default_enable = True
# color = self.value_default_color
# if follower_index is not None:
# loaded_from = option.information.get(
# f"loaded_from_{follower_index}", None
# )
# else:
# loaded_from = option.information.get("loaded_from", None)
# values.append(
# {
# "value": default_value,
# "color": color,
# "loaded_from": loaded_from,
# }
# )
def get_subconfig_with_default_value(self, config):
default_owner = config.owner.default()
if default_owner == owners.default:
return self.root.config
if not self.root.config_owner_is_path:
meta_config = self.root.config
while True:
meta_config = meta_config.parent()
if not meta_config.owner.isdefault():
break
else:
meta_config = self.root.metaconfig
for child in default_owner.split(".")[1:]:
meta_config = meta_config.config(child)
return meta_config
def convert_value(
self,
option,
value: Any,
) -> str:
"""Dump variable, means transform bool, ... to yaml string"""
if (
value is not None
and not self.root.show_secrets
and option.type() == "password"
):
return "*" * 10
if isinstance(value, str):
return value
with BytesIO() as ymlfh:
self._yaml.dump(value, ymlfh)
ret = ymlfh.getvalue().decode("utf-8").strip()
if ret.endswith("..."):
ret = ret[:-3].strip()
return ret