Fix Windows FastAPI import: Replace collect_all with collect_submodules

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
This commit is contained in:
2025-12-26 11:30:29 -08:00
parent 926910177d
commit 6ec350af69

View File

@@ -10,9 +10,8 @@ block_cipher = None
# Determine if we're on Windows # Determine if we're on Windows
is_windows = sys.platform == 'win32' is_windows = sys.platform == 'win32'
# Import PyInstaller utilities (only needed on Windows for FastAPI fix) # Import PyInstaller utilities
if is_windows: from PyInstaller.utils.hooks import collect_submodules, collect_data_files
from PyInstaller.utils.hooks import collect_all
# Find faster_whisper assets folder # Find faster_whisper assets folder
import faster_whisper import faster_whisper
@@ -82,19 +81,33 @@ hiddenimports = [
'charset_normalizer', 'charset_normalizer',
] ]
# On Windows, use more aggressive collection for FastAPI packages # Collect all submodules for FastAPI and related packages
# This fixes import errors on Windows while keeping Linux builds stable # This approach is more reliable than collect_all() which has design flaws
if is_windows: # Particularly important for pydantic which uses compiled cpython extensions
print("Windows build: Using collect_all for FastAPI packages...") print("Collecting submodules for FastAPI packages...")
for package in ['fastapi', 'starlette', 'pydantic', 'pydantic_core', 'anyio', 'uvicorn', 'websockets', 'h11']: for package in ['fastapi', 'starlette', 'pydantic', 'pydantic_core', 'anyio', 'uvicorn', 'websockets', 'h11', 'httptools', 'uvloop']:
try: try:
tmp_datas, tmp_binaries, tmp_hiddenimports = collect_all(package) submodules = collect_submodules(package)
datas += tmp_datas hiddenimports += submodules
binaries += tmp_binaries print(f" Collected {len(submodules)} submodules from {package}")
hiddenimports += tmp_hiddenimports except Exception as e:
print(f" Collected {package}") print(f" Warning: Could not collect {package}: {e}")
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( a = Analysis(
['main.py'], ['main.py'],