pip

pip is Python's standard package installer — the tool that fetches, installs, upgrades, and removes third-party libraries from PyPI or other indexes.

pip is the reference package installer for Python. It downloads distributions (wheels or source distributions) from a package index — by default the Python Package Index, PyPI — resolves their dependencies, and installs them into whichever interpreter's site-packages it is invoked against. pip has shipped with the standard CPython installers since Python 3.4.

What pip Does

pip is invoked as pip <command> ... or, more robustly, as python -m pip <command> ... — the latter form guarantees you're using the pip tied to the specific interpreter you named, which matters when multiple Python installations or virtual environments are present.

python -m pip --version
python -m pip install requests

Package Installation

The basic form installs the latest compatible release of a named package and everything it depends on.

Always quote version specifiers that contain < or > on shells (like bash) where those characters mean redirection.

pip install requests
pip install requests beautifulsoup4       # multiple packages
pip install requests==2.31.0              # exact version
pip install "requests>=2.28,<3"           # constrained range
pip install -e .                          # editable install of the current project
pip install package_name --no-deps        # skip installing dependencies

Requirements Files

A requirements file lists packages (one per line, optionally version-constrained) so an entire set of dependencies can be installed with a single command — the standard way to share and reproduce a project's dependency set.

# requirements.txt
requests==2.31.0
click>=8.0,<9
python-dateutil
pip install -r requirements.txt

Version Constraints

pip understands the version specifiers defined by PEP 440. The common operators:

Pinning too loosely (no upper bound) risks a future release silently breaking your project; pinning too tightly (only ==) makes upgrades and security patches harder to apply. Libraries typically use loose, compatible ranges; applications typically pin exact versions via a lock file (see Dependency Management).

==2.31.0     exactly this version
>=2.28       at least this version
<3.0         strictly below this version
>=2.28,<3.0  a bounded range (combine with a comma)
~=2.28       "compatible release": >=2.28, <2.29 (only the last segment may increase)
!=2.30.0     any version except this one

Upgrading Packages

Upgrading one package can pull in newer versions of its dependencies too, since pip's resolver must keep the whole installed set consistent.

pip install --upgrade requests
pip install -U requests            # short form
python -m pip install --upgrade pip   # upgrading pip itself

Uninstalling Packages

pip uninstall removes only the named package(s), not their now-unused dependencies (there is no automatic 'autoremove').

pip uninstall requests
pip uninstall -r requirements.txt -y   # uninstall everything listed, no prompt

Inspecting What's Installed

pip list                 # everything installed in this environment
pip show requests        # metadata + dependencies for one package
pip freeze                # pip-install-ready "name==version" lines
pip check                 # verify installed packages' dependencies are satisfied

Package Indexes

By default pip fetches from PyPI (pypi.org). Organizations often run a private or mirrored index (for internal packages, or to control what versions are approved) and point pip at it instead of, or in addition to, PyPI.

--index-url replaces the default index entirely; --extra-index-url adds another index pip may also search — the latter has a well-known security footgun covered next.

pip install mypkg --index-url https://pypi.company.internal/simple/
pip install mypkg --extra-index-url https://pypi.company.internal/simple/

Security Considerations

