/commands/poetry/poetry-add-dev-dependency
poetry add --group dev pytest: add a development dependency
- tool
- Poetry
- versions
- Poetry 1.2+, Poetry 2.x
- os
- any
poetry add --group dev pytest adds pytest to a dependency group named dev, so it is installed for development but can be left out of production installs with poetry install --without dev (or --only main). poetry add -D pytest (--dev) is a shortcut for --group dev; before Poetry 1.2 it wrote to [tool.poetry.dev-dependencies]. Add --lock to update pyproject.toml and poetry.lock without installing.
Example
cat > pyproject.toml <<'EOF'
[project]
name = "demo"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = []
[tool.poetry]
package-mode = false
EOF
# keep the virtual environment inside the project folder
uvx -q poetry@2.5.1 config virtualenvs.in-project true --local
uvx -q poetry@2.5.1 add --group dev --lock -q "pytest==8.3.4"
cat pyproject.toml
echo "--- pytest in poetry.lock"
grep -A4 '^name = "pytest"' poetry.lock | grep -E "^(name|version|groups)"Output
[project]
name = "demo"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = []
[tool.poetry]
package-mode = false
[dependency-groups]
dev = [
"pytest (==8.3.4)"
]
--- pytest in poetry.lock
name = "pytest"
version = "8.3.4"Poetry 2.x records the group in the standard [dependency-groups] table (PEP 735). Poetry 1.x writes [tool.poetry.group.dev.dependencies] instead, with the same meaning.
Working with groups
| Command | Effect |
|---|---|
| poetry add --group dev pytest | Add to the dev group (-G dev for short) |
| poetry add --group docs mkdocs | Any group name works |
| poetry install | Install main dependencies and all non-optional groups |
| poetry install --without dev | Skip the dev group (production images) |
| poetry install --only main | Only the main dependencies |
| poetry install --with docs | Also install an optional group |
| poetry remove --group dev pytest | Remove it from the group |
Mark a group as optional (installed only with --with) in pyproject.toml:
[tool.poetry.group.docs]
optional = trueWhat a group is
Groups are labels on dependencies. The packages in [project] dependencies (or [tool.poetry.dependencies]) form the implicit main group, which is what your code needs at run time. Everything else, such as test runners, linters, type checkers and documentation tools, goes into named groups like dev, test, lint or docs. A group is created automatically the first time you add a package to it.
Groups are only for development tooling. Packages that add optional runtime features for your users belong in extras instead (poetry add --optional with an extra name), because groups are not part of the published package metadata.
Pitfalls
- All groups are resolved together. Poetry locks every group, including optional ones you never install, so a dev tool that pins an old version of a library your code also uses makes
poetry lockfail. Loosen the constraint on one side. - The old dev flags are gone.
poetry install --no-devwas replaced by--without devand then removed; Poetry 2 stops with The option "--no-dev" does not exist. - Changing pyproject.toml by hand without locking leaves
poetry.lockstale, and the next install complains that pyproject.toml changed significantly.poetry addupdates both files, which avoids it. --withoutbeats--with. When both name the same group, the group is not installed.
Groups can also include other groups: in the PEP 735 table, dev = [{ include-group = "test" }, { include-group = "lint" }] makes --with dev pull in both.
FAQ
What is the difference between poetry add -D and poetry add --group dev?
None in current Poetry: --dev (-D) is a shortcut for --group dev. Prefer the explicit --group form, which also works for any other group name.
Why does poetry install still install pytest in my Docker image?
poetry install installs every non-optional group by default. Use poetry install --without dev, or --only main, in the production stage of the Dockerfile.
Can a dev dependency conflict with a main dependency?
Yes. Poetry resolves all groups together, even the ones you do not install, so every group must be compatible with the others. A conflict in the dev group blocks the lock just like one in main.