Coverage for src / ezqt_app / utils / printer.py: 39.60%
77 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-26 07:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-26 07:07 +0000
1# ///////////////////////////////////////////////////////////////
2# UTILS.PRINTER - Console output utility
3# Project: ezqt_app
4# ///////////////////////////////////////////////////////////////
6"""Centralized console printer with consistent formatting and color support."""
8from __future__ import annotations
10# ///////////////////////////////////////////////////////////////
11# IMPORTS
12# ///////////////////////////////////////////////////////////////
13# Third-party imports
14from colorama import Fore, Style
17# ///////////////////////////////////////////////////////////////
18# CLASSES
19# ///////////////////////////////////////////////////////////////
20class Printer:
21 """Centralized printer for console output with consistent formatting.
23 Provides methods for different message types:
24 - Info messages (light gray)
25 - Success messages (green)
26 - Warning messages (yellow)
27 - Error messages (red)
28 - Verbose messages (light black, gated by ``verbose`` flag)
29 """
31 def __init__(self, verbose: bool = False, debug: bool = False):
32 self.verbose = verbose
33 self.debug = debug
35 def info(self, message: str, prefix: str = "~") -> None:
36 """Print an info message."""
37 print(f"{Fore.WHITE}{prefix} {message}{Style.RESET_ALL}")
39 def success(self, message: str, prefix: str = "✓") -> None:
40 """Print a success message."""
41 print(f"{Fore.GREEN}{prefix} {message}{Style.RESET_ALL}")
43 def warning(self, message: str, prefix: str = "!") -> None:
44 """Print a warning message."""
45 print(f"{Fore.YELLOW}{prefix} {message}{Style.RESET_ALL}")
47 def error(self, message: str, prefix: str = "✗") -> None:
48 """Print an error message."""
49 print(f"{Fore.RED}{prefix} {message}{Style.RESET_ALL}")
51 def verbose_msg(self, message: str, prefix: str = "~") -> None:
52 """Print a verbose message (only when ``verbose`` is enabled)."""
53 if self.verbose: 53 ↛ 54line 53 didn't jump to line 54 because the condition on line 53 was never true
54 print(f"{Fore.LIGHTBLACK_EX}{prefix} {message}{Style.RESET_ALL}")
56 def debug_msg(self, message: str, prefix: str = "~") -> None:
57 """Print a debug message (only when ``debug`` is enabled)."""
58 if self.verbose or self.debug: 58 ↛ 59line 58 didn't jump to line 59 because the condition on line 58 was never true
59 print(f"{Fore.LIGHTBLACK_EX}{prefix} {message}{Style.RESET_ALL}")
61 def action(self, message: str, prefix: str = "+") -> None:
62 """Print an action message (blue)."""
63 print(f"{Fore.BLUE}{prefix} {message}{Style.RESET_ALL}")
65 def init(self, message: str, prefix: str = "🚀") -> None:
66 """Print an initialization message (magenta)."""
67 print(f"{Fore.MAGENTA}{prefix} {message}{Style.RESET_ALL}")
69 def section(self, title: str, prefix: str = "=") -> None:
70 """Print a section header with separator."""
71 separator = prefix * (len(title) + 4)
72 print(f"{Fore.CYAN}{separator}{Style.RESET_ALL}")
73 print(f"{Fore.CYAN}{prefix} {title} {prefix}{Style.RESET_ALL}")
74 print(f"{Fore.CYAN}{separator}{Style.RESET_ALL}")
76 def config_display(self, config_data: dict, _title: str = "Configuration") -> None:
77 """Display configuration data in a formatted ASCII box."""
78 self.action("[System] Loaded application settings.")
79 print(f"{Fore.LIGHTBLACK_EX}...{Style.RESET_ALL}")
80 print(
81 f"{Fore.LIGHTBLACK_EX} ┌───────────────────────────────────────────────┐{Style.RESET_ALL}"
82 )
83 for key, val in config_data.items():
84 print(
85 f"{Fore.LIGHTBLACK_EX} |- {key}: {Fore.LIGHTWHITE_EX}{val}{Style.RESET_ALL}"
86 )
87 print(
88 f"{Fore.LIGHTBLACK_EX} └───────────────────────────────────────────────┘{Style.RESET_ALL}"
89 )
90 print(f"{Fore.LIGHTBLACK_EX}...{Style.RESET_ALL}")
92 def list_items(
93 self, items: list[str], title: str | None = None, max_items: int = 3
94 ) -> None:
95 """Print a (possibly truncated) list of items."""
96 if title:
97 self.info(title)
98 if items:
99 display_items = items[:max_items]
100 items_str = ", ".join(display_items)
101 if len(items) > max_items:
102 items_str += "..."
103 self.verbose_msg(f" {items_str}")
104 else:
105 self.verbose_msg(" (no items)")
107 def file_operation(
108 self, operation: str, file_path: str, status: str = "completed"
109 ) -> None:
110 """Print a file operation message."""
111 if status == "completed":
112 self.info(f"[{operation}] {file_path}")
113 elif status == "error":
114 self.error(f"[{operation}] {file_path}")
115 elif status == "warning":
116 self.warning(f"[{operation}] {file_path}")
118 def custom_print(
119 self, message: str, color: str = "WHITE", prefix: str = ""
120 ) -> None:
121 """Print a message with an arbitrary colorama color name."""
122 color_attr = getattr(Fore, color.upper(), Fore.WHITE)
123 prefix_part = f"{prefix} " if prefix else ""
124 print(f"{color_attr}{prefix_part}{message}{Style.RESET_ALL}")
126 def raw_print(self, message: str) -> None:
127 """Print a raw message without any formatting."""
128 print(message)
130 def qrc_compilation_result(
131 self, success: bool, error_message: str | None = None
132 ) -> None:
133 """Print QRC compilation result."""
134 if success:
135 self.action("[Initializer] Generated binaries definitions from QRC file.")
136 else:
137 self.warning("[Initializer] QRC compilation skipped")
138 if error_message:
139 self.debug_msg(f"[Initializer] Error details: {error_message}")
142# ///////////////////////////////////////////////////////////////
143# SINGLETONS
144# ///////////////////////////////////////////////////////////////
145_default_printer = Printer()
148# ///////////////////////////////////////////////////////////////
149# FUNCTIONS
150# ///////////////////////////////////////////////////////////////
151def get_printer(verbose: bool | None = None, debug: bool | None = None) -> Printer:
152 """Return a printer configured from global defaults and optional overrides."""
153 resolved_verbose = _default_printer.verbose if verbose is None else bool(verbose)
154 resolved_debug = _default_printer.debug if debug is None else bool(debug)
155 if (
156 resolved_verbose == _default_printer.verbose
157 and resolved_debug == _default_printer.debug
158 ):
159 return _default_printer
160 return Printer(verbose=resolved_verbose, debug=resolved_debug)
163def set_global_verbose(verbose: bool) -> None:
164 """Set the global verbose mode on the default printer instance."""
165 global _default_printer
166 _default_printer = Printer(verbose=verbose, debug=_default_printer.debug)
169def set_global_debug(debug: bool) -> None:
170 """Set the global debug mode on the default printer instance."""
171 global _default_printer
172 _default_printer = Printer(verbose=_default_printer.verbose, debug=debug)