search iconsearch icon
Type something to search...

Fast Python Package Management and Linting with uv and ruff

Fast Python Package Management and Linting with uv and ruff

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 Domain LogoManaging 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'

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 Domain LogoTrusted 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 Domain LogoOfficial Astral documentation:

4.1. uv benchmarks

At Domain Logouv 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 Domain LogoZulip codebase (~ 250k lines of code).

More info at Domain LogoRuff 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!