python-enviroment-install.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Python 依赖安装和同步脚本
  5. 功能:检查、安装 Python 依赖到虚拟环境,然后同步所有已安装的包到 environment.txt
  6. """
  7. import os
  8. import sys
  9. import subprocess
  10. import platform
  11. from pathlib import Path
  12. # 获取脚本所在目录和项目根目录
  13. SCRIPT_DIR = Path(__file__).parent.absolute()
  14. PROJECT_ROOT = SCRIPT_DIR.parent.absolute()
  15. VENV_PATH = SCRIPT_DIR / "env"
  16. ENVIRONMENT_FILE = SCRIPT_DIR / "environment.txt"
  17. REQUIREMENTS_FILE = PROJECT_ROOT / "requirements.txt"
  18. # 根据操作系统确定虚拟环境的 Python 和 pip 路径
  19. if platform.system() == "Windows":
  20. VENV_PYTHON = VENV_PATH / "Scripts" / "python.exe"
  21. VENV_PIP = VENV_PATH / "Scripts" / "pip.exe"
  22. else:
  23. VENV_PYTHON = VENV_PATH / "bin" / "python"
  24. VENV_PIP = VENV_PATH / "bin" / "pip"
  25. def run_command(cmd, check=True, capture_output=True):
  26. """运行命令并返回结果"""
  27. try:
  28. result = subprocess.run(
  29. cmd,
  30. shell=True,
  31. check=check,
  32. capture_output=capture_output,
  33. text=True,
  34. encoding='utf-8'
  35. )
  36. return result.returncode == 0, result.stdout, result.stderr
  37. except subprocess.CalledProcessError as e:
  38. return False, e.stdout if hasattr(e, 'stdout') else "", str(e)
  39. def ensure_venv():
  40. """确保虚拟环境存在"""
  41. if not VENV_PATH.exists():
  42. print("[WARN] Virtual environment not found, creating...")
  43. success, _, error = run_command(f'python -m venv "{VENV_PATH}"', check=False)
  44. if not success:
  45. print(f"[X] Failed to create virtual environment: {error}")
  46. sys.exit(1)
  47. print("[OK] Virtual environment created successfully")
  48. return True
  49. def get_venv_pip():
  50. """获取虚拟环境的 pip 命令"""
  51. if platform.system() == "Windows":
  52. return str(VENV_PIP)
  53. else:
  54. return str(VENV_PIP)
  55. def read_dependencies(source_file):
  56. """读取依赖列表"""
  57. if not source_file.exists():
  58. return []
  59. dependencies = []
  60. with open(source_file, 'r', encoding='utf-8') as f:
  61. for line in f:
  62. line = line.strip()
  63. # 跳过注释和空行
  64. if line and not line.startswith('#'):
  65. dependencies.append(line)
  66. return dependencies
  67. def check_package_installed(package_name, venv_pip):
  68. """检查包是否已安装"""
  69. # 提取包名(支持 ==, >=, <=, >, <, ~= 等版本操作符)
  70. pkg_name = package_name.split('==')[0].split('>=')[0].split('<=')[0].split('>')[0].split('<')[0].split('~=')[0].strip()
  71. cmd = f'"{venv_pip}" show {pkg_name}'
  72. success, _, _ = run_command(cmd, check=False, capture_output=True)
  73. return success
  74. def install_packages(packages, source_file, venv_pip):
  75. """安装包到虚拟环境"""
  76. failed_packages = []
  77. if source_file == REQUIREMENTS_FILE and REQUIREMENTS_FILE.exists():
  78. # 使用 requirements.txt 批量安装
  79. cmd = f'"{venv_pip}" install -r "{source_file}"'
  80. success, _, error = run_command(cmd, check=False)
  81. if not success:
  82. print(f"[X] Installation failed: {error}")
  83. return False, failed_packages
  84. else:
  85. # 逐个安装
  86. for package in packages:
  87. cmd = f'"{venv_pip}" install {package}'
  88. success, _, error = run_command(cmd, check=False)
  89. if not success:
  90. print(f"[X] Failed to install: {package}")
  91. failed_packages.append(package)
  92. if failed_packages:
  93. return False, failed_packages
  94. return True, []
  95. def sync_environment_file(venv_pip, silent=False):
  96. """同步所有已安装的包到 environment.txt"""
  97. cmd = f'"{venv_pip}" freeze'
  98. success, output, error = run_command(cmd, check=False)
  99. if not success:
  100. if not silent:
  101. print(f"[X] Failed to get installed packages list: {error}")
  102. return False
  103. # 使用 UTF-8 无 BOM 编码写入文件
  104. with open(ENVIRONMENT_FILE, 'w', encoding='utf-8', newline='\n') as f:
  105. f.write(output)
  106. if not silent:
  107. package_count = len([line for line in output.strip().split('\n') if line.strip()])
  108. print(f"[OK] All installed packages synced to {ENVIRONMENT_FILE}")
  109. print(f" Total packages: {package_count}")
  110. return True
  111. def main():
  112. """主函数"""
  113. # 确保虚拟环境存在
  114. if not ensure_venv():
  115. sys.exit(1)
  116. venv_pip = get_venv_pip()
  117. # 确定依赖源文件(优先使用 requirements.txt,如果没有则使用 environment.txt)
  118. if REQUIREMENTS_FILE.exists():
  119. source_file = REQUIREMENTS_FILE
  120. elif ENVIRONMENT_FILE.exists():
  121. source_file = ENVIRONMENT_FILE
  122. else:
  123. sync_environment_file(venv_pip)
  124. sys.exit(0)
  125. # 读取依赖列表
  126. required_packages = read_dependencies(source_file)
  127. if not required_packages:
  128. print("[OK] No dependencies specified")
  129. sys.exit(0)
  130. # 检查缺失的依赖
  131. missing_packages = []
  132. installed_count = 0
  133. missing_count = 0
  134. for package in required_packages:
  135. package_line = package.strip()
  136. if not package_line:
  137. continue
  138. # 提取包名
  139. package_name = package_line.split('==')[0].split('>=')[0].split('<=')[0].split('>')[0].split('<')[0].split('~=')[0].strip()
  140. if check_package_installed(package_name, venv_pip):
  141. installed_count += 1
  142. else:
  143. missing_packages.append(package_line)
  144. missing_count += 1
  145. # 如果有缺失的依赖,显示必要信息并安装
  146. if missing_count > 0:
  147. print(f"[X] Missing {missing_count} package(s) out of {len(required_packages)}")
  148. print("Missing packages:")
  149. for missing in missing_packages:
  150. print(f" - {missing}")
  151. print("\nInstalling missing packages...")
  152. success, failed = install_packages(missing_packages, source_file, venv_pip)
  153. if success:
  154. print("[OK] All packages installed successfully")
  155. else:
  156. if failed:
  157. print(f"[X] Failed to install {len(failed)} package(s):")
  158. for pkg in failed:
  159. print(f" - {pkg}")
  160. else:
  161. print("[X] Some packages installation failed")
  162. # 即使有失败,也继续同步已安装的包
  163. print("[WARN] Continuing to sync installed packages...")
  164. # 同步所有已安装的包到 environment.txt
  165. sync_environment_file(venv_pip)
  166. else:
  167. # 所有依赖都齐全时,只显示一行信息(包含同步结果)
  168. sync_environment_file(venv_pip, silent=True)
  169. print(f"[OK] All dependencies are installed ({len(required_packages)} packages)")
  170. sys.exit(0)
  171. if __name__ == "__main__":
  172. main()