Files
LEDMatrix/src/common
Chuck 6bea1a7c21 fix(sports): say when the schema cannot be read, instead of failing silently (#520)
_schema_font_size swallowed every exception and cached an empty dict. That is
not cosmetic. With no schema, a configured font size can no longer be compared
against the schema default, so every size is treated as a deliberate user
choice and skips the snap to the font's pixel grid -- which renders
4x6-font.ttf at 6 instead of 7: a 3px-wide glyph instead of 4px.

That shipped. On a 256x64 panel it made the odds, the team records and the date
row hard to read, and it was found by a user counting pixels on a photo of the
panel rather than by anything here. The cause (_plugin_dir returning None under
the real plugin loader) is fixed in #519; this makes the same class of failure
audible next time:

    Orphan: could not read config_schema.json (FileNotFoundError: ...); every
    font size will be treated as user-chosen and will skip its pixel grid
    snap. Font sizes may render a pixel narrow.

The message names the consequence, not just the error, because the error alone
does not suggest "your fonts are a pixel narrow".

Logged rather than raised: an unreadable schema must not stop a plugin
rendering. The cache is built once per class (per schema path in sports_card),
so this cannot repeat per frame.

Scope deliberately small. An audit of the three shared modules found 23 handlers
that swallow and return a default, but all 23 catch specific types -- TypeError,
ValueError, ImportError -- turning bad config values into defaults, which is
what they are for. Of 77 broad handlers across the font and odds paths, 74
already log. Only these two were both broad and silent.
2026-09-04 16:01:50 -04:00
..
2025-12-27 14:15:49 -05:00
2025-12-27 14:15:49 -05:00

Common Utilities

This directory contains reusable utilities and helpers for LEDMatrix plugins and core modules.

Adaptive Layout & Images (src/adaptive_layout.py, src/adaptive_images.py)

The recommended way to lay out plugins that render legibly on any panel size (64x32 through 256x128+) without hand-tuned coordinates. Re-exported from src.common for convenience; canonical import paths are src.adaptive_layout / src.adaptive_images.

# Every BasePlugin already has self.layout and the draw helpers:
regs = scoreboard_regions(self.layout.bounds, ctx=self.layout)
self.draw_image(away_logo, regs.away_slot, mode="fill_height",
                crop_to_ink=True, cache_key=f"logo:{abbr}")
self.draw_fit(score_text, regs.score_area)     # largest crisp font that fits
self.draw_fit(status, regs.status_band)

Key pieces: Region (rect algebra: bands/columns/splits/offset), font ladders (LADDER_GRID, LADDER_ARCADE — discrete crisp sizes, never fractional scaling), LayoutContext (fit_text, fit_image, by_tier, px), and composite carvers scoreboard_regions() / media_row(). Full guide: docs/ADAPTIVE_LAYOUT.md.

Error Handling (error_handler.py)

Common error handling patterns and utilities:

  • handle_file_operation() - Handle file I/O with consistent error handling
  • handle_json_operation() - Handle JSON operations with error handling
  • safe_execute() - Safely execute operations with error handling
  • retry_on_failure() - Decorator for retrying failed operations
  • log_and_continue() - Log non-critical errors and continue
  • log_and_raise() - Log errors and raise exceptions

Example Usage

from src.common.error_handler import handle_json_operation, safe_execute

# Handle JSON loading
config = handle_json_operation(
    lambda: json.load(open('config.json')),
    "Failed to load config",
    logger,
    default={}
)

# Safe execution with error handling
result = safe_execute(
    lambda: risky_operation(),
    "Operation failed",
    logger,
    default=None
)

API Helpers (api_helper.py)

Utilities for making HTTP requests and handling API responses.

Configuration Helpers (config_helper.py)

Utilities for loading, saving, and validating configuration files.

Display Helpers (display_helper.py)

Utilities for rendering content to the LED matrix display.

Game Helpers (game_helper.py)

Utilities for processing game data and team information.

Logo Helpers (logo_helper.py)

Utilities for loading and managing team logos.

Text Helpers (text_helper.py)

Utilities for text processing and formatting.

Scroll Helpers (scroll_helper.py)

Utilities for scrolling text on the display.

General Utilities (utils.py)

General-purpose utility functions:

  • Team abbreviation normalization
  • Time formatting
  • Boolean parsing
  • Logger creation (deprecated - use src.logging_config.get_logger())

Permission Utilities (permission_utils.py)

Helpers for ensuring directory permissions and ownership are correct when running as a service (used by CacheManager to set up its persistent cache directory).

Best Practices

  1. Use centralized logging: Import from src.logging_config instead of creating loggers directly
  2. Use error handlers: Use error_handler utilities for consistent error handling
  3. Reuse utilities: Check existing utilities before creating new ones
  4. Document additions: Add documentation when adding new utilities