Skip to content

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.py supports, 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]:

setup.cfg
[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.0

The matching setup.py can be reduced to a single setup() call, since all configuration lives in setup.cfg:

setup.py
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.

pyproject.toml
[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] from setup.cfg, tox.ini, or .flake8. Flake8 has declined to add native pyproject.toml support; the third-party Flake8-pyproject plugin bridges the gap for projects that want TOML-only config.
  • coverage.py reads [coverage:*] sections from setup.cfg when no .coveragerc or pyproject.toml is present.
  • mypy reads [mypy] and per-module [mypy-*] sections from setup.cfg, mypy.ini, .mypy.ini, or pyproject.toml. The pyproject.toml form ([tool.mypy]) is now the preferred location.
  • pytest still accepts [tool:pytest] in setup.cfg, but the pytest documentation recommends pytest.ini, tox.ini, or pyproject.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.py empty 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.cfg is not PEP 621 standard, so switching build backends requires a rewrite into pyproject.toml
  • Duplicates concerns with pyproject.toml when 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

Last updated on

Please submit corrections and feedback...