From 6ec350af6933ca66cfd609cbe825f22dc0ddb5d6 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 26 Dec 2025 11:30:29 -0800 Subject: [PATCH] Fix Windows FastAPI import: Replace collect_all with collect_submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Research findings: - collect_all() has design flaws and poor performance with pydantic - Pydantic uses compiled cpython extensions that prevent module discovery - collect_submodules() is the recommended approach per PyInstaller docs Changes: - Replaced collect_all() with collect_submodules() for better reliability - Now collects 105 pydantic submodules (vs unreliable collect_all) - Added collect_data_files() for packages requiring data files - Added explicit pydantic dependencies: colorsys, decimal, json, etc. - Applies to both Windows AND Linux (no longer platform-specific) Results: ✓ Collected 52 submodules from fastapi ✓ Collected 34 submodules from starlette ✓ Collected 105 submodules from pydantic ✓ Collected 3 submodules from pydantic_core ✓ Plus uvicorn, websockets, h11, anyio Fixes: ModuleNotFoundError: No module named 'fastapi' on Windows Based on: https://github.com/pyinstaller/pyinstaller/issues/5359 --- local-transcription.spec | 45 ++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/local-transcription.spec b/local-transcription.spec index 8749a1f..dfca379 100644 --- a/local-transcription.spec +++ b/local-transcription.spec @@ -10,9 +10,8 @@ block_cipher = None # Determine if we're on Windows is_windows = sys.platform == 'win32' -# Import PyInstaller utilities (only needed on Windows for FastAPI fix) -if is_windows: - from PyInstaller.utils.hooks import collect_all +# Import PyInstaller utilities +from PyInstaller.utils.hooks import collect_submodules, collect_data_files # Find faster_whisper assets folder import faster_whisper @@ -82,19 +81,33 @@ hiddenimports = [ 'charset_normalizer', ] -# On Windows, use more aggressive collection for FastAPI packages -# This fixes import errors on Windows while keeping Linux builds stable -if is_windows: - print("Windows build: Using collect_all for FastAPI packages...") - for package in ['fastapi', 'starlette', 'pydantic', 'pydantic_core', 'anyio', 'uvicorn', 'websockets', 'h11']: - try: - tmp_datas, tmp_binaries, tmp_hiddenimports = collect_all(package) - datas += tmp_datas - binaries += tmp_binaries - hiddenimports += tmp_hiddenimports - print(f" ✓ Collected {package}") - except Exception as e: - print(f" ⚠ Warning: Could not collect {package}: {e}") +# Collect all submodules for FastAPI and related packages +# This approach is more reliable than collect_all() which has design flaws +# Particularly important for pydantic which uses compiled cpython extensions +print("Collecting submodules for FastAPI packages...") +for package in ['fastapi', 'starlette', 'pydantic', 'pydantic_core', 'anyio', 'uvicorn', 'websockets', 'h11', 'httptools', 'uvloop']: + try: + submodules = collect_submodules(package) + hiddenimports += submodules + print(f" ✓ Collected {len(submodules)} submodules from {package}") + except Exception as e: + print(f" ⚠ Warning: Could not collect {package}: {e}") + +# Collect data files for packages that need them +for package in ['fastapi', 'starlette', 'pydantic', 'uvicorn']: + try: + data_files = collect_data_files(package) + if data_files: + datas += data_files + print(f" ✓ Collected {len(data_files)} data files from {package}") + except Exception as e: + pass # Not all packages have data files + +# Add critical pydantic dependencies that may be missed +hiddenimports += [ + 'colorsys', 'decimal', 'json', 'ipaddress', 'pathlib', 'uuid', + 'email.message', 'typing_extensions', +] a = Analysis( ['main.py'],