Python’s venv module creates lightweight virtual environments with isolated site-packages and a private copy (or symlink) of the Python interpreter. import venv. Quick create: venv.create(env_dir, system_site_packages=False, clear=False, symlinks=False, with_pip=False, prompt=None) — creates an env at env_dir. Customisable: subclass venv.EnvBuilder and override setup_python(context), setup_scripts(context), post_setup(context). Context: the SimpleNamespace passed to hooks has env_dir, env_exe (Python binary), bin_path, inc_path, lib_path, cfg_path, prompt. Scripts: activate (POSIX bash/zsh), activate.fish, activate.csh, Activate.ps1 (Windows PowerShell). Binaries: {env}/bin/python (POSIX), {env}/Scripts/python.exe (Windows). Install packages after creation: subprocess.run([str(env_exe), "-m", "pip", "install", ...]) — always use the env’s own executable, not the system pip. Claude Code generates project scaffolders, CI environment provisioners, reproducible build runners, and environment health checkers.
CLAUDE.md for venv
## venv Stack
- Stdlib: import venv, subprocess, sys
- Create: venv.create(".venv", with_pip=True, clear=False)
- Bin: POSIX: ".venv/bin/python" Windows: ".venv/Scripts/python.exe"
- Install: subprocess.run([python, "-m", "pip", "install", pkg])
- Check: Path(".venv/pyvenv.cfg").exists() → env is set up
- Note: always use env's own python binary for pip/scripts
venv Virtual Environment Pipeline
# app/venvutil.py — create, install, check, scaffold, reproduce
from __future__ import annotations
import subprocess
import sys
import venv
from dataclasses import dataclass, field
from pathlib import Path
from types import SimpleNamespace
from typing import Sequence
# ─────────────────────────────────────────────────────────────────────────────
# 1. Environment path helpers
# ─────────────────────────────────────────────────────────────────────────────
def env_python(env_dir: "str | Path") -> Path:
"""
Return the path to the Python interpreter inside a virtual environment.
Example:
python = env_python(".venv")
subprocess.run([str(python), "-c", "import sys; print(sys.prefix)"])
"""
p = Path(env_dir)
if sys.platform == "win32":
return p / "Scripts" / "python.exe"
return p / "bin" / "python"
def env_pip(env_dir: "str | Path") -> Path:
"""
Return the path to pip inside a virtual environment.
Example:
pip = env_pip(".venv")
subprocess.run([str(pip), "install", "requests"])
"""
p = Path(env_dir)
if sys.platform == "win32":
return p / "Scripts" / "pip.exe"
return p / "bin" / "pip"
def env_site_packages(env_dir: "str | Path") -> Path:
"""
Return the site-packages directory for a virtual environment.
Example:
sp = env_site_packages(".venv")
print(list(sp.iterdir())[:5])
"""
p = Path(env_dir)
if sys.platform == "win32":
return p / "Lib" / "site-packages"
# POSIX: lib/python3.X/site-packages
lib = p / "lib"
for child in lib.iterdir():
if child.name.startswith("python"):
return child / "site-packages"
return lib / "site-packages" # fallback
def is_venv(env_dir: "str | Path") -> bool:
"""
Return True if env_dir looks like a valid venv (has pyvenv.cfg + python binary).
Example:
if not is_venv(".venv"):
venv.create(".venv", with_pip=True)
"""
p = Path(env_dir)
return (p / "pyvenv.cfg").exists() and env_python(p).exists()
def read_pyvenv_cfg(env_dir: "str | Path") -> dict[str, str]:
"""
Parse pyvenv.cfg into a dict of key=value pairs.
Example:
cfg = read_pyvenv_cfg(".venv")
print(cfg.get("home")) # base Python home
"""
cfg_path = Path(env_dir) / "pyvenv.cfg"
result: dict[str, str] = {}
for line in cfg_path.read_text().splitlines():
if "=" in line:
k, _, v = line.partition("=")
result[k.strip()] = v.strip()
return result
# ─────────────────────────────────────────────────────────────────────────────
# 2. Build helpers
# ─────────────────────────────────────────────────────────────────────────────
def create_venv(
env_dir: "str | Path",
with_pip: bool = True,
clear: bool = False,
system_site_packages: bool = False,
symlinks: bool = (sys.platform != "win32"),
prompt: "str | None" = None,
) -> Path:
"""
Create a virtual environment at env_dir. Returns the env directory path.
Example:
create_venv(".venv", with_pip=True, clear=False)
"""
venv.create(
str(env_dir),
system_site_packages=system_site_packages,
clear=clear,
symlinks=symlinks,
with_pip=with_pip,
prompt=prompt,
)
return Path(env_dir)
def ensure_venv(
env_dir: "str | Path",
with_pip: bool = True,
) -> Path:
"""
Create the venv only if it does not already exist.
Example:
python = env_python(ensure_venv(".venv"))
"""
if not is_venv(env_dir):
create_venv(env_dir, with_pip=with_pip)
return Path(env_dir)
# ─────────────────────────────────────────────────────────────────────────────
# 3. Package installation helpers
# ─────────────────────────────────────────────────────────────────────────────
def pip_install(
env_dir: "str | Path",
packages: "Sequence[str]",
upgrade: bool = False,
quiet: bool = False,
) -> subprocess.CompletedProcess:
"""
Install packages into a virtual environment using the env's own pip.
Example:
pip_install(".venv", ["requests", "httpx>=0.27"])
"""
python = env_python(env_dir)
cmd = [str(python), "-m", "pip", "install"]
if upgrade:
cmd.append("--upgrade")
if quiet:
cmd.append("-q")
cmd.extend(packages)
return subprocess.run(cmd, check=True, capture_output=quiet)
def pip_install_requirements(
env_dir: "str | Path",
requirements_file: "str | Path",
upgrade: bool = False,
) -> subprocess.CompletedProcess:
"""
Install from a requirements.txt file.
Example:
pip_install_requirements(".venv", "requirements.txt")
"""
python = env_python(env_dir)
cmd = [str(python), "-m", "pip", "install", "-r", str(requirements_file)]
if upgrade:
cmd.append("--upgrade")
return subprocess.run(cmd, check=True)
def pip_freeze(env_dir: "str | Path") -> list[str]:
"""
Return the output of pip freeze as a list of requirement strings.
Example:
reqs = pip_freeze(".venv")
Path("requirements-lock.txt").write_text("\n".join(reqs))
"""
python = env_python(env_dir)
result = subprocess.run(
[str(python), "-m", "pip", "freeze"],
check=True, capture_output=True, text=True,
)
return [line for line in result.stdout.splitlines() if line.strip()]
def list_installed(env_dir: "str | Path") -> list[dict[str, str]]:
"""
Return installed packages as list of {name, version} dicts.
Example:
for pkg in list_installed(".venv"):
print(f" {pkg['name']}=={pkg['version']}")
"""
python = env_python(env_dir)
result = subprocess.run(
[str(python), "-m", "pip", "list", "--format=json"],
check=True, capture_output=True, text=True,
)
import json
return json.loads(result.stdout)
# ─────────────────────────────────────────────────────────────────────────────
# 4. Environment health check
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class EnvHealth:
"""
Health report for a virtual environment.
Example:
health = check_env(".venv")
print(health)
if not health.ok:
print("Recreating environment...")
create_venv(".venv", with_pip=True, clear=True)
"""
env_dir: Path
exists: bool
python_ok: bool
pip_ok: bool
python_version: str
cfg: dict[str, str]
errors: list[str] = field(default_factory=list)
@property
def ok(self) -> bool:
return self.exists and self.python_ok and not self.errors
def __str__(self) -> str:
status = "OK" if self.ok else "DEGRADED"
return (
f"EnvHealth({self.env_dir} {status} "
f"python={self.python_version} pip={self.pip_ok})"
)
def check_env(env_dir: "str | Path") -> EnvHealth:
"""
Run health checks on a virtual environment directory.
Example:
h = check_env(".venv")
if not h.ok:
create_venv(".venv", clear=True)
"""
p = Path(env_dir)
errors: list[str] = []
if not is_venv(p):
return EnvHealth(
env_dir=p, exists=False, python_ok=False, pip_ok=False,
python_version="", cfg={}, errors=["not a valid venv"],
)
cfg = read_pyvenv_cfg(p)
python = env_python(p)
# Check python binary
python_version = ""
python_ok = False
try:
r = subprocess.run(
[str(python), "--version"], capture_output=True, text=True, timeout=10
)
if r.returncode == 0:
python_version = r.stdout.strip() or r.stderr.strip()
python_ok = True
else:
errors.append(f"python exited {r.returncode}")
except Exception as e:
errors.append(f"python error: {e}")
# Check pip
pip_ok = False
try:
r2 = subprocess.run(
[str(python), "-m", "pip", "--version"],
capture_output=True, text=True, timeout=10,
)
pip_ok = r2.returncode == 0
except Exception:
pass
return EnvHealth(
env_dir=p, exists=True, python_ok=python_ok, pip_ok=pip_ok,
python_version=python_version, cfg=cfg, errors=errors,
)
# ─────────────────────────────────────────────────────────────────────────────
# 5. Custom EnvBuilder subclass
# ─────────────────────────────────────────────────────────────────────────────
class PostSetupEnvBuilder(venv.EnvBuilder):
"""
EnvBuilder subclass that runs post-setup hooks (install packages, write files).
Example:
builder = PostSetupEnvBuilder(
with_pip=True,
packages=["requests", "httpx"],
extra_files={"src/sitecustomize.py": "# custom startup\n"},
)
builder.create("/tmp/myenv")
"""
def __init__(
self,
*,
packages: "list[str] | None" = None,
extra_files: "dict[str, str] | None" = None,
**kwargs,
) -> None:
super().__init__(**kwargs)
self._packages = packages or []
self._extra_files = extra_files or {}
self._context: "SimpleNamespace | None" = None
def post_setup(self, context: SimpleNamespace) -> None:
self._context = context
if self._packages:
subprocess.run(
[context.env_exe, "-m", "pip", "install", "-q"] + self._packages,
check=True,
)
for rel_path, content in self._extra_files.items():
dest = Path(context.env_dir) / rel_path
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(content)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import tempfile
print("=== venv demo ===")
with tempfile.TemporaryDirectory() as td:
env_dir = Path(td) / ".venv"
# ── create_venv ────────────────────────────────────────────────────────
print("\n--- create_venv ---")
create_venv(env_dir, with_pip=True)
print(f" created: {env_dir.name} is_venv={is_venv(env_dir)}")
# ── env paths ─────────────────────────────────────────────────────────
print("\n--- env paths ---")
print(f" python: {env_python(env_dir)}")
print(f" pip: {env_pip(env_dir)}")
sp = env_site_packages(env_dir)
print(f" site-packages: {sp}")
# ── pyvenv.cfg ────────────────────────────────────────────────────────
print("\n--- pyvenv.cfg ---")
cfg = read_pyvenv_cfg(env_dir)
for k in ["home", "include-system-site-packages", "version"]:
print(f" {k} = {cfg.get(k, 'n/a')}")
# ── check_env ─────────────────────────────────────────────────────────
print("\n--- check_env ---")
health = check_env(env_dir)
print(f" {health}")
# ── pip_install ────────────────────────────────────────────────────────
print("\n--- pip_install ---")
pip_install(env_dir, ["pip-autoremove"], quiet=True)
pkgs = list_installed(env_dir)
names = [p["name"] for p in pkgs]
print(f" installed count: {len(pkgs)}")
# Show a sample
for p in pkgs[:3]:
print(f" {p['name']}=={p['version']}")
# ── pip_freeze ────────────────────────────────────────────────────────
print("\n--- pip_freeze ---")
frozen = pip_freeze(env_dir)
print(f" freeze lines: {len(frozen)}")
for line in frozen[:3]:
print(f" {line}")
# ── ensure_venv (idempotent) ───────────────────────────────────────────
print("\n--- ensure_venv (idempotent) ---")
before = list(env_dir.iterdir())
ensure_venv(env_dir)
after = list(env_dir.iterdir())
print(f" content unchanged: {sorted(before) == sorted(after)}")
print("\n=== done ===")
For the virtualenv (PyPI) alternative — virtualenv provides faster creation using cached wheels, supports older Python versions without built-in venv, and has cross-version environment creation — use virtualenv in CI pipelines where creation speed matters or when targeting Python versions below 3.3; use venv (stdlib) for zero-dependency scripting and when you want to subclass EnvBuilder for custom post-setup logic. For the conda/mamba alternative — conda manages full environment stacks including non-Python packages and can switch between Python versions — use conda for data science workflows with numpy/scipy/CUDA dependencies that benefit from binary package management; use venv for pure-Python application environments where a lightweight stdlib solution is preferred. The Claude Skills 360 bundle includes venv skill sets covering env_python()/env_pip()/env_site_packages() path helpers, is_venv()/read_pyvenv_cfg() inspection, create_venv()/ensure_venv() builders, pip_install()/pip_install_requirements()/pip_freeze()/list_installed() package management, EnvHealth + check_env() health reporter, and PostSetupEnvBuilder with packages and extra_files hooks. Start with the free tier to try virtual environment patterns and venv pipeline code generation.