Skip to content

Python Poetry: Modern Dependency Management and Packaging Guide

Updated on

Managing Python dependencies should not be painful. Yet every Python developer has experienced this scenario: you clone a project, run pip install -r requirements.txt, and watch as dependency conflicts cascade through your terminal. Package A needs version 1.x of a library, package B needs version 2.x, and pip installs whichever it resolves last -- silently breaking one of them. Hours of debugging follow, all because pip lacks a proper dependency resolver and lock file mechanism.

This problem compounds in team environments. One developer pins exact versions, another uses loose ranges, and a third forgets to update requirements.txt after installing a new package. Deployments fail. Tests pass locally but break in CI. The root cause is always the same: Python's default tooling treats dependency management as an afterthought.

Poetry solves this by providing a single tool that handles dependency resolution, virtual environment management, and package publishing. It uses a standardized pyproject.toml file, generates deterministic lock files, and resolves dependency conflicts before installation -- not after. This guide covers everything from installation to publishing, with practical examples for every workflow.

📚

What Is Python Poetry?

Poetry is an open-source dependency management and packaging tool for Python. Created by Sebastien Eustace in 2018, it addresses the fragmented state of Python packaging by combining several functions into one coherent tool:

  • Dependency resolution: Poetry analyzes all dependencies and their sub-dependencies to find compatible versions before installing anything.
  • Lock files: A poetry.lock file records the exact versions of every installed package, guaranteeing reproducible builds across machines.
  • Virtual environment management: Poetry automatically creates and manages isolated virtual environments per project.
  • Package building and publishing: Poetry builds source distributions and wheels, and publishes directly to PyPI or private repositories.
  • Project scaffolding: Poetry generates project structures with proper pyproject.toml configuration.

Poetry uses pyproject.toml as its configuration file, following PEP 518 and PEP 621 standards. This replaces the combination of setup.py, setup.cfg, requirements.txt, and MANIFEST.in that traditional Python projects require.

Installing Poetry

Poetry provides its own installer that isolates it from your project dependencies, preventing version conflicts between Poetry itself and your project packages.

Recommended Installation (Official Installer)

Linux, macOS, and Windows (WSL):

curl -sSL https://install.python-poetry.org | python3 -

Windows (PowerShell):

