macOS下PyInstaller.spec生成Bundle频繁遇模块缺失问题
PyInstaller打包macOS Bundle反复出现ModuleNotFoundError的解决思路
我在为项目生成macOS平台Bundle时遇到问题:仓库中的.spec文件在Windows上运行正常,但macOS下打包后启动总是抛出ModuleNotFoundError——先是缺openai,手动添加路径后又缺pydantic,逐个手动添加依赖显然不是长久之计。修改后的.spec文件如下:
# -*- mode: python ; coding: utf-8 -*- import os.path from pathlib import Path from PyInstaller.compat import is_darwin, is_win from PyInstaller.utils.hooks import copy_metadata, collect_data_files, collect_all datas = [] datas += collect_data_files("transformers", include_py_files=True) datas += collect_data_files("lightning") datas += collect_data_files("lightning_fabric") datas += collect_data_files("speechbrain") datas += collect_data_files("pyannote") datas += collect_data_files("asteroid_filterbanks") datas += collect_data_files("whisperx") datas += collect_data_files("librosa") datas += copy_metadata("tqdm") datas += copy_metadata("regex") datas += copy_metadata("requests") datas += copy_metadata("packaging") datas += copy_metadata("filelock") datas += copy_metadata("numpy") datas += copy_metadata("tokenizers") datas += copy_metadata("pillow") datas += copy_metadata("huggingface_hub") datas += copy_metadata("safetensors") datas += copy_metadata("pyyaml") datas += [("res", "res")] datas += [("config.ini", ".")] datas += [(".env", ".")] datas += [("venv/lib/python3.10/site-packages/customtkinter", "customtkinter")] datas += [("venv/lib/python3.10/site-packages/torch", "torch")] datas += [("venv/lib/python3.10/site-packages/torchgen", "torchgen")] datas += [("venv/lib/python3.10/site-packages/speech_recognition", "speech_recognition")] datas += [("venv/lib/python3.10/site-packages/moviepy", "moviepy")] datas += [("venv/lib/python3.10/site-packages/dotenv", "dotenv")] datas += [("venv/lib/python3.10/site-packages/openai", "openai")] hiddenimports = [ "huggingface_hub.repository", "pytorch", "sklearn.utils._cython_blas", "sklearn.neighbors.typedefs", "sklearn.neighbors.quad_tree", "sklearn.tree", "sklearn.tree._utils", "darkdetect", "tkinter.font", "tkinter.ttk", "cmath", "pickletools", "unittest.mock", "aifc", "proglog", "imageio", "decorator", ] block_cipher = None debug = True if debug: options = [("v", None, "OPTION")] else: options = [] a = Analysis( ["src/app.py"], pathex=[], binaries=[], datas=datas, hiddenimports=hiddenimports, hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) macos_icon = "res/macos/icon.icns" windows_icon = "res/windows/icon.ico" exe = EXE( pyz, a.scripts, options, exclude_binaries=True, #NEW name="Audiotext", debug=debug, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=debug, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file="res/macos/entitlements.plist" if is_darwin else None, icon=windows_icon if is_win else macos_icon, ) coll = COLLECT( exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name="Audiotext", ) app = BUNDLE( coll, name="Audiotext.app", icon=macos_icon, bundle_identifier="com.henestrosadev.audiotext", version="2.3.0", info_plist={ "NSPrincipalClass": "NSApplication", "NSAppleScriptEnabed": False, "NSHighResolutionCapable": True, "NSMicrophoneUsageDescription": "Allow Audiotext to record audio from your microphone to generate transcriptions.", } )
问题根源分析
- 硬编码虚拟环境路径干扰自动收集:.spec中手动添加
venv/lib/python3.10/site-packages/xxx路径,会绕过PyInstaller的自动依赖扫描逻辑,导致间接依赖(比如pydantic是openai的依赖)无法被自动收集。 - 依赖收集方式单一:仅用
collect_data_files无法完整收集复杂库的二进制文件和隐式导入,部分库需要专用钩子才能正确处理。 - 环境不一致:macOS和Windows的Python环境、虚拟环境结构存在差异,直接复用Windows的.spec逻辑会导致依赖扫描不完整。
针对性解决方案
1. 移除硬编码的venv路径,让PyInstaller自动收集依赖
删掉所有形如datas += [("venv/lib/python3.10/site-packages/xxx", "xxx")]的行,改用collect_all()工具批量收集库的模块、数据文件和二进制文件:
# 替换原有单个collect_data_files调用 datas = [] binaries = [] hiddenimports = [] # 批量收集复杂库的所有资源 for pkg in ["transformers", "lightning", "lightning_fabric", "speechbrain", "pyannote", "asteroid_filterbanks", "whisperx", "librosa", "customtkinter", "torch", "speech_recognition", "moviepy", "dotenv", "openai"]: pkg_datas, pkg_binaries, pkg_hidden = collect_all(pkg) datas += pkg_datas binaries += pkg_binaries hiddenimports += pkg_hidden # 保留必要的元数据收集 datas += copy_metadata("tqdm") datas += copy_metadata("regex") datas += copy_metadata("requests") datas += copy_metadata("packaging") datas += copy_metadata("filelock") datas += copy_metadata("numpy") datas += copy_metadata("tokenizers") datas += copy_metadata("pillow") datas += copy_metadata("huggingface_hub") datas += copy_metadata("safetensors") datas += copy_metadata("pyyaml") datas += copy_metadata("pydantic") # 若仍有缺失可手动添加元数据 # 保留项目自有资源 datas += [("res", "res")] datas += [("config.ini", ".")] datas += [(".env", ".")] # 添加额外需要的隐式导入(仅保留确实缺失的) hiddenimports += [ "huggingface_hub.repository", "sklearn.utils._cython_blas", "sklearn.neighbors.typedefs", "sklearn.neighbors.quad_tree", "sklearn.tree", "sklearn.tree._utils", "darkdetect", "tkinter.font", "tkinter.ttk", "proglog", "imageio", "decorator", ]
2. 更新PyInstaller及钩子库
确保使用最新版PyInstaller和第三方钩子,以支持更多库的自动收集:
pip install --upgrade pyinstaller pyinstaller-hooks-contrib
3. 重建macOS虚拟环境
在macOS上重新创建干净的虚拟环境,确保依赖安装完整:
rm -rf venv python3.10 -m venv venv source venv/bin/activate pip install -r requirements.txt
激活环境后再运行pyinstaller audiotext.spec打包。
4. 调试依赖收集过程
添加调试日志查看依赖收集详情,定位缺失模块:
pyinstaller --log-level DEBUG audiotext.spec
根据日志中的WARNING: Missing module信息,针对性补充hiddenimports或钩子。
优化后的完整.spec示例
# -*- mode: python ; coding: utf-8 -*- from PyInstaller.compat import is_darwin, is_win from PyInstaller.utils.hooks import copy_metadata, collect_all datas = [] binaries = [] hiddenimports = [] # 批量收集第三方库资源 for pkg in ["transformers", "lightning", "lightning_fabric", "speechbrain", "pyannote", "asteroid_filterbanks", "whisperx", "librosa", "customtkinter", "torch", "speech_recognition", "moviepy", "dotenv", "openai"]: pkg_datas, pkg_binaries, pkg_hidden = collect_all(pkg) datas += pkg_datas binaries += pkg_binaries hiddenimports += pkg_hidden # 收集元数据 datas += copy_metadata("tqdm") datas += copy_metadata("regex") datas += copy_metadata("requests") datas += copy_metadata("packaging") datas += copy_metadata("filelock") datas += copy_metadata("numpy") datas += copy_metadata("tokenizers") datas += copy_metadata("pillow") datas += copy_metadata("huggingface_hub") datas += copy_metadata("safetensors") datas += copy_metadata("pyyaml") datas += copy_metadata("pydantic") # 项目自有资源 datas += [("res", "res")] datas += [("config.ini", ".")] datas += [(".env", ".")] # 额外隐式导入 hiddenimports += [ "huggingface_hub.repository", "sklearn.utils._cython_blas", "sklearn.neighbors.typedefs", "sklearn.neighbors.quad_tree", "sklearn.tree", "sklearn.tree._utils", "darkdetect", "tkinter.font", "tkinter.ttk", "proglog", "imageio", "decorator", ] block_cipher = None debug = True options = [("v", None, "OPTION")] if debug else [] a = Analysis( ["src/app.py"], pathex=[], binaries=binaries, datas=datas, hiddenimports=hiddenimports, hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) macos_icon = "res/macos/icon.icns" windows_icon = "res/windows/icon.ico" exe = EXE( pyz, a.scripts, options, exclude_binaries=True, name="Audiotext", debug=debug, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=debug, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file="res/macos/entitlements.plist" if is_darwin else None, icon=windows_icon if is_win else macos_icon, ) coll = COLLECT( exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name="Audiotext", ) app = BUNDLE( coll, name="Audiotext.app", icon=macos_icon, bundle_identifier="com.henestrosadev.audiotext", version="2.3.0", info_plist={ "NSPrincipalClass": "NSApplication", "NSAppleScriptEnabled": False, # 修正原拼写错误 "NSHighResolutionCapable": True, "NSMicrophoneUsageDescription": "Allow Audiotext to record audio from your microphone to generate transcriptions.", } )
内容的提问来源于stack exchange,提问作者user15675885
相关产品推荐
相关产品推荐

