Claude Code for venv: Python Virtual Environment Creation — Claude Skills 360 Blog
Blog / AI / Claude Code for venv: Python Virtual Environment Creation
AI

Claude Code for venv: Python Virtual Environment Creation

Published: December 16, 2028
Read time: 5 min read
By: Claude Skills 360

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.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free