setup.cfg: Setuptools Declarative Configuration File
setup.cfg is an INI-style configuration file read by setuptools for declarative package configuration. Setuptools 30.3.0 (December 2016) introduced support for reading project metadata and build options from setup.cfg, letting authors write a one-line setup.py and keep everything else declarative. The PEP 621 [project] table in pyproject.toml has since superseded it for metadata, though setup.cfg remains a valid place for setuptools-specific options and for configuring tools that read it.
Important
New projects should use pyproject.toml for metadata. setup.cfg is legacy for that purpose as of setuptools 61.0 (March 2022), which added PEP 621 support. The file format itself is not deprecated, but its main metadata use case has been replaced.
Key Features
- Declarative INI format for setuptools metadata and options, readable without executing Python
- Section-based layout:
[metadata],[options],[options.extras_require],[options.entry_points],[options.package_data],[options.packages.find] - Tool configuration host for workflows that read from it: flake8, coverage.py, mypy (still supported), pytest (still accepted but no longer recommended)
- Environment markers in dependency specifications, such as
pywin32; sys_platform == 'win32' - Supports most setuptools features that
setup.pysupports, minus the ability to run arbitrary code at build time
Example
A representative setup.cfg declares metadata in [metadata], build options in [options], and optional dependency groups under [options.extras_require]:
[metadata]
name = example
version = 0.1.0
description = Example package configured through setup.cfg
long_description = file: README.md
long_description_content_type = text/markdown
license = MIT
[options]
python_requires = >=3.8
packages = find:
install_requires =
requests>=2.28
click>=8.0
[options.extras_require]
dev =
pytest>=7.0
mypy>=1.0The matching setup.py can be reduced to a single setup() call, since all configuration lives in setup.cfg:
from setuptools import setup
setup()Newer setuptools releases accept a project with no setup.py at all when pyproject.toml declares the build backend, but the one-line file remains common in projects built around setup.cfg.
Relationship to pyproject.toml
After setuptools 61.0, the same metadata fits into a PEP 621 [project] table in pyproject.toml, alongside a [build-system] table that pins the build backend. See the PEP 621 explainer for the underlying standard.
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "example"
version = "0.1.0"
description = "Example package configured through setup.cfg"
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.8"
dependencies = [
"requests>=2.28",
"click>=8.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"mypy>=1.0",
]The pyproject.toml form is portable across build backends. The same [project] table works with hatchling, uv’s uv_build, flit-core, and others. A setup.cfg is readable only by setuptools.
Tool Configuration under setup.cfg
Several tools outside setuptools historically read their configuration from setup.cfg. The picture as of 2025 and 2026:
- flake8 reads
[flake8]fromsetup.cfg,tox.ini, or.flake8. Flake8 has declined to add nativepyproject.tomlsupport; the third-partyFlake8-pyprojectplugin bridges the gap for projects that want TOML-only config. - coverage.py reads
[coverage:*]sections fromsetup.cfgwhen no.coveragercorpyproject.tomlis present. - mypy reads
[mypy]and per-module[mypy-*]sections fromsetup.cfg,mypy.ini,.mypy.ini, orpyproject.toml. Thepyproject.tomlform ([tool.mypy]) is now the preferred location. - pytest still accepts
[tool:pytest]insetup.cfg, but the pytest documentation recommendspytest.ini,tox.ini, orpyproject.toml([tool.pytest.ini_options]) instead, citing parser differences that produce hard-to-trace bugs.
Pros
- Works with any setuptools release from 30.3.0 onward, covering long-lived projects with pinned build environments
- Keeps
setup.pyempty or one line, removing arbitrary Python execution at build time for most projects - Colocates tool config (for tools that do not yet read
pyproject.toml) next to build config in a single file - Preserves a known-good path for setuptools-specific knobs that have no PEP 621 equivalent, such as
[options.packages.find]include and exclude patterns
Cons
- INI format has no nested structures; setuptools expresses lists through newline-indented continuations, a convention that is easy to break with a stray space
- Metadata in
setup.cfgis not PEP 621 standard, so switching build backends requires a rewrite intopyproject.toml - Duplicates concerns with
pyproject.tomlwhen both files exist in the same project, producing two places to look for the same setting - No validation of unknown keys: typos in field names are silently ignored by setuptools
Learn More
- setuptools declarative config documentation
- setuptools configuration keywords reference
- PEP 621: Storing project metadata in pyproject.toml and the handbook PEP 621 explainer
- Handbook: pyproject.toml reference, setuptools reference, distutils reference
- Handbook How To: Migrating from setup.py to pyproject.toml