config-cli-gui 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ """Centralized logging configuration for config_cli_gui.
2
+
3
+ This module provides a unified logging setup that supports:
4
+ - File logging with rotation
5
+ - GUI integration via custom handler
6
+ - Configurable log levels from config
7
+ - Structured logging with consistent formatting
8
+ """
9
+
10
+ import logging
11
+ import logging.handlers
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ from config_cli_gui.config_framework import ConfigManager
16
+
17
+
18
+ class GuiLogHandler(logging.Handler):
19
+ """Custom logging handler that can write to GUI text widgets."""
20
+
21
+ def __init__(self, gui_writer=None):
22
+ super().__init__()
23
+ self.gui_writer = gui_writer
24
+ self.setFormatter(
25
+ logging.Formatter("%(asctime)s - %(levelname)s - %(message)s", datefmt="%H:%M:%S")
26
+ )
27
+
28
+ def emit(self, record):
29
+ """Emit a log record to the GUI if writer is available."""
30
+ if self.gui_writer:
31
+ try:
32
+ msg = self.format(record) + "\n"
33
+ self.gui_writer.write(msg)
34
+ except Exception:
35
+ # Fail silently to avoid recursive logging errors
36
+ pass
37
+
38
+
39
+ class LoggerManager:
40
+ """Manages all logging configuration and handlers."""
41
+
42
+ def __init__(self, config: ConfigManager):
43
+ self.config = config
44
+ self.logger = logging.getLogger("config_cli_gui")
45
+ self.gui_handler = None
46
+ self.file_handler = None
47
+ self.console_handler = None
48
+ self._setup_logging()
49
+
50
+ def _setup_logging(self):
51
+ """Configure all logging handlers and formatters."""
52
+ # Clear any existing handlers
53
+ self.logger.handlers.clear()
54
+
55
+ # Set log level from config
56
+ log_level = getattr(logging, self.config.get_category("app").log_level.default.upper())
57
+ self.logger.setLevel(log_level)
58
+
59
+ # Create formatters
60
+ detailed_formatter = logging.Formatter(
61
+ "%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
62
+ )
63
+ simple_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
64
+
65
+ # Setup file handler with rotation
66
+ self._setup_file_handler(detailed_formatter)
67
+
68
+ # Setup console handler
69
+ self._setup_console_handler(simple_formatter)
70
+
71
+ # Setup GUI handler (will be connected later if needed)
72
+ self._setup_gui_handler()
73
+
74
+ def _setup_file_handler(self, formatter):
75
+ """Setup rotating file handler."""
76
+ log_dir = Path("logs")
77
+ log_dir.mkdir(exist_ok=True)
78
+
79
+ log_file = log_dir / "config_cli_gui.log"
80
+
81
+ # Use RotatingFileHandler to prevent huge log files
82
+ self.file_handler = logging.handlers.RotatingFileHandler(
83
+ log_file,
84
+ maxBytes=10 * 1024 * 1024, # 10MB
85
+ backupCount=5,
86
+ encoding="utf-8",
87
+ )
88
+ self.file_handler.setFormatter(formatter)
89
+ self.logger.addHandler(self.file_handler)
90
+
91
+ def _setup_console_handler(self, formatter):
92
+ """Setup console handler for CLI output."""
93
+ self.console_handler = logging.StreamHandler(sys.stdout)
94
+ self.console_handler.setFormatter(formatter)
95
+ self.logger.addHandler(self.console_handler)
96
+
97
+ def _setup_gui_handler(self):
98
+ """Setup GUI handler (initially without writer)."""
99
+ self.gui_handler = GuiLogHandler()
100
+ # Don't add to logger yet - will be done when GUI connects
101
+
102
+ def connect_gui_writer(self, gui_writer):
103
+ """Connect a GUI writer to the logging system.
104
+
105
+ Args:
106
+ gui_writer: Object with write() method (like the LogHandler from gui.py)
107
+ """
108
+ if self.gui_handler:
109
+ # Remove old handler if it exists
110
+ if self.gui_handler in self.logger.handlers:
111
+ self.logger.removeHandler(self.gui_handler)
112
+
113
+ # Create new handler with GUI writer
114
+ self.gui_handler = GuiLogHandler(gui_writer)
115
+ self.logger.addHandler(self.gui_handler)
116
+
117
+ def disconnect_gui_writer(self):
118
+ """Disconnect GUI writer (useful when GUI closes)."""
119
+ if self.gui_handler and self.gui_handler in self.logger.handlers:
120
+ self.logger.removeHandler(self.gui_handler)
121
+
122
+ def get_logger(self, name: str = None) -> logging.Logger:
123
+ """Get a logger instance.
124
+
125
+ Args:
126
+ name: Logger name (defaults to main project logger)
127
+
128
+ Returns:
129
+ Logger instance
130
+ """
131
+ if name:
132
+ return logging.getLogger(f"config_cli_gui.{name}")
133
+ return self.logger
134
+
135
+ def set_log_level(self, level: str):
136
+ """Change log level dynamically.
137
+
138
+ Args:
139
+ level: New log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
140
+ """
141
+ log_level = getattr(logging, level.upper())
142
+ self.logger.setLevel(log_level)
143
+
144
+ # Update config
145
+ self.config.get_category("app").log_level.default = level.upper()
146
+
147
+ def log_config_summary(self):
148
+ """Log current configuration summary."""
149
+ self.logger.info("=== Configuration Summary ===")
150
+ self.logger.info(f"Log level: {self.config.get_category('app').log_level.default}")
151
+ self.logger.info(f"Input: {self.config.get_category('cli').input.default}")
152
+ self.logger.info(f"Output: {self.config.get_category('cli').output.default}")
153
+ self.logger.info(f"Max workers: {self.config.get_category('app').max_workers.default}")
154
+ self.logger.info("==============================")
155
+
156
+
157
+ # Global logger manager instance
158
+ _logger_manager = None
159
+
160
+
161
+ def initialize_logging(config: ConfigManager) -> LoggerManager:
162
+ """Initialize the global logging system.
163
+
164
+ Args:
165
+ config: Configuration manager instance
166
+
167
+ Returns:
168
+ LoggerManager instance
169
+ """
170
+ global _logger_manager
171
+ _logger_manager = LoggerManager(config)
172
+ return _logger_manager
173
+
174
+
175
+ def get_logger(name: str = None) -> logging.Logger:
176
+ """Get a logger instance.
177
+
178
+ Args:
179
+ name: Logger name (optional)
180
+
181
+ Returns:
182
+ Logger instance
183
+
184
+ Raises:
185
+ RuntimeError: If logging not initialized
186
+ """
187
+ if _logger_manager is None:
188
+ raise RuntimeError("Logging not initialized. Call initialize_logging() first.")
189
+ return _logger_manager.get_logger(name)
190
+
191
+
192
+ def get_logger_manager() -> LoggerManager:
193
+ """Get the global logger manager.
194
+
195
+ Returns:
196
+ LoggerManager instance
197
+
198
+ Raises:
199
+ RuntimeError: If logging not initialized
200
+ """
201
+ if _logger_manager is None:
202
+ raise RuntimeError("Logging not initialized. Call initialize_logging() first.")
203
+ return _logger_manager
204
+
205
+
206
+ def connect_gui_logging(gui_writer):
207
+ """Connect GUI writer to logging system.
208
+
209
+ Args:
210
+ gui_writer: GUI writer object with write() method
211
+ """
212
+ if _logger_manager:
213
+ _logger_manager.connect_gui_writer(gui_writer)
214
+
215
+
216
+ def disconnect_gui_logging():
217
+ """Disconnect GUI from logging system."""
218
+ if _logger_manager:
219
+ _logger_manager.disconnect_gui_writer()
File without changes
@@ -0,0 +1,8 @@
1
+ """GUI entry point for project_name."""
2
+
3
+ import sys # pragma: no cover
4
+
5
+ from .gui import main # pragma: no cover
6
+
7
+ if __name__ == "__main__": # pragma: no cover
8
+ sys.exit(main())