* feat(common): sports_helpers, the helpers all nine scoreboards copy verbatim
Add src/common/sports_helpers.py: the helpers the scoreboard plugins'
sports.py carry byte-identical copies of (docstring-stripped AST, checked at
ledmatrix-plugins f09bff2), so a later plugins PR can delete its copies once
it floors on the core release that ships this.
- Free functions: clamp_window, clamp_seconds, logo_needs_refresh (lazy
src.logo_downloader import, as in the plugins), spread_weighted_order,
MIN_WINDOW_DAYS / MAX_WINDOW_DAYS. All nine plugins.
- SportsHelpersMixin (no __init__, stateless): _mode_customization,
_setting_int, _reset_dwell_on_reentry, _next_switch_index,
_spread_weighted_order (all nine), _odds_color and
_upcoming_date_and_time_text (all but ufc), plus the _favorite_key seam
from base_classes core.py for later phases.
A new module rather than more methods on sports_shared: a plugin that
deletes a copy and relies on an existing module having grown the method
fails at runtime with AttributeError on an older core, which neither the
loader nor check_min_core_version.py can see; a missing module fails at load.
Tests: behaviour for every helper, a derived host contract, and a parity
test that AST-compares every body against every plugin copy when
LEDMATRIX_PLUGINS points at a checkout (skipped otherwise).
test_common_is_hardware_free.py imports src.common and every sports_* module
with rgbmatrix blocked and scans src/common for module-level imports of
src.base_classes, src.display_manager and src.plugin_system (no existing
violations).
Nothing in core imports the new module; no behaviour change. CHANGELOG
Unreleased entry and a converging note in docs/SPORTS_UNIFICATION.md.
__version__ is not bumped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(common): address review on sports_helpers and the hardware-free test
- SportsHelpersMixin docstring and CHANGELOG: constructor-free, but it keeps
lazy state on its host (_reset_dwell_on_reentry, _next_switch_index).
- test_common_is_hardware_free: the runtime check now filters every
FORBIDDEN package, src.plugin_system included; the AST scan resolves
relative imports against src.common, so `from .. import plugin_system`
and `from ..plugin_system import x` are caught. Guard tests for both.
- Parity skip reason names the CI guard that runs the same comparison:
ledmatrix-plugins scripts/check_sports_helpers_parity.py (#495).
- _odds_color: line-level pylint disable for a not-callable false positive
(getter is None-checked); the AST is unchanged, parity still passes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>