Migrating from pip to UV
A guide for developers transitioning from pip/venv to UV.
Quick Command Translation
| What you want | pip/venv | uv |
|---|---|---|
| Create environment | python -m venv .venv | uv sync |
| Activate environment | source .venv/bin/activate | Not needed with uv run |
| Install from requirements | pip install -r requirements.txt | uv sync |
| Install package | pip install package | uv add package |
| Install specific version | pip install package==1.0.0 | uv add "package==1.0.0" |
| Install from git | pip install git+https://... | uv add git+https://... |
| Uninstall package | pip uninstall package | uv remove package |
| List installed | pip list | uv pip list |
| Show package info | pip show package | uv pip show package |
| Freeze dependencies | pip freeze > requirements.txt | uv lock (automatic) |
| Run script | python script.py | uv run python script.py |
| Run with module | python -m pytest | uv run pytest |
| Upgrade package | pip install --upgrade package | uv add package@latest |
| Upgrade all | pip install --upgrade -r requirements.txt | uv sync --upgrade |
| Install editable | pip install -e . | uv pip install -e . |
Key Conceptual Differences
1. No Manual Activation Needed
pip/venv:
source .venv/bin/activate # Must activate
python script.py # Then run
deactivate # Then deactivate
uv:
uv run python script.py # Just run, no activation!
You can still activate if you prefer, but uv run is faster and more convenient.
2. Lock File Instead of requirements.txt
pip/venv:
# Manual process
pip freeze > requirements.txt
git add requirements.txt
uv:
# Automatic lock file
uv add package # Automatically updates uv.lock
git add uv.lock # Commit the lock file
The lock file is automatically maintained and includes transitive dependencies.
3. Workspace Instead of Multiple venvs
pip/venv (monorepo with multiple packages):
cd package1
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cd ../package2
python -m venv .venv # Another venv!
source .venv/bin/activate
pip install -r requirements.txt
uv (workspace):
# One environment for all packages
uv sync # Installs everything once
4. Dependency Resolution
pip: Best-effort, can produce incompatible versions
uv: Always produces a valid dependency resolution or fails clearly
5. Speed
pip: ~30 seconds for typical install
uv: ~3 seconds (10-100x faster with cache)
Migration Steps
Step 1: Install UV
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
Step 2: Initial Sync
# Remove old venv
rm -rf .venv
# Let UV create new one
uv sync
This reads the root pyproject.toml and creates everything.
Step 3: Update Your Workflow
Old workflow:
source .venv/bin/activate
pip install package
python script.py
deactivate
New workflow:
uv add package
uv run python script.py
Step 4: Update Scripts
Old shell script:
#!/bin/bash
source .venv/bin/activate
python my_script.py
New shell script:
#!/bin/bash
uv run python my_script.py
Step 5: Update CI/CD
Old CI:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
- name: Run tests
run: |
source .venv/bin/activate
pytest
New CI:
- name: Install UV
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install dependencies
run: uv sync --frozen
- name: Run tests
run: uv run pytest
Converting requirements.txt to pyproject.toml
If you have a requirements.txt, you can convert it:
Old requirements.txt:
numpy>=1.26.0
pandas>=2.1.0
scikit-learn>=1.4.0
matplotlib>=3.8.0
New pyproject.toml:
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
"numpy>=1.26.0",
"pandas>=2.1.0",
"scikit-learn>=1.4.0",
"matplotlib>=3.8.0",
]
Then run:
uv sync
Converting setup.py to pyproject.toml
Old setup.py:
from setuptools import setup, find_packages
setup(
name="my-package",
version="0.1.0",
packages=find_packages(),
install_requires=[
"numpy>=1.26.0",
"pandas>=2.1.0",
],
python_requires=">=3.10",
)
New pyproject.toml:
[project]
name = "my-package"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
"numpy>=1.26.0",
"pandas>=2.1.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Common Gotchas
1. Forgetting to Use uv run
Won’t work:
pytest # Uses system Python, not .venv!
Will work:
uv run pytest # Uses .venv Python
# or
source .venv/bin/activate
pytest
2. Editing pyproject.toml Without Syncing
Won’t work:
# Edit pyproject.toml manually
python script.py # Old dependencies still in .venv
Will work:
# Edit pyproject.toml manually
uv sync # Update .venv
uv run python script.py
3. Expecting requirements.txt to Work
UV doesn’t use requirements.txt by default. Use:
pyproject.tomlfor project dependenciesuv.lockfor locked versions (committed to git)
4. Installing Globally
Don’t do this:
uv pip install package # Don't install globally
Do this:
uv add package # Add to project dependencies
FAQ
Can I still use pip?
Yes, but you’ll lose UV’s speed and workspace benefits. Within a UV environment:
uv run pip install package # Works, but not recommended
uv add package # Better, updates lock file
Do I need requirements.txt?
No. UV uses pyproject.toml and uv.lock. But you can still generate one:
uv pip freeze > requirements.txt
Can I use both pip and uv?
Not recommended. Choose one:
- Use UV for project development
- Use pip only for quick experiments or legacy projects
What about conda?
UV doesn’t replace conda environments. For conda:
- Create conda environment
- Install UV in it:
conda install uvorpip install uv - Use UV for Python packages:
uv add package
How do I share my environment?
With pip:
pip freeze > requirements.txt
# Share requirements.txt
With uv:
# Commit uv.lock to git
git add uv.lock pyproject.toml
git commit -m "Update dependencies"
# Others just run: uv sync
What about virtual environment managers (pyenv, virtualenv)?
UV replaces most of these:
- pyenv →
uv python install 3.13 - virtualenv/venv →
uv synccreates.venv - pipenv → UV has lock files built-in
- poetry → Similar, but UV is faster
Benefits You’ll Gain
- Speed: 10-100x faster installs
- Reliability: Always valid dependency resolution
- Reproducibility: Lock file ensures everyone has same versions
- Simplicity: Fewer commands, less configuration
- Modern: Uses latest Python packaging standards
- Workspace: Manage multiple packages easily
Common Patterns
Development Workflow
Old:
source .venv/bin/activate
pip install -e .
pip install pytest
pytest
deactivate
New:
uv sync # Installs everything including dev deps
uv run pytest
Adding Dependencies
Old:
pip install new-package
pip freeze | grep new-package >> requirements.txt
New:
uv add new-package # Automatically updates lock file
Updating Dependencies
Old:
pip install --upgrade package
pip freeze > requirements.txt
New:
uv add package@latest
# or
uv sync --upgrade # Update all
Running Scripts
Old:
source .venv/bin/activate
python -m pytest
python -m black .
python script.py
New:
uv run pytest
uv run black .
uv run python script.py
Still Confused?
Check these resources:
- UV Guide - Complete UV documentation
- Quick Start - Get started quickly
- Troubleshooting - Common issues
- UV Documentation - Official docs
Summary
Stop doing:
python -m venv .venvsource .venv/bin/activatepip install -r requirements.txtpip freeze > requirements.txt
Start doing:
uv sync(once at start)uv run python script.py(no activation!)uv add package(to add dependencies)- Commit
uv.lock(automatic versioning)
Result: Faster, simpler, more reliable development! 🚀