From c3cb7a4da96c1be1110fb2dba229b17628136e43 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Tue, 7 Apr 2026 22:57:26 +0300 Subject: [PATCH] fix(dist): stop stripping .py sources; wipe payload on NSIS upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two root causes for the 'imaging extension was built for another version of Pillow' error users hit after install: 1) cleanup_site_packages ran 'find ... -name "*.py" ! -name "__init__.py" -delete' with a comment claiming 'keep .pyc only' — but no compileall step exists. Result: the dist shipped __init__.py + .pyd only, missing every submodule (Image.py, ImageDraw.py, _version.py, ...). Fresh installs were broken; in-place upgrades produced a half-old/half-new site-packages. Removed the deletion entirely. 2) NSIS installer extracted over the previous install without cleaning python/, app/, scripts/. Upgrades left stale files (old PIL/_version.py next to new PIL/_imaging.pyd) which raised the Pillow ABI mismatch. Wipe those subtrees before File /r, preserving config.yaml at the install root. --- build-common.sh | 11 +++++++++-- installer.nsi | 13 +++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/build-common.sh b/build-common.sh index c1b0d98..a29f39a 100644 --- a/build-common.sh +++ b/build-common.sh @@ -109,6 +109,13 @@ cleanup_site_packages() { # Strip debug symbols from native extensions find "$sp_dir" -name "*.$ext_suffix" -exec strip --strip-debug {} \; 2>/dev/null || true - # Remove .py source files (keep .pyc only) — saves ~30-40% on pure-Python packages - find "$sp_dir" -name "*.py" ! -name "__init__.py" -delete 2>/dev/null || true + # NOTE: do NOT strip .py source files. A previous version of this function + # ran `find ... -name "*.py" ! -name "__init__.py" -delete` with a comment + # claiming "keep .pyc only" — but no compileall step exists, so the dist + # shipped with __init__.py + .pyd only, missing every submodule (Image.py, + # ImageDraw.py, _version.py, ...). Fresh installs would fail with + # ModuleNotFoundError; in-place upgrades over an older install produced a + # half-old/half-new site-packages where PIL/__init__.py was new but + # PIL/_version.py was stale, yielding the runtime "_imaging extension was + # built for another version of Pillow" import error. } diff --git a/installer.nsi b/installer.nsi index e7a9229..dc4ee10 100644 --- a/installer.nsi +++ b/installer.nsi @@ -70,6 +70,19 @@ Section "!Core (required)" SecCore SetOutPath "$INSTDIR" + ; Wipe previous payload before extracting so stale files from an older + ; version cannot survive an upgrade. Without this, in-place upgrades + ; produce a half-old/half-new site-packages — e.g. an old PIL/_version.py + ; alongside a new PIL/_imaging.pyd, which raises "_imaging extension was + ; built for another version of Pillow" at runtime. config.yaml lives at + ; $INSTDIR root and is preserved. + RMDir /r "$INSTDIR\python" + RMDir /r "$INSTDIR\app" + RMDir /r "$INSTDIR\scripts" + Delete "$INSTDIR\${EXENAME}" + Delete "$INSTDIR\VERSION" + Delete "$INSTDIR\config.example.yaml" + ; Copy entire distribution File /r "dist\media-server\*.*"