An sdist is a .tar.gz holding your source, a pyproject.toml and a PKG-INFO metadata file. A wheel is a .whl zip that holds the files exactly as they will be installed, plus a dist-info folder of metadata. The sdist is rebuilt on install, while the wheel is just unpacked.
Definitions
A source distribution, or sdist, is the archive an installer builds from. A wheel is a built distribution that needs no build step on install. The Packaging User Guide says to build and upload both, and python -m build produces both by default.
Inside an sdist
The sdist specification says the archive has a single top level directory named after the project and version, such as foo-1.0. Inside it, two files are required: a pyproject.toml and a PKG-INFO file with core metadata.
Beyond that, the spec is blunt: no other content is required or defined, and build systems can store whatever they need. That means the rest of the contents come from your backend's include rules. Typically that is your package source, and often tests, docs and license files.
| Item | Required in an sdist |
|---|---|
| Top level folder named name and version | Yes |
| pyproject.toml | Yes |
| PKG-INFO | Yes |
| License files named in metadata | Yes, for newer metadata versions |
| Tests and docs | No, backend decides |
The filename is normalized too: the name, a hyphen, the version, then .tar.gz.
Inside a wheel
A wheel is a zip file with a specially formatted name. Its root holds the files that will go into site-packages. Next to them sits a dist-info folder named after the distribution and version, containing metadata such as WHEEL and RECORD files. If the package ships extra data, a data folder may also appear.
You can list both without installing anything.
tar tzf dist/example_library-1.0.0.tar.gz
unzip -l dist/example_library-1.0.0-py3-none-any.whl
How the install differs
When pip installs a wheel, it unpacks it and updates RECORD. No build code runs. The packaging guide describes this as faster and more convenient for end users.
When pip installs an sdist, it must build first, running your backend and everything listed in build requirements, then install the result. For packages with compiled extensions, it also needs a compiler.
If no suitable wheel exists for a user's platform, pip falls back to the sdist. That fallback is why publishing an sdist still matters.
What this means for your release
- A file can exist in your sdist and be missing from your wheel, or the reverse. Check both.
- The sdist and wheel carry the same name and version, and PyPI will not let you reuse a filename.
- Anything in either archive is public the moment you upload. Treat both as published artifacts, not build scratch.
We cover a pre upload checklist in how to inspect your sdist before uploading.
Safeguard's SBOM generation records what is in a project. A free scan can show what a build declares.
Quick check
Do I need to upload both an sdist and a wheel?
The packaging guide recommends uploading an sdist and wheels for the platforms you support. A pure Python package usually needs one generic wheel.
Why does my wheel name end in py3-none-any?
Those are compatibility tags. They say the wheel works on any Python 3, needs no particular ABI, and runs on any platform.
Can an sdist run code on install?
Installing from an sdist runs your build backend and its build requirements. A wheel does not run a build at all.
Sources
- Source distribution format, Python Packaging User Guide
- Binary distribution format, Python Packaging User Guide
- The Packaging Flow, Python Packaging User Guide
- Packaging Python Projects, Python Packaging User Guide
- PyPI help, FAQ
Last reviewed: 19 October 2026.