Make mypy run again, fix what it found, and add one check script

a7fb56c pinned mypy to Python 3.11 for the Pi. In a dev venv that also has the
analysis extra, numpy's stubs use 3.12 syntax, so mypy aborted on them before
checking a single file - and from the next day on four real errors went in
unnoticed (ebab03b, 0ef5a04, 9fd106b). Skip following numpy/librosa instead; only
librosa_analyzer.py touches them. Fix the four errors (variables reused with a
narrower type).

There is no CI, so scripts/check.sh is the gate: pytest, ruff, mypy, vitest, tsc
and eslint in one command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 00:22:25 +02:00
parent 14b5133543
commit b791beed9c
6 changed files with 69 additions and 12 deletions

View File

@@ -106,6 +106,8 @@ ruff check .
mypy # --strict, configured in pyproject.toml
```
`../scripts/check.sh` runs these three plus the web front-end's checks in one go.
`tests/test_wire.py` parses `../esp-firmware/src/Messages.h` and fails if the Python
message ids drift from the firmware's - the contract is hand-duplicated in two
languages, and it had already drifted once (`BUTTON_EVENT` was missing on the Python

View File

@@ -343,12 +343,12 @@ def shrink_cover(
for a browser to decode.
"""
try:
with Image.open(io.BytesIO(data)) as image:
if max(image.size) <= max_px:
with Image.open(io.BytesIO(data)) as source:
if max(source.size) <= max_px:
return data
# JPEG can decode straight to 1/2, 1/4 or 1/8 size; a no-op for other formats.
image.draft("RGB", (max_px, max_px))
image = image.convert("RGB")
source.draft("RGB", (max_px, max_px))
image = source.convert("RGB")
# `thumbnail` keeps the aspect ratio and never scales up.
image.thumbnail((max_px, max_px), Image.Resampling.LANCZOS)
buffer = io.BytesIO()

View File

@@ -135,9 +135,9 @@ def _cover_for(
return cache.store_cover(identifier, art), art
for path in paths[:3]:
# Podcast feeds sometimes art only some episodes; a couple of tries is enough.
art = _embedded_art(path)
if art is not None:
return cache.store_cover(identifier, art), art
embedded = _embedded_art(path)
if embedded is not None:
return cache.store_cover(identifier, embedded), embedded
return None, None
@@ -155,9 +155,9 @@ def _cover_for_episode(
if candidate.is_file():
art = candidate.read_bytes()
return cache.store_cover(identifier, art), art
art = _embedded_art(path)
if art is not None:
return cache.store_cover(identifier, art), art
embedded = _embedded_art(path)
if embedded is not None:
return cache.store_cover(identifier, embedded), embedded
for name in _COVER_NAMES:
candidate = folder / name
if candidate.is_file():

View File

@@ -77,3 +77,12 @@ warn_unreachable = true
# checkout must still type-check clean, so it needs the same treatment as librosa.
module = ["vlc", "serial_asyncio", "ruamel.*", "librosa.*", "numpy", "feedparser"]
ignore_missing_imports = true
[[tool.mypy.overrides]]
# Recent numpy ships stubs written in 3.12+ syntax (`type X = ...`). Checking against
# the device's 3.11 makes mypy refuse to parse them and abort before checking anything
# at all - which is what happened in a dev venv that has the analysis extra. Treat the
# analysis stack as untyped instead: it is only touched by librosa_analyzer.py.
module = ["numpy", "numpy.*", "librosa", "librosa.*"]
follow_imports = "skip"
follow_imports_for_stubs = true