2. Use uv for Python Package and Environment Management
Date: 2024-09-01
Status
Accepted
Context
Rhiza needs a reliable, fast, and consistent tool for managing Python environments and dependencies across local development, CI/CD pipelines, and DevContainer setups. The traditional Python toolchain (pip + venv, or pip-tools) presents several challenges:
- Fragmentation: Different tools handle different concerns—
pipfor installation,venvfor environments,pip-toolsfor lock files,pyenvfor Python versions. Each has its own configuration and mental model. - Speed:
pipresolution and installation can be slow, especially in CI where dependencies are installed from scratch on every run. - Reproducibility: Without lock files, builds can diverge. With
requirements.txtlock files, the workflow is verbose and error-prone. - Python version management: Installing the correct Python version requires a
separate tool (
pyenv,asdf, or system packages), adding complexity to onboarding.
As a template system, Rhiza must bootstrap with minimal prerequisites and operate reliably across many different downstream projects and environments. Every extra tool in the prerequisite chain is a potential friction point for users adopting Rhiza.
Decision
We will use uv as the single tool for Python version management, virtual environment creation, and dependency installation.
Key aspects:
- Single binary:
uvis a single self-contained binary written in Rust, installable via a one-linecurlcommand. No Python required to install it. - Python version management:
uvreads.python-versionand automatically downloads and installs the correct Python version when needed. - Virtualenv creation:
uv venvcreates a virtual environment using the managed Python version. - Dependency installation:
uv syncinstalls dependencies frompyproject.tomlusing a lock file (uv.lock) for reproducible builds. - Script execution:
uv run <command>ensures commands run inside the correct environment without requiring manual activation. - Bootstrap location:
uvis installed to./bin/uvwhen not found in PATH, keeping the repository self-contained.
The Makefile bootstrap sequence becomes:
Consequences
Positive
- Single source of truth for Python: The
.python-versionfile controls the Python version;uvreads it automatically with no extra configuration. - Dramatically faster CI:
uvdependency resolution and installation is typically 10–100× faster thanpip, reducing CI run times. - Reproducible builds:
uv.lockcaptures the exact resolved dependency graph, ensuring identical environments across machines and over time. - Simplified onboarding: New contributors run
make installand everything—Python, venv, and dependencies—is set up automatically. - No manual venv activation:
uv runand Makefile targets handle environment activation transparently. - Renovate integration: The
uv-pre-commithook anduv.lockfile integrate with Renovate for automated dependency update PRs.
Neutral
- New tool: Contributors unfamiliar with
uvneed to learn a new command. However, the Makefile abstracts mostuvinvocations, so directuvknowledge is rarely needed. - Lock file:
uv.lockmust be committed and kept up-to-date. This is enforced via a pre-commit hook.
Negative
- Dependency on Astral:
uvis maintained by Astral, a private company. If development stops, we would need to migrate. The tool is open source (MIT licence) which mitigates this risk. - Rust toolchain not required:
uvships as a pre-built binary, so no Rust installation is needed. However, building from source requires Rust.