Basic Usage & Project Setup
|
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 walks through the everyday Poetry cycle: create or adopt a project, add dependencies, install, run, update. It was written
against Poetry 2.5.1 with poetry-core 2.5.0; every command ran on CPython 3.14.6 on 2026-10-22 with
POETRY_VIRTUALENVS_IN_PROJECT=true, so that each project’s venv is the .venv next to its pyproject.toml. Scratch paths are
shortened to …. Installing Poetry itself is on
Installation & Self-Management.
poetry add and poetry remove lock and install for you; poetry lock on its own is needed after you edit pyproject.toml by hand.
Creating a project: poetry new
poetry new --author "The birdtally authors" birdtally
find birdtally | sort
cat birdtally/pyproject.toml
Created package birdtally in birdtally
birdtally
birdtally/README.md
birdtally/pyproject.toml
birdtally/src
birdtally/src/birdtally
birdtally/src/birdtally/__init__.py
birdtally/tests
birdtally/tests/__init__.py
[project]
name = "birdtally"
version = "0.1.0"
description = ""
authors = [
{name = "The birdtally authors"}
]
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
]
[tool.poetry]
packages = [{include = "birdtally", from = "src"}]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
-
Since Poetry 2.1 the src layout is the default;
--srcis now a deprecated no-op and--flatcreatesbirdtally/birdtally/instead (the trade-offs are on Preparing a Library). -
requires-pythondefaults to the minor version of the Python that Poetry finds (here 3.14). For a library, lower it to the oldest Python you support,>=3.10for birdtally. -
Without
--author, the author comes from your Git configuration. -
Other options:
--name(package name if it differs from the directory),--readme md|rst|txt,--description,--python,--dependency,--dev-dependency,-l/--license, and-i/--interactiveto be asked for each value.
poetry new --flat --name flatbirds --readme rst flatbirds
find flatbirds | sort
Created package flatbirds in flatbirds
flatbirds
flatbirds/README.rst
flatbirds/flatbirds
flatbirds/flatbirds/__init__.py
flatbirds/pyproject.toml
flatbirds/tests
flatbirds/tests/__init__.py
Source: Poetry: Project setup, Poetry CLI: new.
Adopting an existing project: poetry init
poetry init writes a pyproject.toml in the current directory, asking questions unless you pass --no-interaction. It accepts the
same metadata options as new plus --dependency and --dev-dependency. A worked migration of a requirements.txt project is on
Choosing & Migrating.
Setting the Python version
requires-python is the range the project supports; it constrains resolution (every dependency must support the whole range) and
becomes Requires-Python in the metadata. Which interpreter the environment uses is a separate choice, made with
poetry env use python3.14 or by running Poetry from an already active venv. If no environment exists yet, Poetry 2.5.1 starts from
the python on your PATH (or from the Python Poetry itself runs on, with virtualenvs.use-poetry-python = true) and, if that
does not satisfy requires-python, tries to find one that does; failing that it asks you to choose one with env use. See Managing Environments.
Source: Poetry: Setting a Python version.
Package mode and non-package mode
| Package mode (default) | Non-package mode (package-mode = false, Poetry 1.8) |
|
|---|---|---|
Use for |
Libraries and anything you build or publish |
Applications, scripts, notebooks, services |
|
Required |
Optional |
|
Also installs the project itself, in editable mode |
Installs only the dependencies (as |
|
Work |
Not possible |
birdtally-report is an application, so its file says:
[project]
name = "birdtally-report"
version = "0.1.0"
description = "Weekly survey report built on birdtally."
requires-python = ">=3.10"
dependencies = ["birdtally[pretty] (>=0.1.0)"]
[tool.poetry]
package-mode = false
[tool.poetry.dependencies]
birdtally = { source = "birdwatch" }
[[tool.poetry.source]]
name = "birdwatch"
url = "http://localhost:8080/simple/"
priority = "explicit"
poetry install
poetry run python report.py
…
Package operations: 5 installs, 0 updates, 0 removals
- Installing birdtally (0.1.0)
- Installing markdown-it-py (4.2.0)
- Installing mdurl (0.1.2)
- Installing pygments (2.21.0)
- Installing rich (15.0.0)
Weekly survey
┏━━━━━━━━━━┳━━━━━━━┓
┃ Species ┃ Count ┃
┡━━━━━━━━━━╇━━━━━━━┩
│ lapwing │ 7 │
│ redshank │ 2 │
└──────────┴───────┘
Source: Poetry: Operating modes.
Adding dependencies
poetry add rich resolves, writes "rich (>=15.0.0,<16.0.0)" into [project] dependencies, updates poetry.lock and installs. Groups,
extras, Git and path dependencies are on
Managing Dependencies & Groups, and the
constraint syntax on Dependency Specification.
In short, [project] dependencies takes PEP 508 strings ("rich (>=13)"), and Poetry’s caret (^13) and tilde (~13.7)
shorthands are allowed only in [tool.poetry] tables and on the poetry add command line.
Using the environment
poetry run
poetry run <command> runs a command inside the project’s environment without activating it:
poetry run birdtally survey.csv --by site
{
"north-marsh": 6,
"east-dunes": 3
}
The errors you meet with it all come from the environment being out of step with pyproject.toml:
-
a command that is not installed in the environment, for example a script added to
[project.scripts]without runningpoetry installagain:Command not found: nosuch -
a script entry that names a module that does not exist (
birdtally-old = "birdtally.commandline:main"):Traceback (most recent call last): File "…/pscr/.venv/bin/birdtally-old", line 3, in <module> from birdtally.commandline import main ModuleNotFoundError: No module named 'birdtally.commandline' -
a script entry with a misspelt function (
birdtally-typo = "birdtally.cli:mian"); with Poetry 2.5.1’s generated launcher this is anImportError, not anAttributeError:Traceback (most recent call last): File "…/pscr/.venv/bin/birdtally-typo", line 3, in <module> from birdtally.cli import mian ImportError: cannot import name 'mian' from 'birdtally.cli' (…/pscr/src/birdtally/cli.py)
Source: Poetry: Using poetry run, Poetry CLI: run.
Activating the environment
poetry shell left the core in Poetry 2.0. poetry env activate prints the command that activates the environment; feed it to your
shell:
poetry env activate
source …/poetry-flavour/birdtally/.venv/bin/activate
eval $(poetry env activate) # bash, zsh, csh
eval (poetry env activate) # fish
Invoke-Expression (poetry env activate)
If you prefer the old sub-shell behaviour, install the poetry-plugin-shell plugin (see
Plugins). When a venv is already active, or you manage one
yourself (conda, a hand-made venv), Poetry uses it and poetry run is not needed.
Installing
Without poetry.lock, poetry install resolves the dependencies, writes the lock and installs. With poetry.lock, it installs
exactly the locked versions and does not resolve; if pyproject.toml changed in a way that makes the lock stale, it stops and asks
you to run poetry lock (see
Lock File: install, update & sync).
In package mode poetry install also installs the project itself, in editable mode. --no-root skips it (dependencies only),
--only-root installs only the project:
poetry install --no-root --only main -E pretty
poetry run python -c "import birdtally"
poetry install --only-root
poetry run python -c "import birdtally; print('ok')"
Updating dependencies
Resolving dependencies...
Package operations: 4 installs, 0 updates, 0 removals
- Installing mdurl (0.1.2)
- Installing markdown-it-py (4.2.0)
- Installing pygments (2.21.0)
- Installing rich (15.0.0)
Writing lock file
…
ModuleNotFoundError: No module named 'birdtally'
Installing dependencies from lock file
Installing the current project: birdtally (0.1.0)
ok
The first call had no lock file, so it resolved and wrote one ("Updating dependencies", "Writing lock file"); the second installed
"from lock file". --no-root is the first step of the Docker pattern on
CI Caching & Containers.
Committing the lock file
Commit poetry.lock for an application: everybody, CI and production then install the same versions. For a library it is optional,
because the lock has no effect on your users; if you commit it, refresh it regularly. The reasoning is on
Lock Files & Reproducible Installs.
Updating
poetry update re-resolves all dependencies to the newest versions the constraints allow and rewrites the lock; poetry update rich
does it for one package (and what it needs). Constraints are not changed: to move beyond <16.0.0, use poetry add rich@latest.
pip install -e . ignores the lock
pip does not read poetry.lock. In a project whose lock pins rich 14.3.3 (locked with a long cooldown to make the point), Poetry
installs that version and an editable pip install of the same directory gets the newest one:
poetry install --only main -E pretty
poetry run python -c "import importlib.metadata as m; print('poetry env:', m.version('rich'))"
python -m venv ../pipvenv && ../pipvenv/bin/python -m pip install -q -e ".[pretty]"
../pipvenv/bin/python -c "import importlib.metadata as m; print('pip -e env:', m.version('rich'))"
- Downgrading rich (15.0.0 -> 14.3.3)
poetry env: 14.3.3
pip -e env: 15.0.0
If someone must use pip, give them an export of the lock (poetry export) and pip install --no-deps -e . after it; see
Exporting Requirements & Bundling.