ruff/scripts/ty_benchmark/src/benchmark/venv.py

91 lines
2.5 KiB
Python

from __future__ import annotations
import logging
import subprocess
import sys
from pathlib import Path
class Venv:
project_path: Path
def __init__(self, path: Path):
self.project_path = path
@property
def path(self) -> Path:
return self.project_path / "venv"
@property
def name(self) -> str:
"""The name of the virtual environment directory."""
return self.path.name
@property
def python(self) -> Path:
"""Returns the path to the python executable"""
return self.script("python")
@property
def bin(self) -> Path:
bin_dir = "scripts" if sys.platform == "win32" else "bin"
return self.path / bin_dir
def script(self, name: str) -> Path:
extension = ".exe" if sys.platform == "win32" else ""
return self.bin / f"{name}{extension}"
@staticmethod
def create(parent: Path, python_version: str) -> Venv:
"""Creates a new, empty virtual environment."""
command = [
"uv",
"venv",
"--quiet",
"--python",
python_version,
"venv",
]
try:
subprocess.run(
command, cwd=parent, check=True, capture_output=True, text=True
)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Failed to create venv: {e.stderr}")
return Venv(parent)
def install(self, pip_install_args: list[str]) -> None:
"""Installs the dependencies required to type check the project."""
logging.debug(f"Installing dependencies: {', '.join(pip_install_args)}")
command = [
"uv",
"pip",
"install",
"--python",
self.python.as_posix(),
"--quiet",
# We pass `--exclude-newer` to ensure that type-checking of one of
# our projects isn't unexpectedly broken by a change in the
# annotations of one of that project's dependencies
"--exclude-newer",
"2025-12-13T00:00:00Z",
"mypy", # We need to install mypy into the virtual environment or it fails to load plugins.
*pip_install_args,
]
try:
subprocess.run(
command,
cwd=self.project_path,
check=True,
capture_output=True,
text=True,
)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Failed to install dependencies: {e.stderr}")