fix: 修复Python 3.13环境下PyInstaller构建崩溃问题
问题: - PyInstaller在分析阶段尝试导入torch/transformers等ML库 - 这些库与Python 3.13不兼容,导致"Windows fatal exception: access violation" - 构建过程无法完成,无法生成exe文件 解决方案: 1. 添加pyi_hooks/pyi_rth_ignore_torch.py运行时hook - 在PyInstaller分析阶段阻止torch等模块的导入 - 这些模块将在运行时动态安装 2. 添加pyi_hooks/hook-exclude-ml.py和hook-paddleocr.py - 明确告诉PyInstaller不收集这些ML库 3. 更新build.bat和build.sh - 使用--runtime-hook和--additional-hooks-dir - 添加--exclude-module参数排除所有ML库 测试: - 验证所有hook文件语法正确 - 验证项目核心功能可以在不导入torch的情况下正常工作 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
16
build.bat
16
build.bat
@@ -43,12 +43,16 @@ if exist dist rmdir /s /q dist
|
|||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo 4/5. Building executable...
|
echo 4/5. Building executable...
|
||||||
|
echo NOTE: Using custom hooks to exclude heavy ML libraries that cause Python 3.13 issues
|
||||||
|
echo These will be installed dynamically at runtime if needed
|
||||||
python -m PyInstaller ^
|
python -m PyInstaller ^
|
||||||
--noconfirm ^
|
--noconfirm ^
|
||||||
--name "CutThenThink" ^
|
--name "CutThenThink" ^
|
||||||
--windowed ^
|
--windowed ^
|
||||||
--onefile ^
|
--onefile ^
|
||||||
--add-data "src:src" ^
|
--add-data "src:src" ^
|
||||||
|
--runtime-hook=pyi_hooks/pyi_rth_ignore_torch.py ^
|
||||||
|
--additional-hooks-dir=pyi_hooks ^
|
||||||
--hidden-import=PyQt6.QtCore ^
|
--hidden-import=PyQt6.QtCore ^
|
||||||
--hidden-import=PyQt6.QtGui ^
|
--hidden-import=PyQt6.QtGui ^
|
||||||
--hidden-import=PyQt6.QtWidgets ^
|
--hidden-import=PyQt6.QtWidgets ^
|
||||||
@@ -67,6 +71,18 @@ python -m PyInstaller ^
|
|||||||
--hidden-import=yaml ^
|
--hidden-import=yaml ^
|
||||||
--hidden-import=requests ^
|
--hidden-import=requests ^
|
||||||
--collect-all pyqt6 ^
|
--collect-all pyqt6 ^
|
||||||
|
--exclude-module=torch ^
|
||||||
|
--exclude-module=transformers ^
|
||||||
|
--exclude-module=tensorflow ^
|
||||||
|
--exclude-module=onnx ^
|
||||||
|
--exclude-module=onnxruntime ^
|
||||||
|
--exclude-module=sentencepiece ^
|
||||||
|
--exclude-module=tokenizers ^
|
||||||
|
--exclude-module=diffusers ^
|
||||||
|
--exclude-module=accelerate ^
|
||||||
|
--exclude-module=datasets ^
|
||||||
|
--exclude-module=huggingface_hub ^
|
||||||
|
--exclude-module=safetensors ^
|
||||||
src/main.py
|
src/main.py
|
||||||
|
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
|
|||||||
29
build.sh
29
build.sh
@@ -44,15 +44,44 @@ fi
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "4/4. 开始打包..."
|
echo "4/4. 开始打包..."
|
||||||
|
echo "NOTE: 使用自定义 hooks 排除与 Python 3.13 不兼容的 ML 库"
|
||||||
$PYTHON -m PyInstaller \
|
$PYTHON -m PyInstaller \
|
||||||
--name "CutThenThink" \
|
--name "CutThenThink" \
|
||||||
--windowed \
|
--windowed \
|
||||||
--onefile \
|
--onefile \
|
||||||
--add-data "src:src" \
|
--add-data "src:src" \
|
||||||
|
--runtime-hook=pyi_hooks/pyi_rth_ignore_torch.py \
|
||||||
|
--additional-hooks-dir=pyi_hooks \
|
||||||
--hidden-import=PyQt6.QtCore \
|
--hidden-import=PyQt6.QtCore \
|
||||||
--hidden-import=PyQt6.QtGui \
|
--hidden-import=PyQt6.QtGui \
|
||||||
--hidden-import=PyQt6.QtWidgets \
|
--hidden-import=PyQt6.QtWidgets \
|
||||||
--hidden-import=sqlalchemy \
|
--hidden-import=sqlalchemy \
|
||||||
|
--hidden-import=sqlalchemy.orm \
|
||||||
|
--hidden-import=PIL \
|
||||||
|
--hidden-import=PIL.Image \
|
||||||
|
--hidden-import=PIL.ImageEnhance \
|
||||||
|
--hidden-import=PIL.ImageFilter \
|
||||||
|
--hidden-import=numpy \
|
||||||
|
--hidden-import=pyperclip \
|
||||||
|
--hidden-import=tkinter \
|
||||||
|
--hidden-import=tkinter.ttk \
|
||||||
|
--hidden-import=tkinter.scrolledtext \
|
||||||
|
--hidden-import=tkinter.messagebox \
|
||||||
|
--hidden-import=yaml \
|
||||||
|
--hidden-import=requests \
|
||||||
|
--collect-all pyqt6 \
|
||||||
|
--exclude-module=torch \
|
||||||
|
--exclude-module=transformers \
|
||||||
|
--exclude-module=tensorflow \
|
||||||
|
--exclude-module=onnx \
|
||||||
|
--exclude-module=onnxruntime \
|
||||||
|
--exclude-module=sentencepiece \
|
||||||
|
--exclude-module=tokenizers \
|
||||||
|
--exclude-module=diffusers \
|
||||||
|
--exclude-module=accelerate \
|
||||||
|
--exclude-module=datasets \
|
||||||
|
--exclude-module=huggingface_hub \
|
||||||
|
--exclude-module=safetensors \
|
||||||
src/main.py
|
src/main.py
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
7
pyi_hooks/__init__.py
Normal file
7
pyi_hooks/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
PyInstaller hooks for CutThenThink
|
||||||
|
|
||||||
|
This directory contains custom PyInstaller hooks to handle:
|
||||||
|
- Excluding heavy ML libraries (torch, transformers, etc.)
|
||||||
|
- Runtime-only dependencies like PaddleOCR
|
||||||
|
"""
|
||||||
36
pyi_hooks/hook-exclude-ml.py
Normal file
36
pyi_hooks/hook-exclude-ml.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"""
|
||||||
|
PyInstaller hook to exclude ML libraries during build
|
||||||
|
|
||||||
|
This prevents PyInstaller from attempting to analyze and bundle
|
||||||
|
heavy ML libraries that are incompatible with Python 3.13 or
|
||||||
|
cause build crashes.
|
||||||
|
|
||||||
|
These libraries will be installed at runtime if needed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Collect any hidden imports (empty for this hook)
|
||||||
|
hiddenimports = []
|
||||||
|
|
||||||
|
# Exclude all heavy ML dependencies
|
||||||
|
excludedimports = [
|
||||||
|
'torch',
|
||||||
|
'torch.nn',
|
||||||
|
'torch.utils',
|
||||||
|
'torchvision',
|
||||||
|
'transformers',
|
||||||
|
'tensorflow',
|
||||||
|
'tensorflow.keras',
|
||||||
|
'onnx',
|
||||||
|
'onnxruntime',
|
||||||
|
'sentencepiece',
|
||||||
|
'tokenizers',
|
||||||
|
'diffusers',
|
||||||
|
'accelerate',
|
||||||
|
'datasets',
|
||||||
|
'huggingface_hub',
|
||||||
|
'safetensors',
|
||||||
|
'optimum',
|
||||||
|
'coloredlogs',
|
||||||
|
'docutils',
|
||||||
|
'tqdm',
|
||||||
|
]
|
||||||
29
pyi_hooks/hook-paddleocr.py
Normal file
29
pyi_hooks/hook-paddleocr.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"""
|
||||||
|
PyInstaller hook for PaddleOCR
|
||||||
|
|
||||||
|
Prevents PyInstaller from trying to bundle PaddleOCR and its dependencies.
|
||||||
|
These will be installed dynamically at runtime.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PyInstaller.utils.hooks import is_module_satisfies
|
||||||
|
|
||||||
|
# Tell PyInstaller to NOT collect these modules
|
||||||
|
# They will be installed at runtime via ensure_paddleocr()
|
||||||
|
|
||||||
|
hiddenimports = []
|
||||||
|
|
||||||
|
# Explicitly exclude heavy ML dependencies
|
||||||
|
excludedimports = [
|
||||||
|
'torch',
|
||||||
|
'transformers',
|
||||||
|
'tensorflow',
|
||||||
|
'onnx',
|
||||||
|
'onnxruntime',
|
||||||
|
'sentencepiece',
|
||||||
|
'tokenizers',
|
||||||
|
'diffusers',
|
||||||
|
'accelerate',
|
||||||
|
'datasets',
|
||||||
|
'huggingface_hub',
|
||||||
|
'safetensors',
|
||||||
|
]
|
||||||
47
pyi_hooks/pyi_rth_ignore_torch.py
Normal file
47
pyi_hooks/pyi_rth_ignore_torch.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
PyInstaller runtime hook to prevent torch import during analysis
|
||||||
|
|
||||||
|
This hook runs before PyInstaller's module analysis phase.
|
||||||
|
It blocks problematic modules from being imported during the build process.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# List of modules to block during PyInstaller analysis
|
||||||
|
BLOCKED_MODULES = [
|
||||||
|
'torch',
|
||||||
|
'transformers',
|
||||||
|
'tensorflow',
|
||||||
|
'onnx',
|
||||||
|
'onnxruntime',
|
||||||
|
'sentencepiece',
|
||||||
|
'tokenizers',
|
||||||
|
'diffusers',
|
||||||
|
'accelerate',
|
||||||
|
'datasets',
|
||||||
|
'huggingface_hub',
|
||||||
|
'safetensors',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Override the import mechanism to block these modules
|
||||||
|
class BlockModuleImport:
|
||||||
|
"""Meta path importer to block specific modules during PyInstaller build"""
|
||||||
|
|
||||||
|
def find_module(self, fullname, path=None):
|
||||||
|
if fullname in BLOCKED_MODULES or any(
|
||||||
|
fullname.startswith(m + '.') for m in BLOCKED_MODULES
|
||||||
|
):
|
||||||
|
# Return self to handle the import (will raise ImportError)
|
||||||
|
return self
|
||||||
|
return None
|
||||||
|
|
||||||
|
def load_module(self, fullname):
|
||||||
|
raise ImportError(
|
||||||
|
f"Module '{fullname}' is excluded from PyInstaller build. "
|
||||||
|
f"It will be installed at runtime if needed."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install the blocker at the start of PyInstaller analysis
|
||||||
|
if '_MEIPASS' not in sys.__dict__:
|
||||||
|
# Only during build time (not when running the frozen app)
|
||||||
|
sys.meta_path.insert(0, BlockModuleImport())
|
||||||
Reference in New Issue
Block a user