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:
congsh
2026-02-12 13:31:49 +08:00
parent bb27db586d
commit 4589289635
6 changed files with 164 additions and 0 deletions

View File

@@ -43,12 +43,16 @@ if exist dist rmdir /s /q dist
echo.
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 ^
--noconfirm ^
--name "CutThenThink" ^
--windowed ^
--onefile ^
--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.QtGui ^
--hidden-import=PyQt6.QtWidgets ^
@@ -67,6 +71,18 @@ python -m PyInstaller ^
--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
if errorlevel 1 (

View File

@@ -44,15 +44,44 @@ fi
echo ""
echo "4/4. 开始打包..."
echo "NOTE: 使用自定义 hooks 排除与 Python 3.13 不兼容的 ML 库"
$PYTHON -m PyInstaller \
--name "CutThenThink" \
--windowed \
--onefile \
--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.QtGui \
--hidden-import=PyQt6.QtWidgets \
--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
echo ""

7
pyi_hooks/__init__.py Normal file
View 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
"""

View 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',
]

View 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',
]

View 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())