UV vs PIP: A Comprehensive Guide to Python Package Management

7 minute read

Published:

Overview

uv is an extremely fast Python package and project manager written in Rust, designed as a modern drop-in replacement for pip, pip-tools, and virtualenv.

Core Architectural Distinctions

  • Pip: Imperative and environment-centric. Requires manual virtual environment creation, explicit shell activation, and separate tools for locking (pip-compile / pip freeze).
  • UV: Declarative and project-centric. Manages virtual environments automatically, resolves dependencies using universal lockfiles (uv.lock), and supports ephemeral execution without manual activation.

1. Traditional Pip Workflow

Virtual Environment Creation and Management

# Create virtual environment
python -m venv .venv
# or specify a specific Python version
python3.11 -m venv .venv

# Activate environment
# macOS/Linux:
source .venv/bin/activate

# Windows:
.venv\Scripts\activate

# Work in activated environment
pip install numpy pandas
python script.py
pip list

# Deactivate when finished
deactivate

Dependency Management with Pip

# Install packages directly
pip install numpy pandas matplotlib

# Install from requirements file
pip install -r requirements.txt

# Export pinned environment snapshot
pip freeze > requirements.txt

# Install development dependencies
pip install pytest black flake8

# Uninstall a package
pip uninstall numpy

# Inspect installed packages
pip list
pip show numpy

Project Structure with Pip

my-project/
├── .venv/                  # Virtual environment
├── src/
│   └── myproject/
├── requirements.txt        # Production dependencies
├── requirements-dev.txt    # Development dependencies
├── setup.py                # Package configuration
└── README.md

Running Projects with Pip

# Activate environment
source .venv/bin/activate

# Execute application or tests
python script.py
pytest

# Deactivate
deactivate

2. UV Workflow

Virtual Environment Creation and Management

# Automatically create and sync virtual environment from pyproject.toml
uv sync

# Run scripts directly without manual activation (automatically uses .venv)
uv run python script.py

# Optional: Manual activation remains supported
source .venv/bin/activate
python script.py

Dependency Management with UV

# Add dependencies (automatically updates pyproject.toml and uv.lock)
uv add numpy pandas matplotlib

# Add development dependencies
uv add --dev pytest black ruff

# Add optional / extra dependency groups
uv add --optional plotting seaborn matplotlib

# Remove dependencies
uv remove numpy

# Sync environment precisely with pyproject.toml and uv.lock
uv sync

# List packages in current environment
uv pip list

Project Structure with UV

my-project/
├── .venv/          # Virtual environment (auto-created)
├── src/
│   └── myproject/
├── pyproject.toml  # Unified project configuration & dependencies
├── uv.lock         # Cross-platform deterministic lockfile (auto-generated)
└── README.md

Running Projects with UV

# Method 1: uv run (Recommended — auto-detects and provisions .venv)
uv run python script.py
uv run python -m myproject
uv run pytest

# Method 2: Activated environment (Traditional workflow)
source .venv/bin/activate
python script.py
pytest
deactivate

# Method 3: Direct binary invocation
.venv/bin/python script.py

3. Virtual Environment Internals & Behavioral Differences

Pip Environment Binary Layout

A standard venv provisioned via python -m venv bundles python, pip, and shell activation scripts. pip is always resident inside the environment.

# Inspect contents of a pip-created .venv/bin/
ls .venv/bin/

source .venv/bin/activate
which pip     # Points to .venv/bin/pip
pip install numpy

UV Environment Binary Layout

A virtual environment created by uv contains the Python binaries and activation scripts, but does not include pip by default to optimize speed and footprint.

# Inspect contents of a uv-created .venv/bin/
ls .venv/bin/

# Traditional pip is not included by default
source .venv/bin/activate
which pip     # Points to system pip, not environment pip
python -m pip # Error: No module named pip

# uv pip interface manages the environment without requiring resident pip
uv pip install numpy
uv pip list

Operating Inside an Activated UV Environment

If legacy scripts or workflows strictly require the pip binary inside .venv, install pip as a development dependency:

# After activating UV environment
source .venv/bin/activate

# These UV commands still work
uv add matplotlib       # Works, updates pyproject.toml
uv pip install ipython  # Works, temporary installation

# Traditional pip doesn't work
pip install numpy       # X Error: no pip module

