|
|
@@ -0,0 +1,97 @@
|
|
|
+#! /usr/bin/env python3
|
|
|
+"""
|
|
|
+Create symlinks to homebrewed python-installations that can be used alongside
|
|
|
+pyenv.
|
|
|
+
|
|
|
+After running this script, you can use e.g. homebrew's python@3.12 via
|
|
|
+```
|
|
|
+pyenv shell 3.12
|
|
|
+```
|
|
|
+- You can still install pyenv versions
|
|
|
+- you can still create virtualenvironments based on the homebrew versions
|
|
|
+"""
|
|
|
+
|
|
|
+import subprocess
|
|
|
+from pathlib import Path
|
|
|
+from dataclasses import dataclass
|
|
|
+import logging
|
|
|
+import re
|
|
|
+
|
|
|
+logging.basicConfig(level=logging.DEBUG)
|
|
|
+
|
|
|
+
|
|
|
+@dataclass
|
|
|
+class Binary:
|
|
|
+ executable: str
|
|
|
+ links: list[str]
|
|
|
+
|
|
|
+
|
|
|
+BINARIES_TO_LINK = [
|
|
|
+ Binary(executable="python{version}", links=["python3", "python"]),
|
|
|
+ Binary(executable="pip{version}", links=["pip3", "pip"]),
|
|
|
+ Binary(executable="idle{version}", links=["idle3", "idle"]),
|
|
|
+ Binary(executable="pydoc{version}", links=["pydoc3", "pydoc"]),
|
|
|
+ Binary(executable="wheel{version}", links=["wheel3", "wheel"]),
|
|
|
+ Binary(
|
|
|
+ executable="python{version}-config",
|
|
|
+ links=["python3-config", "python-config"],
|
|
|
+ ),
|
|
|
+]
|
|
|
+DIRS_TO_LINK = ["include", "lib", "share"]
|
|
|
+
|
|
|
+logging.warning("Did not find homebrew, trying macports :P")
|
|
|
+brew_prefix = Path(subprocess.check_output(["which", "port"]).decode()).parent.parent / "Library/Frameworks/Python.framework/Versions"
|
|
|
+installed_brew_packages = ["".join(s.strip().split()[:2]) for s in subprocess.getoutput("port installed").split("\n")[1:]]
|
|
|
+installed_versions = [re.match(r"python(\d+)@.*", p).group(1) for p in installed_brew_packages if re.match(r"python(\d+)@.*", p)]
|
|
|
+installed_versions = [f"{v[0]}.{v[1:]}" for v in installed_versions]
|
|
|
+
|
|
|
+logging.debug(f"{brew_prefix=}")
|
|
|
+logging.debug(f"{installed_brew_packages=}")
|
|
|
+logging.debug(f"{installed_versions=}")
|
|
|
+
|
|
|
+logging.info(f"Will link following installed versions: {installed_versions}")
|
|
|
+
|
|
|
+homedir = Path.home()
|
|
|
+pyenv_dir = Path(subprocess.getoutput("pyenv root")) / "versions"
|
|
|
+logging.info(f"Will add all necessary links to {pyenv_dir}")
|
|
|
+
|
|
|
+for pyversion in installed_versions:
|
|
|
+ logging.info(f"Check dir for {pyversion}")
|
|
|
+ (pyenv_dir / pyversion).mkdir(exist_ok=True)
|
|
|
+ for binary in BINARIES_TO_LINK:
|
|
|
+ (pyenv_dir / pyversion / "bin").mkdir(exist_ok=True)
|
|
|
+ target = (
|
|
|
+ brew_prefix / pyversion / "bin" / binary.executable.format(version=pyversion)
|
|
|
+ )
|
|
|
+ source = pyenv_dir / pyversion / "bin" / binary.executable.format(version=pyversion)
|
|
|
+ logging.debug(f"linking {source=} to {target=}")
|
|
|
+
|
|
|
+ if source.is_symlink():
|
|
|
+ logging.info(f"Unlinking existing link {source=}")
|
|
|
+ source.unlink()
|
|
|
+
|
|
|
+ source.symlink_to(target)
|
|
|
+ for link in binary.links:
|
|
|
+ source = pyenv_dir / pyversion / "bin" / link
|
|
|
+ target = brew_prefix / pyversion / "bin" / binary.executable.format(version=pyversion)
|
|
|
+ if source.is_symlink():
|
|
|
+ logging.info(f"Unlinking existing {source=} -> {target=}.")
|
|
|
+ source.unlink()
|
|
|
+ logging.debug(f"Linking {source=} -> {target=}.")
|
|
|
+ source.symlink_to(target)
|
|
|
+
|
|
|
+ for dir_ in DIRS_TO_LINK:
|
|
|
+ target = brew_prefix / pyversion / dir_
|
|
|
+ source = pyenv_dir / pyversion / dir_
|
|
|
+ if source.is_dir():
|
|
|
+ if source.resolve() != target.resolve():
|
|
|
+ logging.warning(
|
|
|
+ f"{source} is a proper directory, skipping. "
|
|
|
+ f"Linking for {pyversion} might be broken now."
|
|
|
+ )
|
|
|
+ continue
|
|
|
+ if not source.is_symlink():
|
|
|
+ source.symlink_to(target, target_is_directory=True)
|
|
|
+
|
|
|
+logging.info("Rehashing pyenv")
|
|
|
+subprocess.call(["pyenv", "rehash"])
|