A practical guide for data scientists and ML engineers — concepts first, commands second.
pip resolves dependencies one-by-one in Python. uv is written in Rust and resolves everything in parallel — typically 10–100× faster on fresh installs.
pip + requirements.txt can drift across machines. uv uses a lockfile (uv.lock) that pins every transitive dependency to an exact version and hash.
pip, virtualenv, pyenv, pip-tools — uv replaces all of them. One binary, one mental model.
uv understands pyproject.toml natively — the new standard for Python projects, replacing setup.py and requirements.txt.
Key insight: In the pip world, the environment IS the project. In the uv world, the project defines what the environment should be. uv creates and manages the environment for you from a declarative spec.
pyproject.toml (the "what I want" file)uv.lock (auto-generated lockfile, much more reliable).venv automaticallyuv python — uv can download and manage Python versions# One-liner installer (recommended)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or via Homebrew (macOS)
brew install uv
# Verify
uv --version
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
uv self update to get the latest versionuv init my-project
cd my-project
This creates a pyproject.toml with sensible defaults. Look inside it — it's readable and editable by hand.
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"numpy>=1.24",
"pandas>=2.0",
"scikit-learn",
"jupyter",
]
# Optional: dev/test tools kept separate
[dependency-groups]
dev = [
"pytest",
"ruff",
]
| Command | What it does | pip equivalent |
|---|---|---|
uv init |
Scaffold a new project with pyproject.toml | manual setup |
uv add numpy |
Add a dependency → updates pyproject.toml + uv.lock + installs it | pip install numpy + manual edit |
uv add --dev pytest |
Add to dev group only | pip install pytest + manual edit |
uv remove numpy |
Remove a dependency and clean up lockfile | pip uninstall + manual edit |
uv sync |
Make the .venv exactly match uv.lock (install missing, remove extra) | pip install -r requirements.txt |
uv sync --group dev |
Sync + include the dev dependency group | pip install -r dev-requirements.txt |
uv lock |
Regenerate uv.lock from pyproject.toml without installing | pip-compile |
uv run python script.py |
Run a command inside the project's environment (auto-syncs first) | source .venv/bin/activate && python script.py |
uv run jupyter lab |
Launch Jupyter inside the environment | source .venv/bin/activate && jupyter lab |
uv python install 3.12 |
Download and manage Python version | pyenv install 3.12 |
uv python pin 3.12 |
Set default Python version for this project | pyenv local 3.12 |
uv pip install numpy |
Drop-in pip replacement (no lockfile, for scripts/quick use) | pip install numpy |
uv run is your friend. Instead of activating the virtualenv manually, just prefix your commands with uv run. It activates, syncs if needed, runs, and exits cleanly. No more "why is the wrong python in my PATH" confusion.
git clone https://github.com/org/project && cd projectuv sync # production deps only
uv sync --group dev # include dev tools toouv add lightgbmuv run to run things inside the environment without activating it.uv run jupyter lab
uv run python train.py
uv run pytestuv.lock. This is what makes the environment reproducible for teammates and CI.git add pyproject.toml uv.lock
git commit -m "add lightgbm"# Step 1 — in your existing project dir, initialize uv
uv init --no-package # --no-package = don't treat this as a library
# Step 2 — import your existing requirements.txt
uv add -r requirements.txt
# Step 3 — (optional) import dev requirements
uv add --group dev -r requirements-dev.txt
# Step 4 — verify everything works
uv sync
uv run python -c "import pandas; print(pandas.__version__)"
Don't delete requirements.txt yet. If other tools (CI, Dockerfiles, teammates) depend on it, keep it around. You can also export: uv export --format requirements-txt > requirements.txt
| Task | Command |
|---|---|
| Update a single package | uv add numpy --upgrade |
| Update all packages | uv lock --upgrade then uv sync |
| See what's outdated | uv tree --outdated |
| Show dependency tree | uv tree |
pyproject.toml or a .python-version file# See what Python versions are available
uv python list
# Install a specific version
uv python install 3.12
# Pin this project to Python 3.12
uv python pin 3.12 # creates .python-version file
# Create environment with a specific Python
uv sync --python 3.12
Tip: If you set requires-python = ">=3.11" in pyproject.toml, uv will automatically find or download a compatible Python when syncing. You rarely need to think about this manually.
# Start a new DS project
uv init my-analysis && cd my-analysis
# Add the core stack
uv add numpy pandas scikit-learn matplotlib seaborn
# Add Jupyter as a dev tool (you don't ship notebooks to prod)
uv add --group dev jupyterlab ipykernel
# Register the kernel so Jupyter sees your environment
uv run python -m ipykernel install --user --name=my-analysis
# Launch
uv run jupyter lab
# PyTorch with CUDA 12.1
uv add torch torchvision --index https://download.pytorch.org/whl/cu121
# Or pin it in pyproject.toml under [[tool.uv.index]]
conda vs uv: If your workflow relies on non-Python system libraries installed via conda (CUDA toolkit, MKL, etc.), uv doesn't replace conda for that layer. A common pattern is: conda for system libs → uv inside the conda env for Python packages.
pip install X inside the .venv works but bypasses the lockfile — your environment will silently drift from uv.lock.uv sync.uv run handles it. But if you need to activate manually: source .venv/bin/activate (macOS/Linux) or .venv\Scripts\activate (Windows).uv remove — don't manually delete from the file without re-running uv sync.| Situation | Command |
|---|---|
| Quick one-off script, no project | uv run --with pandas python script.py |
| See what's installed | uv pip list |
| Export to requirements.txt format | uv export --format requirements-txt |
| Run a tool without installing it | uv tool run ruff check . (or just uvx ruff check .) |
| Install a global CLI tool | uv tool install ruff |
| Check environment is healthy | uv sync --check |
pyproject.toml — your dependency declarationsuv.lock — the exact pinned environment (this is the point!).venv/ — always regenerate with uv sync.python-version is optional — commit it if you want to enforce a specific Python for the whole team.venv/
__pycache__/
*.pyc
.ipynb_checkpoints/