# But it might work if pip was added
uv add --dev pip      # Add pip to environment
source .venv/bin/activate
pip install numpy       # Now works

4. Advanced Dependency & Installation Patterns

Pip Installation Patterns

source .venv/bin/activate

# Install individual packages
pip install numpy
pip install "numpy>=1.20.0"

# Install from requirements.txt
pip install -r requirements.txt
pip install -r requirements-dev.txt

# Install in development mode
pip install -e .

# Install from git
pip install git+https://github.com/user/repo.git

UV Installation Patterns

# Project dependencies (updates pyproject.toml)
uv add numpy # Latest version
uv add "numpy>=1.20.0" # Version constraint
uv add --dev pytest # Development dependency

# Direct environment installation (doesn't update pyproject.toml)
uv pip install numpy

# Install project in development mode
uv add -e .

# Install from git
uv add git+https://github.com/user/repo.git

# Sync from pyproject.toml
uv sync # Installs default and dev dependencies
uv sync --no-dev # Exclude dev dependencies

5. Execution Approaches Comparison

Pip Execution Approaches

source .venv/bin/activate

python script.py
python -m mypackage
pytest
jupyter notebook

deactivate

UV Execution Approaches

Approach 1: uv run (Zero Activation)

Ensures dependencies are locked and synced before execution without modifying active shell state:

uv run python script.py
uv run python -m mypackage
uv run pytest
uv run jupyter notebook
uv run mycommand # For project with script entry points

Approach 2: Traditional Activation

Standard virtual environment activation for interactive development:

source .venv/bin/activate
python script.py
mycommand
pytest
deactivate

Approach 3: Direct Path Execution

Calling the environment interpreter directly without shell mutation:

.venv/bin/python script.py
.venv/bin/mycommand # If installed as script

6. Technical Reference & Command Cheat Sheet

Configuration & Project Metadata

DimensionPip EcosystemUV Ecosystem
Dependency Specificationrequirements.txtpyproject.toml ([project.dependencies])
Development Dependenciesrequirements-dev.txtpyproject.toml ([dependency-groups])
Deterministic LockfileManual / External (pip-tools, pip freeze)uv.lock (automatic, multi-platform)
Build & Packaging Configsetup.py / setup.cfgpyproject.toml ([build-system])

Command Mapping Reference

OperationPip CommandUV EquivalentNotes
Add dependencypip install <pkg>uv add <pkg>uv updates pyproject.toml & uv.lock
Sync dependenciespip install -r requirements.txtuv syncReconciles environment with lockfile
Remove dependencypip uninstall <pkg>uv remove <pkg>Removes package from pyproject.toml
List packagespip listuv pip listInspects .venv package index
Inspect packagepip show <pkg>uv pip show <pkg>Shows package metadata
Freeze statepip freeze > requirements.txtHandled by uv.lockuv.lock is cross-platform and hashed
Run scriptpython script.py (requires activation)uv run python script.pyAuto-discovers and uses .venv
Ad-hoc installpip install <pkg>uv pip install <pkg>Installs without editing pyproject.toml

Environment Management Matrix

FeaturePip / VirtualenvUV
Creationpython -m venv .venvuv sync or uv venv
Shell ActivationMandatory for isolated executionOptional (superseded by uv run)
Bundled Pip BinaryIncluded (.venv/bin/pip)Excluded by default (uv add --dev pip to enable)
Resolution SpeedStandard Python / PyPI network callsHigh-concurrency Rust resolver & global cache
Lock GuaranteePlatform-dependent snapshotDeterministic, multi-platform universal lockfile

Key Migration Notes

  1. uv add vs. uv pip install:
    • Use uv add for application and library projects where dependencies should be tracked in pyproject.toml and pinned in uv.lock.
    • Use uv pip install as a drop-in replacement for pip install when working imperatively in legacy environments or ad-hoc scripts.
  2. Missing pip in .venv:
    • If third-party tooling or scripts call pip directly inside .venv, install it via uv add --dev pip or invoke operations via uv pip <cmd>.
  3. Deterministic CI/CD Pipelines:
    • In deployment environments, use uv sync --no-dev --frozen to guarantee that installations strictly match uv.lock without recalculating dependencies or touching network indexes unnecessarily.

Leave a Comment