UniteLabs
Blog

Python Environment and Project Management

A tool comparison.

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

hatch is an official Python Packaging Authority (PyPA) project meaning it is always PEP-compliant. 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.

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.

Feature Comparison

Featureuvhatchpoetry
Handles python installationyesyesno
Environment scopeone env per project1one env per use-caseone env per project
Dependency Lockyesno2yes
Auto-updating dependenciesyes -- complete sync must be actively prevented by useryes -- constraint check updates only when current env violates declared dependenciesno
Command-line aliasesno3yesno
Inline-metadata scriptingyes -- can add and modify dependency blocks via CLIyesno
Workspace support (shared environment across multiple projects)yesnono
ENVVAR configurationnoyesno

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

First delete the .venv directory:

uv venv --python <new_version>
uv sync --all-extras

Update a project's python version:

uv venv --refresh --python <new_version>

The env will be recreated on next uv run call.

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

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

N/A uv automatically updates dependencies before running commands unless told otherwise.

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 from the command-line:

poetry update <package>

Run a script

Project scripts declared in pyproject.toml:

uv run <cmd> <args>

uv allows one to use -- to pass arbitrary commands to uv run, e.g.

uv run flask run -p 3000

Using the uv tool interface:

uv tool run <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.

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:

uv run ruff format <path>

Or without the declared dependency, using the uv tool interface:

uv tool run ruff format <path>
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

Footnotes

  1. For non-workspace usage.
  2. PEP-751 was accepted on 01.04.2025. Since hatch is a standards-based library, we can assume that lockfiles will be implemented in hatch in the near future.
  3. Astral, the company behind uv, has said that they are planning support for this feature since August 2024

Copyright © 2025