1
0

brew_pyenv_setup.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. #! /usr/bin/env python3
  2. """
  3. Create symlinks to homebrewed python-installations that can be used alongside
  4. pyenv.
  5. After running this script, you can use e.g. homebrew's python@3.12 via
  6. ```
  7. pyenv shell 3.12
  8. ```
  9. - You can still install pyenv versions
  10. - you can still create virtualenvironments based on the homebrew versions
  11. """
  12. import subprocess
  13. from pathlib import Path
  14. from dataclasses import dataclass
  15. import logging
  16. logging.basicConfig(
  17. level=logging.INFO,
  18. format="{%(pathname)s:%(lineno)d} %(levelname)s - %(message)s",
  19. )
  20. @dataclass
  21. class Binary:
  22. executable: str
  23. links: list[str]
  24. BINARIES_TO_LINK = [
  25. Binary(executable="python{version}", links=["python3", "python"]),
  26. Binary(executable="pip{version}", links=["pip3", "pip"]),
  27. Binary(executable="idle{version}", links=["idle3", "idle"]),
  28. Binary(executable="pydoc{version}", links=["pydoc3", "pydoc"]),
  29. Binary(executable="wheel{version}", links=["wheel3", "wheel"]),
  30. Binary(
  31. executable="python{version}-config",
  32. links=["python3-config", "python-config"],
  33. ),
  34. ]
  35. DIRS_TO_LINK = ["include", "lib", "share"]
  36. installed_brew_packages = subprocess.getoutput("brew list").split()
  37. brew_prefix = Path(subprocess.getoutput("brew --prefix")) / "opt"
  38. installed_versions = [
  39. p for p in installed_brew_packages if "python@" in p or "python-tk@" in p
  40. ]
  41. logging.info(f"Will link following installed versions: {installed_versions}")
  42. homedir = Path.home()
  43. pyenv_dir = Path(subprocess.getoutput("pyenv root")) / "versions"
  44. logging.info(f"Will add all necessary links to {pyenv_dir}")
  45. for pyversion in installed_versions:
  46. logging.info(f"Check dir for {pyversion}")
  47. _, version = pyversion.split("@")
  48. if "-tk" in pyversion:
  49. link_dir = pyenv_dir / f"{version}-tk"
  50. else:
  51. link_dir = pyenv_dir / version
  52. link_dir.mkdir(exist_ok=True)
  53. for binary in BINARIES_TO_LINK:
  54. (link_dir / "bin").mkdir(exist_ok=True)
  55. target = (
  56. brew_prefix / pyversion / "bin" / binary.executable.format(version=version)
  57. )
  58. source = link_dir / "bin" / binary.executable.format(version=version)
  59. if source.is_file():
  60. if source.resolve() != target.resolve():
  61. logging.warning(
  62. f"{source} is different from {target}, skipping {pyversion}"
  63. )
  64. continue
  65. source.symlink_to(target)
  66. for link in binary.links:
  67. source = link_dir / "bin" / link
  68. target = brew_prefix / pyversion / "libexec/bin" / link
  69. if source.is_symlink():
  70. logging.warning(f"Link {target} -> {source} already exists. Skipping.")
  71. continue
  72. source.symlink_to(target)
  73. for dir_ in DIRS_TO_LINK:
  74. target = brew_prefix / pyversion / dir_
  75. source = link_dir / dir_
  76. if source.is_dir():
  77. if source.resolve() != target.resolve():
  78. logging.warning(
  79. f"{source} is a proper directory, skipping. "
  80. f"Linking for {pyversion} might be broken now."
  81. )
  82. continue
  83. if not source.is_symlink():
  84. source.symlink_to(target, target_is_directory=True)
  85. logging.info("Rehashing pyenv")
  86. subprocess.call(["pyenv", "rehash"])