""" 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 . """ 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