ADR-009: pyproject.toml Standard for Python Projects
Status: Accepted
Date: 2025-07-10
Authors: Piotr Brzozowski
Reviewers: Piotr Brzozowski
pyproject.toml is adopted as the unified configuration standard for all Python projects in the Entirius ecosystem. This modern Python packaging standard (PEP 518, PEP 621) centralizes project configuration, dependencies, and tool settings in a single file.
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "entirius-service-example"
version = "1.0.0"
description = "Description of the project"
authors = [{name = "Author Name", email = "[email protected]"}]
license = {text = "MPL-2.0"}
requires-python = ">=3.11"
dependencies = ["django>=5.0", "djangorestframework>=3.14"]
[project.optional-dependencies]
dev = ["ruff>=0.1.0", "pytest>=7.0", "mypy>=1.0"]
test = ["pytest>=7.0", "pytest-django>=4.5", "coverage>=7.0"]
[tool.ruff]
line-length = 88
target-version = "py311"
# Convert existing setup.py to pyproject.toml
uv init --package project-name
# Install dependencies using pyproject.toml
uv pip install -e .
# Install with development dependencies
uv pip install -e ".[dev]"
The Entirius project consists of multiple Python components including Django services, Python modules, and Python libraries. Currently, these projects use different approaches for dependency management and project configuration:
- Some projects use
setup.pyfor package configuration - Others use
requirements.txtfor dependency management - Configuration is scattered across multiple files (
setup.py,setup.cfg,requirements.txt,Pipfile, etc.)
Python’s ecosystem has evolved toward standardization around pyproject.toml (PEP 518, PEP 621) as the unified configuration file for Python projects. This standard provides:
- Centralized configuration - Single file for all project metadata
- Tool compatibility - Supported by modern Python tools (pip, UV, setuptools, etc.)
- Standardization - Official Python packaging standard
- Better dependency management - Clear separation of runtime, development, and optional dependencies
- Description: Migrate all Python projects to use
pyproject.tomlas the primary configuration file - Pros:
- Modern Python packaging standard (PEP 518, PEP 621)
- Single file for all project configuration
- Better tool integration and compatibility
- Consistent dependency management
- Future-proof approach aligned with Python ecosystem
- Excellent support in UV package manager (per ADR-007)
- Cons:
- Migration effort for existing projects
- Learning curve for team members
- Some legacy tools may not support it
- Impact on system: Standardizes configuration across all Python projects
Chosen option: Adopt pyproject.toml Standard
Key decision factors:
- Modern standard: PEP 518 and PEP 621 establish pyproject.toml as the official Python packaging approach
- Tool unification: Single file consolidates project metadata, dependencies, and tool configurations
- UV integration: Excellent compatibility with UV package manager (per ADR-007) for optimal performance
- Dependency management: Clear separation of runtime, development, test, and optional dependencies
- Risk analysis: Low risk with widespread adoption and official Python community backing
- Business impact: Standardized configuration reduces maintenance overhead and improves developer productivity
- Compatibility: Native support in modern Python tools including Hatchling, Ruff, pytest, and mypy
Standard Configuration Structure:
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "entirius-service-example"
version = "1.0.0"
description = "Description of the project"
authors = [{name = "Piotr Brzozowski", email = "[email protected]"}]
license = {text = "MPL-2.0"}
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"django>=5.0",
"djangorestframework>=3.14",
]
[project.optional-dependencies]
dev = [
"black>=23.0",
"pytest>=7.0",
"mypy>=1.0",
"ruff>=0.1.0",
]
test = [
"pytest>=7.0",
"pytest-django>=4.5",
"coverage>=7.0",
]
[project.urls]
Homepage = "https://github.com/entirius/entirius-service-example"
Documentation = "https://docs.entirius.com"
Repository = "https://github.com/entirius/entirius-service-example"
Issues = "https://github.com/entirius/entirius-service-example/issues"
[tool.black]
line-length = 88
target-version = ['py311']
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "main.settings.testing"
python_files = ["tests.py", "test_*.py", "*_tests.py"]
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
[tool.ruff]
line-length = 88
target-version = "py311"
