Managing Dependencies & Groups
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the Python Reference bibliography. |
This page covers the commands that change a Poetry project’s dependencies and the way Poetry 2.5.1 organises them in groups. The
examples ran on CPython 3.14.6 on 2026-10-22, on a copy of the Poetry flavour of birdtally whose docs group is marked optional.
The syntax of each dependency is on Dependency Specification.
poetry add
poetry add adds one or more requirements, writes them into pyproject.toml, updates poetry.lock and installs the result.
| Option | Effect |
|---|---|
|
Add to a dependency group (created if needed). |
|
Add a path or VCS dependency in editable (develop) mode. |
|
Extras of the added package to activate. |
|
Add it as an optional dependency of your extra |
|
Only for these Pythons (becomes a |
|
Only for these platforms (a |
|
Any PEP 508 marker. |
|
Pin it to a package source. |
|
Accept pre-releases. |
|
Show the operations, change nothing. |
|
Update |
The argument forms: rich, "rich>=15", rich@^15, rich@latest, "rich[jupyter]@^15", git+https://…/repo.git#ref,
git+ssh://…, "git+https://…#ref&subdirectory=sub", a local path (../birdtally), a wheel or sdist file or URL. Quote anything with
[, <, > or ^ so that the shell does not interpret it.
Adding a test tool to the existing PEP 735 test group:
poetry add --group test pytest-cov
…
Package operations: 8 installs, 0 updates, 0 removals
- Installing iniconfig (2.3.1)
- Installing packaging (26.3)
- Installing pluggy (1.7.0)
- Installing pygments (2.21.0)
- Installing pytest (9.1.1)
- Installing coverage (7.16.2)
- Installing ruff (0.17.1)
- Installing pytest-cov (7.1.0)
Writing lock file
[dependency-groups]
test = ["pytest>=8", "pytest-cov (>=7.1.0,<8.0.0)"]
lint = ["ruff>=0.6"]
docs = ["mkdocs>=1.6"]
dev = [{ include-group = "test" }, { include-group = "lint" }]
[tool.poetry.group.docs]
optional = true
Poetry kept the file’s PEP 735 style and also installed the rest of the non-optional groups (lint arrived with this call), but
not docs.
--optional takes an extra name. poetry add --optional click therefore fails with Not enough arguments (missing: "name"): click
was consumed as the extra’s name and no package was left. Write poetry add --optional pretty rich-pixels.
poetry remove
poetry remove rich removes a dependency from pyproject.toml, the lock and the environment; -G/--group for a group, --dry-run
and --lock as for add. Unlike pip uninstall, it also removes the dependencies that nothing else needs any more.
poetry show
poetry show lists the locked packages; poetry show <name> details one. With --with, --without and --only it accepts the
group options of install.
poetry show --only test
coverage 7.16.2 Code coverage measurement for Python
iniconfig 2.3.1 brain-dead simple config-ini parsing
packaging 26.3 Core utilities for Python packages
pluggy 1.7.0 plugin and hook calling mechanisms for python
pygments 2.21.0 Pygments is a syntax highlighting package written in Python.
pytest 9.1.1 pytest: simple powerful testing with Python
pytest-cov 7.1.0 Pytest plugin for measuring coverage.
poetry show --tree --only test
pytest 9.1.1 pytest: simple powerful testing with Python
├── colorama >=0.4
├── exceptiongroup >=1
│ └── typing-extensions >=4.6.0
├── iniconfig >=1.0.1
├── packaging >=22
├── pluggy >=1.5,<2
├── pygments >=2.7.2
└── tomli >=1
pytest-cov 7.1.0 Pytest plugin for measuring coverage.
├── coverage >=7.10.6
│ └── tomli *
…
--why explains where a package comes from, on the list or together with --tree for one package (alone with a package name it
answers Error: --why cannot be used without --tree when displaying a single package.):
poetry show --why --only test
poetry show --tree --why pygments
coverage 7.16.2 from pytest-cov Code coverage measurement for Python
iniconfig 2.3.1 from pytest brain-dead simple config-ini parsing
packaging 26.3 from mkdocs,pytest Core utilities for Python packages
pluggy 1.7.0 from pytest,pytest-cov plugin and hook calling mechanisms...
pygments 2.21.0 from pytest,rich Pygments is a syntax highlighting ...
pytest 9.1.1 from pytest-cov pytest: simple powerful testing wi...
pytest-cov 7.1.0 Pytest plugin for measuring coverage.
pytest 9.1.1 pytest: simple powerful testing with Python
└── pygments >=2.7.2
rich 15.0.0 Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal
└── pygments >=2.13.0,<3.0.0
poetry show -T # --top-level: direct dependencies only
poetry show --outdated # -o; --latest (-l) shows the newest version of everything
poetry show -f json --only lint
pytest 9.1.1 pytest: simple powerful testing with Python
pytest-cov 7.1.0 Pytest plugin for measuring coverage.
rich (!) 15.0.0 Render rich text, tables, progress bars, syntax h...
ruff 0.17.1 An extremely fast Python linter and code formatte...
coverage 7.16.2 7.17.0 Code coverage measurement for Python
[{"name": "ruff", "installed_status": "installed", "version": "0.17.1", "description": "An extremely fast Python linter and code formatter, written in Rust."}]
(!) marks a locked package that is not installed (here rich, needed only for the pretty extra). -a/--all also shows packages
that do not apply to the current platform, and --no-truncate stops shortening the descriptions. --format json (2.2) cannot be
combined with --tree.
Source: Poetry CLI: show.
Dependency groups
The dependencies in [project] dependencies (or [tool.poetry.dependencies]) form the implicit main group: what the project
needs at run time, and the only group that reaches the published metadata. Everything else goes in named groups, in either of two
syntaxes:
PEP 735 [dependency-groups] (Poetry 2.2) |
[tool.poetry.group.<name>] (Poetry 1.2) |
|---|---|
|
|
-
PEP 735 groups are read by pip, uv and other tools as well; prefer them for new projects. Requiring Poetry 2.2 or later is the cost, and since 2.3 the groups are part of the lock’s content hash, so a project that uses them should set
requires-poetry = ">=2.3"(an older Poetry sees a 2.3 lock as outdated). -
Both syntaxes can describe the same group;
[tool.poetry.group.<name>]then only adds Poetry settings (optional, sources). -
Optional groups (
optional = true) are not installed by default. They are still resolved with everything else (below). -
Including groups:
{ include-group = "test" }in PEP 735,include-groups = [...]in the Poetry syntax. -
Names are normalized like project names, so
Docsanddocsare one group.
Source: Poetry: Dependency groups, PEP 735 tab, tool.poetry tab, Poetry: Optional groups, optional, PEP 735 tab, optional, tool.poetry tab, Poetry: Including dependencies from other groups, include, PEP 735 tab, include, tool.poetry tab, PyPA: Dependency groups.
Installing groups
poetry install installs main and all non-optional groups. The selection options, here with --dry-run:
| Command | Result |
|---|---|
|
|
|
|
|
Only the runtime dependencies (and the project). |
|
Only that group, not even |
|
Every group, optional ones included. |
--only main is how production images and poetry export usually select the runtime set.
All groups are resolved together
There is one lock for the whole project, so every group, optional or not, is resolved in a single solution. A constraint in a group
you never install still affects the others. Adding pygments<2.13 to the optional docs group:
docs = ["mkdocs>=1.6", "pygments<2.13"]
poetry lock
grep -A1 -E '^name = "(rich|pygments)"' poetry.lock
Writing lock file
name = "pygments"
version = "2.12.0"
--
name = "rich"
version = "13.2.0"
The library’s pretty extra is now locked to rich 13.2.0 (the newest rich that accepts an old Pygments) for everybody, including
production installs with --only main -E pretty. If two groups really need incompatible versions, they cannot live in one Poetry
project; use separate environments (for example tox or nox sessions).
Synchronizing, and extras
poetry sync (2.0, replacing install --sync) makes the environment match the lock and the selected groups exactly, removing
everything else:
poetry sync --only main --dry-run
Package operations: 0 installs, 0 updates, 8 removals, 3 skipped
- Removing coverage (7.16.2)
- Removing iniconfig (2.3.1)
- Removing packaging (26.3)
- Removing pluggy (1.7.0)
- Removing pygments (2.21.0)
- Removing pytest (9.1.1)
- Removing pytest-cov (7.1.0)
- Removing ruff (0.17.1)
Extras are handled separately from groups: with sync, any extra that is not selected is removed. After poetry install -E pretty,
a plain poetry install left rich in place in 2.5.1, while a plain poetry sync removed it:
Package operations: 0 installs, 0 updates, 3 removals
- Removing markdown-it-py (4.2.0)
- Removing mdurl (0.1.2)
- Removing rich (15.0.0)
(The pyproject.toml documentation says that extras not specified are removed by install too; with Poetry 2.5.1 only sync did
it.)
Source: Poetry: Synchronizing dependencies.
Layering optional groups
Because install (unlike sync) never removes groups that are already installed, you can add optional groups in steps, for example
in successive stages of a multi-stage Docker build: poetry install --only main in one stage, poetry install --with docs in the
next.
Source: Poetry: Layering optional groups.
pip and Poetry
| Poetry | pip |
|---|---|
|
Edit |
|
Edit |
|
|
|
Recreate the venv (pip never removes what you did not name) |
|
No equivalent in pip |