Skip to main content

Dependencies, Locking, and Updates

What You'll Learn​

How to lock dependency versions so builds are reproducible, how to safely update packages, and how to use modern tools like pip-compile and uv.

Direct vs Transitive Dependencies​

When you run pip install requests:

  • Direct dependency: requests (you chose this)
  • Transitive dependencies: urllib3, certifi, charset-normalizer, idna (requests needs these)

pip freeze captures all of them — direct and transitive — with pinned versions:

certifi==2024.2.2
charset-normalizer==3.3.2
idna==3.6
requests==2.31.0
urllib3==2.2.1

This is good for reproducibility (exact same install on every machine) but bad for maintainability (you can't easily see which are yours vs. auto-pulled).

The Two-File Pattern​

Separate what you want from what actually gets installed:

requirements.in ← YOUR choices (short, human-maintained)
requirements.txt ← LOCKED full list (generated, not edited by hand)

requirements.in:

requests>=2.28
flask>=3.0
python-dotenv

requirements-dev.in:

-r requirements.in
pytest>=8.0
ruff
mypy
pytest-cov

pip-compile — Lock Dependencies​

pip install pip-tools

# Compile requirements.in → requirements.txt
pip-compile requirements.in

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

# Upgrade all packages to latest compatible versions
pip-compile --upgrade requirements.in

# Upgrade a single package
pip-compile --upgrade-package requests requirements.in

Generated requirements.txt (managed by pip-compile, don't edit):

# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
# pip-compile requirements.in
#
certifi==2024.2.2
# via requests
charset-normalizer==3.3.2
# via requests
idna==3.6
# via requests
requests==2.31.0
# via -r requirements.in
urllib3==2.2.1
# via requests

Install from the locked file:

pip-sync requirements.txt # installs exactly these, removes others
pip-sync requirements-dev.txt # for development

uv — Ultra-Fast Modern Package Manager​

uv is the new generation tool — 10–100x faster than pip, with built-in locking:

# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# Create venv and install
uv venv
source .venv/bin/activate
uv pip install requests flask

# Lock dependencies
uv pip freeze > requirements.txt

# Install from lockfile
uv pip install -r requirements.txt

# Sync environment to exact lockfile state
uv pip sync requirements.txt

Checking for Security Vulnerabilities​

# Check for known vulnerabilities
pip install pip-audit
pip-audit

# With uv
uv pip audit

Example output:

Found 1 known vulnerability in 1 package:
Name Version ID Fix Versions
------ ------- ------------------- ------------
pillow 9.0.0 GHSA-4fx9-vc88-q2xc 9.0.1

Updating Packages Safely​

Never just run pip install --upgrade-all blindly — it can break things. Instead:

# Check what's outdated
pip list --outdated

# Update one package at a time and test
pip install --upgrade requests
pytest tests/ # verify nothing broke

# With pip-compile: controlled upgrade
pip-compile --upgrade-package requests requirements.in
pip-sync requirements.txt
pytest tests/
git add requirements.txt requirements.in
git commit -m "chore: upgrade requests to 2.32.0"

Checking Dependency Conflicts​

pip install pipdeptree
pipdeptree --warn all # show dependency tree with conflicts
pip check # check for broken requirements

Separating Environments by Purpose​

FilePurpose
requirements.inYour direct production deps
requirements.txtLocked production deps (generated)
requirements-dev.inYour direct dev deps
requirements-dev.txtLocked dev deps (generated)
requirements-test.txtCI/test-only deps

In CI/CD, install only what's needed:

# Production deploy
pip-sync requirements.txt

# CI testing
pip-sync requirements-dev.txt

Workflow Summary​

# Starting a new project
python3 -m venv venv
source venv/bin/activate
pip install pip-tools

# Add a dependency
echo "requests>=2.28" >> requirements.in
pip-compile requirements.in
pip-sync requirements.txt
git add requirements.in requirements.txt

# Weekly: check and upgrade
pip list --outdated
pip-compile --upgrade requirements.in
pip-sync requirements.txt
pytest tests/ # verify
git commit -m "chore: weekly dependency update"

# Security audit
pip-audit

Common Mistakes​

MistakeFix
Editing requirements.txt by handEdit requirements.in, run pip-compile
No locked file, just loose version specsAlways lock for production
Upgrading all packages at onceUpgrade one at a time and test
No security auditsRun pip-audit regularly
Different package versions per developerCommit the lockfile

Quick Reference​

# pip-tools
pip install pip-tools
pip-compile requirements.in # generate lockfile
pip-compile --upgrade requirements.in # upgrade all
pip-sync requirements.txt # install exact versions

# uv (faster alternative)
uv venv && source .venv/bin/activate
uv pip install requests
uv pip sync requirements.txt

# Security
pip install pip-audit
pip-audit

# Check state
pip list --outdated
pip check
pipdeptree

What's Next​

→ Lesson 3: Packaging Internal Tools