pyproject.toml Builder

Generate a PEP 621 pyproject.toml for a Python package or app

Build a modern pyproject.toml with build-system, project metadata, dependencies, optional-dependencies and a console-script entry point. Choose Hatchling, setuptools, Flit or Poetry as the build backend. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is the build-system table for?

The build-system table follows PEP 517 and tells pip which backend to use to turn your source into a wheel. requires lists the build tools (such as hatchling) and build-backend names the import path pip calls, so a clean environment can build your package without you installing anything globally.

The pyproject.toml Builder generates a modern, PEP 621 compliant project file — the single configuration file that replaces setup.py, setup.cfg and requirements.txt for packaging. Enter your metadata and dependencies and the tool emits a complete [build-system] and [project] block plus extras, a script entry point and basic tool config.

How it works

Two tables do the core work. [build-system] (PEP 517) names the backend pip uses to build a wheel — Hatchling, setuptools, Flit or Poetry core — so a fresh environment can build your package with nothing installed globally. [project] (PEP 621) holds the metadata PyPI displays: name, version, description, requires-python, license, authors and the dependencies list.

The tool also adds [project.optional-dependencies] with a dev extra (pytest, ruff, mypy), an optional [project.scripts] console entry point, project URLs, and starter [tool.ruff] / [tool.mypy] sections. The package name is normalised to a valid PyPI distribution name, and the import module name is derived by replacing hyphens with underscores.

Example generated file

For a package named my-data-tool with one dependency:

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

[project]
name = "my-data-tool"
version = "0.1.0"
description = "A data processing utility"
requires-python = ">=3.10"
dependencies = [
    "httpx>=0.27",
]

[project.optional-dependencies]
dev = [
    "pytest>=8",
    "ruff>=0.4",
    "mypy>=1.10",
]

[project.scripts]
my-data-tool = "my_data_tool.__main__:main"

[tool.ruff]
line-length = 88

[tool.mypy]
strict = true

The name my-data-tool becomes the distribution name on PyPI, while the import module is my_data_tool (hyphens converted to underscores). The console script maps the command my-data-tool to my_data_tool.__main__:main.

Example workflow

Once you have a pyproject.toml and a source package, build and install like this:

python -m build              # produces dist/*.whl and *.tar.gz
pip install -e ".[dev]"      # editable install with the dev extra

Choosing a build backend

BackendBest forNotes
HatchlingPure-Python modern packagesFast, good defaults, no legacy config
setuptoolsPackages with C extensionsMost widely supported; needed for ext_modules
FlitMinimal pure-Python packagesVery simple; no extras support in core
Poetry coreProjects already using PoetryConsistent with pyproject.toml-only workflow

All four produce standard wheels that pip install accepts.

Tips and notes

A console script such as mypkg = mypkg.__main__:main requires a __main__.py that defines a main() function — the generated entry point assumes that layout. Keep runtime dependencies lean in dependencies and push test or build tooling into extras, so end users do not pull in your whole toolchain. For reproducible builds, pair this with a lock file from uv, pip-tools or Poetry.

The requires-python specifier protects users from installing on an incompatible interpreter — always set it. A specifier like >=3.10 is both honest and forward-compatible; ==3.10.* is unnecessarily narrow unless you have tested and rely on a specific minor version.