2.4 uv: The Blazing-Fast Python Package and Project Manager
While the traditional pip and venv toolchain has served the Python community well, it can be slow, especially when resolving complex dependency graphs or creating new virtual environments. Enter uv, a project and package manager written in Rust and developed by Astral (the creators of Ruff). It is designed as a drop-in replacement for pip, pip-tools, and venv, offering a dramatically faster, more modern experience. Its core value proposition is speed and a unified toolchain, reducing the cognitive overhead of managing multiple CLI utilities.
Installation and Initial Setup
uv can be installed via a standalone installer, which is the recommended method as it provides a single, self-contained binary. This method works on macOS, Linux, and Windows.
# On macOS and Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
# On Windows (PowerShell):
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
Once installed, the uv binary will be placed in ~/.cargo/bin on Unix systems or %USERPROFILE%\.cargo\bin on Windows. Ensure this directory is added to your system’s PATH. You can verify the installation by running uv --version. The reason for this installation method is to provide a versioned, standalone binary that is independent of any existing Python installation, ensuring it can be used to bootstrap Python itself.
Installing and Managing Python Versions
A fundamental feature of uv is its ability to install and manage multiple Python versions directly, without requiring a tool like pyenv. It downloads Python distributions from the official Python Build Standalone releases.
# Install a specific Python version
uv python install 3.11
# Install the latest Python patch version for a minor release
uv python install 3.12
# Set the default Python version for the current directory
uv python pin 3.11.9
These Python installations are stored in a central directory (~/.uv/python on Unix). The uv python pin command creates a .python-version file. When uv encounters this file (or a tool.uv section in pyproject.toml), it will automatically use the specified Python version for all subsequent operations in that directory, ensuring consistent and reproducible environment builds. This is a crucial best practice for any project.
Creating and Activating Virtual Environments
uv can create virtual environments at incredible speed. While it uses its own internal environment management, it creates environments that are fully compatible with the standard venv structure.
# Create a virtual environment in the default location (.venv)
uv venv
# Create a virtual environment with a specific Python version
uv venv --python 3.11
# Create a virtual environment in a custom directory
uv venv ./my-env
To activate the environment, you use the standard shell commands, because uv creates a conventional virtual environment.
# On macOS/Linux:
source .venv/bin/activate
# On Windows (PowerShell):
.venv\Scripts\activate.ps1
The uv philosophy encourages running commands without activation by using uv run to execute commands within the context of the virtual environment. This is often faster and more explicit.
# Run a command without activating the environment
uv run python script.py
uv run pip list # Uses the venv's pip
Managing Dependencies
This is where uv truly shines as a pip replacement. Its dependency resolver is orders of magnitude faster.
# Install a package (like `pip install`)
uv add requests
# Install multiple packages with version specifiers
uv add "flask>=2.3.0" pytest
# Install from a requirements.txt file
uv install -r requirements.txt
# Install the current project in editable mode (like `pip install -e .`)
uv install -e .
The uv add command is particularly powerful; it will automatically add the package to your pyproject.toml (if one exists) and install it. To generate a locked requirements.txt file, you can use uv lock.
# Write the locked dependencies to requirements.txt
uv lock --output-file requirements.txt
The pyproject.toml Workflow
uv is designed for the modern pyproject.toml era. It can read project metadata and dependencies from [project] or [tool.poetry] tables. A best practice is to explicitly define your dependencies in pyproject.toml and use uv to synchronize the virtual environment.
# Example pyproject.toml
[project]
name = "my-project"
version = "0.1.0"
dependencies = [
"requests>=2.28.0",
"flask>=2.3.0",
]
[project.optional-dependencies]
dev = ["pytest", "black"]
You can then install all project dependencies (including optional groups) and the project itself.
# Install the project and its default dependencies
uv install
# Install with an optional dependency group (e.g., dev)
uv install --extra dev
Common Pitfalls and Best Practices
- PATH Precedence: Ensure
uvis available on yourPATH. If you have an existing Python installation, note that runningpipdirectly might use thepipassociated with your system Python, not theuv-managed virtual environment. Prefer usinguv run piporuv add. - Source of Truth: Your
pyproject.toml(orrequirements.in) should be the source of truth for your dependencies, not therequirements.txtfile. Useuv lockto generate a reproducible lock file from this source of truth. - CI/CD Integration:
uvis ideal for CI/CD pipelines. Its speed drastically reduces build times. A common pattern is to useuv pip install -r requirements.txtto leverage its speed with an existing lock file. - Mixing Tools: Avoid mixing
uvand traditionalpip/venvcommands on the same virtual environment. While it often works due to compatibility, it can lead to unexpected behavior. Choose one toolchain per project for consistency. - Python Discovery:
uvwill first look for a pinned version (via.python-versionorpyproject.toml), then in theUV_PYTHONenvironment variable, and finally fall back to thepythonon yourPATH. Understanding this hierarchy helps debug “Python not found” issues.