A Controlled Python Environment for Claude Code

Isolating ad-hoc Python tasks from system and project environments
claude-code
python
uv
Author

Dan O’Leary

Published

February 2, 2026

The Problem

When Claude Code needs Python for ad-hoc tasks (e.g., parsing Excel files, reading PDFs, quick data analysis) it uses whatever Python is available. This can cause issues:

  • No isolation: packages installed to user site-packages
  • Version uncertainty: using whatever Python happened to be installed
  • Dependency conflicts: risk of polluting project-specific environments
  • Ambiguity: no clear boundary between ad-hoc work and project venvs

I wanted a dedicated, managed environment for Claude Code’s throwaway tasks that wouldn’t interfere with anything else.

The Solution

A uv-managed Python environment specifically for Claude Code’s ad-hoc work, invoked via a distinct shell command.

Why uv?

brew install uv
  • Fast: installs packages in seconds, not minutes
  • Modern: handles both package management and environment isolation
  • Simple: one tool instead of virtualenv + pip + pyenv
  • Flex: all the cool kids use it 😎

Project Setup

Create a dedicated project directory:

mkdir -p /casa/dev/claude-code
cd /casa/dev/claude-code
uv init

The pyproject.toml declares whatever dependencies your agent might need:

[project]
name = "claude-adhoc"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    # Data/Files
    "pandas",
    "numpy",
    "openpyxl",
    "xlrd",
    "python-pptx",
    "python-docx",
    "pypdf",
    "pdfplumber",
    "pillow",
    "pyyaml",
    # Web
    "requests",
    "httpx",
    "beautifulsoup4",
    "lxml",
    # Utilities
    "rich",
    "chardet",
    "matplotlib",
    "seaborn",
    # Dev tools
    "ruff",
    "mypy",
    "pytest",
]

Add these with uv add <package> or just ask your robot to do it. 🤖

Shell Script

Create an executable script (not a shell function) and add it to your PATH:

#!/bin/zsh
echo "⚠️  This Python instance is for ad-hoc usage only."
echo "   If working in a project with its own venv, use that instead."
echo ""
exec uv run --project /casa/dev/claude-code python "$@"

Save this as ~/.config/scripts/claude-adhoc-python (or wherever you keep scripts), make it executable, and symlink it to a directory in your PATH:

chmod +x ~/.config/scripts/claude-adhoc-python
ln -sf ~/.config/scripts/claude-adhoc-python ~/.local/bin/claude-adhoc-python

Why a script instead of a shell function? Claude Code’s Bash tool runs in a non-interactive shell, which only sources .zshenv - not .zshrc or any files it sources. Shell functions defined in .zshrc won’t be available. A standalone script in your PATH works everywhere.

Key design choices:

Choice Rationale
Standalone script Works in non-interactive shells (like Claude Code’s Bash)
Distinct command name Avoids confusion with project-local Python
Warning message Reminds both Claude and user this is for ad-hoc use

Claude Code Instructions

Add to your global ~/.config/claude/CLAUDE.md (see Claude Code’s Instruction Mechanisms for how CLAUDE.md and other config mechanisms work):

## Ad-hoc Python

For quick Python tasks outside of project contexts, use:

    claude-adhoc-python -c "import pandas as pd; ..."

DO NOT use this inside projects that have their own venv or pyproject.toml.
Use the project's environment instead.

This tells Claude when to use the ad-hoc environment and - critically - when not to.

Usage

One-liners:

claude-adhoc-python -c "import pandas as pd; print(pd.read_excel('data.xlsx').head())"

Scripts:

claude-adhoc-python script.py

Or invoke uv directly:

uv run --project /casa/dev/claude-code python -c "import pdfplumber; ..."
NoteSandbox Compatibility

If you use Claude Code’s sandbox, the uv cache is outside the working directory and needs explicit permission. Add your cache path to ~/.claude/settings.json:

"allow": [
  "Read(//path/to/.uv-cache/**)",
  "Edit(//path/to/.uv-cache/**)"
]

Use // for absolute paths - a single / is relative to the settings file. Adjust the path to match your $UV_CACHE_DIR.

Also: early versions of uv (before v0.9.29) panicked inside the sandbox due to a blocked macOS system API. If you hit “Attempted to create a NULL object”, update uv: uv self update.

What I Didn’t Do

No ipykernel: Claude Code’s Bash tool is stateless; each command runs in a fresh shell. A persistent REPL adds no value here.

No activation script: the uv run --project pattern handles isolation without needing to “activate” anything. Simpler mental model.

Maintenance

Add packages as needed:

cd /casa/dev/claude-code
uv add some-new-package

Update everything:

uv sync --upgrade

Resources