dependency-management.md 5.7 KB

Python Dependency Management

Advanced patterns for managing Python dependencies with uv.

Lock File Workflow

Basic Lock Pattern

# requirements.in (loose constraints)
flask>=2.0
sqlalchemy>=2.0
pydantic>=2.0

# Generate locked requirements.txt
uv pip compile requirements.in -o requirements.txt

# Install exact versions
uv pip sync requirements.txt

Separate Dev Dependencies

# requirements.in
flask>=2.0
sqlalchemy>=2.0

# requirements-dev.in
-r requirements.in
pytest>=7.0
ruff>=0.1
mypy>=1.0

# Compile both
uv pip compile requirements.in -o requirements.txt
uv pip compile requirements-dev.in -o requirements-dev.txt

# Install for development
uv pip sync requirements-dev.txt

Update Workflow

# Update all packages to latest compatible versions
uv pip compile requirements.in -o requirements.txt --upgrade

# Update specific package
uv pip compile requirements.in -o requirements.txt --upgrade-package flask

# Update with constraints
uv pip compile requirements.in -o requirements.txt --upgrade --constraint constraints.txt

Constraint Files

# constraints.txt
# Pin versions that need to be consistent across projects
numpy==1.26.0
pandas==2.0.0

# Use constraints during compile
uv pip compile requirements.in -o requirements.txt --constraint constraints.txt

Multiple Environments

Python Version Specific

# Python 3.10
uv pip compile requirements.in -o requirements-py310.txt --python-version 3.10

# Python 3.11
uv pip compile requirements.in -o requirements-py311.txt --python-version 3.11

Platform Specific

# Linux
uv pip compile requirements.in -o requirements-linux.txt --platform linux

# macOS
uv pip compile requirements.in -o requirements-macos.txt --platform macos

# Windows
uv pip compile requirements.in -o requirements-windows.txt --platform windows

Workspace/Monorepo

Structure

my-monorepo/
├── pyproject.toml        # Root workspace config
├── packages/
│   ├── core/
│   │   └── pyproject.toml
│   ├── api/
│   │   └── pyproject.toml
│   └── cli/
│       └── pyproject.toml

Root pyproject.toml

[tool.uv.workspace]
members = ["packages/*"]

[tool.uv.sources]
my-core = { workspace = true }
my-api = { workspace = true }

Package pyproject.toml

# packages/core/pyproject.toml
[project]
name = "my-core"
version = "0.1.0"
dependencies = ["pydantic>=2.0"]

# packages/api/pyproject.toml
[project]
name = "my-api"
version = "0.1.0"
dependencies = ["my-core", "fastapi>=0.100"]

Workspace Commands

# Install all workspace packages
uv pip install -e packages/core -e packages/api -e packages/cli

# Sync entire workspace
uv sync

# Run command in workspace context
uv run pytest

Private Packages

Configure Index

# Extra index for private packages
uv pip install my-private-package --extra-index-url https://pypi.private.com/simple/

# With authentication
uv pip install my-private-package \
  --extra-index-url https://user:token@pypi.private.com/simple/

In requirements.in

--extra-index-url https://pypi.private.com/simple/
my-public-package>=1.0
my-private-package>=2.0

Environment Variable

export UV_EXTRA_INDEX_URL=https://user:token@pypi.private.com/simple/
uv pip install my-private-package

Git Dependencies

# In pyproject.toml
[project]
dependencies = [
    "my-package @ git+https://github.com/user/repo.git",
    "my-package @ git+https://github.com/user/repo.git@v1.0.0",
    "my-package @ git+https://github.com/user/repo.git@main",
    "my-package @ git+ssh://git@github.com/user/repo.git",
]
# requirements.in
git+https://github.com/user/repo.git@main#egg=my-package

Local Dependencies

# In pyproject.toml
[project]
dependencies = [
    "my-local @ file:///path/to/package",
]

# Relative path
[tool.uv.sources]
my-local = { path = "../my-local-package" }

Dependency Resolution

Resolver Options

# Use backtracking resolver (more thorough but slower)
uv pip compile requirements.in -o requirements.txt --resolver=backtracking

# Allow prereleases
uv pip compile requirements.in -o requirements.txt --prerelease=allow

# Exclude specific packages from upgrade
uv pip compile requirements.in -o requirements.txt --upgrade --no-upgrade-package numpy

Resolution Troubleshooting

# Show why a version was chosen
uv pip compile requirements.in --verbose

# Generate dependency tree
uv pip tree

# Check for conflicts
uv pip check

Caching

# Clear uv cache
uv cache clean

# Show cache location
uv cache dir

# Disable cache for one command
uv pip install --no-cache package-name

CI/CD Patterns

GitHub Actions

- name: Install uv
  uses: astral-sh/setup-uv@v1
  with:
    version: "latest"

- name: Install dependencies
  run: |
    uv venv
    uv pip sync requirements.txt

- name: Run tests
  run: uv run pytest

Cache Dependencies

- name: Cache uv
  uses: actions/cache@v3
  with:
    path: ~/.cache/uv
    key: uv-${{ hashFiles('requirements.txt') }}
    restore-keys: uv-

Lock File in CI

- name: Verify lock file is up to date
  run: |
    uv pip compile requirements.in -o requirements-check.txt
    diff requirements.txt requirements-check.txt

Best Practices

  1. Always use lock files in production - Reproducible builds
  2. Separate dev dependencies - Smaller production installs
  3. Use constraints for shared deps - Consistent versions across packages
  4. Pin Python version - Avoid compatibility surprises
  5. Run uv pip check - Catch conflicts early
  6. Cache in CI - Faster builds
  7. Review upgrades carefully - Don't blindly --upgrade