Dependency Specification
|
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 lists the forms a dependency can take in a Poetry project, each in both places Poetry accepts it: the standard
[project] dependencies (PEP 508 strings) and Poetry’s own [tool.poetry.dependencies] (TOML tables). It was written against
Poetry 2.5.1; the poetry add examples ran on CPython 3.14.6 on 2026-10-22 against PyPI, the local birdwatch pypiserver on
localhost:8080, and the birdwatch-mono Git repository served read-only over HTTP on localhost:8090. Commit ids and hashes
inside quoted TOML strings are shortened with ….
project.dependencies and tool.poetry.dependencies
[project] dependencies |
[tool.poetry.dependencies] |
|
|---|---|---|
Syntax |
PEP 508 strings: |
TOML: |
Read by |
Every tool; becomes the published metadata |
Poetry only |
Caret, tilde |
Not allowed |
Allowed |
|
Not possible |
Allowed |
With both present, [project] dependencies is what goes into the metadata of the built package, and [tool.poetry.dependencies]
only enriches those entries for locking (for example, by pinning a source). If you list dependencies in dynamic, Poetry reads
them entirely from [tool.poetry.dependencies], which makes sense mostly in non-package mode.
[project]
dependencies = ["birdtally[pretty] (>=0.1.0)"]
[tool.poetry.dependencies]
birdtally = { source = "birdwatch" } # enrichment: same package, lock from this source
The Poetry documentation still says that "Only main dependencies can be specified in the project section. Other Dependency
groups must still be specified in the tool.poetry section." That is out of date: since Poetry 2.2 non-main groups can be standard
PEP 735 [dependency-groups]; see Managing Dependencies & Groups.
Version constraints
| Form | [tool.poetry] example |
Meaning |
|---|---|---|
Caret |
|
|
Tilde |
|
|
Wildcard |
|
|
Inequality |
|
As in PEP 440. |
Multiple requirements |
|
Comma-separated, all must hold. |
Exact |
|
A single version. |
Compatible release |
|
PEP 440 |
The full caret and tilde translation table, printed by poetry-core’s own parser, is on
Versions & Dependency Specifiers.
In [project] dependencies only PEP 440 operators are valid; poetry add converts a caret into the equivalent range when it writes
there.
The @ operator on the poetry add command line pins a constraint or asks for the newest version:
poetry add "rich[jupyter]@^15"
poetry add pygments@latest
wrote "rich[jupyter] (>=15,<16)" and "pygments (>=2.21.0,<3.0.0)" into [project] dependencies. @latest moves an existing
dependency to the caret range of the newest release, which poetry update never does.
Git dependencies
[project] |
[tool.poetry] |
|---|---|
|
|
In [tool.poetry], branch = "…", rev = "<commit>" or tag = "…" select the revision; without one, Poetry uses the latest commit
on the default branch. With poetry add, the revision follows a # and the subdirectory is joined with &:
poetry add "git+http://localhost:8090/birdwatch-mono.git#v0.1.0&subdirectory=libs/birdtally"
Creating virtualenv birdtally-report in …/depsdemo-c/.venv
Updating dependencies
Resolving dependencies...
Package operations: 1 install, 0 updates, 0 removals
- Installing birdtally (0.1.0 053e5ca)
Writing lock file
poetry add wrote the standard form, "birdtally @ git+http://localhost:8090/birdwatch-mono.git@v0.1.0#subdirectory=libs/birdtally",
and the lock records the commit the tag pointed to:
[package.source]
type = "git"
url = "http://localhost:8090/birdwatch-mono.git"
reference = "v0.1.0"
resolved_reference = "053e5ca7…f986012e4b8"
subdirectory = "libs/birdtally"
-
SSH:
"pendulum @ git+ssh://git@github.com/sdispater/pendulum.git", orgit = "git@github.com:…"in the table. -
Credentials for HTTPS repositories are configured like those for package sources: register the repository URL and give it credentials (
poetry config repositories.birdwatch-git https://git.example.org/birdwatch-mono.gitandpoetry config http-basic.birdwatch-git <user> <token>). -
The Git client. Poetry uses Dulwich, a pure-Python Git implementation, and falls back to the system
gitwhen gitcredentials helpers are used.poetry config system-git-client true(renamed fromexperimental.system-git-clientin 2.0) always uses the system client. -
poetry add "git+http://…#v0.1.0"without thesubdirectorypart failed here withSource …/.venv/src/birdwatch-mono does not appear to be a Python project: no pyproject.toml or setup.py, because the project is not at the repository root.
Path dependencies
[project] accepts only absolute file:// URLs; relative paths and editable mode need [tool.poetry]:
[tool.poetry.dependencies]
birdtally = { path = "../birdtally", develop = true } # a directory, editable
birdtally-data = { path = "../data/birdtally_data-0.1.0.tar.gz" } # a file
poetry add --editable ../birdtally in a sibling project wrote both halves:
dependencies = ["birdtally @ file:///…/pathdemo/birdtally"]
[tool.poetry.dependencies]
birdtally = {develop = true}
They are not portable. Building that project put the absolute path into the published metadata:
poetry build -f wheel
unzip -p dist/*.whl '*/METADATA' | grep Requires-Dist
- Built birdtally_extras-0.1.0-py3-none-any.whl
Requires-Dist: birdtally @ file://…/pathdemo/birdtally
Nobody else can install that wheel, and PyPI rejects it. Path dependencies are for local development: publish the dependency and replace the path with a version constraint before you distribute.
Source: Poetry: path dependencies.
URL dependencies
poetry add "http://localhost:8080/packages/birdtally-0.1.0-py3-none-any.whl"
writes "birdtally @ http://localhost:8080/packages/birdtally-0.1.0-py3-none-any.whl"; the [tool.poetry] form is
birdtally = { url = "…" }. Like path dependencies, direct URLs are fine in applications and rejected by PyPI in published metadata.
Source: Poetry: url dependencies.
Extras of a dependency
"rich[jupyter] (>=15,<16)", or rich = { version = "^15", extras = ["jupyter"] }. This selects extras of the dependency; your
own project’s extras are [project.optional-dependencies], described on
pyproject.toml & Project Metadata.
Source: Poetry: Dependency extras.
Source dependencies
Pinning a package to a source is possible only in [tool.poetry.dependencies]:
[tool.poetry.dependencies]
birdtally = { source = "birdwatch" }
[[tool.poetry.source]]
name = "birdwatch"
url = "http://localhost:8080/simple/"
priority = "explicit"
poetry add --source birdwatch birdtally writes the same. Sources are Poetry-only: they never reach the built package’s metadata, so
pip installing your published library knows nothing about them. See
Repositories & Package Sources.
Source: Poetry: source dependencies.
Python-restricted dependencies and markers
poetry add tomli --python "<3.11"
poetry add colorama --markers "sys_platform == 'win32'"
wrote:
dependencies = [
"tomli (>=2.5.0,<3.0.0) ; python_version < \"3.11\"",
"colorama (>=0.4.6,<0.5.0) ; sys_platform == \"win32\"",
]
In [tool.poetry.dependencies] the same are tomli = { version = "^2.5", python = "<3.11" } and
colorama = { version = "^0.4.6", markers = "sys_platform == 'win32'" }; platform = "win32" is a shortcut for the sys_platform
marker. The lock keeps every marker, so one lock serves all platforms.
The extra marker and exclusive extras
Poetry sets the extra marker to each extra selected for the root project, so "pathlib2 ; sys_platform == 'win32'" inside an
optional-dependency list is installed only with that extra on Windows. Because every combination of extras must be compatible,
alternative versions of one package (the documentation’s example: a CPU and a CUDA build of PyTorch from two explicit sources) need
mutually exclusive markers such as extra != 'cuda' and extra == 'cuda'. This works fully only with installer.re-resolve =
false, which became the default in Poetry 2.3 (it was introduced in 2.0).
Multiple-constraints dependencies
A dependency can have different constraints for different environments, as a list of tables that differ in a marker or python:
[project]
name = "birdtally-report"
version = "0.1.0"
requires-python = ">=3.10"
dynamic = ["dependencies"]
[tool.poetry]
package-mode = false
[tool.poetry.dependencies]
rich = [
{ version = ">=13,<14", python = "<3.11" },
{ version = "^15", python = ">=3.11" },
]
poetry lock resolves both branches; the lock then contains rich 13.9.4 and rich 15.0.0, each with its marker, and the
environment on Python 3.14 got 15.0.0. In [project] dependencies the same is two strings with markers. The constraints must
differ in their markers or Python range, otherwise resolution fails.
Combining direct origins with sources. A git, url or path entry can also satisfy a requirement without a source on another
platform, even with exclusive markers; to force the fallback to come from an index, give the other entry an explicit source.
The expanded table syntax and pre-releases
Any [tool.poetry] dependency can be written as a sub-table, which keeps long entries readable:
[tool.poetry.dependencies.birdtally]
git = "https://git.example.org/birdwatch-mono.git"
tag = "v0.1.0"
subdirectory = "libs/birdtally"
extras = ["pretty"]
Pre-releases are excluded unless no final release satisfies the constraint, the constraint itself names a pre-release, or you set
allow-prereleases = true on the dependency (poetry add --allow-prereleases).
pip and Poetry
Everything in [project] dependencies is plain PEP 508 and reads the same for pip (see
pip: VCS, Direct URL & Local Project Installs).
What pip has no equivalent for: carets and tildes, per-dependency sources, multiple-constraints tables, editable path dependencies
declared in the project, and allow-prereleases per package (pip 26.0’s --all-releases <name> is the closest).