Skip to content

UV package manager & Ruff Formatter - User Guide

This guide helps you modernize your Python tooling using uv and ruff.

It focuses on common real-world use cases such as migration from legacy setups, handling multiple Python versions, formatting code, and integrating tools into development workflows.

📚 Table of Contents

  • introduction to uv
  • migrating from raw pip & requirements.txt to uv & pyproject.toml.
  • managing multiple python versions (old codebases)
  • introduction to ruff
  • ruff configuration in pyproject.toml
  • useful ruff commands

🔗 References

  • UV official documentation: https://github.com/astral-sh/uv/
  • Ruff official documentation: https://docs.astral.sh/ruff/
  • RealPython article on UV: https://realpython.com/python-uv/
  • RealPython article on Ruff: https://realpython.com/ruff-python/

âš¡ UV: Fast Python Package Manager

Introduction to UV

uv is a blazing-fast Python package manager and virtual environment manager developed by Astral. It replaces tools like pip, virtualenv, pip-tools, and even pyenv, providing:

  • Fast dependency resolution
  • Built-in virtualenv management
  • Support for multiple Python versions
  • npx-like tool execution via uv run

Installation

Install uv using either pip or curl:

$ curl -LsSf https://astral.sh/uv/install.sh | sh
# or
$ pipx install uv
# or
$ pip install uv

To update uv, run uv self upgrade or pipx upgrade uv depending on how you installed it:

$ uv self upgrade
# or
$ pipx upgrade uv
# or
$ pip install --upgrade uv

Quick Start

  1. Create a new project using uv init:
    $ uv init uvtest
    
    The command creates the following directory structure under the uvtest/ folder:
    uvtest/
    │
    ├── .git/
    ├── .gitignore
    ├── .python-version
    ├── README.md
    ├── main.py
    └── pyproject.toml
    

You can explore that it generates a README file, pyproject.toml file, main.py file which contains a simple example, and initializes a git repository with .gitignore file. This is cool!

Let's look at pyproject.toml file:

[project]
name = "uvtest"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []
A single file now manages your project's dependencies and configuration. But where is my virtual environment? Don't hurry, it will be generated once you add a new dependency:
$ uv add requests
This command installs the package into the virtual environment and updates pyproject.toml with the new dependency:
[project]
name = "uvtest"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = ["requests"]
In order to lock your dependencies, run uv lock:
$ uv lock
Now, your pyproject.toml file is ready for deployment. The locked dependencies ensure consistent environments across different users and machines.

Migrating from existing legacy projects

Existing legacy projects use conventional and raw pip with requirements.txt and maybe pip-tools with requirements.in. Anyways, uv is here to help you modernize your Python tooling. You can migrate your projects step-by-step using the following steps:

  1. Open the existing project folder and initialize uv: This command will create the uv project structure for you. It won’t overwrite the main.py file if you have one, but it’ll create the file if it’s missing. It neither modifies your Git repository nor your README.md file. However, this command won’t work if you already have a pyproject.toml file in place. If that’s the case, then you can move the file to another location and run the uv init command. Finally, you can update the new file with any relevant configuration from your old pyproject.toml.
    uv init
    
  2. Export existing dependencies: In order to synchronize the current dependencies with uv, we have to first export them to a requirements.txt file if not already done:
    pip freeze > requirements.txt
    
  3. Add dependencies using uv: The following command adds all dependencies to uv which then automatically updates the pyproject.toml file:
    uv add -r requirements.txt
    
  4. Lock dependencies: The following command locks the dependencies in the pyproject.toml file:
    uv lock
    

That's it! You now have a modern Python tooling setup that is easy to use and maintain.

If you want to remove a dependency, you can use the uv remove command:

uv remove requests
And if you want to update a dependency, you can use the uv update command:
uv update requests

Managing Dev Dependencies In most development environments, you’ll have dependencies that aren’t required for running the code but are vital for developing it. For example, testing libraries like pytest, code formatters like Ruff, and static type checkers like mypy might be some of these development dependencies. The following command adds the development dependency as a separate group:

uv add --dev pytest
If you check the content of pyproject.toml after running the command above, then you’ll find the following:
[project]
name = "uvtest"
version = "0.1.0"
description = "UV Test Project Description"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "requests>=2.32.3",
]

[dependency-groups]
dev = [
    "pytest>=8.3.5",
]

Locking and Syncing We have already seen that the uv lock command locks the dependencies in the pyproject.toml file to a separate file called uv.lock. This file is used by the uv sync command to synchronize the dependencies in the pyproject.toml file with the ones in the uv.lock file. It helps to new developers to have a consistent, synchronized setup.

So, a new developer just does:

uv sync
And then they can start working on the project.

Managing Multiple Python Versions

uv installs Python and allows quickly switching between versions. You can install multiple versions of python using the following command:

$ uv python install 3.10 3.11 3.12

Create a new virtual environment using a specific Python version:

$ uv venv --python 3.10

You can also pin a specific Python version in the current directory:

$ uv python pin 3.10

You can find more here.

Conclusion: Commands we used so far

  • uv init: Initialize a new uv project
  • uv add: Add a dependency to the project
  • uv remove: Remove a dependency from the project
  • uv update: Update a dependency in the project
  • uv lock: Lock the dependencies in the project
  • uv sync: Synchronize the dependencies in the project with the ones in the lock file
  • uv self upgrade: Upgrade uv to the latest version
  • uv python install: Install a specific Python version
  • uv python pin: Pin a specific Python version in the current directory
  • uv venv: Create a new virtual environment
  • uv run: Run a command in a virtual environment

✨ Ruff: The Fast Python Linter & Formatter

Introduction to Ruff

ruff is a fast, Rust-based linter and formatter for Python that replaces tools like: - flake8 - isort - pylint - pycodestyle - black

It supports over 500 rules and is highly configurable.

The recommended way to install ruff and integrate to your project is using uv:

$ uv add --dev ruff
# or, if you want it to install user-wide in your system
$ uv tool install ruff

Using ruff is easy. To check for formatting and linting errors, just run:

$ ruff check
# or if you want to fix fixable errors along the way:
$ ruff check --fix

The check command can generate several errors with vague messages. To get more information, you can find the ruff rule description:

$ ruff rule F821

Continuous Linting. To have continuous linting as you code, open a new terminal window and pass the --watch flag to the check command:

$ ruff check --watch
This will watch for any linting errors as you write your code, in real time!

Formatting Python Code. By default, Ruff has sensible formatting rules and was designed to be a drop-in replacement for Black. The following command automatically formats your code on desired directory (current directory if not specified):

$ ruff format
Just like the check command, the format command takes optional arguments for a path to a single file or directory.

Configuring Ruff

We need a configuration file to manage Ruff's rules. There are 2 options for us: - separate ruff.toml file - ruff config directly in pyproject.toml

Let's consider a sample configuration file ruff.toml:

[tool.ruff]
line-length = 79    # PEP 8 recommended line length
select = ["E", "F", "B", "I"]  # Equivalent to flake8 + isort + bugbear
ignore = ["E203", "E266", "E501", "W503", "F403", "F401"]  # From our old flake8 config
exclude = [
  ".git", ".mypy_cache", ".pytest_cache", ".tox", ".venv", "venv", ".env", "env",
  ".vscode", "static", "media", "templates", "developments", "scripts",
  "requirements", "*/templates/*", "*/migrations/*"
]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"