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: "rich (>=15,<16)"

TOML: rich = "^15", rich = { version = "^15", extras = ["jupyter"] }

Read by

Every tool; becomes the published metadata

Poetry only

Caret, tilde

Not allowed

Allowed

source, develop, relative paths, allow-prereleases

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

rich = "^15.0"

>=15.0,<16.0; the left-most non-zero component may not change.

Tilde

rich = "~15.0"

>=15.0,<15.1; with a major version only, ~15 = >=15,<16.

Wildcard

rich = "15.*"

==15.*.

Inequality

rich = ">=13", "!=14.0.0"

As in PEP 440.

Multiple requirements

rich = ">=13,<16"

Comma-separated, all must hold.

Exact

rich = "15.0.0" or "==15.0.0"

A single version.

Compatible release

rich = "~=15.0"

PEP 440 ~=: >=15.0,<16.0 (not the same as ~15.0).

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]
dependencies = [
  "birdtally @ git+https://git.example.org/birdwatch-mono.git@v0.1.0#subdirectory=libs/birdtally",
]
[tool.poetry.dependencies]
birdtally = { git = "https://git.example.org/birdwatch-mono.git", tag = "v0.1.0", subdirectory = "libs/birdtally" }

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", or git = "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.git and poetry config http-basic.birdwatch-git <user> <token>).

  • The Git client. Poetry uses Dulwich, a pure-Python Git implementation, and falls back to the system git when gitcredentials helpers are used. poetry config system-git-client true (renamed from experimental.system-git-client in 2.0) always uses the system client.

  • poetry add "git+http://…#v0.1.0" without the subdirectory part failed here with Source …/.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.

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.

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 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.

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).