# Python Dependency Management

Advanced patterns for managing Python dependencies with uv.

## Project Lockfile (uv.lock) — preferred

For any project with a `pyproject.toml`, uv manages a `uv.lock` automatically.
This is the reproducible, uv-native workflow — prefer it over a hand-managed
`requirements.txt`.

```bash
uv add "flask>=2.0" sqlalchemy     # add runtime deps; uv.lock updated
uv add --dev pytest ruff mypy      # dev-only dependency group
uv remove flask                    # drop a dep
uv sync                            # install exactly from uv.lock
uv sync --frozen                   # CI: fail if uv.lock is stale, don't re-resolve
uv lock --upgrade                  # bump everything within constraints
uv lock --upgrade-package flask    # bump just one
```

## requirements.txt Workflow (uv pip compile)

Use this only when you specifically need a `requirements.txt` — Docker layer
caching, non-uv CI, or legacy tooling. Otherwise prefer `uv.lock` above.

### Basic Lock Pattern

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

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

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

### Package pyproject.toml

```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

```bash
# Install all workspace members into one env (native — no editable pip installs)
uv sync

# Run a command in the workspace env
uv run pytest

# Operate on a specific member
uv run --package my-api pytest
```

## Private Packages

### Configure Index (pyproject.toml)

```toml
[[tool.uv.index]]
name = "private"
url = "https://pypi.private.com/simple/"
```

```bash
uv add my-private-package          # resolves against configured indexes
```

### Authentication via environment

```bash
# Per-index credentials: UV_INDEX_<NAME>_USERNAME / _PASSWORD
export UV_INDEX_PRIVATE_USERNAME=user
export UV_INDEX_PRIVATE_PASSWORD=token
uv add my-private-package
```

### requirements.txt workflow

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

## Git Dependencies

```toml
# 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",
]
```

```bash
# requirements.in
git+https://github.com/user/repo.git@main#egg=my-package
```

## Local Dependencies

```toml
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```yaml
- name: Install uv
  uses: astral-sh/setup-uv@v5

- name: Install dependencies
  run: uv sync --frozen

- name: Run tests
  run: uv run pytest
```

### Cache Dependencies

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

### Lock File in CI

```yaml
- name: Verify lock file is up to date
  run: uv lock --check
```

## 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`
