Workaround for Claude Code running `python` instead of `uv`
uv is now the de facto default Python package manager. I have already deleted all python
s from my system except for the one that has to be installed for other packages in brew
.
Unfortunately, Claude Code often ignores instructions in CLAUDE.md
files to use uv run python
instead of plain python
commands. Even with clear documentation stating “always use uv”, Claude Code will attempt to run python
directly, leading to “command not found” errors in projects that rely on uv for Python environment management.
The built-in Claude Code hooks and environment variable settings also don’t reliably solve this issue due to shell context limitations.
The reason is that Claude (and most other AI models) take time to catch up to such changes, because their learning horizon is longer, up to months to years. Somebody will need to include this information explicitly in the training data.
Until then, we can prevent wasting tokens by mapping python
and python3
to uv
.
I personally don’t want to map these globally, because a lot of other packages might depend on system installed python
s, like brew
packages, gcloud
CLI and so on.
Because of that, I map them at the project level, using direnv:
An OK-ish solution: direnv + dynamic wrapper scripts
We can force Claude Code (and any developer) to use uv run python
by dynamically creating wrapper scripts in a .envrc
file that direnv automatically loads when entering the project directory.
This will override python
and python3
to map to uv run python
, and also print a nice message to the model:
Use "uv run python ..." instead of "python ..." idiot
.
This is probably not the best solution, but it is a solution. Feel free to suggest a better one.
Step 1: Install direnv
# macOS
brew install direnv
# Ubuntu/Debian
sudo apt install direnv
# Add to your shell (bash/zsh)
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc # or ~/.bashrc
source ~/.zshrc # or restart terminal
Step 2: Setup direnv with dynamic wrapper scripts
# Create .envrc file in project root
cat > .envrc << 'EOF'
#!/bin/bash
# Create temporary bin directory for python overrides
TEMP_BIN_DIR="$PWD/.direnv/bin"
mkdir -p "$TEMP_BIN_DIR"
# Create python wrapper scripts
cat > "$TEMP_BIN_DIR/python" << 'INNER_EOF'
#!/bin/bash
echo "Use \"uv run python ...\" instead of \"python ...\" idiot"
exec uv run python "$@"
INNER_EOF
cat > "$TEMP_BIN_DIR/python3" << 'INNER_EOF'
#!/bin/bash
echo "Use \"uv run python ...\" instead of \"python3 ...\" idiot"
exec uv run python "$@"
INNER_EOF
# Make them executable
chmod +x "$TEMP_BIN_DIR/python" "$TEMP_BIN_DIR/python3"
# Add to PATH
export PATH="$TEMP_BIN_DIR:$PATH"
EOF
# Allow direnv to load this configuration
direnv allow
Step 3: Update .gitignore
# Add direnv generated files to .gitignore
echo "# direnv generated files" >> .gitignore
echo ".direnv/" >> .gitignore
Step 4: Update documentation
Add to your CLAUDE.md
something like this:
## Python Package Management with uv
**IMPORTANT**: This project uses `uv` as the Python package manager. ALWAYS use `uv` instead of `pip` or `python` directly.
DO NOT RUN:
```bash
python my_script.py
# OR
chmod +x my_script.py
./my_script.py
```
INSTEAD, RUN:
```bash
uv run my_script.py
```
### Key uv Commands
- **Run Python code**: `uv run <script.py>` (NOT `python <script.py>`)
- **Run module**: `uv run -m <module>` (e.g., `uv run -m pytest`)
- **Add dependencies**: `uv add <package>` (e.g., `uv add requests`)
- **Add dev dependencies**: `uv add --dev <package>`
- **Remove dependencies**: `uv remove <package>`
- **Install all dependencies**: `uv sync`
- **Update lock file**: `uv lock`
- **Run with specific package**: `uv run --with <package> <command>`
How It Works
- direnv automatically loads
.envrc
when youcd
into the project directory .envrc
dynamically creates executable wrapper scripts in.direnv/bin/
- Scripts display a helpful message and redirect to
uv run python
.direnv/bin/
is prepended to PATH, overriding system python commands- Works for any shell session in the directory (Claude Code, terminal, IDE)
To see if it works:
cd your-project/
python -c "print('Hello World')" # Shows message, uses uv
python3 --version # Shows message, uses uv
Let me know if this doesn’t work for you, or if you find a better solution.