---
title: "Python: Dependency Management"
slug: "python-dependency-management"
category: "coding"
tags: ["coding", "python", "dependencies", "uv", "packaging", "security"]
status: "stable"
last_updated: 2026-05-29
summary: "uv over pip+venv for installs and lockfiles, pyproject.toml extras for dev and test dependencies, pipx for global tools, and pip-audit for security."
related: ["[[coding/python]]", "[[coding/python-packaging]]", "[[coding/python-security]]", "[[file-organization/config-files]]", "[[coding/general-principles]]"]
---

## Overview

Use `uv` for all Python dependency and environment management. It replaces `pip`, `pip-tools`, `virtualenv`, and `venv` with a single fast tool that understands `pyproject.toml` natively. This page is about the day-to-day workflow; for package structure and wheel building see [[coding/python-packaging]].

## Use `uv` instead of pip and venv

`uv` installs packages, creates virtualenvs, and maintains lockfiles. It is 10-100x faster than pip for resolution, uses the system Rust binary with no Python dependency at install time, and produces reproducible installs via `uv.lock`.

```bash
# Create a venv and install all project deps (including dev)
uv sync --all-extras

# Add a new runtime dependency
uv add httpx

# Add a dev-only dependency
uv add --dev ruff

# Run a command inside the project venv without activating
uv run pytest
```

Never run `pip install` inside a project that uses `uv`. Mix-and-match installs bypass the lockfile and break reproducibility.

## Declare extras for different install profiles

`[project.optional-dependencies]` in `pyproject.toml` defines named groups. Use at least `dev` (tools) and `test` (test runners and libraries).

```toml
[project.optional-dependencies]
dev = [
  "ruff>=0.5",
  "pyright>=1.1.370",
  "pre-commit>=3",
]
test = [
  "pytest>=8",
  "pytest-asyncio>=0.23",
  "pytest-cov>=5",
  "hypothesis>=6",
]
```

CI for linting installs `.[dev]`. CI for tests installs `.[test]`. Production Docker images install bare `.` with no extras. This reduces the attack surface in production and shrinks image sizes.

## Commit `uv.lock` to version control

`uv.lock` pins every transitive dependency to an exact version and hash. Commit it for applications; omit it for libraries (libraries publish a range in `pyproject.toml` and let the application resolve).

```bash
# Regenerate the lockfile after changing dependencies
uv lock

# Install exactly what is in the lockfile (CI, production)
uv sync --frozen
```

`uv sync --frozen` fails if `uv.lock` is out of date, catching cases where a developer added a dependency but forgot to commit the updated lockfile.

## Use `pipx` for global CLI tools

`pipx` installs Python CLI tools into isolated virtualenvs, one per tool, and adds the executables to `PATH`. It prevents tool dependencies from leaking into project envs.

```bash
# Install tools globally
pipx install ruff
pipx install pyright
pipx install pre-commit

# Upgrade all tools at once
pipx upgrade-all
```

Do not install tools like `ruff` or `pytest` into the system Python with `pip`. Use `pipx` for tools you run from any directory and `uv add --dev` for tools that are project-specific.

## Audit dependencies for known vulnerabilities

`pip-audit` scans the installed packages against the Python Packaging Advisory Database. Run it in CI on every build.

```bash
uv run pip-audit
```

Or add it as a CI step that reads the lockfile:

```bash
pip-audit -r <(uv export --format requirements-txt)
```

Fix or pin around vulnerable versions promptly. A patched version that introduces a breaking API change is less risky than shipping a known CVE. See [[coding/python-security]] for the full security checklist.

## Pin the Python version itself

Set `requires-python` in `pyproject.toml` to the minimum version the code actually tests against. Specify the interpreter version in CI explicitly.

```toml
[project]
requires-python = ">=3.13"
```

```yaml
# .github/workflows/ci.yml
- uses: actions/setup-python@v5
  with:
    python-version: "3.13"
```

Avoid `requires-python = ">=3.8"` as a default; each older version you support adds testing cost and blocks newer language features.

## Related

- [[coding/python]]
- [[coding/python-packaging]]
- [[coding/python-security]]
- [[file-organization/config-files]]
- [[coding/general-principles]]
