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.
- __init__.py +0 -0
- config_cli_gui/__init__.py +0 -0
- config_cli_gui/_version.py +21 -0
- config_cli_gui/cli_generator.py +177 -0
- config_cli_gui/config_framework.py +362 -0
- config_cli_gui/gui_generator.py +225 -0
- config_cli_gui-0.0.2.dist-info/METADATA +282 -0
- config_cli_gui-0.0.2.dist-info/RECORD +26 -0
- config_cli_gui-0.0.2.dist-info/WHEEL +5 -0
- config_cli_gui-0.0.2.dist-info/entry_points.txt +3 -0
- config_cli_gui-0.0.2.dist-info/licenses/LICENSE +24 -0
- config_cli_gui-0.0.2.dist-info/top_level.txt +4 -0
- example_project/__init__.py +0 -0
- example_project/__main__.py +8 -0
- example_project/cli/__init__.py +0 -0
- example_project/cli/__main__.py +8 -0
- example_project/cli/cli.py +132 -0
- example_project/config/__init__.py +0 -0
- example_project/config/config.py +209 -0
- example_project/core/__init__.py +0 -0
- example_project/core/base.py +634 -0
- example_project/core/logging.py +219 -0
- example_project/gui/__init__.py +0 -0
- example_project/gui/__main__.py +8 -0
- example_project/gui/gui.py +542 -0
- main.py +153 -0
|
@@ -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
|