10@brief The file in charge of managing the beautified output on the terminal.
17from typing
import List, Dict, Union
23if __name__ ==
"__main__":
24 from colours
import LoggerColours
25 from constants
import ERR, SUCCESS, OUT_TTY, OUT_STRING, OUT_FILE, OUT_DEFAULT, KEY_OUTPUT_MODE, KEY_PRETTIFY_OUTPUT, KEY_PRETTIFY_OUTPUT_IN_BLOCKS, KEY_ANIMATION_DELAY, KEY_ANIMATION_DELAY_BLOCKY, TOML_CONF, FORBIDDEN_NUMBER_LOG_LEVELS_CORRESPONDANCE, FORBIDDEN_NUMBER_LOG_LEVELS
26 from log_level_tracker
import LogLevelTracker
28 from .colours
import LoggerColours
29 from .constants
import ERR, SUCCESS, OUT_TTY, OUT_STRING, OUT_FILE, OUT_DEFAULT, KEY_OUTPUT_MODE, KEY_PRETTIFY_OUTPUT, KEY_PRETTIFY_OUTPUT_IN_BLOCKS, KEY_ANIMATION_DELAY, KEY_ANIMATION_DELAY_BLOCKY, TOML_CONF, FORBIDDEN_NUMBER_LOG_LEVELS_CORRESPONDANCE, FORBIDDEN_NUMBER_LOG_LEVELS
30 from .log_level_tracker
import LogLevelTracker
36 @brief Represents a placeholder for the logging library. This is not a functioning class.
46 @brief The class in charge of displaying messages with various styles and animations.
48 @details This class provides methods to display messages in different formats, log messages, and manage output configurations.
51 def __init__(self, toml_content: Dict[str, any], save_to_file: bool =
False, file_name: str =
"text_output_run.txt", file_descriptor: any =
None, debug: bool =
False, logger: Union[Logging, str,
None] =
None, success: int = SUCCESS, error: int = ERR, log_warning_when_present: bool =
True, log_errors_when_present: bool =
True) ->
None:
53 @brief Constructor for the Disp class.
55 @param toml_content Dictionary containing configuration values.
56 @param save_to_file Boolean indicating whether to save output to a file.
57 @param file_name Name of the file to save output to.
58 @param file_descriptor File descriptor for the output file.
59 @param debug Boolean indicating whether debug mode is enabled.
60 @param logger Logger instance or name to use for logging.
61 @param success Integer representing the success status code.
62 @param error Integer representing the error status code.
63 @param log_warning_when_present Boolean indicating whether to log warnings when they arise in one of the function calls.
64 @param log_errors_when_present Boolean indicating whether to log errors when they arise in one of the function calls.
69 self.
author =
"(c) Created by Henry Letellier"
103 msg = f
"Invalid output mode. Must be one of '{OUT_FILE}', "
104 msg += f
"'{OUT_STRING}', '{OUT_TTY}', '{OUT_DEFAULT}'"
105 raise ValueError(msg)
112 @brief Setup the logger for the class.
114 @param logger The logger to use. If None, a default logger will be used.
117 if callable(logger)
and hasattr(logger,
"debug"):
120 if isinstance(logger, str)
is True:
121 self.
logger = logging.getLogger(logger)
123 self.
logger = logging.getLogger(self.__class__.__name__)
124 if not self.
logger.hasHandlers():
125 handler = colorlog.StreamHandler()
126 format_string =
'[%(asctime)s] %(log_color)s%('
128 format_string +=
'_log_color)s%(levelname)s%(reset)s %(name)s: \'%(message)s\''
129 formatter = colorlog.ColoredFormatter(
139 'CRITICAL':
'bold_red'
141 secondary_log_colors={
145 'WARNING':
'bg_black',
147 'CRITICAL':
'bg_black',
151 handler.setFormatter(formatter)
152 self.
logger.addHandler(handler)
156 if node.check_presence()
is False:
164 exec(func_code, globals(), namespace)
167 func = namespace[name]
172 @brief Add a dynamically created function to an instance.
174 @param func_dest The destination object to add the function to.
175 @param func_name Name of the function.
176 @param func_code Code of the function.
179 setattr(func_dest, func_name, function_instance)
183 @brief Update the debug mode.
185 @param debug Boolean indicating whether debug mode is enabled.
191 @brief Update the logger level.
193 @param level The log importance level. Defaults to NOTSET.
195 @return The status code of the operation.
197 _func_name = inspect.currentframe().f_code.co_name
199 if isinstance(level, str):
200 level = level.upper()
201 if hasattr(logging,
"LogLevelTracker"):
202 level = logging.LogLevelTracker.get_level(level)
203 if isinstance(level, int)
is False or level
not in LogLevelTracker.Levels.__all__:
204 level = LogLevelTracker.Levels.NOTSET
207 f
"The level is not valid, defaulting to {level}",
210 self.
logger.setLevel(level)
214 @brief Check if the logger instance is valid.
216 @param logger_instance The logger instance to validate.
217 @return A valid logger instance.
220 _func_name = inspect.currentframe()
221 if _func_name.f_back
is not None:
222 _func_name = _func_name.f_back.f_code.co_name
224 _func_name = _func_name.f_code.co_name
227 if logger_instance
is None or not isinstance(logger_instance, logging.Logger):
230 "No logger instance provided, using the default logger",
233 logger_instance = self.
logger
234 return logger_instance
236 def _check_colour_data(self, colour: Union[str, int], logger_instance: logging.Logger =
None) -> Union[int, str]:
238 @brief Check if the provided colour data is valid.
240 @param colour The colour to validate.
241 @param logger_instance The logger instance to use for logging errors.
242 @return The validated colour or an error code.
244 _func_name = inspect.currentframe()
245 if _func_name.f_back
is not None:
246 _func_name = _func_name.f_back.f_code.co_name
248 _func_name = _func_name.f_code.co_name
251 if isinstance(colour, int):
252 colour = LoggerColours.get_colour_string(LoggerColours, colour)
253 if LoggerColours.check_if_colour_present(LoggerColours, colour)
is False:
256 "The provided colour is not valid",
262 def _check_level_data(self, level_name: Union[str, int], logger_instance: logging.Logger =
None) -> Union[int, str]:
264 @brief Check if the provided level data is valid.
266 @param level_name The level name or number to validate.
267 @param logger_instance The logger instance to use for logging errors.
268 @return The validated level name or an error code.
270 _func_name = inspect.currentframe()
271 if _func_name.f_back
is not None:
272 _func_name = _func_name.f_back.f_code.co_name
274 _func_name = _func_name.f_code.co_name
277 if len(logger_instance.handlers) == 0:
280 'No handlers are present in this logging instance',
286 if isinstance(level_name, str):
287 name_string = level_name.upper()
288 elif isinstance(level_name, int):
289 name_string = logging.getLevelName(level_name)
293 "The level name must be a string or an integer",
301 @brief Get the colour formatter from the logger instance.
303 @param logger_instance The logger instance to retrieve the formatter from.
304 @return The colour formatter or an error code.
306 _func_name = inspect.currentframe()
307 if _func_name.f_back
is not None:
308 _func_name = _func_name.f_back.f_code.co_name
310 _func_name = _func_name.f_code.co_name
313 colour_handler =
None
314 for i
in logger_instance.handlers:
315 if isinstance(i, colorlog.StreamHandler):
318 if not colour_handler:
321 'No colour handler is present in this logging instance',
327 if hasattr(colour_handler,
"formatter")
is False:
330 'The colour handler has no formatter',
334 colour_formatter: colorlog.ColoredFormatter = colour_handler.formatter
335 if isinstance(colour_formatter, colorlog.ColoredFormatter)
is False:
338 'The formatter is not a ColoredFormatter',
342 if hasattr(colour_formatter,
"log_colors")
is False:
345 'The formatter has no log_colors',
349 return colour_formatter
353 @brief Update or insert a logging colour for the text of the specified level.
355 @param colour The colour to use (string or number).
356 @param level_name The level name or number.
357 @param logger_instance The logger instance to update.
358 @return The status code of the operation.
360 _func_name = inspect.currentframe().f_code.co_name
368 if name_string == self.
error:
371 f
"The level name {level_name} is not valid",
376 name_string = name_string.upper()
379 colour_input = colour
384 if colour == self.
error:
387 f
"The colour {colour_input} is not valid",
393 if internal_log_colors == self.
error or internal_log_colors
is None:
396 'The colour logging library is not valid',
400 lib_log_colors = internal_log_colors.log_colors
401 if isinstance(lib_log_colors, dict)
is False:
404 'The log_colors is not a dictionary',
408 for i
in lib_log_colors:
409 if i.upper() == name_string:
410 lib_log_colors[i] = colour
412 lib_log_colors[name_string.upper()] = colour
417 @brief Update or insert a logging colour for the background of the specified level.
419 @param colour The colour to use (string or number).
420 @param level_name The level name or number.
421 @param logger_instance The logger instance to update.
422 @return The status code of the operation.
424 _func_name = inspect.currentframe().f_code.co_name
432 if name_string == self.
error:
435 f
"The level name {level_name} is not valid",
439 name_string = name_string.upper()
442 colour_input = colour
447 if colour == self.
error:
450 f
"The colour {colour_input} is not valid",
456 if colour.startswith(
"bg_")
is False:
457 colour =
"bg_" + colour
460 if secondary_log_colors == self.
error:
463 'The secondary_log_colors is not valid',
467 secondary_log_colors = secondary_log_colors.secondary_log_colors
468 if isinstance(secondary_log_colors, dict)
is False:
471 'The secondary_log_colors is not a dictionary',
479 name_string.upper(): colour
483 if i.upper() == name_string:
487 secondary_log_colors[
489 ][name_string.upper()] = colour
492 def add_custom_level(self, level: int, name: str, colour_text: Union[int, str] =
"", colour_bg: Union[int, str] =
"") -> int:
494 @brief Add a custom level to the logger.
496 @param level The integer value of the custom level.
497 @param name The name of the custom level.
498 @param colour_text The text colour for the custom level.
499 @param colour_bg The background colour for the custom level.
500 @return The status code of the operation.
502 _func_name = inspect.currentframe().f_code.co_name
505 if level
in FORBIDDEN_NUMBER_LOG_LEVELS:
508 f
"The provided level is forbidden because already taken '{level}'",
512 if name
in FORBIDDEN_NUMBER_LOG_LEVELS_CORRESPONDANCE:
515 f
"The provided name is forbidden because already taken '{name}'",
520 logging.addLevelName(level, name.upper())
521 if hasattr(logging.getLogger(),
"log_level_tracker")
is False:
524 "The log level tracker is not present, adding",
530 if logging.getLogger().log_level_tracker.add_level(name, level)
is False:
533 "The level could not be added to the log level tracker",
538 if colour_text !=
"" or colour_text < 0:
544 if colour_text_status == self.
error:
547 "The colour for the text could not be set",
550 if colour_bg !=
"" or colour_bg < 0:
556 if colour_bg_status == self.
error:
559 "The colour for the background could not be set",
564 func_name = name.lower()
576 func_disp_name = f
"disp_print_{func_name}"
577 function_disp_code = f
"""
578def {func_disp_name}(self, string: str = "", func_name: Union[str, None] = None) -> None:
579 if isinstance(func_name, str) is False or func_name is None:
580 _func_name = inspect.currentframe()
581 if _func_name.f_back is not None:
582 func_name = _func_name.f_back.f_code.co_name
584 func_name = _func_name.f_code.co_name
585 self.log_custom_level({level}, string, func_name)
593 func_log_name = f
"log_{func_name}"
594 function_disp_short_code = f
"""
595def {func_log_name}(self, string: str = "", func_name: Union[str, None] = None) -> None:
596 if isinstance(func_name, str) is False or func_name is None:
597 _func_name = inspect.currentframe()
598 if _func_name.f_back is not None:
599 _func_name = _func_name.f_back.f_code.co_name
601 _func_name = _func_name.f_code.co_name
602 self.log_custom_level({level}, string, _func_name)
607 function_disp_short_code
613 @brief Print a message with a custom level.
615 @param level The custom level to use.
616 @param string The message to print.
617 @param func_name The name of the calling function.
619 if isinstance(func_name, str)
is False or func_name
is None:
620 _func_name = inspect.currentframe()
621 if _func_name.f_back
is not None:
622 func_name = _func_name.f_back.f_code.co_name
624 func_name = _func_name.f_code.co_name
625 if isinstance(level, str):
626 log_level_tracker: LogLevelTracker = logging.getLogger().log_level_tracker
627 level = log_level_tracker.get_level(level)
630 "The provided level is not valid"
633 if self.
logger.isEnabledFor(level):
634 self.
logger.log(level,
"(%s) %s", func_name, string)
638 @brief Print a debug message (using logger).
640 @param string The message to print.
641 @param func_name The name of the calling function.
643 if isinstance(func_name, str)
is False or func_name
is None:
644 _func_name = inspect.currentframe()
645 if _func_name.f_back
is not None:
646 func_name = _func_name.f_back.f_code.co_name
648 func_name = _func_name.f_code.co_name
652 def disp_print_info(self, string: str =
"", func_name: Union[str,
None] =
None) ->
None:
654 @brief Print an information message (using logger).
656 @param string The message to print.
657 @param func_name The name of the calling function.
659 if isinstance(func_name, str)
is False or func_name
is None:
660 _func_name = inspect.currentframe()
661 if _func_name.f_back
is not None:
662 func_name = _func_name.f_back.f_code.co_name
664 func_name = _func_name.f_code.co_name
665 self.
logger.info(
"(%s) %s", func_name, string)
669 @brief Print a warning message (using logger).
671 @param string The message to print.
672 @param func_name The name of the calling function.
674 if isinstance(func_name, str)
is False or func_name
is None:
675 _func_name = inspect.currentframe()
676 if _func_name.f_back
is not None:
677 func_name = _func_name.f_back.f_code.co_name
679 func_name = _func_name.f_code.co_name
680 self.
logger.warning(
"(%s) %s", func_name, string)
684 @brief Print an error message (using logger).
686 @param string The message to print.
687 @param func_name The name of the calling function.
689 if isinstance(func_name, str)
is False or func_name
is None:
690 _func_name = inspect.currentframe()
691 if _func_name.f_back
is not None:
692 func_name = _func_name.f_back.f_code.co_name
694 func_name = _func_name.f_code.co_name
699 @brief Print a critical message (using logger).
701 @param string The message to print.
702 @param func_name The name of the calling function.
704 if isinstance(func_name, str)
is False or func_name
is None:
705 _func_name = inspect.currentframe()
706 if _func_name.f_back
is not None:
707 func_name = _func_name.f_back.f_code.co_name
709 func_name = _func_name.f_code.co_name
710 self.
logger.critical(
"(%s) %s", func_name, string)
712 def log_custom_level(self, level: Union[int, str], string: str, func_name: Union[str,
None] =
None) ->
None:
714 @brief Log a message with a custom level.
716 @param level The custom level to use.
717 @param string The message to log.
718 @param func_name The name of the calling function.
720 if isinstance(func_name, str)
is False or func_name
is None:
721 _func_name = inspect.currentframe()
722 if _func_name.f_back
is not None:
723 func_name = _func_name.f_back.f_code.co_name
725 func_name = _func_name.f_code.co_name
728 def log_debug(self, string: str =
"", func_name: Union[str,
None] =
None) ->
None:
730 @brief Log a debug message.
732 @param string The message to log.
733 @param func_name The name of the calling function.
735 if isinstance(func_name, str)
is False or func_name
is None:
736 _func_name = inspect.currentframe()
737 if _func_name.f_back
is not None:
738 func_name = _func_name.f_back.f_code.co_name
740 func_name = _func_name.f_code.co_name
743 def log_info(self, string: str =
"", func_name: Union[str,
None] =
None) ->
None:
745 @brief Log an info message.
747 @param string The message to log.
748 @param func_name The name of the calling function.
750 if isinstance(func_name, str)
is False or func_name
is None:
751 _func_name = inspect.currentframe()
752 if _func_name.f_back
is not None:
753 func_name = _func_name.f_back.f_code.co_name
755 func_name = _func_name.f_code.co_name
758 def log_warning(self, string: str =
"", func_name: Union[str,
None] =
None) ->
None:
760 @brief Log a warning message.
762 @param string The message to log.
763 @param func_name The name of the calling function.
765 if isinstance(func_name, str)
is False or func_name
is None:
766 _func_name = inspect.currentframe()
767 if _func_name.f_back
is not None:
768 func_name = _func_name.f_back.f_code.co_name
770 func_name = _func_name.f_code.co_name
773 def log_error(self, string: str =
"", func_name: Union[str,
None] =
None) ->
None:
775 @brief Log an error message.
777 @param string The message to log.
778 @param func_name The name of the calling function.
780 if isinstance(func_name, str)
is False or func_name
is None:
781 _func_name = inspect.currentframe()
782 if _func_name.f_back
is not None:
783 func_name = _func_name.f_back.f_code.co_name
785 func_name = _func_name.f_code.co_name
788 def log_critical(self, string: str =
"", func_name: Union[str,
None] =
None) ->
None:
790 @brief Log a critical message.
792 @param string The message to log.
793 @param func_name The name of the calling function.
795 if isinstance(func_name, str)
is False or func_name
is None:
796 _func_name = inspect.currentframe()
797 if _func_name.f_back
is not None:
798 func_name = _func_name.f_back.f_code.co_name
800 func_name = _func_name.f_code.co_name
805 @brief Close the log file if it was opened.
810 "The file was not opened, no need to close it",
811 inspect.currentframe().f_code.co_name
819 @brief Return the generated string.
821 @return The generated content.
829 @brief Generate the required amount of spaces for the padding of the shape.
831 @param string_length The length of the provided string.
832 @return The number of spaces required for the padding.
837 calculated_length = int(
840 if calculated_length % 2 == 1
and calculated_length != 0:
841 calculated_length += 1
850 @brief Open the file if required and add the current date and time.
864 @brief Check if an item is safe to write or not.
866 @param content The item to check.
867 @return True if the item is safe to write, False otherwise.
869 if isinstance(content, (str, int, float, tuple, complex, bytes, bytearray, memoryview))
is False:
875 @brief Create a string based on a character and a length.
877 @param length The length of the string.
878 @param character The character to use.
879 @return The created string.
881 line = [character
for i
in range(0, length)]
882 string =
"".join(line)
887 @brief Print the message letter by letter while applying a provided delay.
889 @param message The message to display.
890 @param delay The delay between each letter.
893 for letter
in message.split(
" "):
894 sys.stdout.write(letter)
896 sys.stdout.write(
" ")
899 for letter
in message:
900 sys.stdout.write(letter)
904 sys.stdout.write(message)
907 def animate_message(self, message: str =
"Hello World!", delay: float = 0.02) ->
None:
909 @brief Display or dump (to file) a message.
911 @param message The message to display or dump.
912 @param delay The delay between each letter.
915 message = f
"{message}"
925 @brief Display a message in a box.
927 @param msg The message to display.
928 @param char The character to use for the box.
930 @note The text is displayed in the center of the box, it is just difficult to show that in a function comment.\n
931 @note This is a sample box (characters and dimensions depend on the provided configuration):\n
932 @example #############################\n
933 @example # Sample text #\n
934 @example #############################
941 lines = msg.split(
"\n")
943 string_length = len(i)
945 title_content += char
946 title_content += white_spaces
948 if string_length % 2 == 1
and string_length != 0:
949 white_spaces = white_spaces[:-1]
950 title_content += white_spaces
951 title_content += char
952 title_content +=
'\n'
954 string_length = len(msg)
957 title_content += char
958 title_content += white_spaces
960 if string_length % 2 == 1
and string_length != 0:
961 white_spaces = white_spaces[:-1]
962 title_content += white_spaces
963 title_content += char
964 title_content +=
"\n"
966 generated_content = f
"{box_wall}\n"
967 generated_content += f
"{title_content}"
968 generated_content += f
"{box_wall}"
970 f
"{generated_content}",
976 @brief Display a message in a rounded box.
978 @param msg The message to display.
980 @note The text is displayed in the center of the box, it is just difficult to show that in a function comment.\n
981 @note This is a sample box (characters and dimensions depend on the provided configuration):\n
982 @example ╔══════════════════════╗\n
983 @example ║ Sample text ║\n
984 @example ╚══════════════════════╝\n
996 "The top left corner is not defined, using the default one",
997 inspect.currentframe().f_code.co_name
1002 self.
nb_chr - offset_reset,
1008 "The horizontal line is not defined, using the default one",
1009 inspect.currentframe().f_code.co_name
1012 self.
nb_chr-offset_reset,
1020 "The top right corner is not defined, using the default one",
1021 inspect.currentframe().f_code.co_name
1032 "The bottom left corner is not defined, using the default one",
1033 inspect.currentframe().f_code.co_name
1039 self.
nb_chr-offset_reset,
1045 "The horizontal line is not defined, using the default one",
1046 inspect.currentframe().f_code.co_name
1049 self.
nb_chr-offset_reset,
1057 "The bottom right corner is not defined, using the default one",
1058 inspect.currentframe().f_code.co_name
1062 border_character =
""
1068 "The vertical line is not defined, using the default one",
1069 inspect.currentframe().f_code.co_name
1071 border_character =
"║"
1075 lines = msg.split(
"\n")
1077 string_length = len(i)
1079 center_content += border_character
1080 center_content += white_spaces
1082 if string_length % 2 == 1
and string_length != 0:
1083 white_spaces = white_spaces[:-1]
1084 center_content += white_spaces
1085 center_content += border_character
1086 center_content +=
'\n'
1088 string_length = len(msg)
1090 center_content += border_character
1091 center_content += white_spaces
1092 center_content += msg
1093 if string_length % 2 == 1
and string_length != 0:
1094 white_spaces = white_spaces[:-1]
1095 center_content += white_spaces
1096 center_content += border_character
1097 center_content +=
"\n"
1099 generated_content = f
"{top_wall}\n"
1100 generated_content += f
"{center_content}"
1101 generated_content += f
"{bottom_wall}"
1103 f
"{generated_content}",
1109 @brief Display a message in a box with different side and top characters.
1111 @param msg The message to display.
1113 @note The text is displayed in the center of the box, it is just difficult to show that in a function comment.\n
1114 @note This is a sample box (characters and dimensions depend on the provided configuration):\n
1115 @example _____________________________\n
1116 @example | Sample text |\n
1117 @example _____________________________
1125 "The ceiling boxes are not defined, using the default one",
1126 inspect.currentframe().f_code.co_name
1130 border_character =
""
1136 "The border character is not defined, using the default one",
1137 inspect.currentframe().f_code.co_name
1139 border_character =
"|"
1145 lines = msg.split(
"\n")
1147 string_length = len(i)
1149 title_content += border_character
1150 title_content += white_spaces
1152 if string_length % 2 == 1
and string_length != 0:
1153 white_spaces = white_spaces[:-1]
1154 title_content += white_spaces
1155 title_content += border_character
1156 title_content +=
'\n'
1158 string_length = len(msg)
1160 title_content += border_character
1161 title_content += white_spaces
1162 title_content += msg
1163 if string_length % 2 == 1
and string_length != 0:
1164 white_spaces = white_spaces[:-1]
1165 title_content += white_spaces
1166 title_content += border_character
1167 title_content +=
"\n"
1169 generated_content = f
"{box_wall}\n"
1170 generated_content += f
"{title_content}"
1171 generated_content += f
"{box_wall}"
1173 f
"{generated_content}",
1179 @brief Print a box format without internal vertical bars.
1181 @param message The message to display.
1182 @param character The character to use for the box.
1183 @note The text is displayed in the center of the box, it is just difficult to show that in a function comment.\n
1184 @note This is a sample box (characters and dimensions depend on the provided configuration):\n
1185 @example #############################\n
1186 @example Sample text \n
1187 @example #############################
1194 "The box character is not defined, using the default one",
1195 inspect.currentframe().f_code.co_name
1203 lines = message.split(
"\n")
1205 string_length = len(i)
1207 title_content += white_spaces
1209 if string_length % 2 == 1
and string_length != 0:
1210 white_spaces = white_spaces[:-1]
1211 title_content += white_spaces
1212 title_content +=
'\n'
1214 string_length = len(message)
1216 title_content += white_spaces
1217 title_content += message
1218 if string_length % 2 == 1
and string_length != 0:
1219 white_spaces = white_spaces[:-1]
1220 title_content += white_spaces
1221 title_content +=
"\n"
1223 generated_content = f
"{box_wall}\n"
1224 generated_content += f
"{title_content}"
1225 generated_content += f
"{box_wall}"
1227 f
"{generated_content}",
1233 @brief Display a message in a box with vertical bars.
1235 @param msg The message to display.
1236 @param character The character to use for the box.
1237 @note The text is displayed in the center of the box, it is just difficult to show that in a function comment.\n
1238 @note This is a sample box (characters and dimensions depend on the provided configuration):\n
1239 @example ###############\n
1243 @example # Sample text #\n
1247 @example ###############
1252 elif character ==
'':
1255 "The box character is not defined, using the default one",
1256 inspect.currentframe().f_code.co_name
1264 lines = msg.split(
"\n")
1266 string_length = len(i)
1268 title_content += character
1269 title_content += white_spaces
1271 if string_length % 2 == 1
and string_length != 0:
1272 white_spaces = white_spaces[:-1]
1273 title_content += white_spaces
1274 title_content += character
1275 title_content +=
"\n"
1278 string_length = len(msg)
1280 title_content += character
1281 title_content += white_spaces
1282 title_content += msg
1283 if string_length % 2 == 1
and string_length != 0:
1284 white_spaces = white_spaces[:-1]
1285 title_content += white_spaces
1286 title_content += character
1287 title_content +=
"\n"
1294 inner_line = f
"{character}{inner_line}{character}"
1296 generated_content = f
"{box_wall}\n"
1298 max_height = (inner_length / 4) - len(msg)
1304 while i < max_height:
1305 generated_content += f
"{inner_line}\n"
1307 generated_content += f
"{title_content}"
1309 while i < max_height:
1310 generated_content += f
"{inner_line}\n"
1313 generated_content += f
"{box_wall}"
1316 f
"{generated_content}",
1322 @brief Print a box format without internal horizontal bars.
1324 @param message The message to display.
1325 @param character The character to use for the box.
1326 @note The text is displayed in the center of the box, it is just difficult to show that in a function comment.\n
1327 @note This is a sample box (characters and dimensions depend on the provided configuration):\n
1333 @example # Sample text #\n
1342 elif character ==
'':
1345 "The box character is not defined, using the default one",
1346 inspect.currentframe().f_code.co_name
1352 lines = message.split(
"\n")
1354 string_length = len(i)
1356 title_content += character
1357 title_content += white_spaces
1359 if string_length % 2 == 1
and string_length != 0:
1360 white_spaces = white_spaces[:-1]
1361 title_content += white_spaces
1362 title_content += character
1363 title_content +=
"\n"
1366 string_length = len(message)
1368 title_content += character
1369 title_content += white_spaces
1370 title_content += message
1371 if string_length % 2 == 1
and string_length != 0:
1372 white_spaces = white_spaces[:-1]
1373 title_content += white_spaces
1374 title_content += character
1375 title_content +=
"\n"
1383 inner_line = f
"{character}{inner_line}{character}"
1385 generated_content =
""
1387 max_height = (inner_length / 4) - len(message)
1393 while i < max_height:
1394 generated_content += f
"{inner_line}\n"
1396 generated_content += f
"{title_content}"
1398 while i < max_height:
1399 if i+1 >= max_height:
1400 generated_content += f
"{inner_line}"
1402 generated_content += f
"{inner_line}\n"
1406 f
"{generated_content}",
1412 @brief Print a beautified title.
1414 @param title The title to display.
1420 @brief Print a beautified subtitle.
1422 @param sub_title The subtitle to display.
1428 @brief Print a beautified sub-subtitle.
1430 @param sub_sub_title The sub-subtitle to display.
1434 def message(self, message: Union[str, list]) ->
None:
1436 @brief Print a beautified message.
1438 @param message The message to display.
1439 @note This function displays the provided message using the 'MESSAGE_CHARACTER' key in the toml configuration\n
1440 @note Here is an example for the output (This is determined by the key repeated twice)\n
1441 @example @@ This is an example message @@
1443 if isinstance(message, list)
is True:
1445 m_msg = f
"{self.message_char}{self.message_char} {msg} "
1446 m_msg += f
"{self.message_char}{self.message_char}"
1452 msg = f
"{self.message_char}{self.message_char} "
1453 msg += f
" {message} {self.message_char}{self.message_char}"
1461 @brief Print a beautified error message.
1463 @param message The error message to display.
1464 @note This function displays the provided message using the 'MESSAGE_CHARACTER' key in the toml configuration\n
1465 @note Here is an example for the output (This is determined by the key repeated twice)\n
1466 @example ## This is an example message ##
1468 if isinstance(message, list)
is True:
1469 m_msg = f
"{self.message_error_char}{self.message_error_char} Error: "
1470 m_msg += f
"{self.message_error_char}{self.message_error_char}"
1472 m_msg = f
"{self.message_error_char}{self.message_error_char} {msg} "
1473 m_msg += f
"{self.message_error_char}{self.message_error_char}"
1479 msg = f
"{self.message_error_char}{self.message_error_char} Error:"
1480 msg += f
" {message} {self.message_error_char}{self.message_error_char}"
1488 @brief Print a beautified success message.
1490 @param message The success message to display.
1491 @note This function displays the provided message using the 'MESSAGE_CHARACTER' key in the toml configuration\n
1492 @note Here is an example for the output (This is determined by the key repeated twice)\n
1493 @example // This is an example message //
1495 if isinstance(message, list)
is True:
1496 m_msg = f
"{self.message_success_char}{self.message_success_char} Success: "
1497 m_msg += f
"{self.message_success_char}{self.message_success_char}"
1499 m_msg = f
"{self.message_success_char}{self.message_success_char} {msg} "
1500 m_msg += f
"{self.message_success_char}{self.message_success_char}"
1506 msg = f
"{self.message_success_char}{self.message_success_char} Success:"
1507 msg += f
" {message} {self.message_success_char}{self.message_success_char}"
1515 @brief Print a beautified warning message.
1517 @param message The warning message to display.
1518 @note This function displays the provided message using the 'MESSAGE_CHARACTER' key in the toml configuration\n
1519 @note Here is an example for the output (This is determined by the key repeated twice)\n
1520 @example !! This is an example message !!
1522 if isinstance(message, list)
is True:
1523 m_msg = f
"{self.message_warning_char}{self.message_warning_char} Warning: "
1524 m_msg += f
"{self.message_warning_char}{self.message_warning_char}"
1526 m_msg = f
"{self.message_warning_char}{self.message_warning_char} {msg} "
1527 m_msg += f
"{self.message_warning_char}{self.message_warning_char}"
1533 msg = f
"{self.message_warning_char}{self.message_warning_char} Warning:"
1534 msg += f
" {message} {self.message_warning_char}{self.message_warning_char}"
1542 @brief Print a beautified question message.
1544 @param message The question message to display.
1545 @note This function displays the provided message using the 'MESSAGE_CHARACTER' key in the toml configuration\n
1546 @note Here is an example for the output (This is determined by the key repeated twice)\n
1547 @example ?? This is an example message ??
1549 if isinstance(message, list)
is True:
1550 m_msg = f
"{self.message_question_char}{self.message_question_char} Question: "
1551 m_msg += f
"{self.message_question_char}{self.message_question_char}"
1553 m_msg = f
"{self.message_question_char}{self.message_question_char} {msg} "
1554 m_msg += f
"{self.message_question_char}{self.message_question_char}"
1560 msg = f
"{self.message_question_char}{self.message_question_char} Question:"
1561 msg += f
" {message} {self.message_question_char}{self.message_question_char}"
1569 @brief Print a beautified information message.
1571 @param message The information message to display.
1572 @note This function displays the provided message using the 'MESSAGE_CHARACTER' key in the toml configuration\n
1573 @note Here is an example for the output (This is determined by the key repeated twice)\n
1574 @example ii This is an example message ii
1576 if isinstance(message, list)
is True:
1578 m_msg = f
"{self.message_inform_char}{self.message_inform_char} {msg} "
1579 m_msg += f
"{self.message_inform_char}{self.message_inform_char}"
1585 msg = f
"{self.message_inform_char}{self.message_inform_char} {message} "
1586 msg += f
"{self.message_inform_char}{self.message_inform_char}"
1592 def _tree_node(self, line: str, offset: int, index: int, max_lenght: int) -> str:
1594 @brief Display a line of the tree.
1596 @param line The line to display.
1597 @param offset The offset for the line.
1598 @param index The index of the line.
1599 @param max_lenght The maximum length of the tree.
1600 @return The processed line.
1601 @note The characters displayed in this tree function is managed by the following keys:\n
1602 @note * TREE_NODE_CHAR\n
1603 @note * TREE_NODE_END_CHAR\n
1604 @note * TREE_LINE_SEPERATOR_CHAR\n
1605 @note * TREE_COLUMN_SEPERATOR_CHAR\n
1606 @example Here is an example generated by this function:\n
1607 @example ├─── data1\n
1610 processed_line = str()
1613 processed_line += f
"{self.tree_column_seperator_char} "
1615 if index
is max_lenght:
1616 processed_line += f
"{self.tree_node_end_char}{self.tree_line_seperator_char}"
1617 processed_line += f
"{self.tree_line_seperator_char}{self.tree_line_seperator_char}"
1619 processed_line += f
"{self.tree_node_char}{self.tree_line_seperator_char}"
1620 processed_line += f
"{self.tree_line_seperator_char}{self.tree_line_seperator_char}"
1623 processed_line +=
" "
1624 processed_line += line
1625 processed_line +=
'\n'
1626 return processed_line
1628 def tree(self, title: str, data: List[str], offset: int = 0) -> Union[str,
None]:
1630 @brief Print a list under the form of a beautified tree.
1632 @param title The title of the tree.
1633 @param data The data to display in the tree.
1634 @param offset The offset for the tree.
1635 @note The characters displayed in this tree function is managed by the following keys:\n
1636 @note * TREE_NODE_CHAR\n
1637 @note * TREE_NODE_END_CHAR\n
1638 @note * TREE_LINE_SEPERATOR_CHAR\n
1639 @note * TREE_COLUMN_SEPERATOR_CHAR\n
1640 @example Here is an example generated by this function:\n
1641 @example ├─── data1\n
1643 @return A stringified version of the tree if not set to be displayed.
1645 generated_content =
""
1647 generated_content += f
"{title}\n"
1648 length = len(data) - 1
1650 for line
in enumerate(data):
1651 if isinstance(data, list)
and isinstance(line[1], (list, dict)):
1658 generated_content += self.
tree(line[0], line[1], offset + 1)
1660 if isinstance(data, dict)
and isinstance(data[line[1]], (list, dict)):
1667 generated_content += self.
tree(
1673 if isinstance(data, dict)
and isinstance(data[line[1]], dict)
is False:
1675 f
"{line[1]}: {data[line[1]]}",
1689 f
"{generated_content}",
1693 return generated_content
1697 @brief Add the date and time at which the program was launched.
1698 @note The text is displayed in the center of the box, it is just difficult to show that in a function comment.\n
1699 @note This is a sample box (characters and dimensions depend on the provided configuration):\n
1700 @example ########################################\n
1701 @example # Run date: 07/06/2024 22:26:10 #\n
1702 @example ########################################
1704 self.
title(f
"Run date: {time.strftime('%d/%m/%Y %H:%M:%S')} ")
1708 @brief Test function to ensure all implemented methods work as expected.
1711 "test_data1":
"test_data1.1",
1712 "test_data2":
"test_data2.1",
1714 "test_data_list3.1",
1715 "test_data_list3.2",
1716 "test_data_list3.3",
1717 "test_data_list3.4",
1720 "test_data4":
"test_data4.1",
1722 "test_data5.1":
"test_data5.1.1",
1723 "test_data5.2":
"test_data5.2.1",
1724 "test_data5.3":
"test_data5.3.1",
1725 "test_data5.4":
"test_data5.4.1"
1729 "test_data6.1":
"test_data6.1.1",
1730 "test_data6.2":
"test_data6.2.1"
1733 "test_data_list6.3.1",
1734 "test_data_list6.3.1",
1735 "test_data_list6.3.1",
1736 "test_data_list6.3.1"
1741 "test_data7.1.1":
"test_data7.1.1.1",
1742 "test_data7.1.2":
"test_data7.1.2.1"
1760 self.
title(
"Test title")
1766 "Test Disp diff side and top message box"
1770 self.
tree(
"Test data", test_data)
1780 logging.WARNING,
"This is a test warning for custom level messages"
1782 custom_level_int = 2
1783 level_name =
"DARLING"
1791 f
"The custom level '{level_name}' could not be added, please check the configuration"
1796 f
"This is a test for custom level message \"{logging.getLevelName(custom_level_int)}\""
1798 custom_level_int = 196
1799 level_name =
"Ikuno"
1807 f
"The custom level '{level_name}' could not be added, please check the configuration"
1812 f
"This is a test for custom level message \"{logging.getLevelName(custom_level_int)}\""
1817if __name__ ==
"__main__":
1819 toml_content=TOML_CONF,
1821 file_name=
"test_run.tmp",
1822 file_descriptor=
None,
Union[int, str] _check_level_data(self, Union[str, int] level_name, logging.Logger logger_instance=None)
str create_string(self, length, character)
None disp_print_custom_level(self, Union[int, str] level, str string, Union[str, None] func_name=None)
None log_info(self, str string="", Union[str, None] func_name=None)
None disp_round_message_box(self, str msg="Sample text")
None question_message(self, Union[str, list] message)
None disp_print_error(self, str string="", Union[str, None] func_name=None)
int update_logging_colour_background(self, Union[str, int] colour, Union[str, int] level_name, logging.Logger logger_instance=None)
None append_run_date(self)
Union[str, None] tree(self, str title, List[str] data, int offset=0)
None box_vertical_no_horizontal(self, str message, str character="")
None log_error(self, str string="", Union[str, None] func_name=None)
str get_generated_content(self)
bool _is_safe(self, any content)
None log_warning(self, str string="", Union[str, None] func_name=None)
None sub_sub_title(self, str sub_sub_title)
None disp_print_warning(self, str string="", Union[str, None] func_name=None)
None _add_function_to_instance(self, object func_dest, str func_name, str func_code)
None disp_print_critical(self, str string="", Union[str, None] func_name=None)
None animate_message(self, str message="Hello World!", float delay=0.02)
None success_message(self, Union[str, list] message)
None disp_print_info(self, str string="", Union[str, None] func_name=None)
None __init__(self, Dict[str, any] toml_content, bool save_to_file=False, str file_name="text_output_run.txt", any file_descriptor=None, bool debug=False, Union[Logging, str, None] logger=None, int success=SUCCESS, int error=ERR, bool log_warning_when_present=True, bool log_errors_when_present=True)
str _tree_node(self, str line, int offset, int index, int max_lenght)
_create_function(self, name, func_code)
logging.Logger _check_the_logging_instance(self, logging.Logger logger_instance=None)
None log_critical(self, str string="", Union[str, None] func_name=None)
None log_custom_level(self, Union[int, str] level, str string, Union[str, None] func_name=None)
Union[None, colorlog.ColoredFormatter] _get_colour_formatter(self, logging.Logger logger_instance=None)
None sub_title(self, str sub_title)
None disp_message_box(self, str msg, str char="#")
None update_disp_debug(self, bool debug)
None log_debug(self, str string="", Union[str, None] func_name=None)
Union[int, str] _check_colour_data(self, Union[str, int] colour, logging.Logger logger_instance=None)
tree_column_seperator_char
None disp_box_no_vertical(self, str message, str character="@")
str _calculate_required_spaces(self, int string_length)
None _setup_logger(self, Union[Logging, str, None] logger)
None display_animation(self, str message="Hello World!", float delay=0.02)
None title(self, str title)
None error_message(self, Union[str, list] message)
str background_colour_key
None message(self, Union[str, list] message)
int update_logging_colour_text(self, Union[str, int] colour, Union[str, int] level_name, logging.Logger logger_instance=None)
None warning_message(self, Union[str, list] message)
None disp_diff_side_and_top_message_box(self, str msg)
None update_logger_level(self, Union[int, LogLevelTracker.Levels] level=LogLevelTracker.Levels.NOTSET)
None disp_print_debug(self, str string="", Union[str, None] func_name=None)
None disp_vertical_message_box(self, str msg, str character='')
None inform_message(self, Union[str, List] message)
int add_custom_level(self, int level, str name, Union[int, str] colour_text="", Union[int, str] colour_bg="")
None test_the_class(self)