python-enviroment-install.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """Install project Python deps into embedded python/py (site-packages). No virtual environment."""
  4. import os
  5. import subprocess
  6. import sys
  7. from pathlib import Path
  8. PIP_INDEX_URL = os.environ.get("PIP_INDEX_URL", "https://pypi.tuna.tsinghua.edu.cn/simple")
  9. PIP_TRUSTED_HOST = "pypi.tuna.tsinghua.edu.cn"
  10. PIP_INDEX_ARGS = f"-i {PIP_INDEX_URL} --trusted-host {PIP_TRUSTED_HOST}"
  11. SCRIPT_DIR = Path(__file__).parent.absolute()
  12. PROJECT_ROOT = SCRIPT_DIR.parent.absolute()
  13. PY_EMBED = SCRIPT_DIR / "py" / "python.exe"
  14. if not PY_EMBED.exists():
  15. PY_EMBED = SCRIPT_DIR / "py" / "python"
  16. ENVIRONMENT_FILE = SCRIPT_DIR / "environment.txt"
  17. REQUIREMENTS_FILE = PROJECT_ROOT / "requirements.txt"
  18. def run_pip(args: str) -> subprocess.CompletedProcess:
  19. cmd = f'"{PY_EMBED}" -m pip {args}'
  20. return subprocess.run(cmd, shell=True, capture_output=True, text=True, encoding="utf-8")
  21. def installed_names() -> set:
  22. out = set()
  23. sp = SCRIPT_DIR / "py" / "Lib" / "site-packages"
  24. if not sp.exists():
  25. return out
  26. for item in sp.iterdir():
  27. if not item.is_dir():
  28. continue
  29. name = item.name
  30. if name.endswith(".dist-info"):
  31. base = name.replace(".dist-info", "").rsplit("-", 1)[0]
  32. out.add(base.lower().replace("_", "-"))
  33. elif name.endswith(".egg-info"):
  34. base = name.replace(".egg-info", "").rsplit("-", 1)[0]
  35. out.add(base.lower().replace("_", "-"))
  36. elif (item / "__init__.py").exists() or any(item.glob("*.py")):
  37. out.add(name.lower())
  38. out.add(name.lower().replace("_", "-"))
  39. if "cv2" in out:
  40. out.add("opencv-python")
  41. return out
  42. def pkg_key(line: str) -> str:
  43. line = line.strip()
  44. if not line or line.startswith("#"):
  45. return ""
  46. for sep in ("==", ">=", "<=", "~=", ">", "<"):
  47. if sep in line:
  48. line = line.split(sep)[0]
  49. break
  50. return line.strip().lower()
  51. def main() -> None:
  52. if not PY_EMBED.exists():
  53. print(f"[X] Embedded Python not found: {PY_EMBED}")
  54. sys.exit(1)
  55. if run_pip("--version").returncode != 0:
  56. print("[X] pip not usable. Run enviroment-check.ps1 (copies from python/backup/site-packages first, then get-pip.py if needed).")
  57. sys.exit(1)
  58. if REQUIREMENTS_FILE.exists():
  59. source = REQUIREMENTS_FILE
  60. elif ENVIRONMENT_FILE.exists():
  61. source = ENVIRONMENT_FILE
  62. else:
  63. fr = run_pip("freeze")
  64. if fr.returncode == 0 and fr.stdout:
  65. ENVIRONMENT_FILE.write_text(fr.stdout, encoding="utf-8", newline="\n")
  66. print("[OK] Created environment.txt from pip freeze")
  67. sys.exit(0)
  68. lines = [ln.strip() for ln in source.read_text(encoding="utf-8").splitlines() if ln.strip() and not ln.strip().startswith("#")]
  69. if not lines:
  70. print("[OK] No packages listed")
  71. sys.exit(0)
  72. have = installed_names()
  73. missing = []
  74. for ln in lines:
  75. k = pkg_key(ln)
  76. if not k:
  77. continue
  78. if k in have or k.replace("-", "_") in have:
  79. continue
  80. missing.append(ln)
  81. if missing:
  82. print(f"Installing {len(missing)} package(s) into python/py...")
  83. for pkg in missing:
  84. print(f" pip install {pkg}")
  85. ins = run_pip(f"install {PIP_INDEX_ARGS} --no-warn-script-location {pkg}")
  86. if ins.returncode != 0:
  87. print(ins.stderr or ins.stdout or "")
  88. sys.exit(1)
  89. fr = run_pip("freeze")
  90. if fr.returncode != 0:
  91. sys.exit(1)
  92. ENVIRONMENT_FILE.write_text(fr.stdout, encoding="utf-8", newline="\n")
  93. print(f"[OK] Synced {ENVIRONMENT_FILE}")
  94. sys.exit(0)
  95. if __name__ == "__main__":
  96. main()