ADR 0002 — Single-source configuration with pyproject.toml¶
- Status: Accepted
- Date: 2026-06-02
- Deciders: @javierpa95
Context and Problem Statement¶
CGMPy needs to manage build configuration, tool settings (ruff, pytest,
mypy, coverage), and project metadata across multiple files. Historically
each tool required its own config file (setup.cfg, .flake8,
pytest.ini, .coveragerc, etc.). This caused fragmentation: settings
were duplicated, contradictory, or fell out of sync when the project
grew.
We need a single source of truth that is standard, machine-readable, and understood by the Python ecosystem tooling.
Considered Options¶
- pyproject.toml (PEP 517/518/621) — the modern Python standard.
All tool config goes in
[tool.*]tables; build system is declared via[build-system]. - setup.cfg + tool-specific files — traditional approach used before pyproject.toml became the norm.
- pyproject.toml for build only + separate tool files — hybrid: build metadata in pyproject.toml, but tool settings stay in their legacy locations.
Decision Outcome¶
Chosen option: pyproject.toml (full adoption), because:
- It is the only option that makes
pip install -e ".[dev,docs]"work without asetup.pyorsetup.cfgfile. - It is natively supported by ruff, pytest, mypy, coverage, and
setuptools via
[tool.*]tables. - It eliminates five separate config files and the cognitive overhead of knowing which config lives where.
- It is future-proof: the Python ecosystem is consolidating around pyproject.toml as the universal config file.
Consequences¶
- Good, because there is exactly one place to look for project configuration.
- Good, because new contributors only need to understand one file.
- Good, because CI scripts can read the version from
pyproject.tomlviatomllib(stdlib in Python 3.11+). - Bad, because not all legacy tools support pyproject.toml yet
(e.g., some pre-commit hooks). We keep a minimal
.pre-commit-config.yamlfor those.
Pros and Cons of the Options¶
pyproject.toml (full adoption)¶
- Good, standard and supported by all modern Python tooling.
- Good, allows
[project]table for metadata,[build-system]for build,[tool.*]for tool config. - Good, supports optional dependencies (
[project.optional-dependencies]). - Bad, older tools (flake8, isort) did not support it at first; but we migrated away from those tools (ruff replaces both).
setup.cfg + tool files¶
- Good, well-understood.
- Bad, fragmented: pytest.ini, mypy.ini, .coveragerc, .flake8, ...
- Bad, no standard way to declare build backend.
Hybrid (pyproject.toml for build only)¶
- Good, incremental migration.
- Bad, same fragmentation problem for tool config.
- Bad, confusing: "why is some config in pyproject.toml and some not?"