# script/reinstall_dev.py """ Reinstall multiple local libraries in editable mode for development. Workflow: 1) Try to uninstall all target libraries in one command (ignore errors if some are not installed). 2) Reinstall each library in editable mode from a sibling folder: ../. This script uses the same Python interpreter that runs it (sys.executable), so pip operations happen in the same environment (e.g., your active venv). """ from __future__ import annotations import os import shutil import subprocess import sys import sysconfig from typing import Iterable # Absolute path to the Python executable that runs this script. # Using sys.executable ensures pip targets the same environment (e.g., your venv). PY = sys.executable def run(cmd: list[str]) -> None: """ Echo and execute a subprocess command. Raises: subprocess.CalledProcessError: if the command returns a non-zero exit code. """ print("$", " ".join(cmd), flush=True) subprocess.check_call(cmd) def uninstall_many(packages: Iterable[str]) -> None: """ Attempt to uninstall multiple packages at once. If uninstall fails (e.g., a package is not installed), we keep going. Args: packages: An iterable of package names (pip distribution names). """ pkgs = list(packages) if not pkgs: return try: run([PY, "-m", "pip", "uninstall", "-y", *pkgs]) except subprocess.CalledProcessError as e: # Continue even if the uninstall step fails (for one or more packages) print(f"[WARN] Uninstall returned {e.returncode} — continuing...", flush=True) def install_editable_many(packages: Iterable[str]) -> None: """ Install each package in editable mode from ../. Assumes your project layout has sibling folders one level up, e.g.: ../guidata Args: packages: An iterable of package names (also used as directory names). """ for pkg in packages: run([PY, "-m", "pip", "install", "-e", f"../{pkg}"]) def remove_residual_dirs(packages: Iterable[str]) -> None: """ Force remove residual package directories from site-packages. This mimics: rm -rf .venv/Lib/site-packages/ to ensure a clean slate before reinstalling. """ # Locates site-packages, e.g. .venv/Lib/site-packages site_packages = sysconfig.get_path("purelib") for pkg in packages: target = os.path.join(site_packages, pkg) if os.path.isdir(target): print(f"Removing residual directory: {target}", flush=True) try: shutil.rmtree(target) except OSError as e: print(f"[WARN] Failed to remove {target}: {e}", flush=True) def reinstall_packages(packages: list[str]) -> None: """ High-level orchestration for many packages: - Uninstall all of them (ignore failures) - Force remove residual directories from site-packages - Install each in editable mode """ # 1) Uninstall (ignore if not installed) uninstall_many(packages) # 2) Force remove residual directories (essential for clean reinstall) remove_residual_dirs(packages) # 3) Editable installs install_editable_many(packages) if __name__ == "__main__": # ⭐ Fixed, editable local libraries to manage (edit as needed) PACKAGES = ["guidata"] print("🏃 Reinstalling editable packages:", ", ".join(PACKAGES)) reinstall_packages(PACKAGES)