2024-07-28 17:52:11 +02:00
"""
Silique ( https : / / www . silique . fr )
2025-02-10 09:54:59 +01:00
Copyright ( C ) 2022 - 2025
2024-07-28 17:52:11 +02:00
2024-10-31 18:55:00 +01:00
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 / > .
2024-07-28 17:52:11 +02:00
"""
2024-09-04 09:04:08 +02:00
from typing import Any , List , Optional
2024-12-02 20:22:50 +01:00
from io import BytesIO
2024-07-28 17:52:11 +02:00
from rich . tree import Tree
from rich . console import Console
from rich . table import Table
from rich . panel import Panel
2024-12-02 20:22:50 +01:00
from ruamel . yaml import YAML
2024-07-28 17:52:11 +02:00
2024-11-27 16:50:48 +01:00
from tiramisu . error import PropertiesOptionError , ConfigError
2025-05-08 21:30:51 +02:00
from rougail . utils import undefined
2024-11-27 16:50:48 +01:00
from . i18n import _
2025-04-09 18:43:58 +02:00
from . __version__ import __version__
2024-07-28 17:52:11 +02:00
2024-11-27 16:50:48 +01:00
class RougailOutputConsole :
2024-11-01 10:58:53 +01:00
variable_hidden_color = " orange1 "
variable_advanced_color = " bright_blue "
variable_advanced_and_modified_color = " red1 "
value_unmodified_color = " gold1 "
value_default_color = " green "
2024-07-28 17:52:11 +02:00
2024-11-01 10:58:53 +01:00
def __init__ (
self ,
2024-11-27 16:50:48 +01:00
config : " Config " ,
rougailconfig : " RougailConfig " = None ,
user_data_errors : Optional [ list ] = None ,
user_data_warnings : Optional [ list ] = None ,
2024-11-01 10:58:53 +01:00
) - > None :
2024-11-27 16:50:48 +01:00
if rougailconfig is None :
from rougail import RougailConfig
rougailconfig = RougailConfig
2024-07-28 17:52:11 +02:00
self . rougailconfig = rougailconfig
2024-11-27 16:50:48 +01:00
self . config = config
self . read_write = self . rougailconfig [ " console.read_write " ]
self . is_mandatory = self . rougailconfig [ " console.mandatory " ]
self . show_secrets = self . rougailconfig [ " console.show_secrets " ]
2025-05-08 21:30:51 +02:00
self . key_is_description = self . rougailconfig [ " console.key_is_description " ]
2025-04-09 08:53:01 +02:00
self . variable_default_enable = False
self . variable_hidden_enable = False
self . variable_advanced_enable = False
self . variable_advanced_and_modified_enable = False
self . value_modified_enable = False
self . value_unmodified_enable = False
self . value_default_enable = False
2024-11-27 16:50:48 +01:00
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 . console = Console ( force_terminal = True )
2024-07-28 17:52:11 +02:00
self . out = [ ]
2024-11-27 16:50:48 +01:00
self . root = self . get_root ( )
def mandatory ( self ) :
if not self . is_mandatory :
return
try :
mandatories = self . config . value . mandatory ( )
except ( ConfigError , PropertiesOptionError ) as err :
2025-04-01 21:08:44 +02:00
self . errors . append ( _ ( " Error in config: {0} " ) . format ( err ) )
2024-11-27 16:50:48 +01:00
return
2025-02-15 18:42:02 +01:00
except ValueError as err :
self . errors . append ( str ( err ) )
return
2025-04-09 12:17:13 +02:00
options_with_error = [ ]
options = [ ]
if mandatories :
for option in mandatories :
try :
option . value . get ( )
options . append ( option . description ( ) )
except PropertiesOptionError :
options_with_error . append ( option )
if options :
self . errors . append ( _ ( " The following variables are mandatory but have no value: " ) )
self . errors . append ( options )
elif options_with_error :
self . errors . append (
_ (
" The following variables are inaccessible but are empty and mandatory: "
)
)
self . errors . append ( [ option . description ( ) for option in options_with_error ] )
2024-11-27 16:50:48 +01:00
def exporter ( self ) - > bool :
self . config . property . read_write ( )
self . mandatory ( )
if self . read_write :
self . config . property . read_write ( )
else :
self . config . property . read_only ( )
2025-04-09 08:53:01 +02:00
warnings = self . user_data_warnings + self . warnings
if warnings :
self . display_warnings ( warnings )
2024-11-27 16:50:48 +01:00
errors = self . user_data_errors + self . errors
if errors :
self . display_errors ( errors )
return False
2025-05-08 21:30:51 +02:00
old_path_in_description = self . config . information . get ( ' path_in_description ' , True )
self . config . information . set ( ' path_in_description ' , False )
2024-11-27 16:50:48 +01:00
self . parse_options (
self . config ,
self . root ,
)
2025-05-08 21:30:51 +02:00
self . config . information . set ( ' path_in_description ' , old_path_in_description )
2025-04-09 08:53:01 +02:00
self . header ( )
2024-11-27 16:50:48 +01:00
self . end ( )
return True
2024-11-28 22:06:38 +01:00
def run ( self ) - > str :
with self . console . capture ( ) as capture :
2025-02-10 09:54:59 +01:00
ret = self . print ( )
return ret , capture . get ( )
2024-11-28 22:06:38 +01:00
def print ( self ) - > None :
2025-02-10 09:54:59 +01:00
ret = self . exporter ( )
2024-11-28 22:06:38 +01:00
for out in self . out :
self . console . print ( out )
2025-02-10 09:54:59 +01:00
return ret
2024-11-27 16:50:48 +01:00
def parse_options (
self ,
conf ,
parent ,
) :
for option in conf :
if option . isoptiondescription ( ) :
family = parent . add_family ( option )
if option . isleadership ( ) :
self . parse_leadership (
option ,
family ,
)
else :
self . parse_options (
option ,
family ,
)
else :
parent . add_variable ( option )
def parse_leadership (
self ,
conf ,
parent ,
) :
leader , * followers = list ( conf )
leader_values = leader . value . get ( )
for idx , leader_value in enumerate ( leader_values ) :
leader_obj = parent . add_family ( leader )
leader_obj . add_variable (
leader ,
value = leader_value ,
leader_index = idx ,
)
for follower in followers :
if follower . index ( ) != idx :
continue
leader_obj . add_variable ( follower )
2024-07-28 17:52:11 +02:00
def header ( self ) :
2025-04-09 08:53:01 +02:00
header_variable = ' '
if self . variable_default_enable :
header_variable + = _ ( ' Variable ' ) + ' \n '
if self . variable_advanced_enable :
header_variable + = f ' [ { self . variable_advanced_color } ] { _ ( " Undocumented variable " ) } [/ { self . variable_advanced_color } ] \n '
if self . variable_advanced_and_modified_enable :
header_variable + = f ' [ { self . variable_advanced_and_modified_color } ] { _ ( " Undocumented but modified variable " ) } [/ { self . variable_advanced_and_modified_color } ] \n '
if self . variable_hidden_enable :
header_variable + = f ' [ { self . variable_hidden_color } ] { _ ( " Unmodifiable variable " ) } [/ { self . variable_hidden_color } ] \n '
header_value = ' '
if self . value_unmodified_enable :
header_value = f ' [ { self . value_unmodified_color } ] { _ ( " Default value " ) } [/ { self . value_unmodified_color } ] \n '
if self . value_modified_enable :
header_value + = _ ( " Modified value " ) + ' \n '
if self . value_default_enable :
header_value + = f ' ([ { self . value_default_color } ] { _ ( " Original default value " ) } [/ { self . value_default_color } ]) \n '
2024-07-28 17:52:11 +02:00
header = Table . grid ( padding = 1 , collapse_padding = True )
header . pad_edge = False
2025-04-09 08:53:01 +02:00
header . add_row ( header_variable [ : - 1 ] , header_value [ : - 1 ] )
2024-07-28 17:52:11 +02:00
self . out . append ( Panel . fit ( header , title = _ ( " Caption " ) ) )
2024-11-27 16:50:48 +01:00
def display_errors (
2024-11-01 10:58:53 +01:00
self ,
errors ,
) - > None :
tree = Tree (
2025-04-09 08:53:01 +02:00
f " [bold][bright_red]:stop_sign: { _ ( ' ERRORS ' ) } [/bright_red][/bold] " ,
2024-11-01 10:58:53 +01:00
guide_style = " bold bright_red " ,
)
2024-07-28 17:52:11 +02:00
for error in errors :
2025-04-09 12:17:13 +02:00
if isinstance ( error , list ) :
for err in error :
previous_tree . add ( err )
else :
previous_tree = tree . add ( error )
2024-07-28 17:52:11 +02:00
self . out . append ( tree )
2024-11-27 16:50:48 +01:00
def display_warnings (
2024-11-01 10:58:53 +01:00
self ,
warnings : list ,
) - > None :
2025-04-09 08:53:01 +02:00
tree = Tree ( f " [bold][bright_yellow]:bell: { _ ( ' WARNINGS ' ) } [/bright_yellow][/bold] " ,
guide_style = " bold bright_yellow " ,
)
2024-07-28 17:52:11 +02:00
for warning in warnings :
tree . add ( warning )
self . out . append ( tree )
2024-11-27 16:50:48 +01:00
def get_root ( self ) - > None :
2024-12-02 20:22:50 +01:00
yaml = YAML ( )
yaml . indent ( mapping = 2 , sequence = 4 , offset = 2 )
2024-11-01 10:58:53 +01:00
self . output = OutputFamily (
_ ( " Variables: " ) ,
None ,
self ,
2024-12-02 20:22:50 +01:00
yaml ,
2025-05-08 21:30:51 +02:00
self . key_is_description ,
2024-11-01 10:58:53 +01:00
no_icon = True ,
)
2024-07-28 17:52:11 +02:00
return self . output
def end ( self ) :
self . out . append ( self . output . tree )
class OutputFamily :
2024-11-01 10:58:53 +01:00
def __init__ (
2025-05-08 21:30:51 +02:00
self , family , parent , root , _yaml , key_is_description , * , is_leader : bool = False , no_icon : bool = False
2024-11-01 10:58:53 +01:00
) - > None :
2024-07-28 17:52:11 +02:00
if parent is None :
tree = Tree
else :
tree = parent . add
if is_leader :
2024-11-01 10:58:53 +01:00
self . tree = tree (
2025-04-09 08:53:01 +02:00
" :notebook: " + _ ( ' {0} : ' ) . format ( family ) ,
2024-11-01 10:58:53 +01:00
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
elif no_icon :
2024-11-01 10:58:53 +01:00
self . tree = tree (
f " { family } " ,
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
else :
2024-11-01 10:58:53 +01:00
self . tree = tree (
f " :open_file_folder: { family } " ,
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
self . root = root
2024-12-02 20:22:50 +01:00
self . _yaml = _yaml
2025-05-08 21:30:51 +02:00
self . key_is_description = key_is_description
2024-11-01 10:58:53 +01:00
def add_family (
self ,
option ,
) - > None :
2024-07-28 17:52:11 +02:00
properties = option . property . get ( )
2024-11-01 10:58:53 +01:00
if " hidden " in properties :
2025-04-09 08:53:01 +02:00
self . root . variable_hidden_enable = True
2024-07-28 17:52:11 +02:00
color = self . root . variable_hidden_color
2024-11-01 10:58:53 +01:00
elif " advanced " in properties :
2025-04-09 08:53:01 +02:00
self . root . variable_advanced_enable = True
2024-07-28 17:52:11 +02:00
color = self . root . variable_advanced_color
else :
2025-04-09 08:53:01 +02:00
self . root . variable_default_enable = True
2024-07-28 17:52:11 +02:00
color = None
2025-05-08 21:30:51 +02:00
if self . key_is_description :
key_name = option . description ( )
else :
key_name = option . name ( )
2024-11-01 10:58:53 +01:00
return OutputFamily (
self . colorize (
None ,
2025-05-08 21:30:51 +02:00
key_name ,
2024-11-01 10:58:53 +01:00
color ,
None ,
) ,
self . tree ,
self . root ,
2024-12-02 20:22:50 +01:00
self . _yaml ,
2025-05-08 21:30:51 +02:00
self . key_is_description ,
2024-11-01 10:58:53 +01:00
)
2024-07-28 17:52:11 +02:00
2024-11-01 10:58:53 +01:00
def add_variable (
self , option , value : Any = undefined , leader_index : Optional [ int ] = None
) :
2024-07-28 17:52:11 +02:00
properties = option . property . get ( )
variable_color = None
if option . owner . isdefault ( ) :
2024-11-01 10:58:53 +01:00
if " hidden " in properties :
2025-04-09 08:53:01 +02:00
self . root . variable_hidden_enable = True
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_hidden_color
2024-11-01 10:58:53 +01:00
elif " advanced " in properties :
2025-04-09 08:53:01 +02:00
self . root . variable_advanced_enable = True
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_advanced_color
2025-04-09 08:53:01 +02:00
self . root . value_unmodified_enable = True
2024-07-28 17:52:11 +02:00
color = self . root . value_unmodified_color
default_value = None
else :
2024-11-01 10:58:53 +01:00
if " hidden " in properties :
2025-04-09 08:53:01 +02:00
self . root . variable_hidden_enable = True
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_hidden_color
2024-11-01 10:58:53 +01:00
elif " advanced " in properties :
2025-04-09 08:53:01 +02:00
self . root . variable_advanced_and_modified_enable = True
2024-07-28 17:52:11 +02:00
variable_color = self . root . variable_advanced_and_modified_color
color = None
2025-04-09 08:53:01 +02:00
self . root . value_modified_enable = True
2025-02-17 09:53:32 +01:00
if option . information . get ( " default_value_makes_sense " , True ) :
try :
default_value = option . value . default ( )
except ConfigError :
if option . ismulti ( ) :
default_value = [ ]
else :
default_value = None
if leader_index is not None :
if len ( default_value ) > leader_index :
default_value = default_value [ leader_index ]
else :
default_value = None
else :
default_value = None
2025-04-09 08:53:01 +02:00
if variable_color is None :
self . root . variable_default_enable = True
2024-07-28 17:52:11 +02:00
if value is undefined :
value = option . value . get ( )
2025-05-08 21:30:51 +02:00
if self . key_is_description :
key_name = option . description ( )
else :
key_name = option . name ( )
2024-11-01 10:58:53 +01:00
key = self . colorize (
None ,
2025-05-08 21:30:51 +02:00
key_name ,
2024-11-01 10:58:53 +01:00
variable_color ,
None ,
)
value = self . colorize (
option ,
value ,
color ,
default_value ,
)
2024-07-28 17:52:11 +02:00
if isinstance ( value , list ) :
2024-11-01 10:58:53 +01:00
subtree = self . tree . add (
2025-04-09 08:53:01 +02:00
" :notebook: " + _ ( ' {0} : ' ) . format ( key ) ,
2024-11-01 10:58:53 +01:00
guide_style = " bold bright_blue " ,
)
2024-07-28 17:52:11 +02:00
for val in value :
subtree . add ( str ( val ) )
else :
2025-04-27 10:27:30 +02:00
self . tree . add ( " :notebook: " + _ ( " {0} : {1} " ) . format ( key , value ) )
2024-07-28 17:52:11 +02:00
2024-11-01 10:58:53 +01:00
def colorize (
self ,
option ,
value ,
color : str ,
default_value ,
) - > str :
2024-07-28 17:52:11 +02:00
if isinstance ( value , list ) :
if default_value is None :
default_value = [ ]
2024-12-02 20:22:50 +01:00
# default_value = [self.convert_value(option, val) for val in default_value]
2024-07-28 17:52:11 +02:00
len_value = len ( value )
len_default_value = len ( default_value )
len_values = max ( len_value , len_default_value )
ret = [ ]
for idx in range ( len_values ) :
if idx < len_value :
val = value [ idx ]
else :
2024-11-01 10:58:53 +01:00
val = " "
2024-07-28 17:52:11 +02:00
if idx < len_default_value :
default = default_value [ idx ]
else :
default = None
2024-11-01 10:58:53 +01:00
ret . append (
self . colorize (
option ,
val ,
color ,
default ,
)
)
2024-07-28 17:52:11 +02:00
return ret
2025-04-09 08:53:01 +02:00
if value is None :
ret = ' '
2024-07-28 17:52:11 +02:00
else :
2025-04-09 08:53:01 +02:00
if option :
value = self . convert_value (
option ,
value ,
)
if color is not None :
ret = f " [ { color } ] { value } [/ { color } ] "
else :
ret = value
2024-12-02 20:22:50 +01:00
if default_value is not None and " force_store_value " not in option . property . get ( ) :
2025-04-09 08:53:01 +02:00
self . root . value_default_enable = True
2024-07-28 17:52:11 +02:00
default_value_color = self . root . value_default_color
2024-12-02 20:22:50 +01:00
default_value = self . convert_value ( option , default_value )
if ret :
ret + = f " ([ { default_value_color } ] { default_value } [/ { default_value_color } ]) "
else :
ret = f " [ { default_value_color } ] { default_value } [/ { default_value_color } ] "
2024-07-28 17:52:11 +02:00
return ret
2024-11-01 10:58:53 +01:00
def convert_value (
self ,
option ,
2024-12-02 20:22:50 +01:00
value : Any ,
) - > str :
if isinstance ( value , list ) :
print ( value )
raise Exception ( ' pfff ' )
""" Dump variable, means transform bool, ... to yaml string """
2024-11-01 10:58:53 +01:00
if not self . root . show_secrets and option . type ( ) == " password " :
2024-07-28 17:52:11 +02:00
return " * " * 10
2024-12-02 20:22:50 +01:00
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
2024-11-27 16:50:48 +01:00
RougailOutput = RougailOutputConsole
__all__ = ( " RougailOutputConsole " , )