# setup.cfg: Setuptools Declarative Configuration File


`setup.cfg` is an INI-style configuration file read by [setuptools](https://pydevtools.com/handbook/reference/setuptools.md) 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](https://pydevtools.com/handbook/reference/pyproject.toml.md) 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]`:

```ini {filename="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`:

```python {filename="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](https://pydevtools.com/handbook/explanation/what-is-pep-621-compatibility.md) for the underlying standard.

```toml {filename="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](https://pydevtools.com/handbook/reference/uv.md)'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](https://pydevtools.com/handbook/reference/flake8.md) 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](https://docs.pytest.org/en/stable/reference/customize.html) 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

- [setuptools declarative config documentation](https://setuptools.pypa.io/en/latest/userguide/declarative_config.html)
- [setuptools configuration keywords reference](https://setuptools.pypa.io/en/latest/references/keywords.html)
- [PEP 621: Storing project metadata in pyproject.toml](https://peps.python.org/pep-0621/) and the handbook [PEP 621 explainer](https://pydevtools.com/handbook/explanation/what-is-pep-621-compatibility.md)
- Handbook: [pyproject.toml reference](https://pydevtools.com/handbook/reference/pyproject.toml.md), [setuptools reference](https://pydevtools.com/handbook/reference/setuptools.md), [distutils reference](https://pydevtools.com/handbook/reference/distutils.md)
- Handbook How To: [Migrating from setup.py to pyproject.toml](https://pydevtools.com/handbook/how-to/migrate-setup-py.md)