(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -

After installation, add Poetry to your PATH. The installer prints the exact path -- typically $HOME/.local/bin on Linux/macOS or %APPDATA%\Python\Scripts on Windows.

Verify the installation:

poetry --version

Installation with pipx

If you prefer using pipx (which also isolates CLI tools):

pipx install poetry

Updating Poetry

poetry self update

To update to a specific version:

poetry self update 1.8.0

Enabling Tab Completion

Poetry supports shell completion for Bash, Zsh, and Fish:

# Bash
poetry completions bash >> ~/.bash_completion
 
# Zsh
poetry completions zsh > ~/.zfunc/_poetry
 
# Fish
poetry completions fish > ~/.config/fish/completions/poetry.fish

Creating a New Project

Poetry provides two ways to start a project: creating a new one from scratch or initializing Poetry in an existing project.

New Project from Scratch

poetry new my-project

This generates the following structure:

my-project/
├── pyproject.toml
├── README.md
├── my_project/
│   └── __init__.py
└── tests/
    └── __init__.py

For a flat source layout (package at the root):

poetry new --src my-project

This places the package inside a src/ directory:

my-project/
├── pyproject.toml
├── README.md
├── src/
│   └── my_project/
│       └── __init__.py
└── tests/
    └── __init__.py

Initializing in an Existing Project

Navigate to your existing project directory and run:

cd existing-project
poetry init

Poetry walks you through an interactive setup, asking for package name, version, description, author, Python version compatibility, and dependencies. You can press Enter to accept defaults or skip optional fields.

For a non-interactive setup:

poetry init --name my-package --description "A useful package" --author "Your Name <you@example.com>" --python "^3.9" --no-interaction

Understanding pyproject.toml

The pyproject.toml file is the single source of truth for your project's configuration. Here is a complete example:

[tool.poetry]
name = "my-project"
version = "1.0.0"
description = "A data processing library"
authors = ["Your Name <you@example.com>"]
license = "MIT"
readme = "README.md"
homepage = "https://github.com/yourname/my-project"
repository = "https://github.com/yourname/my-project"
keywords = ["data", "processing", "analytics"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "Topic :: Software Development :: Libraries",
]
 
[tool.poetry.dependencies]
python = "^3.9"
pandas = "^2.0"
requests = "^2.31"
pydantic = ">=2.0,<3.0"
 
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
black = "^24.0"
mypy = "^1.0"
ruff = "^0.3"
 
[tool.poetry.group.docs.dependencies]
sphinx = "^7.0"
sphinx-rtd-theme = "^2.0"
 
[tool.poetry.scripts]
my-cli = "my_project.cli:main"
 
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Version Constraints Syntax

Poetry uses a clear syntax for version constraints:

ConstraintMeaningAllows
^2.0Compatible release>=2.0.0, <3.0.0
^2.1.3Compatible release (patch)>=2.1.3, <3.0.0
~2.1Approximately>=2.1.0, <2.2.0
>=2.0,<3.0RangeExplicit bounds
2.1.3ExactOnly 2.1.3
>=2.0Minimum2.0 and above
*AnyAll versions

The caret (^) constraint is the most common and recommended. It allows updates that do not change the leftmost non-zero digit, following semantic versioning principles.

Dependency Groups

Poetry organizes dependencies into groups, replacing the need for separate requirements files:

  • Main dependencies ([tool.poetry.dependencies]): Required for the package to function. These are included when someone installs your package.
  • Development dependencies ([tool.poetry.group.dev.dependencies]): Tools for development, testing, and linting. Not included in published packages.
  • Custom groups ([tool.poetry.group.docs.dependencies]): Any additional grouping you need, such as documentation tools.

Adding and Removing Dependencies

Adding Packages

Add a package to the main dependencies:

poetry add pandas

Add with a version constraint:

poetry add "pandas>=2.0,<3.0"

Add to the development group:

poetry add --group dev pytest black ruff

Add to a custom group:

poetry add --group docs sphinx

Add a package from a Git repository:

poetry add git+https://github.com/user/repo.git

Add a package from a specific branch or tag:

poetry add git+https://github.com/user/repo.git#branch-name
poetry add git+https://github.com/user/repo.git#v1.0.0

Add a local path dependency:

poetry add ../my-local-package

Add extras for a package:

poetry add "uvicorn[standard]"

Removing Packages

Remove a package:

poetry remove pandas

Remove from a specific group:

poetry remove --group dev black

Listing Installed Packages

View all installed packages:

poetry show

View details for a specific package:

poetry show pandas

Show the dependency tree:

poetry show --tree

Show outdated packages:

poetry show --outdated

The Lock File: poetry.lock

The poetry.lock file is one of Poetry's most important features. When you run poetry install or poetry add, Poetry resolves all dependencies and writes the exact versions (including all transitive dependencies) to poetry.lock.

Why Lock Files Matter

Without a lock file, running pip install -r requirements.txt with loose version constraints can produce different installations on different machines or at different times. A lock file eliminates this randomness.

Consider this scenario:

[tool.poetry.dependencies]
requests = "^2.28"

This allows any version from 2.28.0 to 2.99.x. Without a lock file, your CI server might install 2.31.0 while your local machine uses 2.28.2. The lock file pins the exact version across all environments.

Lock File Workflow

Commit poetry.lock to version control. This guarantees every developer and deployment uses identical package versions.

git add poetry.lock pyproject.toml
git commit -m "Add project dependencies"

Install from the lock file (for reproducible installs):

poetry install

This installs exactly the versions specified in poetry.lock, ignoring any newer compatible versions.

Update the lock file when you want newer versions:

# Update all packages
poetry update
 
# Update a specific package
poetry update pandas
 
# Update the lock file without installing
poetry lock

Verify lock file integrity:

poetry lock --check

This confirms the lock file is consistent with pyproject.toml without modifying anything.

Virtual Environment Management

Poetry automatically creates and manages virtual environments for your projects.

Default Behavior

When you run poetry install or poetry add, Poetry creates a virtual environment if one does not exist. By default, environments are stored in a centralized cache directory:

  • Linux: ~/.cache/pypoetry/virtualenvs/
  • macOS: ~/Library/Caches/pypoetry/virtualenvs/
  • Windows: C:\Users\<user>\AppData\Local\pypoetry\Cache\virtualenvs\

In-Project Virtual Environments

Many developers prefer the virtual environment inside the project directory (similar to Node.js node_modules). Configure this globally:

poetry config virtualenvs.in-project true

This creates a .venv directory in your project root, making it easy to find and compatible with IDE auto-detection.

Running Commands in the Virtual Environment

Run a single command:

poetry run python my_script.py
poetry run pytest
poetry run black .

Activate the environment shell:

poetry shell

This spawns a new shell with the virtual environment activated. Exit with exit or Ctrl+D.

Environment Information

View environment details:

poetry env info

List all environments associated with the project:

poetry env list

Use a specific Python version:

poetry env use python3.11

Remove an environment:

poetry env remove python3.11

Installing Dependencies

Standard Installation

Install all dependencies (main and development):

poetry install

Production Installation

Install without development dependencies:

poetry install --without dev

Install without specific groups:

poetry install --without dev,docs

Installing Only Specific Groups

poetry install --only main
poetry install --only dev

Syncing the Environment

Remove packages not in the lock file (clean install):

poetry install --sync

This removes any packages that were manually installed or are no longer in the lock file, keeping the environment exactly matching the project configuration.

Building and Publishing Packages

Poetry simplifies the entire package publishing workflow.

Building Your Package

poetry build

This creates both a source distribution (.tar.gz) and a wheel (.whl) in the dist/ directory:

dist/
├── my_project-1.0.0.tar.gz
└── my_project-1.0.0-py3-none-any.whl

Publishing to PyPI

First, configure your PyPI credentials:

poetry config pypi-token.pypi your-api-token

Then publish:

poetry publish --build

The --build flag builds and publishes in one step.

Publishing to a Private Repository

Add a private repository:

poetry config repositories.private https://private.pypi.example.com/simple/
poetry config http-basic.private username password

Publish to it:

poetry publish --repository private

Version Management

Bump versions using semantic versioning:

poetry version patch    # 1.0.0 -> 1.0.1
poetry version minor    # 1.0.0 -> 1.1.0
poetry version major    # 1.0.0 -> 2.0.0
poetry version prepatch # 1.0.0 -> 1.0.1a0

Display the current version:

poetry version

Migrating from requirements.txt

If you have an existing project using requirements.txt, migrating to Poetry is straightforward.

Step 1: Initialize Poetry

cd your-project
poetry init --no-interaction

Step 2: Add Dependencies from requirements.txt

For a simple requirements file:

cat requirements.txt | xargs poetry add

For files with version constraints, add packages manually or use a migration script:

import subprocess
 
with open("requirements.txt") as f:
    packages = []
    for line in f:
        line = line.strip()
        if line and not line.startswith("#") and not line.startswith("-"):
            packages.append(line)
 
if packages:
    subprocess.run(["poetry", "add"] + packages)

Step 3: Add Development Dependencies

If you have a requirements-dev.txt:

cat requirements-dev.txt | xargs poetry add --group dev

Step 4: Verify and Lock

poetry install
poetry lock --check

Step 5: Clean Up

After verifying everything works, you can remove the old files:

rm requirements.txt requirements-dev.txt setup.py setup.cfg

Exporting Back to requirements.txt

If you need a requirements.txt for compatibility (Docker builds, legacy CI systems):

poetry export -f requirements.txt --output requirements.txt

Include development dependencies:

poetry export -f requirements.txt --with dev --output requirements-dev.txt

Poetry vs pip vs pipenv vs uv: Comparison

Choosing the right dependency management tool depends on your project requirements. Here is a detailed comparison:

FeaturePoetrypippipenvuv
Dependency resolutionAdvanced SAT solverBasic (backtracking since 2020)ModerateAdvanced, Rust-based
Lock fileYes (poetry.lock)No (manual pip freeze)Yes (Pipfile.lock)Yes (uv.lock)
Virtual env managementAutomaticManual (python -m venv)AutomaticAutomatic
Build and publishBuilt-inRequires setuptools/twineNoBuilt-in
Config filepyproject.tomlrequirements.txtPipfilepyproject.toml
SpeedModerateFast (no resolution)SlowVery fast (Rust)
Python version managementNo (use pyenv)NoNoYes (built-in)
Monorepo supportLimitedN/ANoYes
Script runnerspoetry runNopipenv runuv run
MaturityEstablished (2018)Standard library adjacentEstablished (2017)Newer (2024)
PEP compliancePEP 518, 621N/AProprietary formatPEP 518, 621
CommunityLargeMassiveModerateGrowing fast
Best forFull-lifecycle packagingSimple scripts, legacyWeb applicationsSpeed-critical workflows

When to Choose Each Tool

Choose Poetry when you need a complete packaging solution -- dependency management, building, and publishing to PyPI in a single tool with a mature ecosystem and extensive documentation.

Choose pip for simple scripts, quick prototyping, or when working within constraints that require the standard library tools. Pip remains the foundation that other tools build upon.

Choose pipenv for web applications where you need reproducible environments but do not publish packages to PyPI. Its Pipfile format separates development and production dependencies clearly.

Choose uv when installation speed is a priority, or you want a single tool that also manages Python versions. uv is written in Rust and resolves dependencies significantly faster than Python-based tools. It is pip-compatible and can serve as a drop-in replacement.

Poetry Configuration and Tips

Useful Configuration Options

# Store virtual environments in the project directory
poetry config virtualenvs.in-project true
 
# Use a specific Python version for new environments
poetry config virtualenvs.prefer-active-python true
 
# Disable automatic virtual environment creation
poetry config virtualenvs.create false
 
# View all configuration
poetry config --list

Poetry with Docker

A production Dockerfile using Poetry:

FROM python:3.12-slim
 
ENV POETRY_VERSION=1.8.0 \
    POETRY_HOME="/opt/poetry" \
    POETRY_VIRTUALENVS_CREATE=false \
    POETRY_NO_INTERACTION=1
 
RUN pip install poetry==$POETRY_VERSION
 
WORKDIR /app
 
COPY pyproject.toml poetry.lock ./
RUN poetry install --without dev --no-root
 
COPY . .
RUN poetry install --without dev
 
CMD ["python", "-m", "my_project"]

Key points:

  • POETRY_VIRTUALENVS_CREATE=false skips virtual environment creation inside the container (the container itself provides isolation).
  • Install dependencies before copying source code to leverage Docker layer caching.
  • Run poetry install twice: first without the project (just dependencies), then with the project.

Poetry with CI/CD

GitHub Actions example:

name: CI
 
on: [push, pull_request]
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Install Poetry
        run: pip install poetry
      - name: Install dependencies
        run: poetry install
      - name: Run tests
        run: poetry run pytest
      - name: Run linting
        run: poetry run ruff check .

Poetry in Jupyter Workflows

Data scientists often work in Jupyter notebooks where dependency management is especially important. Poetry integrates well with Jupyter by ensuring that notebooks use the correct environment.

Install Jupyter within your Poetry project:

poetry add --group dev jupyter ipykernel

Register the Poetry environment as a Jupyter kernel:

poetry run python -m ipykernel install --user --name=my-project

Launch Jupyter from within the Poetry environment:

poetry run jupyter notebook

For a streamlined Jupyter experience with automatic dependency management and AI-powered assistance, RunCell (opens in a new tab) provides an integrated environment where you can focus on analysis rather than environment configuration. RunCell handles package installation and kernel management automatically, which complements Poetry's project-level dependency control.

Common Poetry Commands Reference

CommandDescription
poetry new <name>Create a new project
poetry initInitialize in existing directory
poetry add <pkg>Add a dependency
poetry remove <pkg>Remove a dependency
poetry installInstall all dependencies
poetry updateUpdate dependencies
poetry lockUpdate only the lock file
poetry showList installed packages
poetry show --treeShow dependency tree
poetry show --outdatedShow outdated packages
poetry buildBuild the package
poetry publishPublish to PyPI
poetry run <cmd>Run command in virtual env
poetry shellActivate the virtual env shell
poetry env infoDisplay environment details
poetry version <rule>Bump project version
poetry exportExport to requirements.txt
poetry config --listShow all configuration
poetry search <pkg>Search for packages
poetry checkValidate pyproject.toml

Troubleshooting Common Issues

Dependency Resolution Failures

Problem: Poetry cannot find a compatible set of versions.

SolverProblemError: ...unable to find compatible versions...

Solution: Loosen version constraints or check for conflicting requirements:

# Show what conflicts exist
poetry show --tree
 
# Try with a wider constraint
poetry add "problematic-package>=1.0"

Slow Resolution

Problem: poetry lock takes a long time.

Solution: Poetry caches package metadata, but the first resolution for a project with many dependencies can be slow. If resolution exceeds several minutes:

# Clear the cache and retry
poetry cache clear --all pypi
 
# Use verbose output to see what is happening
poetry lock -vvv

Virtual Environment Not Detected by IDE

Problem: VS Code or PyCharm does not find the Poetry environment.

Solution: Configure in-project virtual environments:

poetry config virtualenvs.in-project true
poetry install  # Recreates .venv in project root

Then select .venv/bin/python as the interpreter in your IDE.

Hash Mismatch Errors

Problem: poetry install reports hash mismatches.

Solution: Regenerate the lock file:

poetry lock --no-update

This regenerates hashes without changing dependency versions.

FAQ

What is Python Poetry used for?

Python Poetry is a dependency management and packaging tool that handles installing packages, resolving version conflicts, managing virtual environments, and publishing packages to PyPI. It replaces the combination of pip, venv, setuptools, and twine with a single unified tool centered around the pyproject.toml configuration file.

Is Poetry better than pip?

Poetry and pip serve different purposes. Poetry provides dependency resolution, lock files, virtual environment management, and package publishing in one tool. Pip is simpler and works well for straightforward installations. For projects requiring reproducible builds, team collaboration, or package publishing, Poetry offers significant advantages. For quick scripts or simple projects, pip remains sufficient.

How do I switch from pip to Poetry?

Initialize Poetry in your project with poetry init, then add your dependencies from requirements.txt using cat requirements.txt | xargs poetry add. Poetry creates a pyproject.toml and poetry.lock file. After verifying everything works with poetry install, you can remove the old requirements.txt file. Use poetry export if you still need requirements.txt for compatibility.

Does Poetry replace virtualenv?

Yes. Poetry automatically creates and manages virtual environments for each project. You do not need to manually create or activate environments. Poetry stores environments in a centralized cache by default, or in the project directory if configured with poetry config virtualenvs.in-project true. You can still use poetry shell to activate the environment interactively.

Can Poetry manage multiple Python versions?

Poetry itself does not install Python versions, but it works with whatever Python versions are available on your system. Use pyenv or uv to install multiple Python versions, then tell Poetry which one to use with poetry env use python3.11. Poetry creates separate virtual environments for different Python versions within the same project.

What is the difference between poetry.lock and requirements.txt?

A poetry.lock file records every installed package and its exact version, including all transitive dependencies, along with content hashes for verification. A requirements.txt generated by pip freeze also lists exact versions but lacks hash verification and dependency relationship tracking. The lock file ensures deterministic installations across all environments, while requirements.txt can produce different results when transitive dependencies update.

Conclusion

Python Poetry transforms dependency management from a source of friction into a streamlined workflow. By combining dependency resolution, virtual environment management, and package publishing into a single tool, it eliminates the fragmented toolchain that has long frustrated Python developers. The pyproject.toml file provides a single source of truth, the lock file guarantees reproducible installations, and the dependency resolver catches conflicts before they cause runtime failures.

Whether you are building a library for PyPI, managing a complex application with dozens of dependencies, or collaborating with a team that needs consistent environments, Poetry provides the structure and reliability that pip alone cannot offer. Its learning curve is modest -- most developers become productive within an hour -- and the time saved on dependency debugging pays for itself within the first week.

Start by running poetry new on your next project or poetry init in an existing one. Add your dependencies, commit the lock file, and experience dependency management that works the way it should. For data science workflows in Jupyter, pair Poetry's project-level dependency control with RunCell (opens in a new tab) for an environment that handles both package management and interactive analysis seamlessly.

📚