UV package manager & Ruff Formatter - User Guide
This guide helps you modernize your Python tooling using uv and ruff.
Table of Contents
- introduction to
uv - migrating from raw
pip&requirements.txttouv&pyproject.toml. - managing multiple python versions (old codebases)
- introduction to
ruff - ruff configuration in
pyproject.toml - useful
ruffcommands
UV: Fast Python Package Manager
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 viauv run
Installation
Install uv using either curl or pip:
curl -LsSf https://astral.sh/uv/install.sh | sh # recommended
# 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:
Quick Start
You can create a new project using uv init:
myproject/ folder:
myproject/
│
├── .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 = "myproject"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []
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"]
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:
Migration Steps
- Open the existing project folder and initialize uv:
This command will create the uv project structure for you. It won’t overwrite themain.pyfile if you have one, but it’ll create the file if it’s missing. It neither modifies your Git repository nor yourREADME.mdfile. However, this command won’t work if you already have apyproject.tomlfile in place. If that’s the case, then you can move the file to another location and run theuv initcommand. Finally, you can update the new file with any relevant configuration from your oldpyproject.toml. - Export existing dependencies:
In order to synchronize the current dependencies withuv, we have to first export them to arequirements.txtfile if not already done: - Add dependencies using uv:
The following command adds all dependencies touvwhich then automatically updates thepyproject.tomlfile: This command installs all dependencies listed inrequirements.txt, just like we didpip install -r requirements.txt, but much faster. - Lock dependencies:
The following command locks the dependencies in thepyproject.tomlfile: Locking dependencies ensure that this setup works everywhere, no matter what machine. Because it stores the whole dependency tree with exact versions.
Success
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 update command:
Managing Dev and Prod 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:
If you check the content ofpyproject.toml after running the command above, then you’ll find the following:
[project]
name = "myproject"
version = "0.1.0"
description = "My 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:
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:
Create a new virtual environment using a specific Python version:
You can also pin a specific Python version in the current directory:
You can find more here.
Conclusion: Commands we used so far
uv init: Initialize a new uv projectuv add: Add a dependency to the projectuv remove: Remove a dependency from the projectuv update: Update a dependency in the projectuv lock: Lock the dependencies in the projectuv sync: Synchronize the dependencies in the project with the ones in the lock fileuv self upgrade: Upgrade uv to the latest versionuv python install: Install a specific Python versionuv python pin: Pin a specific Python version in the current directoryuv venv: Create a new virtual environmentuv run: Run a command in a virtual environment
Ruff: The Fast Python Linter & Formatter
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:
Using ruff is easy. To check for formatting and linting errors, just run:
The check command can generate several errors with vague messages. To get more information, you can find the ruff rule description:
Continuous Linting. To have continuous linting as you code, open a new terminal window and pass the --watch flag to the check command:
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):
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"
ruff is essential for standardizing code style and enforcing consistent rules across a project. Whether you use a standalone ruff.toml or integrate it into pyproject.toml, the setup allows you to precisely control linting rules, ignore specific checks, and exclude certain directories. This helps maintain a clean and consistent codebase, improving readability and collaboration.