From e7f3406704594ecd9efc9162523c468af20c29a7 Mon Sep 17 00:00:00 2001 From: Justin Mitchell Date: Wed, 1 Jul 2026 12:10:15 -0400 Subject: [PATCH] fix: Add socket module import to build-sd-fonts script (#2504) (#2505) --- lib/EpdFont/scripts/build-sd-fonts.py | 42 ++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/EpdFont/scripts/build-sd-fonts.py b/lib/EpdFont/scripts/build-sd-fonts.py index 406cbb88..bede32fb 100755 --- a/lib/EpdFont/scripts/build-sd-fonts.py +++ b/lib/EpdFont/scripts/build-sd-fonts.py @@ -33,6 +33,7 @@ import sys import tempfile import threading import time +import socket import urllib.request from concurrent.futures import ProcessPoolExecutor, as_completed from pathlib import Path @@ -49,17 +50,44 @@ INSTANCE_DIR = SCRIPT_DIR / "instanced_fonts" DEFAULT_FALLBACK_FONT = EPDFONTS_DIR / "builtinFonts/source/NotoSans/NotoSans-Regular.ttf" -def download_font(url: str, dest: Path) -> Path: - """Download a font file if not already cached. Returns the local path.""" +_orig_getaddrinfo = socket.getaddrinfo + + +def _ipv4_only_getaddrinfo(*args, **kwargs): + """getaddrinfo variant that drops AAAA records (IPv4 only).""" + return [ai for ai in _orig_getaddrinfo(*args, **kwargs) if ai[0] == socket.AF_INET] + + +def download_font(url: str, dest: Path, retries: int = 3) -> Path: + """Download a font file if not already cached. Returns the local path. + + Some sources (e.g. mirrors.ctan.org) are round-robin redirectors that land + on a different mirror each request; a mirror may advertise an IPv6 address a + host without an IPv6 route cannot reach ([Errno 101] Network is unreachable). + Retry on failure, forcing IPv4 resolution after the first attempt. + """ if dest.exists(): return dest dest.parent.mkdir(parents=True, exist_ok=True) print(f" Downloading {dest.name}...") - try: - urllib.request.urlretrieve(url, dest) - except Exception as e: - dest.unlink(missing_ok=True) - raise RuntimeError(f"Failed to download {url}: {e}") from e + last_err = None + for attempt in range(1, retries + 1): + force_ipv4 = attempt > 1 + if force_ipv4: + socket.getaddrinfo = _ipv4_only_getaddrinfo + try: + urllib.request.urlretrieve(url, dest) + break + except Exception as e: # noqa: BLE001 - reported via RuntimeError below + last_err = e + dest.unlink(missing_ok=True) + if attempt < retries: + print(f" Attempt {attempt} failed ({e}); retrying (IPv4-only)...") + finally: + if force_ipv4: + socket.getaddrinfo = _orig_getaddrinfo + else: + raise RuntimeError(f"Failed to download {url}: {last_err}") from last_err size_kb = dest.stat().st_size / 1024 print(f" Downloaded {dest.name} ({size_kb:.0f} KB)") return dest