#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Python 依赖安装和同步脚本 功能:检查、安装 Python 依赖到虚拟环境,然后同步所有已安装的包到 environment.txt """ import os import sys import subprocess import platform from pathlib import Path # 获取脚本所在目录和项目根目录 SCRIPT_DIR = Path(__file__).parent.absolute() PROJECT_ROOT = SCRIPT_DIR.parent.absolute() VENV_PATH = SCRIPT_DIR / "env" ENVIRONMENT_FILE = SCRIPT_DIR / "environment.txt" REQUIREMENTS_FILE = PROJECT_ROOT / "requirements.txt" # 根据操作系统确定虚拟环境的 Python 和 pip 路径 if platform.system() == "Windows": VENV_PYTHON = VENV_PATH / "Scripts" / "python.exe" VENV_PIP = VENV_PATH / "Scripts" / "pip.exe" else: VENV_PYTHON = VENV_PATH / "bin" / "python" VENV_PIP = VENV_PATH / "bin" / "pip" def run_command(cmd, check=True, capture_output=True): """运行命令并返回结果""" try: result = subprocess.run( cmd, shell=True, check=check, capture_output=capture_output, text=True, encoding='utf-8' ) return result.returncode == 0, result.stdout, result.stderr except subprocess.CalledProcessError as e: return False, e.stdout if hasattr(e, 'stdout') else "", str(e) def ensure_venv(): """确保虚拟环境存在""" if not VENV_PATH.exists(): print("[WARN] Virtual environment not found, creating...") success, _, error = run_command(f'python -m venv "{VENV_PATH}"', check=False) if not success: print(f"[X] Failed to create virtual environment: {error}") sys.exit(1) print("[OK] Virtual environment created successfully") return True def get_venv_pip(): """获取虚拟环境的 pip 命令""" if platform.system() == "Windows": return str(VENV_PIP) else: return str(VENV_PIP) def read_dependencies(source_file): """读取依赖列表""" if not source_file.exists(): return [] dependencies = [] with open(source_file, 'r', encoding='utf-8') as f: for line in f: line = line.strip() # 跳过注释和空行 if line and not line.startswith('#'): dependencies.append(line) return dependencies def check_package_installed(package_name, venv_pip): """检查包是否已安装""" # 提取包名(支持 ==, >=, <=, >, <, ~= 等版本操作符) pkg_name = package_name.split('==')[0].split('>=')[0].split('<=')[0].split('>')[0].split('<')[0].split('~=')[0].strip() cmd = f'"{venv_pip}" show {pkg_name}' success, _, _ = run_command(cmd, check=False, capture_output=True) return success def install_packages(packages, source_file, venv_pip): """安装包到虚拟环境""" failed_packages = [] if source_file == REQUIREMENTS_FILE and REQUIREMENTS_FILE.exists(): # 使用 requirements.txt 批量安装 cmd = f'"{venv_pip}" install -r "{source_file}"' success, _, error = run_command(cmd, check=False) if not success: print(f"[X] Installation failed: {error}") return False, failed_packages else: # 逐个安装 for package in packages: cmd = f'"{venv_pip}" install {package}' success, _, error = run_command(cmd, check=False) if not success: print(f"[X] Failed to install: {package}") failed_packages.append(package) if failed_packages: return False, failed_packages return True, [] def sync_environment_file(venv_pip, silent=False): """同步所有已安装的包到 environment.txt""" cmd = f'"{venv_pip}" freeze' success, output, error = run_command(cmd, check=False) if not success: if not silent: print(f"[X] Failed to get installed packages list: {error}") return False # 使用 UTF-8 无 BOM 编码写入文件 with open(ENVIRONMENT_FILE, 'w', encoding='utf-8', newline='\n') as f: f.write(output) if not silent: package_count = len([line for line in output.strip().split('\n') if line.strip()]) print(f"[OK] All installed packages synced to {ENVIRONMENT_FILE}") print(f" Total packages: {package_count}") return True def main(): """主函数""" # 确保虚拟环境存在 if not ensure_venv(): sys.exit(1) venv_pip = get_venv_pip() # 确定依赖源文件(优先使用 requirements.txt,如果没有则使用 environment.txt) if REQUIREMENTS_FILE.exists(): source_file = REQUIREMENTS_FILE elif ENVIRONMENT_FILE.exists(): source_file = ENVIRONMENT_FILE else: sync_environment_file(venv_pip) sys.exit(0) # 读取依赖列表 required_packages = read_dependencies(source_file) if not required_packages: print("[OK] No dependencies specified") sys.exit(0) # 检查缺失的依赖 missing_packages = [] installed_count = 0 missing_count = 0 for package in required_packages: package_line = package.strip() if not package_line: continue # 提取包名 package_name = package_line.split('==')[0].split('>=')[0].split('<=')[0].split('>')[0].split('<')[0].split('~=')[0].strip() if check_package_installed(package_name, venv_pip): installed_count += 1 else: missing_packages.append(package_line) missing_count += 1 # 如果有缺失的依赖,显示必要信息并安装 if missing_count > 0: print(f"[X] Missing {missing_count} package(s) out of {len(required_packages)}") print("Missing packages:") for missing in missing_packages: print(f" - {missing}") print("\nInstalling missing packages...") success, failed = install_packages(missing_packages, source_file, venv_pip) if success: print("[OK] All packages installed successfully") else: if failed: print(f"[X] Failed to install {len(failed)} package(s):") for pkg in failed: print(f" - {pkg}") else: print("[X] Some packages installation failed") # 即使有失败,也继续同步已安装的包 print("[WARN] Continuing to sync installed packages...") # 同步所有已安装的包到 environment.txt sync_environment_file(venv_pip) else: # 所有依赖都齐全时,只显示一行信息(包含同步结果) sync_environment_file(venv_pip, silent=True) print(f"[OK] All dependencies are installed ({len(required_packages)} packages)") sys.exit(0) if __name__ == "__main__": main()