Files
MeshTerm/meshterm/emulator/fetch.py
T
Jean-Pierre MartineauandClaude Opus 5.5 0b764e4a04 A script builds the Debian package for the Cardputer Zero's app store
scripts/cardputer-zero/build-deb.sh runs on arm64 Debian 13 (the
Cardputer itself) and makes meshterm_<version>-<revision>_arm64.deb.
MeshTerm and all its Python packages, the spi extra included, go in
/usr/lib/meshterm, compiled to bytecode. The package depends only on
python3: the store app installs a missing dependency with apt-get but
never runs apt-get update, so a Debian dependency can fail. For the
same reason the package carries Terminus beside its OFL, because an app
from the launcher has no terminal for --fetch-fonts.

packaging/notices.py gets a site-directory mode, because the walk of
the closure asks for no extras and so misses the spi packages.

scripts/cardputer-zero/store holds the listing that czdev publish reads
and a 256-pixel icon. The screenshots are still to make.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 02:39:27 -04:00

74 lines
3.0 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Download the Terminus 12-pixel fonts that the emulator draws in: one time, only on request.
Terminus has the SIL Open Font License. Thus the repository and the wheel never carry it.
The Debian package for the Cardputer Zero carries it next to its licence, because an app
from the launcher has no terminal in which to run this download. That build also uses
this module (refer to ``scripts/cardputer-zero/build-deb.sh``). This module
downloads the upstream release and checks that it is the correct archive. Then it
extracts ``ter-u12n.bdf``, ``ter-u12b.bdf``, and the licence next to them into the
directory that the emulator reads (:func:`~.font.font_dir`). ``meshterm emulate
--fetch-fonts`` runs it.
Python can fail to download the archive, for example when a certificate store rejects the
mirror. Then you can download the archive in a different way and give it with
``--archive``. In both cases, the module checks the archive against the same digest.
"""
from __future__ import annotations
import hashlib
import io
import tarfile
import urllib.request
from collections.abc import Callable
from pathlib import Path
from .font import BOLD_BDF, REGULAR_BDF, font_dir
URL = (
"https://sourceforge.net/projects/terminus-font/files/terminus-font-4.49/"
"terminus-font-4.49.1.tar.gz/download"
)
#: The SHA-256 of the release archive, so that a mirror cannot return a different file.
SHA256 = "d961c1b781627bf417f9b340693d64fc219e0113ad3a3af1a3424c7aa373ef79"
_ROOT = "terminus-font-4.49.1/"
_WANTED = (REGULAR_BDF, BOLD_BDF, "OFL.TXT")
def install_terminus(archive: Path | None = None, say: Callable[[str], None] = print) -> Path:
"""Download (or take) Terminus 4.49.1, check it, and install the fonts and the licence.
Args:
archive: The release archive, already downloaded. ``None`` downloads it.
say: Where the progress lines go.
Returns:
The directory that the fonts were installed into.
Raises:
ValueError: The archive is not the release (its digest is different), or a file
is missing from it.
OSError: The download or a write failed.
"""
target = font_dir()
if archive is not None:
say(f"installing Terminus 4.49.1 from {archive} into {target}")
data = archive.read_bytes()
else:
say(f"fetching Terminus 4.49.1 into {target}")
with urllib.request.urlopen(URL, timeout=60) as response: # noqa: S310 - fixed https URL
data = response.read()
digest = hashlib.sha256(data).hexdigest()
if digest != SHA256:
raise ValueError(f"refusing the archive: sha256 {digest} is not the release's {SHA256}")
target.mkdir(parents=True, exist_ok=True)
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as tar:
for name in _WANTED:
member = tar.extractfile(_ROOT + name)
if member is None:
raise ValueError(f"the archive has no {name}")
(target / name).write_bytes(member.read())
say(f" {name}")
return target