Library / SDK
pypa/build avatar
pypa/build

pypa build: a frontend that makes you look at the sdist before the wheel

A simple, correct Python build frontend

863 stars189 forksPythonMIT

At a glance

What is it?
The build frontend from the Python Packaging Authority is small on purpose: it installs a PEP 517 backend in an isolated environment and turns a source tree into an sdist and a wheel, defaulting to building the wheel from the sdist. It suits maintainers who want one command that matches what an installer will actually do.
Who is it for?
Adopt pypa/build when you publish packages and want a build that mirrors an install, and especially when your packaging metadata has been wrong, because the default of building the wheel from the sdist catches that. Do not adopt it as a way to bypass isolation, since `--no-isolation` also hands you the dependency checking that protects you.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A frontend that hands the real work to your backend

The one line description is "A simple, correct Python build frontend", and the word frontend is the whole design. Build does not know how to lay out a wheel, how to compile an extension, or how to read your version string. It creates an isolated environment, installs the backend your project declares under PEP 517, and asks that backend to produce artifacts. The evidence sits in the project's own `pyproject.toml`, where the build system section names the backend and the requirement:

toml
[build-system]
build-backend = "flit_core.buildapi"
requires = [ "flit-core>=3.11" ]

That is the standard declaration every modern Python project carries, and build treats it as the contract. The frontend's own job is isolation, dependency resolution for the build environment, and calling the backend hooks in the right order. Everything that matters about your package format stays with the backend you already chose.

The default order builds the wheel from the sdist

Here is the detail that most people miss until it saves them. The README states that if no flag is passed, the default is to build an SDist and then build a wheel from that SDist. That is not the same as building a wheel from your source directory. It means the wheel is produced from the archived, packaged form of your project, so anything your packaging configuration forgets to include is missing from the wheel, and the artefact you upload and the artefact a user installs are guaranteed to agree. The alternative ordering is one flag away, since `--sdist --wheel` produces both from the source directory instead. The default is the stricter one, and it is the reason a project can ship a wheel that works even when its source tree layout is unusual.

Installing the frontend and running your first build

There is one install step and one build step. Installation uses pip or an equivalent:

bash
$ pip install build

Once installed, the tool is reachable as a module and also as a command line entry point named `pyproject-build`:

bash
$ python -m build

The README states that this builds the package in an isolated environment and generates a source distribution and a wheel in the `dist/` directory. So the first thing to look for after a run is a `dist/` folder containing both files. The command line form `pyproject-build` is the same program, and the project's own `pyproject.toml` maps both entry points to `build.__main__:entrypoint`, including a `pipx.run` entry so the tool can be fetched and run in one step. There is no configuration file to write and no project file to edit before your first build.

Choosing the installer and switching off isolation

Isolation is the default, and turning it off is a documented decision with consequences. Two flags matter here. The `--installer` option picks which installer creates the isolated build environment, either `pip` or `uv`, and `--no-isolation` (`-n`) disables isolation altogether. The README also describes `--skip-dependency-check` (`-x`), which disables dependency checking when the build is not isolated, and says this should be done if some requirements or version ranges are not required for non-isolated builds. The combined short form `-nx` disables both, and the documentation notes it is identical to the `--no-build-isolation` flag of pip and uv. The trade-off is straightforward. Isolation gives you a clean environment with exactly the backend requirement your project declared, at the cost of downloading and resolving those requirements on every build.

Passing backend configuration with -C and --config-json

Some backends need settings at build time, and the frontend offers two ways to deliver them. The `-C<option>=<value>` form is described as the PEP 517 way of passing options to a backend, and it can be passed multiple times, with matching options producing a list. For anything complex, `--config-json=<value>` takes config settings as JSON strings instead. The two are mutually exclusive, so you cannot mix them in a single invocation. One warning belongs in large letters: the README notes that setuptools has very limited support for this mechanism. If your project builds with setuptools and relies on custom config keys, this is where the frontend's promise of correctness meets a backend that does not implement the full protocol, and you should check that the settings actually reach your backend before trusting a clean build.

Metadata only, single artefact output, and choosing an output directory

