Building sdists & Wheels

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the Python Reference bibliography.

Two files are uploaded for a pure-Python release: the sdist (.tar.gz, the source plus PKG-INFO) and the wheel (.whl, ready to unpack into site-packages); what each contains is explained on Packaging Concepts & Standards. This page builds them for birdtally with build 1.6.1 (pip flavour, hatchling 1.32.4) and Poetry 2.5.1 (poetry-core 2.5.0), on CPython 3.14.6 on Linux on 2026-10-22. Scratch paths are shortened to ….

The parts of a wheel filename: distribution, version, optional build tag, python tag, ABI tag and platform tag, for birdtally-1.2.0-py3-none-any.whl and a manylinux platform wheel

python -m build

python -m pip install build==1.6.1
python -m build --report build-report.json
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
  - hatchling>=1.27
* Getting build dependencies for sdist...
* Installed build dependency versions:
  - hatchling==1.32.4
* Building sdist...
* Building wheel from sdist
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
  - hatchling>=1.27
* Getting build dependencies for wheel...
* Installed build dependency versions:
  - hatchling==1.32.4
* Building wheel...
Successfully built birdtally-0.1.0.tar.gz and birdtally-0.1.0-py3-none-any.whl

By default build makes the sdist from the project, then the wheel from that sdist, each in a fresh isolated environment with the [build-system] requires. A file missing from the sdist therefore also breaks the wheel, which is the point: what you upload is consistent. --report writes the artefacts' names, kinds, sizes and SHA-256 hashes as JSON (outside dist/, so it is not uploaded):

{
    "version": "1.0",
    "artifacts": [
        {
            "name": "birdtally-0.1.0.tar.gz",
            "path": "…/dist/birdtally-0.1.0.tar.gz",
            "kind": "sdist",
            "size": 2649,
            "hashes": {
                "sha256": "636bbe86…"
            }
        },
        …
    ]
}
Option Meaning

-s, --sdist / -w, --wheel

Build only that (both given: each from the project, not the wheel from the sdist).

-o, --outdir PATH

