Quick Reference · the package installer for Python

pip cheat sheet

pip does one thing: it takes a specification, resolves it against an index, pulls wheels through a cache, and unpacks them into one environment. pip freeze closes the loop back to the spec. Everything else is a flag on that line.

spec / requirements index / source cache & build environment (site-packages) destructive most common

Distilled & cross-checked against: pip.pypa.io (CLI reference, topic guides, requirement specifiers) · packaging.python.org · PEP 440 versions · PEP 508 requirements · PEP 668 externally-managed · PEP 735 dependency groups · PEP 751 pylock.toml

The four places & the commands that move packages between them
Spec what you ask for requirements.txt pyproject.toml "requests>=2.8" Index pypi.org/simple …or VCS, path, .whl pip index versions pip download Cache ~/.cache/pip HTTP + built wheels pip cache info pip wheel Environment .venv/…/site-packages exactly one per project pip list · pip show pip check resolve fetch / build unpack pip install pip freeze · pip lock pip cache purge ⟵ pip uninstall ⟵ YOUR INTENT THE INTERNET YOUR MACHINE
01Invoke pip Correctlybefore anything else
02Virtual Environmentspip has no isolation of its own
03Installthe daily loop
04Where It Comes Fromindex, VCS, disk
05Version OperatorsPEP 440
06Extras & MarkersPEP 508
07Inspect the Environmentwhat's actually in there
08Requirements Filesthe -r format
09Lock & Reproducepylock.toml · PEP 751
10Uninstall & Cachereclaiming space
11Wheels vs Sourcewhen a build goes wrong
12When the Resolver Fightsbacktracking & conflicts
13Config & Env Varsset it once
Choosing the Targetwhere does it land?

The things that actually confuse people

Not more flags — the models underneath them.

What pip install really does

pip doesn't have a dependency graph handed to it. It has to discover one — by downloading candidates and reading their metadata, one guess at a time.

1 · READ the spec you gave (CLI, -r, -c, .toml) 2 · QUERY index: which versions and files exist? 3 · RESOLVE guess a version, read its deps, repeat 4 · FETCH wheel from cache, or build the sdist 5 · UNPACK into site-packages + write metadata wrong guess ⟶ backtrack ⟶ download another candidate. This loop is why pip "hangs". SPEC RESOLVER ENVIRONMENT

Backtracking, illustrated

From pip's own docs. You ask for tea; it picks the newest spoon, then discovers the newest cup is incompatible with it — so it walks cup backwards, downloading each candidate just to read its metadata.

tea 1.9.8 requested spoon 2.27.0 newest — kept now pick cup… CANDIDATES TRIED, NEWEST FIRST → cup 3.22 ✗ cup 3.21 ✗ cup 3.20 ✗ cup 3.19 ✗ cup 3.18 ✗ cup 3.17 ✗ cup 3.16 ✗ cup 3.15 ✗ 3.14 ✓ Each ✗ is a real network download. pip has no way to read a package's dependencies without fetching it. Fix: pin a floor on the culprit — pip install tea "cup>=3.13" — and the search space collapses.

Anatomy of a requirement

Four parts hang off the name. Only the name is mandatory — every other field narrows what pip is allowed to pick.

requests [security] >=2.8.1,<3 ; python_version < "3.12" NAME · required the only mandatory part EXTRAS · optional opt-in dependency bundles the package itself declares VERSION SPECIFIER · optional comma = AND. omit it and you get "newest" ENVIRONMENT MARKER · optional a condition. false ⟹ skipped entirely. sys_platform, platform_machine, os_name… URL form: proj @ git+https://github.com/org/repo@v1.2 — pins a tag/branch/commit instead of a version quote in the shell: < > ;

What ~= actually means

The "compatible release" operator. It pins all but the last segment — so where you put the last dot changes everything.

~=3.1.2 ≡ >=3.1.2, ==3.1.* 3.1.0 3.1.2 3.1.9 3.2.0 4.0 ~=3.1 ≡ >=3.1, ==3.* (much looser!) 3.1 3.9 4.0 One extra dot = a whole minor series more freedom.

externally-managed-environment

PEP 668. Your OS marked its Python as off-limits so a stray pip install can't break apt or Homebrew. It is not a bug.

error: externally-managed-environment building a project? installing a CLI tool? throwaway container? venv python -m venv .venv the right answer pipx pipx install ruff isolated per tool --break-… -system-packages can break your OS Note that --user is blocked too — it is not an escape hatch. Affects Debian/Ubuntu, Fedora, Homebrew Python 3.11+.

Four files, four jobs

The single most common pip confusion. They are not interchangeable, and only one of them is a real lockfile.

pyproject.toml what your project NEEDS abstract ranges, e.g. >=2.8 published with your package read by: pip install . Source of truth for a library. Commit it. pip never auto-reads it for deps requirements.txt what to PUT IN an env concrete pins, usually == generated by: pip freeze read by: pip install -r Deployment recipe for an application. Commit it. not a lockfile: no hashes, no markers constraints.txt what NOT to exceed version caps only read by: pip install -c or env: PIP_CONSTRAINT NEVER causes an install. Only limits one that was happening anyway. pylock.toml the REAL lockfile · PEP 751 exact versions + hashes generated by: pip lock read by: pip install -r Reproducible. Verified. Still experimental. valid for one Python + platform

Three rungs of "reproducible"

Each rung defends against something the one below it cannot. Straight from pip's Repeatable Installs guide — pick the height your risk actually needs.

1 · PIN requests==2.32.3 Stops a surprise new release. Trusts PyPI. 2 · HASH-CHECK requests==2.32.3 \ --hash=sha256:9b1a… Stops a compromised index or CDN. One hash ⟹ the whole file must be hashed. Still needs the network. 3 · WHEELHOUSE pip wheel -r req.txt -w wheels/ pip install --no-index \ -f wheels/ -r req.txt Stops the index being down, and skips every recompile. Works fully offline. Wheels are OS + arch specific — the bundle is not portable. STRONGER GUARANTEE, MORE WORK →

Worth memorizing

python -m pipnever bare pip — it may target a different interpreter
~=3.1.2 ≠ ~=3.1the first allows 3.1.x; the second allows all of 3.x
-r vs -c-r installs things; -c only caps them
freeze ≠ lockno hashes, no markers, no platform info. use pip lock
-U doesn't cascadedependencies stay put unless they're too old to satisfy
uninstall ≠ cleandependencies are left behind as orphans, forever
externally-managedmake a venv. --break-system-packages is not the fix
pip search is deadPyPI removed the API — use pip index versions
quote your specsthe shell eats < > and ; before pip sees them
many downloadsthat's backtracking, not a bug — add a lower bound
pip checkthe only command that tells you the env is self-consistent
not a workflow toolpip installs; it doesn't manage your project. that's uv/poetry/pdm