python-template

Switch to hatchling

Move the build backend from uv_build to hatchling when a project outgrows it

The template builds with uv_build, which is the right backend for a pure Python project: it is already part of uv, so there is no second tool in the build path. Switch to hatchling when you need something uv_build does not do — a compiled extension, or a build hook that generates files at build time.

Nothing else in the project depends on the backend. This is a reversible, four-line change.

Goal

Replace uv_build with hatchling as the build backend, leaving the package layout and every other tool untouched.

Prerequisites

  • A reason. If the project is still pure Python with no build-time codegen, uv_build is doing the job and this change buys nothing.

Steps

Swap the build-system table

In pyproject.toml, replace:

[build-system]
requires = ["uv_build>=0.12.5,<0.13.0"]
build-backend = "uv_build"

[tool.uv.build-backend]
module-name = "pythontemplate"

with:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/pythontemplate"]

Both tables say the same thing in different dialects: where the package lives. uv_build infers it from the distribution name and needs module-name only when the two differ; hatchling is told the path outright.

Relock

Nothing to do. Build backends are resolved by the build frontend at build time, not locked as project dependencies — uv.lock records neither uv_build nor hatchling, and relocking after this swap produces no diff.

If you change project dependencies while you are here, re-lock with task uv:lock. Plain uv lock will not do it: mise.toml exports UV_FROZEN=1, under which uv lock silently no-ops and still exits 0.

Verification

uv build
task dev:check

uv build should emit both a wheel and an sdist into dist/, and the gate should stay green — no source file changes, so nothing else can move.

Notes

The wheel name does not change

Both backends normalise the distribution name the same way, so python-template produces python_template-<version>-py3-none-any.whl either way. Anything pinning the wheel name keeps working.

Adding an extension is a separate step

Switching the backend does not by itself let you ship compiled code. Hatchling needs a build hook — hatch-build-scripts, or a custom hook — and the CI matrix needs to build wheels per platform rather than the single py3-none-any wheel the release workflow currently publishes.

On this page