TL;DR:
uvfrom Astral replaces pip, pip-tools, virtualenv, and pyenv with a single Rust-powered tool that’s 10–100x fasterruffreplaces flake8, black, isort, and several other linters in a single blazing-fast binarypyrightgives you fast, accurate type checking in your editor and CI pipeline without the slowness of mypy
If you’ve been using Python for a while, your mental model of the toolchain probably looks like: pip for packages, pyenv for version management, virtualenv for environments, flake8 and black for linting and formatting, maybe mypy for types. That stack works, but in 2026 there’s a dramatically faster, simpler alternative. Here’s how to switch.
uv: One Tool to Replace Five
uv is a Python package manager and project tool built by Astral (the same team behind Ruff). It’s written in Rust and is fast in a way that changes how you think about package operations. Installing a dependency is nearly instant. Creating a virtual environment takes milliseconds. Syncing a lockfile into a clean environment on CI takes seconds instead of minutes.
Installation:
curl -LsSf https://astral.sh/uv/install.sh | sh
# or via pip if you prefer
pip install uv
Replace pyenv with uv python:
# Install Python versions
uv python install 3.12
uv python install 3.11
# Pin a version for a project
uv python pin 3.12
Create projects:
# Create a new project (creates pyproject.toml, .python-version, a src layout)
uv init my-project
cd my-project
# Or init in an existing directory
uv init
Add and install packages:
# Add a dependency (updates pyproject.toml and uv.lock)
uv add requests
uv add pytest --dev
# Install/sync the environment
uv sync
# Run a command in the project environment without activating it
uv run python my_script.py
uv run pytest
Replace pip-compile with uv lock:
# Generate a lockfile from pyproject.toml
uv lock
# Install from lockfile (reproducible CI installs)
uv sync --frozen
The uv run command is particularly useful: you never have to manually activate a virtual environment. It detects the project’s environment and uses it automatically.
Ruff: One Linter to Rule Them All
Ruff is a Python linter and formatter written in Rust. It implements rules from flake8, isort, pyupgrade, pydocstyle, and several other tools, and it’s fast enough that you stop noticing it. Running on a large codebase in under a second changes how you think about linting — it becomes something you run constantly rather than something you tolerate in CI.
Install and configure:
Add to your pyproject.toml:
[tool.ruff]
target-version = "py312"
line-length = 88
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
]
ignore = ["E501"] # line too long — handled by formatter
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
Run it:
# Lint
uv run ruff check .
# Auto-fix what can be fixed
uv run ruff check --fix .
# Format (replaces black)
uv run ruff format .
# Check formatting without writing (for CI)
uv run ruff format --check .
Ruff’s formatter is intentionally black-compatible, so migrating an existing project that uses black is a drop-in swap. You get the same output, dramatically faster.
Pyright: Fast, Accurate Type Checking
mypy has been the standard Python type checker for years, but it’s slow and sometimes inconsistent. Pyright, Microsoft’s type checker (the same engine that powers Pylance in VS Code), is faster and in practice catches more errors.
Set up in pyproject.toml:
[tool.pyright]
pythonVersion = "3.12"
typeCheckingMode = "standard" # or "strict" if you want to go all in
venvPath = "."
venv = ".venv"
Run from the command line:
uv add pyright --dev
uv run pyright
In VS Code, install the Pylance extension (it uses Pyright under the hood) and you get inline type errors without any additional configuration. In Neovim or other editors, use the pyright language server via your LSP configuration.
Type checking mode: standard checks annotated code and flags common issues. strict enforces that all code is annotated and is significantly more demanding — good for new projects, challenging to retrofit into existing codebases. Start with standard.
Putting It Together in CI
A complete GitHub Actions setup:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Install dependencies
run: uv sync --frozen
- name: Lint
run: uv run ruff check .
- name: Format check
run: uv run ruff format --check .
- name: Type check
run: uv run pyright
- name: Test
run: uv run pytest
The setup-uv action handles caching the uv binary and the package cache. Combined with uv sync --frozen (which installs from the lockfile exactly), you get reproducible, fast CI installs.
What to Keep
You don’t have to migrate everything at once. The minimal starting point:
- Replace
pip installwithuvfor your own development environment — immediate speed improvement, zero risk - Add
ruff checkandruff formatto pre-commit, replacing flake8/black — takes 10 minutes to switch - Add pyright for type checking incrementally — start with
standardmode on a single module
The full migration from pip/pyenv/virtualenv to uv takes longer but pays back in CI time and onboarding friction reduction. New developers cloning your project run one command — uv sync — and they’re ready to go.