What is PEP 735?
PEP 735: Dependency Groups in pyproject.toml standardizes how a Python project declares named groups of non-production dependencies. It adds a single [dependency-groups] table to pyproject.toml that every packaging tool can read, replacing a patchwork of tool-specific tables that used to hold dev, test, and documentation requirements.
The PEP was authored by Stephen Rosen and resolved on 10 October 2024. It fills a gap in PEP 621 project metadata, which covered runtime dependencies and end-user extras but said nothing about the dependencies a project needs only during development.
Fill the gap left by PEP 621
Before PEP 735, every package manager invented its own location for development dependencies. Poetry used [tool.poetry.group.dev.dependencies]. PDM used [tool.pdm.dev-dependencies]. Early uv used [tool.uv.dev-dependencies]. Rye used [tool.rye.dev-dependencies]. None of these tables were portable: switching package managers meant rewriting project metadata, and a reader could not tell a project’s development requirements from the pyproject.toml alone without knowing which tool owned it.
Some projects worked around this by stuffing test and lint dependencies into [project.optional-dependencies] as a fake test or dev extra. That technically worked, but it advertised internal tooling through the package’s published metadata, so anyone inspecting the distribution on PyPI saw pytest and ruff in the user-facing extras list. PEP 735 formalizes a separate, unpublished home for these requirements.
Declare dependency groups
A dependency group is a named list of requirement strings under the new top-level [dependency-groups] table. Each key is the group name and each value is a list following PEP 508 requirement syntax:
[dependency-groups]
test = [
"pytest>=8.0",
"coverage>=7.0",
]
lint = [
"ruff>=0.15",
"mypy>=1.0",
]
docs = [
"sphinx>=9.0",
"myst-parser>=4.0",
]Groups live outside the [project] table on purpose. A project does not need a [project] table at all to define dependency groups, so the mechanism works for applications, scripts, and other non-package projects that still benefit from organized tooling requirements.
Compose groups with include-group
Groups can pull in other groups through the include-group directive. This keeps an umbrella group like dev in sync with the narrower groups it wraps:
[dependency-groups]
test = ["pytest>=8.0"]
lint = ["ruff>=0.15"]
typing = ["mypy>=1.0"]
dev = [
{include-group = "test"},
{include-group = "lint"},
{include-group = "typing"},
]Installing the dev group now resolves to the union of test, lint, and typing, without duplicating the requirement strings.
Keep dependency groups separate from extras
PEP 735 draws a hard line between dependency groups and the [project.optional-dependencies] extras defined by PEP 621. Extras travel with a package to PyPI and let users opt into runtime features. Dependency groups are invisible to users of the installed package and exist only for people working on the project. The table below summarizes the split, which is explored in depth in What are Optional Dependencies and Dependency Groups?.
| Dependency groups | Optional dependencies (extras) | |
|---|---|---|
| Table | [dependency-groups] |
[project.optional-dependencies] |
| Published to PyPI | No | Yes |
| Install audience | Project developers | Package users |
| Typical contents | pytest, ruff, sphinx | numpy, boto3, matplotlib |
Install a group
PEP 735 specifies how groups are declared, not a CLI for installing them. Each packaging tool chose its own flag. The common pattern looks like this:
# uv
uv sync --group test
# pip 25.1+
pip install --group test
# Poetry 2.3+
poetry install --with test
# PDM
pdm install -G testFor the full set of uv flags, including --only-group, --all-groups, default-groups, and group conflicts, see Understanding dependency groups in uv.
Track tool adoption across the ecosystem
Support for the [dependency-groups] table landed across the ecosystem through 2024 and 2025:
- uv 0.4.27 (October 2024) added
[dependency-groups]reading and writing, withuv add --groupanduv sync --group. - pip 25.1 (April 2025) introduced the
--groupflag for installing frompyproject.toml. - Poetry 2.2.0 added reading of
[dependency-groups]alongside its legacy[tool.poetry.group.*]tables. Poetry 2.3.0 fixed lock-file tracking for these groups and is the recommended minimum version. - PDM reads
[dependency-groups]alongside its older[tool.pdm.dev-dependencies]table. - Hatch 1.16.0 added support for installing from
[dependency-groups].
Older tool-specific tables keep working, but new projects should prefer the standard table so the metadata travels cleanly if the project ever changes package managers.