mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-18 04:49:13 +00:00
Eight findings, all reproduced before fixing.
scroll_config.configure() read the refresh rate *after* resolve() had
already used it. resolve() fills in target_fps, pixels_per_frame and the
judder warning from that rate, so on a 60Hz panel every one of them
described 100Hz -- and with snap_to_crisp=False nothing downstream
corrected it, so set_target_fps() paced the helper to 100 FPS. The rate
is now settled first, and falls back to the global config rather than
straight to the default.
refresh_hz_from_config() used `(cfg.get("display") or {}).get(...)`,
which raises AttributeError when either level is truthy but not a
mapping -- out of a function whose whole contract is a rate or a default.
The frame-stats line reported the upper-middle sample as the median and
the 96th sorted sample as p95 of 100. Both are also thresholds (stalls
at 1.5x the median, skips at 0.5x), so the counts were biased too. The
arithmetic is now in frame_stats()/format_frame_stats(), testable
without a clock.
configure()'s docstring and docs/SCROLL_PERFORMANCE.md still said it
applies the frame hold and warns when it cannot. It deliberately does
neither since "tie the frame hold to the scroll, not the plugin"; a
caller following the old text would omit set_scrolling_state() and slow
snapped speeds would still present every refresh.
disk_cache had no policy for non-finite floats: orjson writes null,
the stdlib writes NaN/Infinity, and orjson then rejects those legacy
files so DiskCache.get deleted them as corrupt. One behaviour on both
paths now -- write null, keep legacy records readable. allow_nan=False
detects the values; the replacement walk runs only when there is one,
so the ordinary write path is byte-identical and pays nothing.
build_rgbmatrix_nogil.sh picked the build artifact with a glob piped to
`head -1`, which sorts cpython-311 ahead of cpython-313, so a stale .so
staged in from the source tree was installed as core.so while the GIL
check -- which reads the generated core.cpp, not the .so -- still passed.
It now requires the current interpreter's exact ABI name and fails
closed. Its systemctl calls were also unchecked under `set -uo pipefail`:
a failed stop left the old service running, the following start
succeeded as a no-op, and the health check reported SUCCESS for a
binding that was never loaded.
orjson floor raised to 3.11.6 for CVE-2025-67221 (unbounded recursion
in dumps); it covers the project's Python 3.10-3.13 range.
Adds test/test_cache_nonfinite_floats.py (14) plus regression tests in
test_scroll_config.py and test_scroll_helper.py. 9 of the cache tests
and 9 of the scroll_config tests fail against the pre-fix code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9
75 lines
3.5 KiB
Plaintext
75 lines
3.5 KiB
Plaintext
# LEDMatrix Core Dependencies
|
|
# Compatible with Python 3.10, 3.11, 3.12, and 3.13
|
|
# Tested on Raspbian OS 12 (Bookworm) and 13 (Trixie)
|
|
|
|
# Image processing
|
|
Pillow>=12.2.0,<13.0.0
|
|
numpy>=1.24.0 # For fast array operations in ScrollHelper (compatible with 2.x)
|
|
|
|
# Timezone handling
|
|
pytz>=2024.2,<2025.0 # Updated for latest timezone data
|
|
|
|
# HTTP requests
|
|
requests>=2.33.0,<3.0.0
|
|
urllib3>=2.7.0,<3.0.0 # requests transitive, but imported directly (urllib3.util.retry.Retry); floor is a security floor, not the API floor — 1.26.x carries ~10 CVEs
|
|
|
|
# Google API integration
|
|
|
|
# Font rendering
|
|
freetype-py>=2.5.1,<3.0.0
|
|
|
|
# Spotify integration (used by web_interface/blueprints/api_v3.py OAuth endpoints)
|
|
spotipy>=2.25.2,<3.0.0
|
|
|
|
# Flask web framework
|
|
Flask>=3.1.3,<4.0.0
|
|
|
|
# WebSocket support: intentionally NOT declared here. Plugins that need
|
|
# it (e.g. ledmatrix-music's Socket.IO client) declare it in their own
|
|
# requirements.txt, which the plugin store installs.
|
|
|
|
# JSON Schema validation
|
|
jsonschema>=4.20.0,<5.0.0
|
|
|
|
# Requirement specifier parsing (plugin dependency satisfaction checks)
|
|
packaging>=23.0,<27.0
|
|
|
|
# Testing dependencies live in requirements-test.txt:
|
|
# pip install -r requirements.txt -r requirements-test.txt
|
|
|
|
# ───────────────────────────────────────────────────────────────────────
|
|
# Optional dependencies — the code imports these inside try/except
|
|
# blocks and gracefully degrades when missing. Install them for the
|
|
# full feature set, or skip them for a minimal install.
|
|
# ───────────────────────────────────────────────────────────────────────
|
|
#
|
|
# scipy — sub-pixel interpolation in
|
|
# src/common/scroll_helper.py for smoother
|
|
# scrolling. Falls back to a simpler shift algorithm.
|
|
# pip install 'scipy>=1.10.0,<2.0.0'
|
|
#
|
|
# psutil — per-plugin resource monitoring in
|
|
# src/plugin_system/resource_monitor.py. The monitor
|
|
# silently no-ops when missing (PSUTIL_AVAILABLE = False).
|
|
# Note: web_interface/requirements.txt requires this
|
|
# range as a hard dependency — keep the two in sync.
|
|
# pip install 'psutil>=6.0.0,<7.0.0'
|
|
#
|
|
# orjson — faster JSON for the disk cache
|
|
# (src/cache/disk_cache.py). Encoding a ~1MB cache
|
|
# record drops from ~12ms to ~1.6ms on a Pi 4, which
|
|
# matters because that work holds the GIL and stalls
|
|
# the render thread mid-scroll. Falls back to the
|
|
# stdlib json when missing — see docs/SCROLL_PERFORMANCE.md.
|
|
# The 3.11.6 floor is CVE-2025-67221: orjson.dumps did not
|
|
# limit recursion on deeply nested documents, and the disk
|
|
# cache encodes payloads parsed straight from third-party
|
|
# APIs. 3.11.6 covers the Python range above.
|
|
# pip install 'orjson>=3.11.6,<4.0'
|
|
#
|
|
# Flask-Limiter — request rate limiting in web_interface/app.py
|
|
# (accidental-abuse protection, not security). The
|
|
# web interface starts without rate limiting when
|
|
# this is missing.
|
|
# pip install 'Flask-Limiter>=3.5.0,<4.0.0'
|