UV Package Manager Guide

This project uses uv - an ultra-fast Python package installer and resolver written in Rust. It’s 10-100x faster than pip and provides modern workspace management for our monorepo.

Why UV?

  • ⚡ Speed: 10-100x faster than pip
  • 🔒 Deterministic: Lock file ensures reproducible builds across all environments
  • 🏢 Workspace Support: Unified dependency management for multiple packages
  • 🎯 Modern: Uses latest Python packaging standards (PEP 621)
  • 🔄 Compatible: Works with existing pip packages and requirements.txt

Quick Start

Installation

# macOS and Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Or using pip
pip install uv

# Or using pipx
pipx install uv

# Windows (PowerShell)
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

Add uv to your PATH (add to ~/.bashrc or ~/.zshrc for persistence):

export PATH="$HOME/.local/bin:$PATH"

Verify installation:

uv --version

First-Time Setup

# Clone the repository
git clone <repository-url>
cd mlops-with-mlflow

# Sync all dependencies (creates .venv and installs everything)
uv sync

# This creates a virtual environment and installs:
# - All workspace dependencies
# - All workspace member packages in development mode
# - Dev dependencies (pytest, ruff, etc.)

That’s it! You’re ready to develop.

Daily Development Workflow

Activating the Environment (Optional)

Most uv commands work without activating the virtual environment, but if you prefer:

# Activate
source .venv/bin/activate  # Linux/macOS
.venv\Scripts\activate     # Windows

# Deactivate
deactivate

Building and Testing

# Build all packages
make build

# Run all tests
make test

# Build and test everything
make all

# Clean build artifacts and .venv
make clean

Running Python Scripts

# Using uv run (no activation needed)
uv run python my_script.py

# Run pytest
uv run pytest

# Run a specific test file
uv run pytest tests/test_feature.py

# Run with coverage
uv run pytest --cov=src --cov-report=html

Running Commands in Specific Packages

# Navigate to package
cd src/doe-library

# Run tests for just this package
uv run pytest tests/

# Run a script using this package
uv run python -c "import doe_library; print(doe_library.__version__)"

Dependency Management

Adding Dependencies

Root Project Dependencies

# Add a runtime dependency
uv add numpy

# Add with version constraint
uv add "pandas>=2.1.0"

# Add multiple packages
uv add matplotlib seaborn plotly

# Add a development dependency
uv add --dev pytest-mock

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

Package-Specific Dependencies

# Navigate to the package
cd src/doe-library

# Add dependency to this package
uv add scipy

# Add dev dependency
uv add --dev pytest-benchmark

This updates the package’s pyproject.toml and regenerates uv.lock.

Removing Dependencies

# Remove a dependency
uv remove numpy

# Remove from specific package
cd src/doe-library
uv remove scipy

Updating Dependencies

# Update all dependencies to latest compatible versions
uv sync --upgrade

# Update specific package
uv add numpy@latest

# Update only lock file (don't install)
uv lock --upgrade

Viewing Dependencies

# List installed packages
uv pip list

# Show details about a package
uv pip show mlflow

# Show dependency tree
uv tree

# Show outdated packages
uv pip list --outdated

Working with Workspaces

This project uses uv’s workspace feature to manage multiple Python packages in a monorepo.

Workspace Structure

mlops-with-mlflow/
├── pyproject.toml          # Root workspace configuration
├── uv.lock                 # Locked dependencies (shared by all packages)
├── .python-version         # Python version (3.13)
├── .venv/                  # Shared virtual environment
└── src/
    ├── doe-library/        # Workspace member
    │   ├── pyproject.toml
    │   └── doe_library/
    ├── io-library/         # Workspace member
    │   ├── pyproject.toml
    │   └── io_library/
    └── ...

Workspace Members

Current workspace members (defined in root pyproject.toml):

  • src/doe-library - Design of Experiments utilities
  • src/io-library - I/O utilities
  • src/metrics-library - ML metrics
  • src/feature-library - Feature engineering
  • src/plot-library - Plotting utilities
  • src/hurricane-landfall - Hurricane prediction pipeline
  • src/mlflow-tf - MLflow + TensorFlow integration

Adding a New Package to Workspace

  1. Create the package structure:
mkdir -p src/my-new-package/my_new_package
cd src/my-new-package
  1. Create pyproject.toml:
