|
| 1 | +"""Utility functions for UV virtual environment management.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import os |
| 6 | +import subprocess |
| 7 | +from pathlib import Path |
| 8 | + |
| 9 | + |
| 10 | +def run_uv_command(args: list[str], env: dict | None = None, check: bool = False) -> subprocess.CompletedProcess: |
| 11 | + """Run a UV command and return the result. |
| 12 | +
|
| 13 | + Args: |
| 14 | + args: Command arguments to pass to UV |
| 15 | + env: Optional environment variables |
| 16 | + check: Whether to raise an exception on non-zero return code |
| 17 | +
|
| 18 | + Returns: |
| 19 | + CompletedProcess instance with command output |
| 20 | + """ |
| 21 | + return subprocess.run( |
| 22 | + ["uv", *args], |
| 23 | + capture_output=True, |
| 24 | + text=True, |
| 25 | + env=env, |
| 26 | + check=check, |
| 27 | + ) |
| 28 | + |
| 29 | + |
| 30 | +def create_venv(venv_path: Path, with_pip: bool = True) -> None: |
| 31 | + """Create a virtual environment using UV. |
| 32 | +
|
| 33 | + Args: |
| 34 | + venv_path: Path where the virtual environment should be created |
| 35 | + with_pip: Whether to include pip and other seed packages |
| 36 | + """ |
| 37 | + args = ["venv"] |
| 38 | + if with_pip: |
| 39 | + args.append("--seed") |
| 40 | + args.append(str(venv_path)) |
| 41 | + run_uv_command(args, check=True) |
| 42 | + |
| 43 | + |
| 44 | +def install_package(venv_path: Path, pip_url: str) -> None: |
| 45 | + """Install a package into a virtual environment using UV. |
| 46 | +
|
| 47 | + Args: |
| 48 | + venv_path: Path to the virtual environment |
| 49 | + pip_url: Package specification (name, URL, or path) to install |
| 50 | + """ |
| 51 | + venv_env = get_venv_env(venv_path) |
| 52 | + run_uv_command( |
| 53 | + ["pip", "install", pip_url], |
| 54 | + env=venv_env, |
| 55 | + check=True, |
| 56 | + ) |
| 57 | + |
| 58 | + |
| 59 | +def get_venv_env(venv_path: Path) -> dict: |
| 60 | + """Get environment variables for a virtual environment. |
| 61 | +
|
| 62 | + Args: |
| 63 | + venv_path: Path to the virtual environment |
| 64 | +
|
| 65 | + Returns: |
| 66 | + Dict of environment variables |
| 67 | + """ |
| 68 | + env = os.environ.copy() |
| 69 | + env["VIRTUAL_ENV"] = str(venv_path) |
| 70 | + env["PATH"] = f"{venv_path}/bin:{os.environ['PATH']}" |
| 71 | + return env |
| 72 | + |
| 73 | + |
| 74 | +def get_executable_path(venv_path: Path, executable: str) -> Path: |
| 75 | + """Get the path to an executable in a virtual environment. |
| 76 | +
|
| 77 | + Args: |
| 78 | + venv_path: Path to the virtual environment |
| 79 | + executable: Name of the executable |
| 80 | +
|
| 81 | + Returns: |
| 82 | + Path to the executable |
| 83 | + """ |
| 84 | + bin_dir = "Scripts" if os.name == "nt" else "bin" |
| 85 | + suffix = ".exe" if os.name == "nt" else "" |
| 86 | + return venv_path / bin_dir / f"{executable}{suffix}" |
0 commit comments