mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 08:48:05 +00:00
* chore: remove dead modules and unused dependencies (~1,180 LOC) Deletions, each re-verified with a fresh repo-wide grep (core, web, scripts, docs, plugin monorepo) immediately before removal: Modules with zero live importers: - src/background_cache_mixin.py + src/generic_cache_mixin.py (134+150 LOC — referenced only by each other) - src/font_test_manager.py (134 LOC) - src/image_utils.py (22 LOC, self-documented deprecated) - src/layout_manager.py (408 LOC — only its own test imported it) + test/test_layout_manager.py - src/common/basketball_plugin_example.py (328 LOC sample) requirements.txt entries with zero importers in core (pre-plugin-era manager deps): icalevents, geopy, timezonefinder, unidecode. Plus the google-auth trio (google-auth-oauthlib, google-auth-httplib2, google-api-python-client): their only importer is the calendar PLUGIN, which declares all three in its own requirements.txt (verified in the monorepo and on an installed copy) — the plugin dependency installer owns them. Existing venvs are unaffected (removal doesn't uninstall); fresh installs get them when calendar is installed. Two stale references cleaned (a comment in test_pillow_compat.py, a directory listing in HOW_TO_RUN_TESTS.md). Full suite green except the two documented pre-existing failures (circuit_breaker mock drift, fixed in #400; clock-simple 64x32 overflow, pre-dates this series); all core entry modules verified importing cleanly under the emulator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam * fix: remove content accidentally bundled into the dead-code-removal commit The previous commit's git add/commit swept in a lot of unrelated, unreviewed work alongside the intended dead-code deletions: a new Plugin Composer web UI, new security-audit/CI tooling, a new march-madness plugin, and 23 local-development-only symlinks under plugin-repos/ (per scripts/setup_plugin_repos.py's own docstring, these are meant to be generated locally, never committed -- .gitignore has no entry for them, which is how they slipped in). Removed here, split into their own PRs instead (except plugin-repos/* symlinks and march-madness/ncaa_logos, which are dropped rather than carried forward -- see PR discussion): - web_interface/blueprints/composer.py + composer-app.js + composer-canvas.js + composer.html + manager.py.j2 - scripts/prove_security.py, audit_plugins.py, generate_report.py - .github/workflows/security-audit.yml, .github/workflows/tests.yml, bandit.yaml - All plugin-repos/* symlinks (local dev artifacts, not meant to be committed at all) - plugin-repos/march-madness/* and the 4 new assets/sports/ncaa_logos/* PNGs it needed (left out of every split PR pending a decision on whether march-madness belongs in the core repo or the plugin monorepo) This PR now contains only what its title describes: the dead-code removal from the previous commit, untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ --------- Co-authored-by: Chuck <chuck@example.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
96 lines
3.2 KiB
Python
Executable File
96 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Pillow compatibility smoke test.
|
|
|
|
Exercises the Pillow APIs used throughout LEDMatrix to verify a new
|
|
Pillow version doesn't break image rendering, font handling, or resize ops.
|
|
|
|
Run after upgrading Pillow:
|
|
python3 scripts/dev/test_pillow_compat.py
|
|
"""
|
|
|
|
import sys
|
|
|
|
|
|
def check(label, fn):
|
|
try:
|
|
result = fn()
|
|
print(f" ✓ {label}" + (f" — {result}" if result is not None else ""))
|
|
return True
|
|
except Exception as e:
|
|
print(f" ✗ {label} — {type(e).__name__}: {e}", file=sys.stderr)
|
|
return False
|
|
|
|
|
|
def main():
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
import PIL
|
|
|
|
print(f"Pillow {PIL.__version__} on Python {sys.version.split()[0]}\n")
|
|
|
|
failures = 0
|
|
|
|
print("Image creation:")
|
|
failures += not check("Image.new RGB",
|
|
lambda: Image.new('RGB', (128, 32), (0, 0, 0)).size)
|
|
failures += not check("Image.new RGBA",
|
|
lambda: Image.new('RGBA', (64, 64), (255, 0, 0, 128)).size)
|
|
failures += not check("Image.new 1-bit",
|
|
lambda: Image.new('1', (16, 16)).size)
|
|
|
|
print("\nDraw operations:")
|
|
img = Image.new('RGB', (128, 32), (0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
font = ImageFont.load_default()
|
|
failures += not check("draw.rectangle",
|
|
lambda: draw.rectangle([0, 0, 127, 31], outline=(255, 0, 0)))
|
|
failures += not check("draw.text",
|
|
lambda: draw.text((2, 2), "Hello", fill=(255, 255, 255), font=font))
|
|
failures += not check("draw.line",
|
|
lambda: draw.line([0, 0, 127, 31], fill=(0, 255, 0)))
|
|
|
|
print("\nFont metrics (used in text_helper, scroll_helper):")
|
|
failures += not check("draw.textlength",
|
|
lambda: f"{draw.textlength('Test', font=font):.1f}px")
|
|
failures += not check("draw.textbbox",
|
|
lambda: draw.textbbox((0, 0), "Test", font=font))
|
|
|
|
print("\nResampling (used in logo_helper, sports base):")
|
|
logo = Image.new('RGBA', (200, 200), (255, 128, 0, 200))
|
|
failures += not check("Image.Resampling.LANCZOS exists",
|
|
lambda: str(Image.Resampling.LANCZOS))
|
|
failures += not check("thumbnail with LANCZOS",
|
|
lambda: (logo.thumbnail((64, 32), Image.Resampling.LANCZOS), logo.size)[1])
|
|
big = Image.new('RGB', (300, 300), (0, 128, 255))
|
|
failures += not check("resize with LANCZOS",
|
|
lambda: big.resize((128, 32), Image.Resampling.LANCZOS).size)
|
|
|
|
print("\nComposite / paste (used in display rendering):")
|
|
base = Image.new('RGB', (128, 32), (0, 0, 0))
|
|
overlay = Image.new('RGBA', (32, 32), (255, 0, 0, 128))
|
|
failures += not check("paste RGBA onto RGB",
|
|
lambda: (base.paste(overlay.convert('RGB'), (0, 0)), base.size)[1])
|
|
failures += not check("Image.alpha_composite",
|
|
lambda: Image.alpha_composite(
|
|
Image.new('RGBA', (32, 32)), overlay).size)
|
|
|
|
print("\nImage I/O:")
|
|
import io
|
|
buf = io.BytesIO()
|
|
img.save(buf, format='PNG')
|
|
buf.seek(0)
|
|
failures += not check("save/load PNG roundtrip",
|
|
lambda: Image.open(buf).size)
|
|
|
|
print()
|
|
if failures == 0:
|
|
print(f"All checks passed. Pillow {PIL.__version__} is compatible.")
|
|
return 0
|
|
else:
|
|
print(f"{failures} check(s) failed — review output above.", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|