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.lockfile 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.tomlconfiguration.
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 --versionInstallation with pipx
If you prefer using pipx (which also isolates CLI tools):
pipx install poetryUpdating Poetry
poetry self updateTo update to a specific version:
poetry self update 1.8.0Enabling 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.fishCreating 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-projectThis generates the following structure:
my-project/
├── pyproject.toml
├── README.md
├── my_project/
│ └── __init__.py
└── tests/
└── __init__.pyFor a flat source layout (package at the root):
poetry new --src my-projectThis places the package inside a src/ directory:
my-project/
├── pyproject.toml
├── README.md
├── src/
│ └── my_project/
│ └── __init__.py
└── tests/
└── __init__.pyInitializing in an Existing Project
Navigate to your existing project directory and run:
cd existing-project
poetry initPoetry 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-interactionUnderstanding 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:
| Constraint | Meaning | Allows |
|---|---|---|
^2.0 | Compatible release | >=2.0.0, <3.0.0 |
^2.1.3 | Compatible release (patch) | >=2.1.3, <3.0.0 |
~2.1 | Approximately | >=2.1.0, <2.2.0 |
>=2.0,<3.0 | Range | Explicit bounds |
2.1.3 | Exact | Only 2.1.3 |
>=2.0 | Minimum | 2.0 and above |
* | Any | All 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 pandasAdd with a version constraint:
poetry add "pandas>=2.0,<3.0"Add to the development group:
poetry add --group dev pytest black ruffAdd to a custom group:
poetry add --group docs sphinxAdd a package from a Git repository:
poetry add git+https://github.com/user/repo.gitAdd 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.0Add a local path dependency:
poetry add ../my-local-packageAdd extras for a package:
poetry add "uvicorn[standard]"Removing Packages
Remove a package:
poetry remove pandasRemove from a specific group:
poetry remove --group dev blackListing Installed Packages
View all installed packages:
poetry showView details for a specific package:
poetry show pandasShow the dependency tree:
poetry show --treeShow outdated packages:
poetry show --outdatedThe 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 installThis 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 lockVerify lock file integrity:
poetry lock --checkThis 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 trueThis 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 shellThis spawns a new shell with the virtual environment activated. Exit with exit or Ctrl+D.
Environment Information
View environment details:
poetry env infoList all environments associated with the project:
poetry env listUse a specific Python version:
poetry env use python3.11Remove an environment:
poetry env remove python3.11Installing Dependencies
Standard Installation
Install all dependencies (main and development):
poetry installProduction Installation
Install without development dependencies:
poetry install --without devInstall without specific groups:
poetry install --without dev,docsInstalling Only Specific Groups
poetry install --only main
poetry install --only devSyncing the Environment
Remove packages not in the lock file (clean install):
poetry install --syncThis 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 buildThis 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.whlPublishing to PyPI
First, configure your PyPI credentials:
poetry config pypi-token.pypi your-api-tokenThen publish:
poetry publish --buildThe --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 passwordPublish to it:
poetry publish --repository privateVersion 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.1a0Display the current version:
poetry versionMigrating 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-interactionStep 2: Add Dependencies from requirements.txt
For a simple requirements file:
cat requirements.txt | xargs poetry addFor 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 devStep 4: Verify and Lock
poetry install
poetry lock --checkStep 5: Clean Up
After verifying everything works, you can remove the old files:
rm requirements.txt requirements-dev.txt setup.py setup.cfgExporting Back to requirements.txt
If you need a requirements.txt for compatibility (Docker builds, legacy CI systems):
poetry export -f requirements.txt --output requirements.txtInclude development dependencies:
poetry export -f requirements.txt --with dev --output requirements-dev.txtPoetry vs pip vs pipenv vs uv: Comparison
Choosing the right dependency management tool depends on your project requirements. Here is a detailed comparison:
| Feature | Poetry | pip | pipenv | uv |
|---|---|---|---|---|
| Dependency resolution | Advanced SAT solver | Basic (backtracking since 2020) | Moderate | Advanced, Rust-based |
| Lock file | Yes (poetry.lock) | No (manual pip freeze) | Yes (Pipfile.lock) | Yes (uv.lock) |
| Virtual env management | Automatic | Manual (python -m venv) | Automatic | Automatic |
| Build and publish | Built-in | Requires setuptools/twine | No | Built-in |
| Config file | pyproject.toml | requirements.txt | Pipfile | pyproject.toml |
| Speed | Moderate | Fast (no resolution) | Slow | Very fast (Rust) |
| Python version management | No (use pyenv) | No | No | Yes (built-in) |
| Monorepo support | Limited | N/A | No | Yes |
| Script runners | poetry run | No | pipenv run | uv run |
| Maturity | Established (2018) | Standard library adjacent | Established (2017) | Newer (2024) |
| PEP compliance | PEP 518, 621 | N/A | Proprietary format | PEP 518, 621 |
| Community | Large | Massive | Moderate | Growing fast |
| Best for | Full-lifecycle packaging | Simple scripts, legacy | Web applications | Speed-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 --listPoetry 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=falseskips virtual environment creation inside the container (the container itself provides isolation).- Install dependencies before copying source code to leverage Docker layer caching.
- Run
poetry installtwice: 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 ipykernelRegister the Poetry environment as a Jupyter kernel:
poetry run python -m ipykernel install --user --name=my-projectLaunch Jupyter from within the Poetry environment:
poetry run jupyter notebookFor 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
| Command | Description |
|---|---|
poetry new <name> | Create a new project |
poetry init | Initialize in existing directory |
poetry add <pkg> | Add a dependency |
poetry remove <pkg> | Remove a dependency |
poetry install | Install all dependencies |
poetry update | Update dependencies |
poetry lock | Update only the lock file |
poetry show | List installed packages |
poetry show --tree | Show dependency tree |
poetry show --outdated | Show outdated packages |
poetry build | Build the package |
poetry publish | Publish to PyPI |
poetry run <cmd> | Run command in virtual env |
poetry shell | Activate the virtual env shell |
poetry env info | Display environment details |
poetry version <rule> | Bump project version |
poetry export | Export to requirements.txt |
poetry config --list | Show all configuration |
poetry search <pkg> | Search for packages |
poetry check | Validate 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 -vvvVirtual 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 rootThen 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-updateThis 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.