Build Backends
|
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. |
A build backend is the library named in [build-system] that a frontend (pip, build, Poetry, uv) calls through the PEP 517
hooks to produce an sdist and a wheel; frontends and backends are explained on
Packaging Concepts & Standards.
The backend decides what goes into the distributions and how the metadata is written; the [project] table is the same for all
of them. This page builds birdtally (src layout, static [project] metadata, an MIT license, py.typed) with six backends,
changing only [build-system], using build 1.6.1 on CPython 3.14.6 on 2026-10-22.
The pure-Python backends compared
| Backend (version used) | [build-system] |
PEP 639 since | Metadata written | Dynamic version | sdist of birdtally (beyond PKG-INFO, pyproject.toml, README.md, LICENSE, src/) |
|---|---|---|---|---|---|
setuptools 84.2.1 |
|
77.0.3 |
2.4 (plus |
|
|
hatchling 1.32.4 |
|
1.27.0 |
2.5 |
|
everything Git does not ignore: |
flit-core 4.1.0 |
|
3.12 |
2.5 |
|
nothing else (outside a Git checkout) |
pdm-backend 2.5.0 |
|
2.4.0 |
2.4 |
|
|
poetry-core 2.5.0 |
|
2.2.0 |
2.4 |
|
nothing else; |
uv_build 0.13.5 |
|
0.7.19 |
2.4 |
none: with |
nothing else |
All six built a wheel with the same four modules, py.typed and licenses/LICENSE, and all six worked with pip install -e
(PEP 660 editable installs). Where they differ:
-
Metadata version: hatchling and flit-core already write Core Metadata 2.5 (which adds
Import-Name); the others write 2.4. PyPI and twine 7.0.0 accept both. -
sdist contents vary the most: compare
tar tzf dist/*.tar.gzacross backends before switching, and configure inclusion explicitly ([tool.hatch.build.targets.sdist],[tool.poetry] include,MANIFEST.infor setuptools). -
Dynamic metadata: setuptools, hatchling, pdm-backend and flit-core can compute the version; poetry-core reads it from
[tool.poetry]; uv_build accepts only static metadata. -
Plugins: hatchling, pdm-backend, poetry-core and setuptools support plugins (for example VCS versions); flit-core and uv_build deliberately do not.
-
config-settings (
python -m build -C key=value) are backend-specific. poetry-core acceptslocal-version(-C local-version=dev1builtbirdtally-0.1.0+dev1-py3-none-any.whl), although build’s config-settings page says poetry-core supports none; setuptools wraps its command options in--build-option(-C--build-option=bdist_wheel -C--build-option=--build-number -C--build-option=123builtbirdtally-0.1.0-123-py3-none-any.whl); hatchling, flit-core and uv_build take none. The next table has the details; see also Building sdists & Wheels.
Pin a lower bound for the features you use (PEP 639 needs the versions in the table) and, for backends whose authors recommend it (uv_build, poetry-core), an upper bound below their next breaking release.
Source: PUG: Choosing a build backend, PUG: Tool recommendations: build backends, PUG: Declaring the build backend, build: Available build backends, build: config settings for poetry-core, setuptools: Dynamic metadata, Hatch: File selection, Flit: The pyproject.toml config file, pdm-backend: Metadata, uv: The uv build backend, Poetry: Poetry and PEP 517.
File selection, config-settings and editable installs
What each backend puts into the distributions, which -C/--config-settings keys it reads, and how pip install -e works with
it. The config-settings and editable columns were checked on 2026-10-22 with the same birdtally copies (pip 26.2.2 and
build 1.6.1 on CPython 3.14.6, the same backend versions as above); the file-selection column is from each backend’s
documentation.
| Backend | File selection | config-settings | Editable install (PEP 660) |
|---|---|---|---|
setuptools 84.2.1 |
Packages by automatic discovery (src or flat layout) or |
Command options wrapped in |
Since v64.0.0. By default a |
hatchling 1.32.4 |
Per target ( |
None. hatchling’s hooks ignore the |
A |
flit-core 4.1.0 |
Wheel: the module or package, data files included, |
None, silently: |
Since 3.4: a |
pdm-backend 2.5.0 |
|
|
|
poetry-core 2.5.0 |
|
|
A |
uv_build 0.13.5 |
Exactly one top-level module, |
None: any setting prints |
A |
Every default editable install here is the same simple .pth path entry: new modules under src/ are importable at once, but
changes to metadata (dependencies, entry points, the version) need a reinstall.
Source: setuptools: Automatic discovery, setuptools: package-data, setuptools: Using MANIFEST.in, setuptools: Strict editable installs, Hatch: File selection, Hatch: Dev mode, Hatch: sdist default file selection, Hatch: wheel default file selection, Flit: Contents of distribution files, Flit 3.4 release notes, pdm-backend: Include or exclude files, pdm-backend: Build config settings, pdm-backend: Choose the editable build format, Poetry: exclude and include, Poetry CLI: build, uv: File inclusion and exclusion, uv: Modules.
Stale statements in the documentation
-
The PUG tool recommendations and the build documentation’s backend explanation both say poetry-core uses
[tool.poetry]instead of the standard[project]table. That was true before poetry-core 2.0 (2025); the poetry-core build above used only[project]. -
The PUG packaging tutorial pins
uv_build >= 0.12.19, <0.13.0, while 0.13.5 is current and the uv documentation showsuv_build>=0.13.5,<0.14. -
The build explanation’s setuptools snippet still lists
"wheel"inrequires, which modern setuptools does not need. -
build’s config-settings page shows
-Creproducible=trueand-Cshared-data=/custom/pathfor hatchling, and says poetry-core supports no config settings. hatchling 1.32.4 ignores both settings (they arepyproject.tomloptions), and poetry-core readslocal-version.
setuptools specifics
-
[tool.setuptools.dynamic]fillsdynamickeys from attributes or files (version,readme,classifiers, …); using it fordependenciesandoptional-dependenciesis marked BETA. -
setup.pyis not deprecated, running it is. Asetup.pythat only callssetup()(for C extensions or other programmatic configuration) is still supported, next topyproject.toml;python setup.py sdist,bdist_wheel,install,developanduploadare deprecated or removed. Usepython -m buildandpip install. Theupload,registerandupload_docscommands are gone (setuptools 75.0.0, with the distutils merge). -
pkg_resourceswas removed in setuptools 82.0.0 (2026-02-08); useimportlib.metadataandimportlib.resources. -
To modernise a
setup.pyproject: addpyproject.tomlwith[build-system], move static metadata into[project], keepsetup.pyonly for what cannot be static, and list computed keys indynamic.
Backends for extension modules
These backends were not run on this page (not run here): the last three columns are from each backend’s documentation, and the maturin settings also from its 1.15.0 source.
| Backend (version) | For | File selection | config-settings | Editable install |
|---|---|---|---|---|
setuptools |
C and C++ natively, Rust and Go through plugins. |
As for pure-Python projects (table above). |
|
As above; binary extensions may be a snapshot of the install time, so rebuild by reinstalling. |
scikit-build-core 1.1.1 |
C, C++, Fortran and anything CMake builds. |
sdist from |
Every |
Supported, with an experimental rebuild-on-import mode and an inplace mode. |
meson-python 0.22.1 |
C, C++, Fortran, Rust and the other languages Meson supports; common in scientific Python. |
The sdist is |
|
Rebuilds on import; install with |
maturin 1.15.0 |
Rust (PyO3, cffi or uniffi bindings) through Cargo; worked example on Python Interop with PyO3. |
|
|
Since 0.12.0 ( |
Compiled extensions bring platform tags, manylinux and cibuildwheel into play: Binary Wheels & Extension Modules.
Source: PUG: Build backends for extension modules, build: config settings for scikit-build-core, scikit-build-core: Configuring source file inclusion, scikit-build-core: Editable installs, meson-python: Creating a source distribution, meson-python: Build config settings, meson-python: Editable installs, build dependencies, maturin: Configuration, maturin: PEP 660 editable installs, setuptools: Development mode limitations, maturin 1.15.0 source: the PEP 517 hooks.
Poetry and other backends
Since Poetry 2.1, poetry build honours whatever [build-system] names: with another backend (or another poetry-core version
than the one Poetry bundles) it builds in an isolated environment, like python -m build. Without a [build-system] table it
still falls back to its bundled poetry-core with a warning, and the Poetry team has announced a switch to the PEP 517 default,
setuptools, in a future release. A Poetry-managed project can therefore use hatchling or maturin for its builds and Poetry for
dependencies.