Fast Python Package Management and Linting with uv and ruff
- 2025-03-14
- 🐍 Python
- Python Tools/Utils Performance Benchmark Setup Best Practices

0. Introduction: Virtual Environment and Linting Tools
When working with Python, managing dependencies and ensuring clean code are essential tasks. Over the years, the Python ecosystem has developed multiple tools for these purposes, but there is no universal agreement on which one to use. Tools like pipenv
, venv
, poetry
, and conda
all offer different ways to manage environments and dependencies, while linters like flake8
, pylint
, and black
aim to enforce style and correctness.
Astral, the creators of ruff
, have introduced a new tool called uv
—a blazing-fast virtual environment and package manager built in Rust. Since both uv
and ruff
share the same goal of significantly improving Python workflows through speed and efficiency, this post will explore both tools and why they might be the best options for modern Python development.
1. Why uv
is a Game-Changer
I previously recommended poetry
for dependency management (see Managing Package Versions with Poetry), but
uv
has since proven to be a superior choice. Unlike poetry
, which is written in Python, uv
is built with Rust, making it significantly faster. It offers a simple interface while maintaining compatibility with standard pip
and venv
workflows.
1.1. Installing uv
To install uv
, simply run:
pip install uv
Alternatively, you can use:
curl -LsSf https://astral.sh/uv/install.sh | sh
Or, if using brew
on macOS:
brew install astral-sh/uv/uv
1.2. Single-Script Execution and Ad-Hoc Dependencies
One of uv
’s standout features is the ability to execute scripts with dependencies without manually managing a virtual environment:
uv run --with rich example.py
You can also add dependencies dynamically to a script:
uv add --script example.py 'requests<3' 'rich'
More info at Declaring script dependencies | Astral uv
This makes uv
an excellent choice for quick experiments or one-off scripts that need isolated dependencies.
1.3. Initializing a Project
To create a new Python project with uv
, run:
uv init
This will prompt you to enter project details like name, version, and dependencies. It will create pyproject.toml
and .python-version
.
1.4. Creating a Virtual Environment
A major feature of uv
is its fast virtual environment management:
uv venv .venv
You can also specify a Python version:
uv venv --python=3.11 .venv
If a .python-version
file exists, uv
will automatically use the specified version.
1.5. Installing Dependencies
Install packages like you would with pip
:
uv pip install requests pandas
To install packages in editable mode (useful for development environments and CI/CD):
uv pip install --editable .
This ensures your local code changes reflect immediately when running scripts.
1.6. Running Commands in an Isolated Environment
Unlike traditional virtual environment tools, uv
lets you run commands directly without needing to activate the environment:
uv run python script.py
uv run pytest
1.7. Handling Editable Installs and Running a Package
One issue many users face with uv
is running a local package. If you are working with a structured project (e.g., using src/
layout), the correct way to install and execute it is:
uv pip install --editable .
uv run python -m src.main
This ensures that the installed package is properly recognized when executing it as a module.
2. ruff
: The Ultra-Fast Linter and Formatter
Linters and formatters are essential for maintaining clean and readable code. Traditionally, flake8
, black
, and pylint
have been widely used, but they are slow due to being written in Python.
Enter ruff
, a Rust-based linter that combines linting, formatting, and import sorting into a single, high-performance tool. It serves as a replacement for flake8
, black
, isort
, and even mypy
-style type checking in some cases.
2.1. Installing ruff
To install ruff
, use:
pip install ruff
Or, for even better performance, use uv
:
uv pip install ruff
2.2. Running ruff
To check your Python files, simply run:
ruff check .
For formatting, use:
ruff format .
This command is a drop-in replacement for black
but executes significantly faster.
3. CI/CD Integration
Here you have some common recipies for integrating uv
and ruff
with CI/CD workflows.
3.1. Running ruff
with pre-commit
For CI/CD workflows, you can use pre-commit
with:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.6
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
3.2. Exporting uv
Dependencies
In some setups, you might need a requirements.txt
. For example, when working with Prefect
it expects that file.
To generate the requirements.txt
file run:
uv export --format requirements-txt > requirements.txt
For CI/CD workflows, you can use pre-commit
to ensure dependencies are always up-to-date:
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.6.3
hooks:
- id: uv-export
args: ["--no-hashes", "--frozen", "--output-file=requirements.txt"]
3.3. Running Tests with uv
To run tests inside the virtual environment:
uv run pytest # Or any other testing suite you use
For GitHub Actions:
unit_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: Install UV
run: pip install uv
- name: Set up virtual environment
run: uv venv .venv
- name: Install dependencies
run: uv pip install --editable .
- name: Run tests
run: uv run pytest .
3.4. Publishing to PyPI with uv
To build and publish a package to PyPI using uv
:
uv build
uv publish
For automation, add this to GitHub Actions:
jobs:
publish_pypi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: Install uv
run: pip install uv
- name: Build
run: uv build
- name: Publish
run: uv publish
I strongly suggest you set up Trusted publishers in pypi to avoid adding tokens.
It’s the prefered way of uploading packages to pypi.
4. Benchmarks: How Fast Are uv
and ruff
?
Rust-based tooling provides substantial speed improvements over Python-based alternatives. The following benchmarks are sourced from the Official Astral documentation:
4.1. uv
benchmarks
At uv benchmarks you can see a very comprehensive speed analysis.
For quick reference here is a summary:
4.2. ruff
benchmarks
Here you can see the time it took to format Zulip codebase (~ 250k lines of code).
More info at Ruff formatter .
5. Conclusion
Astral’s tools, uv
and ruff
, provide a powerful, high-performance alternative to traditional Python package management and linting solutions. By leveraging Rust’s efficiency, they dramatically reduce execution time while maintaining full compatibility with existing workflows.
If you’re looking for a faster, smoother experience in Python development, consider replacing poetry
with uv
and switching from flake8
+ black
to ruff
. The time savings alone make them well worth the transition.
To get started, try installing uv
and ruff
today, and experience the speed difference firsthand!