PyPI and the registry
How pip finds, downloads, and installs your package — and what it means to claim a name on PyPI.
- Explain how pip install finds and downloads a package via the simple API
- Distinguish between a wheel and a source distribution
- Understand the difference between PyPI and TestPyPI
- Describe what claiming a package name means and why typosquatting is a real risk
When you run pip install requests, a precise chain of events unfolds. Understanding
that chain is the foundation of publishing your own tool reliably and safely.
The simple API
PyPI exposes the simple repository API: a
hierarchy of plain HTML pages at https://pypi.org/simple/. Each package has
an index page listing every available file with its hash. pip fetches that
page, filters for files compatible with your Python version and platform, and
downloads the best match.
You can inspect this yourself:
curl -s https://pypi.org/simple/requests/ | head -20The response is a list of links like
requests-2.32.0-py3-none-any.whl#sha256=.... The hash in the URL fragment is
pip's integrity check — if the downloaded bytes do not match, pip aborts.
Wheels vs source distributions
Two artifact types appear on the index page:
- Wheel (
.whl): a ZIP archive of the package already assembled for installation. File name encodes the Python version, ABI, and platform tags (e.g.py3-none-anymeans pure Python, runs anywhere).pipprefers wheels because they require no build step. - Source distribution (
.tar.gz, also called sdist): the raw source tree.pipdownloads it and runspython -m buildlocally. Slower, and requires the project's build backend to be available.
For a pure-Python CLI tool, your wheel will have the py3-none-any tag, meaning
one artifact runs on every platform — a significant advantage over compiled
extensions.
TestPyPI: the sandbox
TestPyPI is a parallel registry, operated by the Python Packaging Authority, that is wiped periodically. It exists for exactly one purpose: practising the upload workflow without polluting the real registry.
Install from TestPyPI by overriding the index URL:
pip install -i https://test.pypi.org/simple/ my-toolIf a dependency is not on TestPyPI (most are not), pip will fail. Use
--extra-index-url https://pypi.org/simple/ to fall back:
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ my-toolName ownership and typosquatting
PyPI names are first-come, first-served. Once you claim my-tool, no one
else can publish under that name. The downside is that malicious actors exploit
this: they register names that look like popular packages (reqeusts, numpyy)
hoping that a typo in a pip install command will pull down their code instead.
This matters when you publish, because your package name is now one character away from every close variation. Use a distinctive, project-specific name, not a near-miss of something well-known. The PyPI name advisory (PEP 541) covers how to report and reclaim names in clear cases.
PyPI uses API tokens for authentication rather than usernames and passwords.
Create a project-scoped token in your PyPI account settings and store it in
~/.pypirc or as a CI secret. Never commit it to source control.
Where to go next
Next: build backends — writing a complete pyproject.toml with hatchling
and producing your first wheel with python -m build.