relocatable.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. """
  2. Rewrites absolute paths in `.pth` files inside a Python virtualenv to be relative.
  3. Used to ensure that Hue's virtual environments are relocatable across systems.
  4. Complements `virtualenv-make-relocatable` for robust deployment support.
  5. """
  6. import os
  7. import site
  8. import shutil
  9. from pathlib import Path
  10. def relocate_pth_with_relative_paths(site_packages: str, make_backup: bool = True) -> None:
  11. """
  12. Converts absolute paths in .pth files to relative paths, relative to the site-packages directory.
  13. Only modifies paths that:
  14. - Exist on the filesystem.
  15. - Are not already within the site-packages directory.
  16. Ignores empty lines and lines starting with 'import'.
  17. Parameters:
  18. site_packages (str): Path to the site-packages directory.
  19. make_backup (bool): Whether to save a backup copy of each modified .pth file.
  20. """
  21. site_packages = Path(site_packages).resolve()
  22. pth_files = site_packages.glob("*.pth")
  23. for pth_file in pth_files:
  24. updated_lines = []
  25. modified = False
  26. with pth_file.open("r") as f:
  27. for line in f:
  28. stripped = line.strip()
  29. # Preserve empty lines and import statements
  30. if not stripped or stripped.startswith("import"):
  31. updated_lines.append(line.rstrip())
  32. continue
  33. path = Path(stripped)
  34. if path.is_absolute() and path.exists():
  35. # Only convert if path is outside site-packages
  36. if site_packages not in path.parents:
  37. try:
  38. # Compute relative path to site-packages
  39. relative_path = os.path.relpath(path, site_packages)
  40. updated_lines.append(relative_path)
  41. modified = True
  42. except Exception as e:
  43. print(f"️Failed to compute relative path for: {path}\n Error: {e}")
  44. updated_lines.append(stripped) # fallback: keep original
  45. else:
  46. updated_lines.append(stripped)
  47. else:
  48. updated_lines.append(stripped)
  49. if modified:
  50. if make_backup:
  51. backup_path = pth_file.with_suffix(pth_file.suffix + ".bak")
  52. shutil.copy(pth_file, backup_path)
  53. with pth_file.open("w") as f:
  54. f.write("\n".join(updated_lines) + "\n")
  55. print(f"Rewritten with relative paths: {pth_file.name}")
  56. else:
  57. print(f"No changes needed: {pth_file.name}")
  58. if __name__ == "__main__":
  59. site_packages_dir = site.getsitepackages()[0]
  60. relocate_pth_with_relative_paths(site_packages_dir)