Migrating to Poetry 2
|
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. |
Poetry 2.0 (2025-01-05) made the standard [project] table the place for project metadata and changed several commands. This
page migrates a Poetry 1.x project to Poetry 2.5.1, the release this section is written against, with every step run on
2026-10-22 on Linux with CPython 3.14.6: birdtally was first locked with Poetry 1.8.5, then taken over by Poetry 2.5.1.
Scratch paths are shortened to ….
What keeps working
Poetry 2.5.1 still reads the 1.x layout and lock files, so a project can be migrated in steps:
-
poetry installinstalled the project from its Poetry 1.8.5 lock file (lock-version = "2.0") without a complaint. -
poetry checkaccepts the old[tool.poetry]metadata but warns about every field (shown on pyproject.toml & Project Metadata); exit code 0, so only--strictfails on it. -
Builds still work: poetry-core 2.x reads both layouts.
Step 1: relock
The release post explains this change under Locking resulting markers and groups.
poetry lock in 2.x keeps every locked version (the old --no-update behaviour is now the default) and rewrites the file in
format 2.1, which records for each package the groups it belongs to and the markers under which it is needed:
poetry lock
head -1 poetry.lock
Resolving dependencies...
Writing lock file
# This file is automatically @generated by Poetry 2.5.1 and should not be changed by hand.
< # This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
> # This file is automatically @generated by Poetry 2.5.1 and should not be changed by hand.
> groups = ["dev"]
> markers = "sys_platform == \"win32\""
…
> groups = ["main", "dev"]
> markers = {main = "extra == \"pretty\""}
(an excerpt of diff between the two lock files, hashes left out). Every package kept its version; the metadata table now says
lock-version = "2.1". With a 2.1 lock file and installer.re-resolve = false (the default since 2.3), installing no longer
resolves again but evaluates those markers. Commit the relocked file on its own, so that the metadata change is a separate,
reviewable step.
Source: Poetry CLI: lock, installer.re-resolve.
Step 2: move the metadata to [project]
The Poetry 1.x file:
[tool.poetry]
name = "birdtally"
version = "0.1.0"
description = "Tally bird-survey sightings from CSV files."
authors = ["The birdtally authors <birds@example.org>"]
license = "MIT"
readme = "README.md"
homepage = "https://example.org/birdtally"
keywords = ["birds", "survey", "csv"]
packages = [{ include = "birdtally", from = "src" }]
[tool.poetry.dependencies]
python = "^3.10"
rich = { version = "^13", optional = true }
[tool.poetry.extras]
pretty = ["rich"]
[tool.poetry.scripts]
birdtally = "birdtally.cli:main"
[tool.poetry.group.dev.dependencies]
pytest = "^8"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
The same project for Poetry 2:
[project]
name = "birdtally"
version = "0.1.0"
description = "Tally bird-survey sightings from CSV files."
authors = [{ name = "The birdtally authors", email = "birds@example.org" }]
license = "MIT"
license-files = ["LICENSE"]
readme = "README.md"
keywords = ["birds", "survey", "csv"]
requires-python = ">=3.10,<4.0"
dependencies = []
[project.optional-dependencies]
pretty = ["rich (>=13,<14)"]
[project.urls]
homepage = "https://example.org/birdtally"
[project.scripts]
birdtally = "birdtally.cli:main"
[dependency-groups]
dev = ["pytest (>=8,<9)"]
[tool.poetry]
packages = [{ include = "birdtally", from = "src" }]
requires-poetry = ">=2.4.2,<3.0"
[build-system]
requires = ["poetry-core>=2.2.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
Field by field:
| Poetry 1.x | Poetry 2 | Note |
|---|---|---|
|
the same keys in |
With no |
|
|
|
|
|
A PEP 639 SPDX expression; poetry-core 2.2 writes |
|
|
PEP 440 has no caret: |
|
|
Translate every |
other |
|
Poetry-only details ( |
|
|
Scripts of type |
|
|
|
|
|
Optional groups ( |
|
|
poetry-core 1.x cannot read |
Source: Poetry FAQ: Migrating to the project section, Poetry: The project section, Poetry 2.0.0 announcement: Supporting the project section.
Three ways to declare dependencies
[tool.poetry.dependencies] is not deprecated, because it can express things [project] dependencies cannot. The 2.0
announcement and the FAQ describe three options:
-
Standard only: move everything to
[project] dependencies(what birdtally does). Best when no Poetry-specific feature is used. -
Standard plus enrichment: keep the PEP 508 entries in
[project] dependenciesand add Poetry details (asource,develop = true,allow-prereleases) for the same names in[tool.poetry.dependencies]. Poetry may not add dependencies there, only describe them. -
Dynamic: declare
dynamic = ["dependencies"]in[project]and keep using[tool.poetry.dependencies]alone, for projects that rely heavily on Poetry’s syntax.
include now defaults to the sdist
Since 2.0, a [tool.poetry] include entry without format goes into the sdist only (before, files went into both
distributions and directories into the sdist). Files that must also be in the wheel need format = ["sdist", "wheel"]; be
explicit everywhere.
Always keep [build-system]
Without a [build-system] table, Poetry 2.5.1 still builds with poetry-core but warns:
WARNING: No build backend defined. Please define one in the pyproject.toml.
Falling back to using the built-in `poetry-core` version.
In a future release Poetry will fallback to `setuptools` as defined by PEP 517.
More details can be found at https://python-poetry.org/docs/libraries/#packaging
Every release post from 2.1 to 2.5 announces that change, and poetry check does not report the missing table. Since 2.1,
poetry build also honours another back end named there (building in an isolated environment) and accepts --config-settings.
Relock and validate
Relock (the content hash covers the new tables) and validate strictly:
poetry check --strict
poetry lock
poetry check --strict
Error: pyproject.toml changed significantly since poetry.lock was last generated. Run `poetry lock` to fix the lock file.
Resolving dependencies...
Writing lock file
All set!
The relock kept every version, and the wheel built from the new file carries the same requirements:
Metadata-Version: 2.4
Name: birdtally
Version: 0.1.0
Summary: Tally bird-survey sightings from CSV files.
License-Expression: MIT
License-File: LICENSE
Keywords: birds,survey,csv
Author: The birdtally authors
Author-email: birds@example.org
Requires-Python: >=3.10,<4.0
Classifier: Programming Language :: Python :: 3
…
Classifier: Programming Language :: Python :: 3.15
Provides-Extra: pretty
Requires-Dist: rich (>=13,<14) ; extra == "pretty"
Project-URL: Homepage, https://example.org/birdtally
Step 3: commands and options
| Poetry 1.x | Poetry 2.5.1 |
|---|---|
|
Plugin |
|
Plugin |
|
|
|
The default; the option is gone: |
|
|
|
|
|
Now really changes to |
|
The |
After 2.0: the later steps
| Release | What to do |
|---|---|
2.1 |
Pass |
2.2 |
Optionally move groups to PEP 735 |
2.3 |
With PEP 735 groups, require Poetry 2.3 or later ( |
2.4 |
Consider a cooldown with |
2.5 |
Try |
Source: Announcing Poetry 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0.
Step 4: settings
-
virtualenvs.prefer-active-pythonbecame its inverse,virtualenvs.use-poetry-python(defaultfalse: the active Python is used, asprefer-active-python = truedid). -
experimental.system-git-clientbecamesystem-git-client. -
virtualenvs.options.no-setuptoolsis gone; environments never get setuptools by default.
poetry config --migrate (and --migrate --local for poetry.toml) rewrites the renamed ones; see
Configuration. (The 2.0.0 changelog calls the new name
experimental.system-git; the release post and the configuration documentation say system-git-client, which is what 2.5.1
lists.)
Checklist
-
Upgrade Poetry everywhere, to 2.4.2 or later for the security fixes (see Versions & Release History), and pin it in CI.
-
poetry lock, commit. -
Move the metadata to
[project], raisepoetry-corein[build-system], addrequires-poetry. -
poetry lock,poetry check --strict, build, and compare the wheel’sMETADATAwith the last release’s. -
Replace
poetry shell,install --sync,lock --no-update,lock --checkandadd --optionalin scripts, Makefiles, Dockerfiles and CI; add the export plugin wherepoetry exportis used. -
poetry config --migrateon every machine and in CI images.