update-environment-list.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Python 环境列表更新脚本
  5. 功能:对比虚拟环境中已安装的包和 environment.txt,如果不一致则更新 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. VENV_PATH = SCRIPT_DIR / "env"
  15. ENVIRONMENT_FILE = SCRIPT_DIR / "environment.txt"
  16. # 根据操作系统确定虚拟环境的 pip 路径
  17. if platform.system() == "Windows":
  18. VENV_PIP = VENV_PATH / "Scripts" / "pip.exe"
  19. else:
  20. VENV_PIP = VENV_PATH / "bin" / "pip"
  21. def run_command(cmd, check=True, capture_output=True):
  22. """运行命令并返回结果"""
  23. try:
  24. result = subprocess.run(
  25. cmd,
  26. shell=True,
  27. check=check,
  28. capture_output=capture_output,
  29. text=True,
  30. encoding='utf-8'
  31. )
  32. return result.returncode == 0, result.stdout, result.stderr
  33. except subprocess.CalledProcessError as e:
  34. return False, e.stdout if hasattr(e, 'stdout') else "", str(e)
  35. def get_installed_packages():
  36. """获取虚拟环境中已安装的包列表(使用 pip freeze)"""
  37. if not VENV_PATH.exists():
  38. print("[ERROR] Virtual environment not found")
  39. return None
  40. cmd = f'"{VENV_PIP}" freeze'
  41. success, output, error = run_command(cmd, check=False)
  42. if not success:
  43. print(f"[ERROR] Failed to get installed packages: {error}")
  44. return None
  45. # 解析输出,提取包名和版本
  46. packages = {}
  47. for line in output.strip().split('\n'):
  48. line = line.strip()
  49. if line and not line.startswith('#'):
  50. # 格式:package==version 或 package>=version 等
  51. if '==' in line:
  52. parts = line.split('==', 1)
  53. packages[parts[0].strip().lower()] = line.strip()
  54. else:
  55. # 如果没有版本号,使用整行
  56. pkg_name = line.split('>=')[0].split('<=')[0].split('>')[0].split('<')[0].split('~=')[0].strip()
  57. packages[pkg_name.lower()] = line.strip()
  58. return packages
  59. def read_environment_file():
  60. """读取 environment.txt 中的包列表"""
  61. if not ENVIRONMENT_FILE.exists():
  62. print(f"[WARN] {ENVIRONMENT_FILE} not found, will create new one")
  63. return {}
  64. packages = {}
  65. with open(ENVIRONMENT_FILE, 'r', encoding='utf-8') as f:
  66. for line in f:
  67. line = line.strip()
  68. if line and not line.startswith('#'):
  69. # 提取包名(支持各种版本操作符)
  70. if '==' in line:
  71. parts = line.split('==', 1)
  72. packages[parts[0].strip().lower()] = line.strip()
  73. else:
  74. pkg_name = line.split('>=')[0].split('<=')[0].split('>')[0].split('<')[0].split('~=')[0].strip()
  75. packages[pkg_name.lower()] = line.strip()
  76. return packages
  77. def compare_and_update():
  78. """对比并更新 environment.txt"""
  79. print("Comparing installed packages with environment.txt...")
  80. print("=" * 60)
  81. # 获取已安装的包
  82. installed_packages = get_installed_packages()
  83. if installed_packages is None:
  84. sys.exit(1)
  85. # 读取 environment.txt 中的包
  86. file_packages = read_environment_file()
  87. # 对比差异
  88. installed_set = set(installed_packages.keys())
  89. file_set = set(file_packages.keys())
  90. added_packages = installed_set - file_set
  91. removed_packages = file_set - installed_set
  92. changed_packages = []
  93. # 检查版本变化
  94. for pkg_name in installed_set & file_set:
  95. if installed_packages[pkg_name] != file_packages[pkg_name]:
  96. changed_packages.append(pkg_name)
  97. # 显示差异
  98. if added_packages:
  99. print(f"\n[+] Added packages ({len(added_packages)}):")
  100. for pkg in sorted(added_packages):
  101. print(f" + {installed_packages[pkg]}")
  102. if removed_packages:
  103. print(f"\n[-] Removed packages ({len(removed_packages)}):")
  104. for pkg in sorted(removed_packages):
  105. print(f" - {file_packages[pkg]}")
  106. if changed_packages:
  107. print(f"\n[~] Changed packages ({len(changed_packages)}):")
  108. for pkg in sorted(changed_packages):
  109. print(f" ~ {file_packages[pkg]} -> {installed_packages[pkg]}")
  110. # 判断是否需要更新
  111. if not added_packages and not removed_packages and not changed_packages:
  112. print("\n[OK] environment.txt is up to date")
  113. print(f" Total packages: {len(installed_packages)}")
  114. return True
  115. # 更新 environment.txt
  116. print(f"\nUpdating {ENVIRONMENT_FILE}...")
  117. # 使用 pip freeze 获取完整列表(包含所有依赖)
  118. cmd = f'"{VENV_PIP}" freeze'
  119. success, output, error = run_command(cmd, check=False)
  120. if not success:
  121. print(f"[ERROR] Failed to get installed packages: {error}")
  122. sys.exit(1)
  123. # 写入文件(使用 UTF-8 无 BOM 编码)
  124. with open(ENVIRONMENT_FILE, 'w', encoding='utf-8', newline='\n') as f:
  125. f.write(output)
  126. # 统计包数量
  127. package_count = len([line for line in output.strip().split('\n') if line.strip()])
  128. print(f"[OK] {ENVIRONMENT_FILE} updated successfully")
  129. print(f" Total packages: {package_count}")
  130. return True
  131. def main():
  132. """主函数"""
  133. if not compare_and_update():
  134. sys.exit(1)
  135. sys.exit(0)
  136. if __name__ == "__main__":
  137. main()