[project]
name = "my_new_package"
version = "0.1.0"
description = "My new package"
requires-python = ">=3.13"
dependencies = [
    # Add dependencies here
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
  1. Add to workspace in root pyproject.toml:
[tool.uv.workspace]
members = [
    "src/doe-library",
    # ...existing members...
    "src/my-new-package",  # Add this line
]
  1. Sync the workspace:
uv sync

Benefits of Workspaces

  • Shared Environment: All packages share the same virtual environment
  • Faster Installs: Dependencies are deduplicated across packages
  • Consistent Versions: One lock file ensures all packages use compatible versions
  • Easy Cross-Package Development: Edit multiple packages simultaneously
  • Simplified CI/CD: One command to install everything

Python Version Management

Viewing Available Python Versions

# List all available Python versions
uv python list

# List installed Python versions
uv python list --only-installed

Installing Python Versions

# Install Python 3.13 (if not already installed)
uv python install 3.13

# Install specific version
uv python install 3.13.7

# Install multiple versions
uv python install 3.12 3.13

Pinning Python Version

# Pin to Python 3.13 (creates/updates .python-version)
uv python pin 3.13

# Pin to specific version
uv python pin 3.13.7

The .python-version file is committed to git, ensuring all developers use the same Python version.

Lock File Management

Understanding uv.lock

The uv.lock file:

  • Contains exact versions of all dependencies (including transitive ones)
  • Ensures reproducible builds across all environments
  • Should be committed to version control
  • Is automatically updated when you add/remove dependencies

Regenerating Lock File

# Regenerate lock file without installing
uv lock

# Regenerate and sync environment
uv sync

# Upgrade all dependencies and regenerate lock
uv lock --upgrade

Using Lock File in CI/CD

# Install exactly what's in the lock file (fast, reproducible)
uv sync --frozen

# Fail if lock file is out of date
uv sync --locked

Building and Publishing Packages

Building a Package

# Navigate to package
cd src/doe-library

# Build wheel and source distribution
uv build

# Output in dist/
ls -l dist/
# doe_library-0.1.0-py3-none-any.whl
# doe_library-0.1.0.tar.gz

Publishing to PyPI

# Install twine (if not already installed)
uv pip install twine

# Build the package
uv build

# Upload to PyPI
twine upload dist/*

# Upload to Test PyPI
twine upload --repository testpypi dist/*

Advanced Usage

Custom Scripts and Tools

Create scripts in pyproject.toml:

[project.scripts]
my-tool = "my_package.cli:main"

Run with:

uv run my-tool --help

Environment Variables

# Prefer copying over hardlinking (useful for different filesystems)
export UV_LINK_MODE=copy

# Use specific Python version
export UV_PYTHON=3.13

# Custom cache location
export UV_CACHE_DIR=/path/to/cache

# Offline mode (only use cache)
export UV_OFFLINE=1

Using uv with Docker

FROM python:3.13-slim

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

# Copy project files
WORKDIR /app
COPY pyproject.toml uv.lock ./
COPY src/ ./src/

# Install dependencies
RUN uv sync --frozen --no-dev

# Run application
CMD ["uv", "run", "python", "main.py"]

Integration with IDEs

VS Code

Add to .vscode/settings.json:

{
  "python.defaultInterpreterPath": ".venv/bin/python",
  "python.terminal.activateEnvironment": false,
  "python.testing.pytestEnabled": true,
  "python.testing.pytestPath": ".venv/bin/pytest"
}

PyCharm

  1. Go to Settings → Project → Python Interpreter
  2. Click the gear icon → Add
  3. Select “Existing environment”
  4. Choose .venv/bin/python

Troubleshooting

UV Command Not Found

# Add to PATH
export PATH="$HOME/.local/bin:$PATH"

# Add to shell config for persistence
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Python Version Not Found

# Install the required Python version
uv python install 3.13

# Verify it's available
uv python list --only-installed

Sync Fails

# Clear cache and try again
uv cache clean
uv sync

# Force reinstall
rm -rf .venv
uv sync

Lock File Out of Date

# Regenerate lock file
uv lock

# Then sync
uv sync

Dependency Conflicts

# See conflict details
uv sync --verbose

# Try upgrading conflicting packages
uv sync --upgrade

Package Not Found After Install

# Ensure you're using the uv environment
uv run python -c "import package_name"

# Or activate the environment first
source .venv/bin/activate
python -c "import package_name"
# Use copy mode instead of hardlinks
export UV_LINK_MODE=copy
uv sync

# Or set permanently
echo 'export UV_LINK_MODE=copy' >> ~/.bashrc

Performance Tips

  1. Use uv run instead of activating: Faster and no activation needed
  2. Keep the lock file: Enables very fast installs (10-100x faster than pip)
  3. Use --frozen in CI: Skip dependency resolution for maximum speed
  4. Cache is your friend: uv’s cache makes repeated installs instant
  5. Workspace benefits: Shared dependencies across packages reduce duplication

Comparison with pip/venv

Taskpip/venvuv
Create environmentpython -m venv .venvuv sync
Activatesource .venv/bin/activateNot needed
Install depspip install -r requirements.txtuv sync
Add packagepip install packageuv add package
Remove packagepip uninstall packageuv remove package
Run scriptpython script.pyuv run python script.py
Lock dependenciespip freeze > requirements.txtAutomatic
SpeedBaseline10-100x faster

Best Practices

  1. Always commit uv.lock: Ensures reproducible builds
  2. Use uv sync regularly: Keep environment in sync with lock file
  3. Pin Python version: Use .python-version for consistency
  4. Use uv run in scripts: Ensures correct environment
  5. Clean builds in CI: Use uv sync --frozen for reproducibility
  6. Update dependencies intentionally: Use uv lock --upgrade when ready
  7. Document custom dependencies: Comment in pyproject.toml why added

Resources

Getting Help