Two smaller switches are useful in scripts. The `--metadata` option produces just the metadata as JSON and cannot be combined with `--sdist` or `--wheel`, which makes it a fast way to read a version number without building anything. The README gives the shell pipeline for that:

bash
--metadata | jq -r .version

If you want only one artefact, `--sdist` (`-s`) produces just the source distribution and `--wheel` (`-w`) produces just a wheel. The output location is controlled by `--outdir` (`-o`), which defaults to `dist`. The remaining question a reader usually has is what these flags mean for reproducibility, and here the README is quiet: it describes what each flag produces without promising byte identical output across runs. If you need that guarantee, this frontend is not where the documentation takes you.

Running build through uv or pipx, and the cibuildwheel path

The frontend is meant to sit inside other tooling, and the README spells out three common arrangements. With pipx, one step downloads and runs build:

bash
$ pipx run build

With uv installed, you can skip the extra and reach the tool through uvx:

bash
$ uvx --from build pyproject-build --installer uv

If you want uv to create the virtual environment faster, the `[uv]` extra supplies a Python wheel for uv, and the pipx form becomes:

bash
$ pipx run 'build[uv]' --installer uv

For wheel builders, cibuildwheel already integrates build and uses it as the default builder in version 3.0 and later, with the frontend selected in configuration:

toml
[tool.cibuildwheel]
build-frontend = "build[uv]"

The README adds a practical warning there: pre-install uv before running cibuildwheel with that setting. On conda-forge, the same package is published under a different name, `python-build`, which matters if you write install instructions for users on conda rather than pip.

Licence, Python floor, and a steady release line

The project is MIT licensed, which in short terms means you can use, modify and redistribute it, with the notice and licence text preserved, and it carries no warranty. That matters because build sits in release automation, and its output artifacts are the ones you publish. The `pyproject.toml` sets `requires-python = ">=3.11"`, and the classifiers list CPython and PyPy across Python 3.11 to 3.15, so a Python 3.10 environment is not supported. The release history shows a steady line of patch releases: 1.6.1 published on 2026-09-10, 1.6.0 on 2026-08-27, and 1.5.1 on 2026-07-09. The repository is not archived and the last push was on 2026-10-19, so the tool is being maintained against current packaging changes. The runtime dependency list is short too, with `packaging>=24`, `pyproject-hooks`, and a conditional `colorama` for Windows.

Editorial conclusion

Adopt pypa/build when you publish packages and want a build that mirrors an install, and especially when your packaging metadata has been wrong, because the default of building the wheel from the sdist catches that. Do not adopt it as a way to bypass isolation, since `--no-isolation` also hands you the dependency checking that protects you. Before switching, confirm your backend accepts the PEP 517 config you pass with `-C`, since the README warns that setuptools has very limited support for it.

Frequently asked questions

How do I install and run pypa build?

Install it with `pip install build`, then run `python -m build`, or use the `pyproject-build` command line entry point once installed. The build runs in an isolated environment and writes a source distribution and a wheel into `dist/`.

What is the difference between `python -m build` and `python -m build --sdist --wheel`?

With no flags, build creates an sdist and then builds the wheel from that sdist. The `--sdist --wheel` combination produces both artifacts from the source directory instead.

How do I pass configuration settings to the build backend?

Use `-C<option>=<value>`, the PEP 517 way, and it can be given multiple times. `--config-json=<value>` is the alternative for complex settings, and the two cannot be combined. The README notes that setuptools has very limited support for this.

Can I use uv instead of pip inside pypa build?

Yes. The `--installer` flag accepts `pip` or `uv`, and the `[uv]` extra supplies a wheel for uv. With uv already present, the README shows `uvx --from build pyproject-build --installer uv`.

How do I get only the version number from a project without building it?

The `--metadata` option produces just the metadata as JSON and cannot be combined with `--sdist` or `--wheel`. The README shows it piped through jq to read the version field.

Official sources

  1. License: MIT
  2. Project website
  3. pypa/build on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pypa-build.svg)](https://hysenlabs.com/projects/pypa-build)

Was this useful?