Installing a package runs arbitrary code (build scripts, and the package's own code once imported), so supply-chain integrity matters.

Dependency confusion: using --extra-index-url alongside PyPI can let an attacker publish a same-named malicious package to public PyPI with a higher version number, which pip may then prefer over your intended private package. Prefer --index-url pointed at a proxying private index that also mirrors PyPI, or pin exact versions/hashes, rather than mixing indexes casually.

Hash checking: pip install --require-hashes -r requirements.txt (with hashes recorded in the file) makes pip refuse to install anything whose downloaded file doesn't match the recorded cryptographic hash, defending against a compromised or substituted package file.

Other habits: install from trusted indexes only, review a package's popularity/maintenance before adopting it, avoid running pip install as an administrator/root user when a virtual environment would do, and keep pip itself updated to receive security fixes.

# requirements.txt with hashes (typically generated by a tool, not by hand)
requests==2.31.0 \
    --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988bce1adf30c9d3d

Practical Usage

A typical day-to-day sequence inside an activated virtual environment:

python -m pip install --upgrade pip     # keep pip itself current
pip install -r requirements.txt          # restore a project's dependencies
pip install black --upgrade              # add or update a dev tool
pip freeze > requirements.txt            # capture the current state
pip check                                # sanity-check the environment

Edge Cases

Installing without an active virtual environment installs into whichever interpreter pip/python -m pip resolves to — potentially the system Python, which some OS package managers actively protect against (an 'externally managed environment' error on modern Debian/Ubuntu, for instance).

Two packages that require incompatible versions of a shared dependency produce a resolution failure (or, with an older resolver, a silent broken install) — see Dependency Management for how lock files and conflict resolution address this.

pip install from a local path or VCS URL (pip install git+https://...) is possible and common for installing unreleased or private code, but bypasses the index entirely.

Best Practices

Always work inside a virtual environment. Prefer python -m pip over a bare pip to be certain which interpreter is targeted. Pin dependencies for applications; use loose, tested ranges for libraries. Run pip check after manual installs. Use hash-checking or a lock file for anything security-sensitive or production-bound. Keep pip itself upgraded.

Complete Example

python -m venv .venv
source .venv/bin/activate

python -m pip install --upgrade pip
pip install "requests>=2.28,<3" click

pip list
pip show requests
pip freeze > requirements.txt

pip uninstall click -y
pip check

Quick Reference

install NAME               install latest matching version
install NAME==X.Y.Z        install exact version
install -r FILE            install from a requirements file
install -U NAME            upgrade
install -e .               editable install of the local project
uninstall NAME             remove a package
list                       show installed packages
show NAME                  show one package's metadata/dependencies
freeze                     print "name==version" for all installed packages
check                      verify installed dependency graph is consistent

Key Takeaways

pip installs from a package index (PyPI by default), resolving and installing dependencies along with the requested package.

python -m pip is safer than a bare pip because it ties the command to a specific interpreter unambiguously.

Version specifiers (PEP 440) express exact pins, ranges, and compatible-release constraints.

requirements.txt plus pip freeze/pip install -r is the baseline way to share and reproduce a dependency set.

Installing runs code, so index trust, hash verification, and avoiding mixed public/private indexes are real security concerns, not formalities.

pip install pandas # unclear which interpreter's pip this is python -m pip install pandas # unambiguously targets THIS python

A bare pip on PATH may resolve to a different interpreter than the one you intend (e.g. system Python instead of an active venv) — python -m pip removes that ambiguity.

pip install "requests" # no version constraint at all, in an application

An unpinned dependency in an application can silently change behavior on the next install as PyPI releases new versions; applications should pin, while only libraries typically use open-ended ranges.

pip install mypkg --extra-index-url https://internal.example/simple/ # and mypkg also exists publicly on PyPI under the same name pip install mypkg --index-url https://internal.example/simple/ # a private index that itself mirrors/proxies PyPI

Combining --extra-index-url with a public index invites dependency confusion: an attacker's higher-versioned public package with the same name can be installed instead of your private one.

Why is python -m pip install X generally preferred over pip install X?

It guarantees the install targets the specific interpreter you named, avoiding ambiguity when multiple Python installations or a virtual environment are on PATH.

What is the difference between >=2.28,<3 and ~=2.28?

>=2.28,<3 allows any version from 2.28 up to (but not including) 3.0; ~=2.28 means 'compatible release' and only allows the version to move forward within the same 2.28.x-style final segment, roughly equivalent to >=2.28,<2.29 for a two-part version.

What does pip freeze produce, and what is it typically used for?

It prints every installed package and its exact version as name==version lines, typically redirected into a requirements.txt so the environment can be recreated elsewhere.

Why can --extra-index-url be a security risk?

If a package name exists on both a private index and public PyPI, pip may select the higher-versioned release, letting an attacker publish a malicious same-named package publicly and have it chosen over the intended private one — dependency confusion.

What does pip check do, and when would you run it?

It verifies that every installed package's declared dependencies are actually satisfied by what's installed, without installing anything — useful after manual or unusual installs to catch a broken dependency graph.

Does pip uninstall remove a package's now-unused dependencies too?

No — it removes only the named package(s); pip has no automatic 'autoremove' of orphaned dependencies.

How would you make pip refuse to install a package whose downloaded file doesn't match an expected checksum?

Use pip install --require-hashes -r requirements.txt with a requirements file that records --hash entries for each package.

Write the pip commands to: install exactly requests version 2.31.0, upgrade click to its latest version, and then list everything currently installed.

pip install requests==2.31.0 pip install --upgrade click pip list install requests==2.31.0 pins an exact version; --upgrade click fetches click's newest compatible release; pip list enumerates every package installed in the current environment.

Given this requirements.txt, what single pip command installs everything it lists?

pip install -r requirements.txt — it reads the file and installs each listed package under its given constraint.

Write a short requirements.txt entry (as a comment) explaining why numpy with no version constraint is risky for an application but might be acceptable for a library, and show a safer application-style pin.

Applications want reproducible, exact installs (pin with ==); libraries generally want compatibility across a range of versions so they don't over-constrain whoever installs them.

# requirements.txt
flask>=2.0,<3
requests==2.31.0