Choosing & Migrating
|
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. |
pip and Poetry are not two answers to the same question. pip installs distributions into an environment; everything else in a workflow comes from separate tools and standard files. Poetry manages the whole project: metadata, dependency resolution, a lock file, virtual environments, building and publishing. This page compares them and shows how to move a project from one to the other. It was written against pip 26.2.2 and Poetry 2.5.1, and the migration commands were run on CPython 3.14.6 on 2026-10-22.
Two different scopes
pip’s own documentation says it plainly: "pip is not a workflow management tool". Creating environments, running tasks, managing
the Python interpreter and managing "the project" are outside its scope, and there is deliberately no pip build for sdists. pip
gives you installation, a resolver, requirements files, editable installs and, recently, standard lock files and dependency groups.
The Packaging User Guide lists Poetry among the workflow tools, alongside Flit, Hatch, nox, PDM, Pipenv and tox: environment managers that also run tasks, build distributions and upload them, and in some cases lock applications.
Feature by feature
| Concern | pip 26.2.2 (+ build, twine, venv) | Poetry 2.5.1 |
|---|---|---|
Resolver |
Backtracking resolver (since 20.3) for the requirements you give it, against this environment’s Python and platform. |
Its own PubGrub-based solver, which resolves for the whole |
Lock file |
|
|
Environments |
None: create them with |
Creates and selects a venv per project ( |
Dependency groups |
|
PEP 735 groups (2.2) and |
Adding a dependency |
Edit |
|
Building |
|
|
Publishing |
|
|
Plugins |
None. |
Application plugins ( |
Python management |
None. |
|
Speed |
A single install is fast, especially with PEP 658 metadata; there is no lock step. |
|
uv, PDM and Hatch are the other common choices. uv (third party, Astral) is a very fast resolver and installer with a pip-compatible
interface plus its own project workflow and uv.lock; PDM is a standards-first workflow tool that writes PEP 621 metadata and
pylock.toml; Hatch is the PyPA’s workflow tool around the hatchling backend, with environments and versioning. They are listed on
the ecosystem page and are not documented in
this section.
When to pick which
-
pip with standard files suits libraries, small projects, CI images and environments that must not depend on one vendor’s tool. Everything is a standard (PEP 517/518/621/735/751), so any other tool can take over later.
-
Poetry suits applications and teams that want one command set, a cross-platform lock file and managed environments without assembling tools. Since 2.0 it writes standard
[project]metadata, so the cost of leaving it later is small.
Migrating a requirements.txt project to Poetry
The pip flavour of birdtally-report has a hand-written, fully pinned requirements file:
# birdtally-report, pip flavour
birdtally[pretty]>=0.1.0
rich==15.0.0
markdown-it-py==4.2.0
mdurl==0.1.2
Pygments==2.21.0
-
Create
pyproject.tomlwithpoetry init(non-interactively here; without--no-interactionit asks for each value):poetry init --no-interaction --name birdtally-report --author "The birdtally authors" --python ">=3.10" cat pyproject.toml[project] name = "birdtally-report" version = "0.1.0" description = "" authors = [ {name = "The birdtally authors"} ] requires-python = ">=3.10" dependencies = [ ] [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api"Without
--author,poetry inittakes the author from your Git configuration. For an application that is never built, add[tool.poetry] package-mode = false(see Poetry: Basic Usage & Project Setup). -
Add the private source and the dependency that lives on it, with a source constraint:
poetry source add --priority=explicit birdwatch http://localhost:8080/simple/ poetry add --source birdwatch "birdtally[pretty]>=0.1.0" --lock -
Add the rest. The tempting one-liner is
poetry add $(cat requirements.txt):poetry add $(grep -v '^#' requirements.txt | grep -v birdtally) --lockwhich leaves:
dependencies = [ "birdtally[pretty] (>=0.1.0)", "rich (==15.0.0)", "markdown-it-py (==4.2.0)", "mdurl (==0.1.2)", "pygments (==2.21.0)" ]
The caveats are visible in the result:
-
Transitive pins became direct dependencies.
markdown-it-py,mdurlandpygmentsare not used by birdtally-report; they were only in the file because it was a freeze. Add only what the project imports, and letpoetry.lockhold the rest. -
Exact pins became the constraints.
==15.0.0in[project]meanspoetry updatecan never move. Replace them with ranges and let the lock pin versions:poetry add "rich>=15". -
Comments,
-r,-c,--hashand option lines in a requirements file are not requirements, and$(cat …)passes them on as words. Filter them out as above, or feed the file line by line. -
Indexes (
--index-url,--extra-index-urllines) have to be re-created as[[tool.poetry.source]]entries.
Source: Poetry CLI: init, Poetry CLI: add.
Migrating from Poetry to pure standards
Since Poetry 2.0 most of a project is already standard. Leaving Poetry means:
-
Metadata in
[project]. If the project still uses the Poetry 1.x layout (name,version,authorsand dependencies under[tool.poetry]), move them to[project]first. Poetry 2 reads both, and the Poetry FAQ shows the transition with either static dependencies ordynamic = ["dependencies"]to keep Poetry’s richer syntax. Caret and tilde constraints must become PEP 440 ranges; see the table in Versions & Dependency Specifiers. -
Groups in
[dependency-groups].[tool.poetry.group.test.dependencies]becomestest = ["pytest>=8"]. -
A lock other tools can read.
poetry export -f pylock.tomlor-f requirements.txtturnspoetry.lockinto a standard or pip-format file. -
Optionally another backend. poetry-core keeps working with every frontend; switching to hatchling or setuptools needs only a new
[build-system]table and the backend’s own equivalent of[tool.poetry] packages/include.
Keeping a project usable by both tools
The Poetry flavour of birdtally uses [project] metadata, PEP 735 groups and poetry-core as its backend, so a contributor without
Poetry can work on it with pip alone:
python -m venv .venv
.venv/bin/python -m pip install . --group test
.venv/bin/python -m pytest -q
Building wheels for collected packages: birdtally
Building wheel for birdtally (pyproject.toml): started
Building wheel for birdtally (pyproject.toml): finished with status 'done'
Successfully built birdtally
Installing collected packages: pygments, pluggy, packaging, iniconfig, birdtally, pytest
Successfully installed birdtally-0.1.0 iniconfig-2.3.1 packaging-26.3 pluggy-1.7.0 pygments-2.21.0 pytest-9.1.1
…
2 passed in 0.02s
The rules that make this work:
-
keep all metadata and dependencies in
[project](no dynamic dependencies); -
keep development tools in
[dependency-groups], which both pip and Poetry read; -
use only PEP 440 constraints in
[project]; -
if you need a private source, put the source constraint in
[tool.poetry.dependencies]and document the equivalent--index-urlfor pip users.
The pip user installs whatever the resolver picks today, not the versions in poetry.lock; export a lock if they need those.