mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-06 19:28:06 +00:00
chore: remove dead modules confirmed unused in-repo and across all store plugins
- src/common/cli.py: imports a 'ledmatrix_common' package that exists nowhere (not in this repo, any requirements file, or the plugin monorepo), so it cannot ever have run; its README section claimed scripts/dev/* used it, which was also untrue. - src/web_interface/logging_config.py: zero callers — the web app uses web_interface/logging_config.py (a different module), and nothing imports the src copy. - handle_errors decorator in src/web_interface/error_handler.py: zero call sites (the module's response helpers stay — they are used). - ConfigManager.get_clock_config(): reads a 'clock' config key that no longer exists anywhere; only caller was its own unit test. Deliberately kept despite zero in-repo callers: DisplayError, src/common/config_helper.py and display_helper.py — all documented as plugin-facing API (docs/PLUGIN_ERROR_HANDLING.md, src/common/README.md), and third-party plugins outside the official monorepo cannot be enumerated. Verified against a fresh clone of ledmatrix-plugins (43 plugins): zero references to any removed symbol. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SXb4mKcAkVaxkeTb3YnAdr
This commit is contained in:
@@ -99,11 +99,6 @@ Helpers for ensuring directory permissions and ownership are correct
|
||||
when running as a service (used by `CacheManager` to set up its
|
||||
persistent cache directory).
|
||||
|
||||
## CLI Helpers (`cli.py`)
|
||||
|
||||
Shared CLI argument parsing helpers used by `scripts/dev/*` and other
|
||||
command-line entry points.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use centralized logging**: Import from `src.logging_config` instead of creating loggers directly
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
"""
|
||||
LEDMatrix Common CLI
|
||||
|
||||
Command-line interface for LEDMatrix Common utilities.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
"""Main CLI entry point."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="LEDMatrix Common Utilities",
|
||||
prog="ledmatrix-common"
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
||||
|
||||
# Test command
|
||||
test_parser = subparsers.add_parser('test', help='Test common utilities')
|
||||
test_parser.add_argument('--display-width', type=int, default=128, help='Display width')
|
||||
test_parser.add_argument('--display-height', type=int, default=64, help='Display height')
|
||||
|
||||
# Validate command
|
||||
validate_parser = subparsers.add_parser('validate', help='Validate configuration')
|
||||
validate_parser.add_argument('config_file', help='Configuration file to validate')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == 'test':
|
||||
test_utilities(args.display_width, args.display_height)
|
||||
elif args.command == 'validate':
|
||||
validate_config(args.config_file)
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
def test_utilities(display_width: int, display_height: int):
|
||||
"""Test common utilities."""
|
||||
print(f"Testing LEDMatrix Common utilities with {display_width}x{display_height} display")
|
||||
|
||||
try:
|
||||
from ledmatrix_common import LogoHelper, TextHelper, DisplayHelper, GameHelper, ConfigHelper
|
||||
|
||||
# Test LogoHelper
|
||||
print("Testing LogoHelper...")
|
||||
logo_helper = LogoHelper(display_width, display_height)
|
||||
print(f"Logo cache stats: {logo_helper.get_cache_stats()}")
|
||||
|
||||
# Test TextHelper
|
||||
print("Testing TextHelper...")
|
||||
text_helper = TextHelper()
|
||||
fonts = text_helper.load_fonts()
|
||||
print(f"Loaded {len(fonts)} fonts")
|
||||
|
||||
# Test DisplayHelper
|
||||
print("Testing DisplayHelper...")
|
||||
display_helper = DisplayHelper(display_width, display_height)
|
||||
img = display_helper.create_base_image()
|
||||
print(f"Created {img.size} base image")
|
||||
|
||||
# Test GameHelper
|
||||
print("Testing GameHelper...")
|
||||
GameHelper()
|
||||
print("GameHelper initialized")
|
||||
|
||||
# Test ConfigHelper
|
||||
print("Testing ConfigHelper...")
|
||||
ConfigHelper()
|
||||
print("ConfigHelper initialized")
|
||||
|
||||
print("All tests passed!")
|
||||
|
||||
except ImportError as e:
|
||||
print(f"Import error: {e}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"Test error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def validate_config(config_file: str):
|
||||
"""Validate configuration file."""
|
||||
config_path = Path(config_file)
|
||||
|
||||
if not config_path.exists():
|
||||
print(f"Configuration file not found: {config_file}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from ledmatrix_common import ConfigHelper
|
||||
|
||||
config_helper = ConfigHelper()
|
||||
config = config_helper.load_config(config_path)
|
||||
|
||||
if config:
|
||||
print(f"Configuration loaded successfully from {config_file}")
|
||||
print(f"Found {len(config)} top-level keys")
|
||||
else:
|
||||
print(f"Failed to load configuration from {config_file}")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Validation error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -449,10 +449,6 @@ class ConfigManager:
|
||||
"""Get display configuration."""
|
||||
return self.config.get('display', {})
|
||||
|
||||
def get_clock_config(self) -> Dict[str, Any]:
|
||||
"""Get clock configuration."""
|
||||
return self.config.get('clock', {})
|
||||
|
||||
def get_config(self) -> Dict[str, Any]:
|
||||
"""Get the full configuration dictionary.
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"""
|
||||
Centralized error handling for web interface.
|
||||
|
||||
Provides decorators and helpers for consistent error handling across API endpoints.
|
||||
Provides helpers for consistent error responses across API endpoints.
|
||||
"""
|
||||
|
||||
import functools
|
||||
from typing import Callable, Any, Optional
|
||||
from typing import Any, Optional
|
||||
from flask import jsonify
|
||||
|
||||
from src.web_interface.errors import (
|
||||
@@ -17,70 +16,6 @@ from src.logging_config import get_logger
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def handle_errors(
|
||||
default_error_code: Optional[ErrorCode] = None,
|
||||
default_category: Optional[ErrorCategory] = None,
|
||||
log_error: bool = True
|
||||
):
|
||||
"""
|
||||
Decorator to handle errors in API endpoints.
|
||||
|
||||
Catches exceptions and converts them to structured error responses.
|
||||
|
||||
Args:
|
||||
default_error_code: Default error code if exception doesn't match known types
|
||||
default_category: Default error category
|
||||
log_error: Whether to log the error
|
||||
"""
|
||||
def decorator(func: Callable) -> Callable:
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except WebInterfaceError as e:
|
||||
# Already a structured error
|
||||
if log_error:
|
||||
logger.error(
|
||||
f"Error in {func.__name__}: {e.message}",
|
||||
extra={
|
||||
'error_code': e.error_code.value,
|
||||
'category': e.category.value,
|
||||
'context': e.context
|
||||
}
|
||||
)
|
||||
return jsonify(e.to_dict()), 500
|
||||
|
||||
except Exception as e:
|
||||
# Convert to structured error
|
||||
web_error = WebInterfaceError.from_exception(
|
||||
e,
|
||||
error_code=default_error_code,
|
||||
context={
|
||||
'function': func.__name__,
|
||||
'endpoint': getattr(func, '__name__', 'unknown')
|
||||
}
|
||||
)
|
||||
|
||||
if default_category:
|
||||
web_error.category = default_category
|
||||
|
||||
if log_error:
|
||||
logger.error(
|
||||
f"Unhandled error in {func.__name__}: {e}",
|
||||
exc_info=True,
|
||||
extra={
|
||||
'error_code': web_error.error_code.value,
|
||||
'category': web_error.category.value,
|
||||
'context': web_error.context
|
||||
}
|
||||
)
|
||||
|
||||
return jsonify(web_error.to_dict()), 500
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def create_error_response(
|
||||
error_code: ErrorCode,
|
||||
message: str,
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
"""
|
||||
Structured logging configuration for web interface.
|
||||
|
||||
Provides JSON-formatted structured logging for better debugging and monitoring.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
|
||||
class StructuredFormatter(logging.Formatter):
|
||||
"""
|
||||
JSON formatter for structured logging.
|
||||
|
||||
Formats log records as JSON for easy parsing and analysis.
|
||||
"""
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
"""Format log record as JSON."""
|
||||
log_data = {
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'level': record.levelname,
|
||||
'logger': record.name,
|
||||
'message': record.getMessage(),
|
||||
'module': record.module,
|
||||
'function': record.funcName,
|
||||
'line': record.lineno
|
||||
}
|
||||
|
||||
# Add exception info if present
|
||||
if record.exc_info:
|
||||
log_data['exception'] = self.formatException(record.exc_info)
|
||||
|
||||
# Add extra fields from record
|
||||
if hasattr(record, 'extra'):
|
||||
log_data.update(record.extra)
|
||||
|
||||
# Add context from record
|
||||
if hasattr(record, 'context'):
|
||||
log_data['context'] = record.context
|
||||
|
||||
return json.dumps(log_data)
|
||||
|
||||
def formatException(self, exc_info) -> Dict[str, Any]:
|
||||
"""Format exception as structured data."""
|
||||
import traceback
|
||||
return {
|
||||
'type': exc_info[0].__name__ if exc_info[0] else None,
|
||||
'message': str(exc_info[1]) if exc_info[1] else None,
|
||||
'traceback': traceback.format_exception(*exc_info)
|
||||
}
|
||||
|
||||
|
||||
def setup_structured_logging(
|
||||
level: int = logging.INFO,
|
||||
use_json: bool = False,
|
||||
output_stream = sys.stdout
|
||||
) -> None:
|
||||
"""
|
||||
Set up structured logging for web interface.
|
||||
|
||||
Args:
|
||||
level: Logging level
|
||||
use_json: Whether to use JSON formatting
|
||||
output_stream: Output stream for logs
|
||||
"""
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.setLevel(level)
|
||||
|
||||
# Remove existing handlers
|
||||
for handler in root_logger.handlers[:]:
|
||||
root_logger.removeHandler(handler)
|
||||
|
||||
# Create handler
|
||||
handler = logging.StreamHandler(output_stream)
|
||||
handler.setLevel(level)
|
||||
|
||||
# Set formatter
|
||||
if use_json:
|
||||
formatter = StructuredFormatter()
|
||||
else:
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
handler.setFormatter(formatter)
|
||||
root_logger.addHandler(handler)
|
||||
|
||||
|
||||
def log_plugin_operation(
|
||||
logger: logging.Logger,
|
||||
operation: str,
|
||||
plugin_id: str,
|
||||
status: str,
|
||||
context: Optional[Dict[str, Any]] = None
|
||||
) -> None:
|
||||
"""
|
||||
Log a plugin operation with structured data.
|
||||
|
||||
Args:
|
||||
logger: Logger instance
|
||||
operation: Operation name (install, update, uninstall, etc.)
|
||||
plugin_id: Plugin identifier
|
||||
status: Operation status (success, failed, etc.)
|
||||
context: Optional additional context
|
||||
"""
|
||||
extra = {
|
||||
'operation': operation,
|
||||
'plugin_id': plugin_id,
|
||||
'status': status
|
||||
}
|
||||
|
||||
if context:
|
||||
extra['context'] = context
|
||||
|
||||
logger.info(
|
||||
f"Plugin operation: {operation} for {plugin_id} - {status}",
|
||||
extra=extra
|
||||
)
|
||||
|
||||
|
||||
def log_config_change(
|
||||
logger: logging.Logger,
|
||||
config_key: str,
|
||||
action: str,
|
||||
before: Optional[Dict[str, Any]] = None,
|
||||
after: Optional[Dict[str, Any]] = None,
|
||||
context: Optional[Dict[str, Any]] = None
|
||||
) -> None:
|
||||
"""
|
||||
Log a configuration change with before/after values.
|
||||
|
||||
Args:
|
||||
logger: Logger instance
|
||||
config_key: Configuration key that changed
|
||||
action: Action performed (save, update, delete, etc.)
|
||||
before: Configuration before change
|
||||
after: Configuration after change
|
||||
context: Optional additional context
|
||||
"""
|
||||
extra = {
|
||||
'config_key': config_key,
|
||||
'action': action
|
||||
}
|
||||
|
||||
if before:
|
||||
extra['before'] = before
|
||||
if after:
|
||||
extra['after'] = after
|
||||
if context:
|
||||
extra['context'] = context
|
||||
|
||||
logger.info(
|
||||
f"Config change: {action} on {config_key}",
|
||||
extra=extra
|
||||
)
|
||||
|
||||
@@ -383,19 +383,6 @@ class TestConfigHelpers:
|
||||
display_config = manager.get_display_config()
|
||||
assert display_config["hardware"]["rows"] == 32
|
||||
|
||||
def test_get_clock_config(self, tmp_path):
|
||||
"""Test getting clock config."""
|
||||
config_file = tmp_path / "config.json"
|
||||
config_data = {"clock": {"format": "12h"}}
|
||||
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(config_data, f)
|
||||
|
||||
manager = ConfigManager(config_path=str(config_file))
|
||||
manager.load_config()
|
||||
|
||||
clock_config = manager.get_clock_config()
|
||||
assert clock_config["format"] == "12h"
|
||||
|
||||
|
||||
class TestPluginConfigManagement:
|
||||
|
||||
Reference in New Issue
Block a user