What is PEP 621?
PEP 621 defines the [project] table in pyproject.toml for declaring project name, version, description, dependencies, license, and other core metadata. Any compliant build tool can read this table without executing Python code or recognizing tool-specific configuration.
How tools stored metadata before PEP 621
Before PEP 621, every build tool stored project metadata in its own format. Setuptools used setup.py (executable Python) or setup.cfg (an INI format with a setuptools-specific schema). Flit used [tool.flit.metadata]. Poetry stored everything under [tool.poetry].
Reading a project’s name or dependencies required knowing which tool built it, or running its build system. Metadata inside setup.py could not be read statically at all.
What the [project] table defines
PEP 621 reserves the [project] key in pyproject.toml for standard metadata. A minimal project configuration:
[project]
name = "my-package"
version = "1.0.0"
description = "A short description."
requires-python = ">=3.11"
dependencies = [
"requests>=2.28",
]
[project.scripts]
my-tool = "my_package.cli:main"name is always required. version is required unless listed in dynamic (see below). Optional fields include:
description,readme,requires-python,licenseauthors,maintainers,keywords,classifiersurls,scripts,gui-scripts,entry-pointsdependencies,optional-dependencies
These fields map directly to core metadata, the wire format inside wheels and sdists. PEP 621 defines how the source-tree declaration translates into the same fields that installers read after a package is built.
Computed fields: the dynamic key
Some projects derive their version from a git tag, a file, or a commit hash rather than hardcoding it in pyproject.toml. The dynamic key lists which fields the backend computes rather than reads from [project]:
[project]
name = "my-package"
dynamic = ["version"]A plugin like hatch-vcs or setuptools-scm then supplies the version during the build. All other metadata still belongs in [project].
Which backends support PEP 621
| Backend | PEP 621 support |
|---|---|
| uv_build | Native |
| Hatchling | Native |
| flit_core | Native (replaced [tool.flit.metadata]) |
| setuptools | From v61.0.0 |
| pdm-backend | Native |
| poetry-core | From Poetry 2.0; [tool.poetry] still works |
PEP 621 standardizes where metadata lives, not how packages get built. The [build-system] table from PEP 517/518 still determines which backend runs. Switching from Hatchling to uv_build means changing [build-system]; the [project] table stays the same.
Related
- What is PEP 517/518? covers the build-system interface that sits alongside the
[project]table. - pyproject.toml reference documents every table the file supports, including
[project]. - What is core metadata? explains the wire format that
[project]fields map to. - Why are there so many Python packaging tools? gives context for why this standardization was needed.