Python Environment and Project Management
The python community's offering of tooling for environment and project management provides semi-overlapping coverage of different use-cases and workflows. Choosing the "correct" tool depends strongly on the use-cases and workflows that one relies on for day-to-day development. This article aims to guide users in choosing which tool to use for common development tasks.
Library Summary
uv offers a simplified interface for package and project management with a universal lockfile to ensure reproducible environments. uv while being relatively new to the scene has taken the python community by storm with a value proposition of 10-100x faster dependency resolution than pip. Since then, Astral, the company backing both uv and ruff, has grown the library to cover many of the most common project management use-cases. It should be noted that Astral is a company which intends to keep its tools free and develop paid-for services on top of its open-source tooling.
Find out more in the official uv documentation
hatch is designed for creating and managing multiple environments for different use-cases, allowing for greater flexibility in project management. hatch is designed for extensibility with plugins for customizing behavior. It can be easily configured to use uv for environment creation and dependency resolution, meaning hatch users can enjoy many of uvs convenience features and optimizations while maintaining the higher degree of customization offered by hatch.
Find out more in the official hatch documentation
poetry's offering is almost purely limited to packaging and dependency management and does not contain many of the project management goodies that the other tools explored in this article offer. It is a hold-out of the previous generation of environment management tools, among the likes of pipenv and pyenv. poetry is only PEP-621 compliant for versions later than 2.0.
Find out more in the official poetry documentation
Feature Comparison
| Feature | uv | hatch | poetry |
|---|---|---|---|
| Handles python installation | yes | yes | no |
| Environment scope | one env per project1 | one env per use-case | one env per project |
| Dependency Lock | yes | no2 | yes |
| Auto-updating dependencies | yes -- complete sync must be actively prevented by user | yes -- constraint check updates only when current env violates declared dependencies | no |
| Command-line aliases | no3 | yes | no |
| Inline-metadata scripting | yes -- can add and modify dependency blocks via CLI | yes | no |
| Workspace support (shared environment across multiple projects) | yes | yes | no |
| ENVVAR configuration | no | yes | no |
- For non-workspace usage. ↩
- PEP-751 was accepted on 01.04.2025. Since
hatchis a standards-based library, we can assume that lockfiles will be implemented inhatchin the near future. ↩ - Astral, the company behind
uv, has said that they are planning support for this feature since August 2024 ↩
Command Comparison
Create a new project
uv init --lib my-app
The --lib arg is required for the src/ directory layout.
hatch new my-app
hatch defaults to the src/ directory layout.
poetry new my-app --src
The --src arg is required for the src/ directory layout.
Install environment
If a lock is present:
uv sync --all-extras
Otherwise, to create a lock:
uv venv --python <version>
uv lock
uv sync --all-extras
Actively create a named environment. Environment names are declared in pyproject.toml or hatch.toml file.
hatch env create <env>
Or one can allow hatch to create an environment on-the-fly when calling hatch run.
If a lock is present:
poetry install --all-extras
Otherwise, to create a lock:
poetry lock
poetry install --all-extras
List environments
N/A The default location of the created environment is the .venv directory.
To configure an alternative path:
uv venv <path>
List all environments:
hatch env show -i
-i includes hatch's internal testing and static analysis environments.
The directory where envs are stored can be configured, here it is set to the .venv directory:
hatch config set dirs.env.virtual .venv
By default poetry envs are created in the {cache-dir}/virtualenvs. Check the official documentation for more information about configuring poetry's cache-dir.
Or configure it to use the .venv directory:
poetry config virtualenvs.in-project true
Remove environment
N/A. There is no way to programmatically removed an environment. One must manually delete .venv directory.
Remove an environment named <env>:
hatch env remove <env>
Remove all environments:
hatch env prune
poetry env remove --all
Change a project's python version
Configure the default python version for a project:
uv python pin <version>
Replace the current environment with a new one with a different version of python:
uv venv --python <new_version> --refresh --clear
uv sync --all-extras
N/A. Hatch is designed to allow multiple isolated environments. Simply run commands in the pre-configured environment with the desired version.
For more information about configuring an environment to run on a particular version of python, check out the official documentation.
Changing to a different version of python with poetry requires a global environment manager, e.g. mise.
First remove the current env and run:
mise use python@3.9
poetry env use python3.9
poetry install --all-extras
List dependencies
uv pip freeze
Or create a pipdeptree visualization of the project's dependencies:
uv tree
For a named environment <env>:
hatch run <env>:uv pip freeze
poetry show
Add a dependency
Update pyproject.toml and then either let uv run automatically update the environment before use or actively update the environment by running:
uv sync
Or add a standard dependency with the command-line:
uv add <package>
For local editable dependencies:
uv add --editable /path/to/pkg/
Add <package> to an optional-dependencies group:
uv add --optional <group_name> <package>
Add <package> to a dependency-groups group:
uv add --group <group_name> <package>
Update pyproject.toml and then recreate the affected environment <env>:
hatch env remove <env>
hatch env create <env>
For local editable dependencies
hatch run <env>:uv pip install -e /path/to/pkg
Update pyproject.toml and update the environment:
poetry lock
poetry install --all-extras
Or add a standard dependency with the command-line:
poetry add <package>
For local editable dependencies:
poetry add --editable /path/to/pkg
Add <package> to an optional-dependencies group:
poetry add --optional <group_name> <package>
Add <package> to a dependency-groups group:
poetry add --group <group_name> <package>
Remove a dependency
Update pyproject.toml and then either let uv run automatically update the environment before use or actively update the environment by running:
uv sync
Or remove a standard dependency with the command-line:
uv remove <package>
Remove <package> from an optional-dependencies group:
uv remove <package> --optional <group_name>
Remove <package> from a dependency-groups group:
uv remove --group <group_name> <package>
Update pyproject.toml and then either let hatch run automatically update the environment before use or actively recreate the affected environment <env>:
hatch env remove <env>
hatch env create <env>
Update pyproject.toml and update the environment:
poetry lock
poetry install --all-extras
Or remove a standard dependency with the command-line:
poetry remove <package>
Remove <package> from an optional-dependencies group:
poetry remove --optional <group_name> <package>
Remove <package> from a dependency-groups group:
poetry remove --group <group_name> <package>
Update a dependency
uv automatically updates dependencies changed in the pyproject.toml before running commands unless told otherwise.
It may, however, be desirable to actively update dependencies, e.g. when testing release candidates or pre-releases.
To update an unpinned dependency's locked version:
uv lock --upgrade-package <package>
Or update all dependencies:
uv lock --upgrade
--upgrade and --upgrade-package both imply --refresh which updates cached data.
Update pyproject.toml and then either let hatch run automatically update the environment before use or actively recreate the affected environment.
Or call uv from within the <env> to update, e.g. to set as a local editable installation:
hatch run <env>:uv pip install <options> <package>
Note: uv is a dependency of hatch only when hatch is configured to use uv as its installer, otherwise pip is the underlying tool that one would call to update a dependency from the command-line. For more about enabling uv from hatch see the official documentation
Update pyproject.toml and update the environment:
poetry lock
poetry install --all-extras
Or update the
poetry update <package>
Run a script
Project scripts declared in pyproject.toml:
uv run <cmd> <args>
Using the uv tool or uvx interface:
uv tool run <tool> <cmd>
which is equivalent to:
uvx <tool> <cmd>
Certain tools must not be installed into the local development environment, but are instead managed by uv, e.g. uv tool run ruff --version
To learn more about the uv tool interface check out the official documentation.
hatch run <env>:<cmd> <args>
where <cmd> is a project script, a named CLI alias declared in a hatch.toml or pyproject.toml, or any arbitrary command-line command.
To learn more about defining scripts see the official documentation.
Project scripts declared in pyproject.toml:
poetry run <cmd> <args>
Format files
Assuming the project has ruff as a dependency and ruff is configured via the pyproject.toml or a ruff.toml:
uv run ruff format <path>
Or without the declared dependency, using the uv tool interface:
uv tool run ruff format <path>
Or with uv>=0.8.13 using the experimental format command:
uv format
hatch fmt <path>
Hatch creates a static-analysis environment which uses ruff to lint and format. To learn more about configuring this to use pyproject.toml linting configuration see the official documentation.
Assuming the project has ruff as a dependency and ruff is configured via the pyproject.toml:
poetry run ruff format <path>
Run tests
uv run pytest
hatch test --all
hatch's default testing environment allows one to easily configure parallelization and randomization of tests, as well as complex testing matrixes. To learn more about how to configure this behavior in the pyproject.toml or hatch.toml files see the official documentation.
Assuming the project has pytest as a dependency and pytest is configured via the pyproject.toml:
poetry run pytest
Last updated