UniteLabs
How-to

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

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)yesyesno
ENVVAR configurationnoyesno
  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 ↩

Command Comparison

Create a new project

uv init --lib my-app

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

List environments

N/A The default location of the created environment is the .venv directory. To configure an alternative path:

uv venv <path>

Remove environment

N/A. There is no way to programmatically removed an environment. One must manually delete .venv directory.


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

List dependencies

uv pip freeze

Or create a pipdeptree visualization of the project's dependencies:

uv tree

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>

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


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.


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

Run tests

uv run pytest

Last updated