What is pyproject.toml file for?

Learn what is pyproject.toml file for? with practical examples, diagrams, and best practices. Covers python, pip, packaging development techniques with visual explanations.

Understanding pyproject.toml: The Modern Python Project Configuration

Hero image for What is pyproject.toml file for?

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].

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.