What is pyproject.toml file for?
Categories:
Understanding pyproject.toml: The Modern Python Project Configuration

Explore the purpose and structure of the pyproject.toml file, a central configuration hub for Python projects, packaging, and tooling.
The pyproject.toml file has emerged as the standard and recommended way to configure Python projects. Introduced by PEP 518 and later expanded by PEP 621, it provides a single, centralized location for build system requirements, project metadata, and tool-specific configurations. This article will delve into what pyproject.toml is, why it's important, and how to use it effectively for your Python projects.
The Evolution of Python Project Configuration
Historically, Python projects relied on setup.py and setup.cfg for packaging and distribution. While functional, this approach had limitations, particularly when it came to specifying build-time dependencies and standardizing tool configurations. The introduction of pyproject.toml aimed to solve these issues by providing a declarative, static, and tool-agnostic configuration format based on the TOML (Tom's Obvious, Minimal Language) specification.
flowchart TD
A[Old Way: setup.py/setup.cfg] --> B{Build System Selection}
B --> C[New Way: pyproject.toml]
C --> D[Standardized Build Requirements]
C --> E[Centralized Tool Config]
D --> F[Improved Dependency Management]
E --> G[Simplified Project Setup]
F & G --> H[Modern Python Packaging]Evolution from traditional Python packaging to pyproject.toml
Key Sections and Their Purpose
A pyproject.toml file is structured into various sections, each serving a distinct purpose. The most common and important sections include [build-system], [project], and tool-specific tables like [tool.poetry], [tool.flit], [tool.hatch], [tool.setuptools], [tool.black], or [tool.mypy].
pyproject.toml is valid TOML syntax. Tools like toml-cli can help validate your file.The [build-system] Table
This is often the first section you'll encounter in a pyproject.toml file. It specifies the build backend that your project uses to create distributable packages (like wheels or source distributions). This section is crucial because it tells pip and other installers how to build your project before they can install it.
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
Example [build-system] configuration using Setuptools
requires: A list of packages that must be installed before the build backend can be imported. These are build-time dependencies.build-backend: The entry point for the build backend. This tells the installer which module and function to call to perform the build.
The [project] Table (PEP 621 Metadata)
The [project] table, standardized by PEP 621, allows you to declare all your project's core metadata directly in pyproject.toml. This includes the project name, version, description, authors, dependencies, and more. This eliminates the need for setup.py or setup.cfg for basic metadata, making project configuration more declarative and less reliant on executable code.
[project]
name = "my-awesome-package"
version = "0.1.0"
authors = [
{ name="John Doe", email="john.doe@example.com" },
]
maintainers = [
{ name="Jane Smith", email="jane.smith@example.com" },
]
description = "A short description of my awesome package."
readme = "README.md"
requires-python = ">=3.8"
license = { file="LICENSE" }
keywords = ["python", "packaging", "example"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"requests>=2.28.1",
"rich~=12.0",
]
[project.urls]
"Homepage" = "https://github.com/my-org/my-awesome-package"
"Bug Tracker" = "https://github.com/my-org/my-awesome-package/issues"
[project.scripts]
my-cli = "my_package.cli:main"
Example [project] table with common metadata fields
Key fields in [project]:
name: The name of your package.version: The package version.authors,maintainers: Contact information for the project.description,readme: Short and long descriptions of the project.requires-python: The Python versions your package supports.license: Licensing information.dependencies: Runtime dependencies of your package.[project.urls]: Links to project resources.[project.scripts]: Entry points for command-line scripts.
Tool-Specific Configuration Tables
Beyond the standardized [build-system] and [project] tables, pyproject.toml also serves as a central place for configuring various Python development tools. This is done using tables prefixed with [tool.], followed by the tool's name. This approach prevents configuration sprawl across multiple dotfiles and provides a consistent way to manage project settings.
[tool.black]
line-length = 88
target-version = ['py38', 'py39', 'py310']
include = '\.pyi?$'
exclude = '''
/(.
| .venv
| migrations
)/
'''
[tool.mypy]
python_version = "3.9"
warn_unused_configs = true
ignore_missing_imports = true
[tool.pytest.ini_options]
min_version = "6.0"
addopts = "--strict-markers --strict-files"
xfail_strict = true
filterwarnings = [
"error",
"ignore::DeprecationWarning",
]
Examples of tool-specific configurations for Black, MyPy, and Pytest
Common tools that leverage pyproject.toml for configuration include:
- Formatters:
black,isort - Linters:
flake8,pylint - Type Checkers:
mypy - Test Runners:
pytest - Packaging Tools:
poetry,flit,hatch,setuptools(for advanced options)
By consolidating these configurations, pyproject.toml makes it easier to onboard new developers, ensure consistent development practices, and manage project settings.
[tool.<tool_name>] tables are entirely dependent on the tool itself. Always refer to the official documentation of each tool for its pyproject.toml configuration options.