Output directory (default dist/). Clean it before a release build: twine upload dist/* uploads whatever is there.

-C, --config-setting KEY=VALUE

Backend settings (repeatable). A value starting with a hyphen needs the = form: -C--build-option=--build-number. Write KEY= rather than a bare KEY, which pip rejects.

--config-json JSON

The same as one JSON object, for nested settings; not combinable with -C.

--installer pip|uv

Which installer fills the isolated environment (uv must be installed).

-n, --no-isolation

Build in the current environment; the build requirements must already be installed.

-x, --skip-dependency-check

With -n, do not check that they are.

--env-dir PATH

Create the isolated environment there; kept on failure for inspection.

--sdist-extract-dir PATH

Where the sdist is unpacked for the wheel build (stable paths for compiler caches).

--metadata

Print the wheel’s metadata as JSON (building it first unless given a .whl).

--report PATH

The JSON report above.

--dependency-constraints-txt PATH

Constrain the build dependencies, for reproducible build environments.

-q, -v

Less or more output.

python -m build --metadata
{
  "summary": "Tally bird-survey sightings from CSV files.",
  "license_files": [
    "LICENSE"
  ],
  "metadata_version": "2.5",
  "name": "birdtally",
  "provides_extra": [
    "pretty"
  ],
  "version": "0.1.0",
  "license_expression": "MIT",
  …
}

The pyproject-build command is the same program. The isolated environment downloads the backend from the configured index: behind a corporate index set PIP_INDEX_URL (and keyring for credentials), which build’s pip honours.

poetry build

poetry build --clean
tar tzf dist/birdtally-0.1.0.tar.gz
Building birdtally (0.1.0)
Building sdist
  - Building sdist
  - Built birdtally-0.1.0.tar.gz
Building wheel
  - Building wheel
  - Built birdtally-0.1.0-py3-none-any.whl
birdtally-0.1.0/CHANGELOG.md
birdtally-0.1.0/LICENSE
birdtally-0.1.0/README.md
birdtally-0.1.0/pyproject.toml
birdtally-0.1.0/src/birdtally/__init__.py
birdtally-0.1.0/src/birdtally/cli.py
birdtally-0.1.0/src/birdtally/py.typed
birdtally-0.1.0/src/birdtally/tally.py
birdtally-0.1.0/PKG-INFO

Options: -f/--format sdist|wheel, --clean (empty the output directory first), -o/--output DIR (1.8), -c/--config-settings KEY=VALUE (2.1), and the deprecated -l/--local-version. Unlike build, poetry build builds the wheel from the project directory, not from the sdist. Since 2.1 it uses the backend in [build-system], in an isolated environment when that is not the poetry-core Poetry bundles. Only CHANGELOG.md is extra here because of include = [{ path = "CHANGELOG.md", format = "sdist" }].

pip wheel

pip wheel --no-deps -w wheelhouse . also builds a wheel through the backend, but no sdist, and it is meant for wheelhouses of dependencies, not for releases:

  Created wheel for birdtally: filename=birdtally-0.1.0-py3-none-any.whl size=3736 sha256=35a50693…
  Stored in directory: /tmp/pip-ephem-wheel-cache-67guagi_/wheels/91/5a/05/c979e85eddb41fc2817c3411fb67082e6776f1ff5db2d88c98
Successfully built birdtally

Inspecting what was built

Look inside before every first release and whenever the build configuration changes:

unzip -l dist/birdtally-0.1.0-py3-none-any.whl
unzip -p dist/birdtally-0.1.0-py3-none-any.whl birdtally-0.1.0.dist-info/RECORD
tar tzf dist/birdtally-0.1.0.tar.gz
birdtally/__init__.py,sha256=…,223
birdtally/cli.py,sha256=…,1089
birdtally/py.typed,sha256=…,0
birdtally/tally.py,sha256=…,979
birdtally-0.1.0.dist-info/METADATA,sha256=…,671
birdtally-0.1.0.dist-info/WHEEL,sha256=…,88
birdtally-0.1.0.dist-info/entry_points.txt,sha256=…,48
birdtally-0.1.0.dist-info/licenses/LICENSE,sha256=…,819
birdtally-0.1.0.dist-info/RECORD,,

(the RECORD of the poetry-core wheel, hashes shortened). Check that every module and data file is present, that tests and local files are not, that licenses/ holds the license, and that METADATA (wheel) and PKG-INFO (sdist) carry the right version, Requires-Python and dependencies. RECORD lists each file’s hash; installers verify it.

Filenames: the distribution name is normalised with - replaced by _, so a project named Bird-Tally builds bird_tally-0.1.0.tar.gz and bird_tally-0.1.0-py3-none-any.whl. The rest of a wheel name is the version, an optional build tag and the compatibility tags (figure above; platform tags are on Binary Wheels & Extension Modules).

Reproducible builds

A reproducible build gives byte-identical files from the same source, so anyone can check that a published file matches its source. The standard knob is SOURCE_DATE_EPOCH, a fixed timestamp for the archive entries:

Backend Two builds of birdtally

hatchling 1.32.4

Identical by default: entries are dated 2020-02-02, and the wheel built here had the same SHA-256 (35a50693…) as the one built two hours earlier for the local index.

poetry-core 2.5.0

Identical by default (cbc8b0e1… twice).

setuptools 84.2.1

Different on each run (2f6bea13…, f3c29c9e…); identical (9d2b6c2d… twice) with SOURCE_DATE_EPOCH=1767225600.

Pin the build dependencies too (--dependency-constraints-txt), or a new backend release changes the output.

When the build fails

Message Cause and fix

ModuleNotFoundError: No module named 'hatchling.biuld' then ERROR Backend 'hatchling.biuld' is not available.

A typo in build-backend, or (with -n) the backend is not installed: ERROR Backend 'flit_core.buildapi' is not available. Fix the name or install the requirements.

No [build-system] table

Not an error for build: it falls back to setuptools' legacy backend and silently builds a setuptools wheel (here with a top_level.txt and today’s timestamps). Always declare [build-system].

Successfully built …, but files are missing

The backend’s file selection: check unzip -l; see Build Backends (sdist contents differ by backend).

ERROR Unmet dependencies (checked against …/bin/python): followed by hatch-vcs>=0.4, wanted: >=0.4, found: not installed

With -n, a build requirement is missing from the current environment; install it or drop -n.

build 1.6.0 (2026-08-27) added --report, --env-dir, --sdist-extract-dir, sdists as srcdir, .whl input for --metadata, the dependency summary printed in isolated builds and the more detailed unmet-dependency message. Every failure ends with build’s tip: pass --env-dir and --sdist-extract-dir to keep the build environment and sources, which leaves both behind for inspection.