Introduction to UV Library¶
UV is a next-generation Python package manager and installer, built in Rust for speed, reliability, and efficiency. Designed as a drop-in replacement for tools like pip, UV dramatically accelerates package installation—often achieving speeds 10 to 100 times faster than traditional solutions.
What sets UV apart is its modern architecture, which tackles long-standing pain points in the Python ecosystem. From sluggish install times and inconsistent dependency resolution to the intricacies of environment management, UV introduces a streamlined approach that improves the developer experience across the board.
More than just a faster installer, UV reimagines package management with robust dependency handling, minimal overhead, and strong reproducibility guarantees. It represents a new wave of tooling built to meet the demands of modern Python development.
A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more.
10-100x faster than pip
Installs and manages Python versions.
Runs and installs tools published as Python packages.
Installable without Rust or Python via curl or pip.
Comparison of Python Package Managers¶
Feature |
UV |
PIP + virtualenv |
Conda |
Poetry |
|---|---|---|---|---|
Implementation |
Rust |
Python |
Python |
Python |
Speed |
10–100x faster than pip |
Baseline |
Slower than pip |
Faster than pip |
Memory Usage |
Very efficient |
Higher |
High |
Moderate |
Environment Management |
Built-in |
Separate tools needed |
Built-in |
Built-in |
Dependency Resolution |
Fast, modern resolver |
Basic |
Comprehensive |
Modern resolver |
Non-Python Packages |
No |
No |
Yes |
No |
Lock Files |
Yes |
No (basic |
Yes |
Yes |
Project Structure |
Yes |
No |
No |
Yes |
Package Publishing |
Yes |
Yes (with twine) |
Yes |
Yes |
Compatibility |
Works with existing pip ecosystem |
Standard Python tool |
Own ecosystem |
More opinionated approach |
Error Handling |
Clear error messages |
Basic |
Good |
Good |
Resource Footprint |
Minimal |
Moderate |
Heavy |
Moderate |
Scientific Computing Focus |
No |
No |
Yes |
No |
Cross-platform Consistency |
Yes |
Limited |
Excellent |
Good |
See UV documentation at uv-doc
Installation and Setup¶
Install uv with the official standalone installer:
!curl -LsSf https://astral.sh/uv/install.sh | sh
downloading uv 0.6.17 x86_64-unknown-linux-gnu
no checksums to verify
installing to /home/gth/.local/bin
uv
uvx
everything's installed!
Or, from The Python Package Index (PyPI):
# With pip
!pip install uv # Make sure you have a virtual environment activated
Afterward, you can verify the installation by running uv version:
! uv version
uv 0.6.17
In some cases on the cluster the uv in Jupyter Notebook environment is not on PATH. To solve this we will create a link to the the uv executable to the a location which is on PATH.
%%bash
mkdir -p ~/bin
ln -f ~/.local/bin/uv ~/bin/uv
Basic Usage¶
Initializing a new project¶
Working on projects is the core part of the UV experience. You start by initializing an empty project using the uv init command:
%%bash
echo "Listing loaded packages ..."
ml list
echo "Unloading all packages ..."
ml purge
echo "Loading python 3.13 package ..."
ml load Python/3.13.1-GCCcore-14.2.0
echo "Python curent version:"
python3 -V
echo "Initialize project explore-uv at ~/explore-uv"
uv init uv-test
cd uv-test
uv run main.py
tree -a
uv add pandas
Listing loaded packages ...
Currently Loaded Modules:
1) XALT/3.0.2 (S)
2) GCCcore/13.2.0
3) zlib/1.2.13-GCCcore-13.2.0
4) binutils/2.40-GCCcore-13.2.0
5) GCC/13.2.0
6) numactl/2.0.16-GCCcore-13.2.0
7) XZ/5.4.4-GCCcore-13.2.0
8) libxml2/2.11.5-GCCcore-13.2.0
9) libpciaccess/0.17-GCCcore-13.2.0
10) hwloc/2.9.2-GCCcore-13.2.0
11) libevent/2.1.12-GCCcore-13.2.0
12) UCX/1.15.0-GCCcore-13.2.0
13) libfabric/1.19.0-GCCcore-13.2.0
14) PMIx/4.2.6-GCCcore-13.2.0
15) UCC/1.2.0-GCCcore-13.2.0
16) OpenMPI/4.1.6-GCC-13.2.0
17) Julia/1.10.0-linux-x86_64
18) bzip2/1.0.8-GCCcore-13.2.0
19) ncurses/6.4-GCCcore-13.2.0
20) libreadline/8.2-GCCcore-13.2.0
21) Tcl/8.6.13-GCCcore-13.2.0
22) SQLite/3.43.1-GCCcore-13.2.0
23) libffi/3.4.4-GCCcore-13.2.0
24) Python/3.11.5-GCCcore-13.2.0
25) libyaml/0.2.5-GCCcore-13.2.0
26) cffi/1.15.1-GCCcore-13.2.0
27) cryptography/41.0.5-GCCcore-13.2.0
28) virtualenv/20.24.6-GCCcore-13.2.0
29) Python-bundle-PyPI/2023.10-GCCcore-13.2.0
30) PyYAML/6.0.1-GCCcore-13.2.0
31) OpenPGM/5.2.122-GCCcore-13.2.0
32) libsodium/1.0.19-GCCcore-13.2.0
33) util-linux/2.39-GCCcore-13.2.0
34) ZeroMQ/4.3.5-GCCcore-13.2.0
35) PyZMQ/25.1.2-GCCcore-13.2.0
36) tornado/6.4-GCCcore-13.2.0
37) BeautifulSoup/4.12.2-GCCcore-13.2.0
38) jupyter-server/2.14.0-GCCcore-13.2.0
39) JupyterLab/4.2.0-GCCcore-13.2.0
40) libxslt/1.1.38-GCCcore-13.2.0
41) lxml/4.9.3-GCCcore-13.2.0
42) jedi/0.19.1-GCCcore-13.2.0
43) IPython/8.17.2-GCCcore-13.2.0
44) JupyterNotebook/7.2.0-GCCcore-13.2.0
Where:
S: Module is Sticky, requires --force to unload or purge
Unloading all packages ...
The following modules were not unloaded:
(Use "module --force purge" to unload all):
1) XALT/3.0.2
Loading python 3.13 package ...
Python curent version:
Python 3.13.1
Initialize project explore-uv at ~/explore-uv
Initialized project `uv-test` at `/home/gth/it4i_course/uv-test`
Using CPython 3.13.2
Creating virtual environment at: .venv
Hello from uv-test!
.
├── main.py
├── pyproject.toml
├── .python-version
├── README.md
├── uv.lock
└── .venv
├── bin
│ ├── activate
│ ├── activate.bat
│ ├── activate.csh
│ ├── activate.fish
│ ├── activate.nu
│ ├── activate.ps1
│ ├── activate_this.py
│ ├── deactivate.bat
│ ├── pydoc.bat
│ ├── python -> /home/gth/.local/share/uv/python/cpython-3.13.2-linux-x86_64-gnu/bin/python3.13
│ ├── python3 -> python
│ └── python3.13 -> python
├── CACHEDIR.TAG
├── .gitignore
├── lib
│ └── python3.13
│ └── site-packages
│ ├── __pycache__
│ │ └── _virtualenv.cpython-313.pyc
│ ├── _virtualenv.pth
│ └── _virtualenv.py
├── lib64 -> lib
└── pyvenv.cfg
7 directories, 23 files
Resolved 7 packages in 104ms
Installed 6 packages in 759ms
+ numpy==2.2.5
+ pandas==2.2.3
+ python-dateutil==2.9.0.post0
+ pytz==2025.2
+ six==1.17.0
+ tzdata==2025.2
The first time you execute the run command, UV creates a new virtual environment in the current working directory. On subsequent runs, UV will reuse the existing virtual environment and only install or update the newly requested packages, ensuring efficient dependency management.
Managing Dependencies Efficiently¶
UV also updates the pyproject.toml and uv.lock files after each add command.
%%bash
echo "Unloading all packages ..."
ml purge
echo "Loading python 3.13 package ..."
ml load Python/3.13.1-GCCcore-14.2.0
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
echo "Python used is: $( which python )"
uv add ruff # Adding initial dependencies to the project
uv pip list -v
cat pyproject.toml
Unloading all packages ...
The following modules were not unloaded:
(Use "module --force purge" to unload all):
1) XALT/3.0.2
Loading python 3.13 package ...
Python used is: /home/gth/it4i_course/uv-test/.venv/bin/python
Resolved 8 packages in 72ms
Installed 1 package in 84ms
+ ruff==0.11.7
DEBUG uv 0.6.17
DEBUG Searching for default Python interpreter in virtual environments or search path
DEBUG Found `cpython-3.13.2-linux-x86_64-gnu` at `/home/gth/it4i_course/uv-test/.venv/bin/python3` (active virtual environment)
DEBUG Using Python 3.13.2 environment at: .venv
Package Version
--------------- -----------
numpy 2.2.5
pandas 2.2.3
python-dateutil 2.9.0.post0
pytz 2025.2
ruff 0.11.7
six 1.17.0
tzdata 2025.2
[project]
name = "uv-test"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"pandas>=2.2.3",
"ruff>=0.11.7",
]
To install or remove a dependency from the environment and the pyproject.toml file:
%%bash
echo "Unloading all packages ..."
ml purge
echo "Loading python 3.13 package ..."
ml load Python/3.13.1-GCCcore-14.2.0
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
echo "the curent installed depandencies are: $(uv pip list -v)"
uv remove ruff # use the `uv remove` command.
cat pyproject.toml
Unloading all packages ...
The following modules were not unloaded:
(Use "module --force purge" to unload all):
1) XALT/3.0.2
Loading python 3.13 package ...
DEBUG uv 0.6.17
DEBUG Searching for default Python interpreter in virtual environments or search path
DEBUG Found `cpython-3.13.2-linux-x86_64-gnu` at `/home/gth/it4i_course/uv-test/.venv/bin/python3` (active virtual environment)
DEBUG Using Python 3.13.2 environment at: .venv
the curent installed depandencies are: Package Version
--------------- -----------
numpy 2.2.5
pandas 2.2.3
python-dateutil 2.9.0.post0
pytz 2025.2
six 1.17.0
tzdata 2025.2
error: The dependency `ruff` could not be found in `project.dependencies`
[project]
name = "uv-test"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"pandas>=2.2.3",
]
Toml files: pyproject.toml¶
TOML, short for Tom’s Obvious, Minimal Language, is a data serialization format created for configuration files. It’s designed to be simple, readable, and easy to understand thanks to its clear and consistent semantics.
TOML serves as an alternative to formats like YAML and JSON. It aims to be more human-friendly than JSON and less complex than YAML. One of TOML’s core design goals is to map cleanly and unambiguously to a hash table, making it straightforward to parse into native data structures across many programming languages.
The pyproject.toml file is a central configuration file in modern Python projects. It standardizes how tools interact with your codebase and allows you to declare metadata, build settings, and tool configurations in one place.
pyproject.toml is a configuration file used by packaging tools and other utilities like linters and type checkers. It can include up to three main TOML tables:
[build-system]– Highly recommended. Specifies the build backend and any dependencies required to build the project.[project]– Defines essential project metadata such as name, version, and dependencies. This format is widely adopted by build backends.[tool]– Contains tool-specific subtables (e.g.,[tool.black],[tool.mypy],[tool.hatch]). Each tool defines its own structure, so refer to their documentation for details.
%%writefile uv-test/pyproject.toml
[project]
name = "explore-uv"
version = "0.1.0"
description = "test toml file"
requires-python = ">=3.13"
dependencies = [
"numpy>=2.2.5",
"httpx>=0.27.2",
]
Overwriting uv-test/pyproject.toml
%%bash
echo "Unloading all packages ..."
ml purge
echo "Loading python 3.13 package ..."
ml load Python/3.13.1-GCCcore-14.2.0
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
echo "the curent installed depandencies are: $(uv pip list -v)"
uv pip install .
cat pyproject.toml
Unloading all packages ...
The following modules were not unloaded:
(Use "module --force purge" to unload all):
1) XALT/3.0.2
Loading python 3.13 package ...
DEBUG uv 0.6.17
DEBUG Searching for default Python interpreter in virtual environments or search path
DEBUG Found `cpython-3.13.2-linux-x86_64-gnu` at `/home/gth/it4i_course/uv-test/.venv/bin/python3` (active virtual environment)
DEBUG Using Python 3.13.2 environment at: .venv
the curent installed depandencies are: Package Version
--------------- -----------
numpy 2.2.5
pandas 2.2.3
python-dateutil 2.9.0.post0
pytz 2025.2
six 1.17.0
tzdata 2025.2
Resolved 9 packages in 92ms
Prepared 1 package in 2.10s
Installed 8 packages in 41ms
+ anyio==4.9.0
+ certifi==2025.4.26
+ explore-uv==0.1.0 (from file:///home/gth/it4i_course/uv-test)
+ h11==0.16.0
+ httpcore==1.0.9
+ httpx==0.28.1
+ idna==3.10
+ sniffio==1.3.1
[project]
name = "explore-uv"
version = "0.1.0"
description = "test toml file"
requires-python = ">=3.13"
dependencies = [
"numpy>=2.2.5",
"httpx>=0.27.2",
]
Running Python scripts with UV¶
%%writefile uv-test/uv_time.py
import datetime
import itertools
def gettime():
"""
A function to return the current time.
Returns
-------
tuple:
A tuple containing the hour, minutes and seconds.
"""
now = datetime.datetime.now()
return now.hour, now.minute, now.second + 1e-6 * now.microsecond
# get the time
hour, minute, seconds = gettime()
print(f"The current time is \033[91m{hour:02}:{minute:02}:{seconds:05.2f}\033[0m")
Writing uv-test/uv_time.py
%%bash
source unload_env.sh # unload envarements and modules
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
echo -e "\n>> Installed packages of venv"
uv pip list -v
uv run uv_time.py
bash: line 1: unload_env.sh: No such file or directory
>> Installed packages of venv
DEBUG uv 0.6.17
DEBUG Searching for default Python interpreter in virtual environments or search path
DEBUG Found `cpython-3.13.2-linux-x86_64-gnu` at `/home/gth/it4i_course/uv-test/.venv/bin/python3` (active virtual environment)
DEBUG Using Python 3.13.2 environment at: .venv
Package Version
--------------- -----------
anyio 4.9.0
certifi 2025.4.26
explore-uv 0.1.0
h11 0.16.0
httpcore 1.0.9
httpx 0.28.1
idna 3.10
numpy 2.2.5
pandas 2.2.3
python-dateutil 2.9.0.post0
pytz 2025.2
six 1.17.0
sniffio 1.3.1
tzdata 2025.2
The current time is 09:38:53.81
uv run is a command provided by uv (a super-fast Python package manager) that allows you to run commands within the virtual environment without explicitly activating it.
It automatically ensures the command runs using the Python interpreter and environment from the virtual environment managed by uv.
This is especially handy in automation, scripting, or just staying out of “activate/deactivate” hassle.
uv lock and uv run both detect the virtual environment automatically if you’re inside a project with a .venv or a pyproject.toml specifying one.
%%bash
cd uv-test # navigate to the uv directory
uv lock --upgrade
Resolved 9 packages in 88ms
This command generates a lockfile uv.lock based on the dependencies specified in pyproject.toml.
The lock file in the context of uv (and other modern Python packaging tools) is a file that freezes the exact versions of all dependencies (and their dependencies) for your project. It makes your builds reproducible.
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
uv sync
Resolved 9 packages in 0.79ms
Uninstalled 6 packages in 755ms
- explore-uv==0.1.0 (from file:///home/gth/it4i_course/uv-test)
- pandas==2.2.3
- python-dateutil==2.9.0.post0
- pytz==2025.2
- six==1.17.0
- tzdata==2025.2
Installs dependencies based on the uv.lock file, ensuring the environment matches the locked versions.
Similar to pip install -r requirements.txt but enforces exact versions from uv.lock.
uv sync is the command that installs dependencies into your virtual environment based on the uv.lock file—not from pyproject.toml.
In plain terms:
pyproject.toml = what you want (e.g., requests = “^2.31”)
uv.lock = what you get (e.g., requests==2.31.0, urllib3==1.26.18, etc.)
uv sync = install exactly what’s in the lock file
uv manages dependencies and environments for single-file scripts.
%%bash
echo 'import requests; print(requests.get("https://astral.sh"))' > example.py #This just creates a tiny Python script named example.py that uses the requests library
uv add --script example.py requests #This tells uv to treat example.py like a mini project.
uv run example.py
Updated `example.py`
Installed 5 packages in 105ms
<Response [200]>
This is a cool and powerful feature of uv: it can manage dependencies and create virtual environments for single-file scripts, without needing a full-blown project structure!
Common Issues and Troubleshooting¶
Download Python versions as needed:
%%bash
cd uv-test # navigate to the uv directory
uv python install 3.10 3.11 3.12
All requested versions already installed
%%bash
cd uv-test # navigate to the uv directory
uv venv --python 3.12.0
Process is interrupted.
Use a specific Python version in the current directory:
%%bash
uv run --python pypy@3.11 -- python #using uv to run a script with a specific interpreter, in this case PyPy 3.8.
Tools¶
Install a tool with uv tool install:
%%bash
cd uv-test # navigate to the uv directory
uv tool install ruff
`ruff` is already installed
uv also supports managing tools—meaning dev tools or CLI tools that aren’t part of your app runtime, like linters, formatters, etc.
Installs ruff in a dedicated tools environment (not your app’s venv).
Makes ruff available via uv tool run:
%%bash
cd uv-test # navigate to the uv directory
uv tool run ruff check main.py
All checks passed!
RUFF¶
Ruff Python linter¶
Linting plays a crucial role in writing clean, readable code that’s easy to share and maintain. A linter, such as Ruff, automatically analyzes your code to identify errors, style issues, and potentially problematic patterns. By catching these issues early, linting helps you enhance code quality before committing or sharing your work.
Ruff is a modern, high-performance linter with a simple interface that makes it easy to use. Designed as a drop-in replacement for tools like Flake8, isort, and Black, Ruff combines speed with broad functionality. It’s rapidly becoming one of the most popular linters in the Python ecosystem.
If your project is already set up with a virtual environment, you can install Ruff using any of the following methods:
%%bash
source unload_env.sh # unload envarements and modules
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
echo -e "\n>> Install ruff"
uv add ruff # add ruff to the virtual environment
echo -e "\n>> the ruff installed version: $(uv pip show ruff -v)"
bash: line 1: unload_env.sh: No such file or directory
>> Install ruff
Resolved 10 packages in 70ms
Installed 1 package in 81ms
+ ruff==0.11.7
DEBUG uv 0.6.17
DEBUG Searching for default Python interpreter in virtual environments or search path
DEBUG Found `cpython-3.13.2-linux-x86_64-gnu` at `/home/gth/it4i_course/uv-test/.venv/bin/python3` (active virtual environment)
DEBUG Using Python 3.13.2 environment at: .venv
>> the ruff installed version: Name: ruff
Version: 0.11.7
Location: /home/gth/it4i_course/uv-test/.venv/lib/python3.13/site-packages
Requires:
Required-by:
When you run Ruff, it analyzes your Python files and reports any problems it finds.
For example, here’s a typical Ruff warning:
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check main.py
All checks passed!
The most basic command the Ruff CLI (command-line interface) has is check. By default, this command will check all files in the current directory. For this example, you can run the check command without any arguments.
$ ruff check .
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check main.py
ruff check . || true
All checks passed!
uv_time.py:3:8: F401 [*] `itertools` imported but unused
|
2 | import datetime
3 | import itertools
| ^^^^^^^^^ F401
4 |
5 | def gettime():
|
= help: Remove unused import: `itertools`
uv_time.py:22:80: E501 Line too long (82 > 79)
|
20 | hour, minute, seconds = gettime()
21 |
22 | print(f"The current time is \033[91m{hour:02}:{minute:02}:{seconds:05.2f}\033[0m")
| ^^^ E501
|
Found 2 errors.
[*] 1 fixable with the `--fix` option.
Success! Ruff found two errors. Not only does it show the file and line numbers of the errors, but it also gives you error codes and messages. In addition, it lets you know that one of the two errors is fixable.
You can tell Ruff to fix errors by applying the –fix flag. Here’s what happens when you follow its suggestion:
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check uv_time.py --fix || true
uv_time.py:21:80: E501 Line too long (82 > 79)
|
19 | hour, minute, seconds = gettime()
20 |
21 | print(f"The current time is \033[91m{hour:02}:{minute:02}:{seconds:05.2f}\033[0m")
| ^^^ E501
|
Found 2 errors (1 fixed, 1 remaining).
The unused import is now fixed, and that line of code has been removed from uv_time.py. The other error isn’t automatically fixable.
Fortunately, Ruff displays the error code and offers a quick way to get more information without digging through the online documentation. Just run the second Ruff command: rule.
By passing the error code to the ruff rule command, you’ll get a detailed explanation of the error, along with an example to help clarify it.
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff rule E501
# line-too-long (E501)
Derived from the **pycodestyle** linter.
## What it does
Checks for lines that exceed the specified maximum character length.
## Why is this bad?
Overlong lines can hurt readability. [PEP 8], for example, recommends
limiting lines to 79 characters. By default, this rule enforces a limit
of 88 characters for compatibility with Black and the Ruff formatter,
though that limit is configurable via the [`line-length`] setting.
In the interest of pragmatism, this rule makes a few exceptions when
determining whether a line is overlong. Namely, it:
1. Ignores lines that consist of a single "word" (i.e., without any
whitespace between its characters).
2. Ignores lines that end with a URL, as long as the URL starts before
the line-length threshold.
3. Ignores line that end with a pragma comment (e.g., `# type: ignore`
or `# noqa`), as long as the pragma comment starts before the
line-length threshold. That is, a line will not be flagged as
overlong if a pragma comment _causes_ it to exceed the line length.
(This behavior aligns with that of the Ruff formatter.)
4. Ignores SPDX license identifiers and copyright notices
(e.g., `# SPDX-License-Identifier: MIT`), which are machine-readable
and should _not_ wrap over multiple lines.
If [`lint.pycodestyle.ignore-overlong-task-comments`] is `true`, this rule will
also ignore comments that start with any of the specified [`lint.task-tags`]
(e.g., `# TODO:`).
## Example
```python
my_function(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
```
Use instead:
```python
my_function(
param1, param2, param3, param4, param5,
param6, param7, param8, param9, param10
)
```
## Error suppression
Hint: when suppressing `E501` errors within multi-line strings (like
docstrings), the `noqa` directive should come at the end of the string
(after the closing triple quote), and will apply to the entire string, like
so:
```python
"""Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.
""" # noqa: E501
```
## Options
- `line-length`
- `lint.task-tags`
- `lint.pycodestyle.ignore-overlong-task-comments`
- `lint.pycodestyle.max-line-length`
[PEP 8]: https://peps.python.org/pep-0008/#maximum-line-length
hitting Ruff’s E501 error, which flags lines longer than 79 characters—this is a PEP 8 recommendation.
%%writefile uv-test/uv_time.py
import datetime
def gettime():
"""
A function to return the current time.
Returns
-------
tuple:
A tuple containing the hour, minutes and seconds.
"""
now = datetime.datetime.now()
return now.hour, now.minute, now.second + 1e-6 * now.microsecond
# get the time
hour, minute, seconds = gettime()
print(
f"The current time is \033[91m{hour:02}:{minute:02}:{seconds:05.2f}\033[0m"
)
Overwriting uv-test/uv_time.py
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check uv_time.py
All checks passed!
Just like the check command, the format command takes optional arguments for a path to a single file or directory.
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff format .
1 file reformatted, 1 file left unchanged
By default, Ruff enables Flake8’s F rules, along with a subset of the E rules, omitting any stylistic rules that overlap with the use of a formatter, like ruff format or Black.
Pyflakes (F)¶
Pyflakes (F) is a fast, lightweight Python tool that checks your code for syntax errors and undefined names. It focuses only on logical issues, not style—so it’s useful for catching real bugs quickly.
Pyflakes (F)¶
For more, see Pyflakes.
Code |
Name |
Message |
|---|---|---|
F401 |
unused-import |
{name} imported but unused; consider using importlib.util.find_spec to test for availability |
F402 |
import-shadowed-by-loop-var |
Import {name} from {row} shadowed by loop variable |
F403 |
undefined-local-with-import-star |
from {name} import * used; unable to detect undefined names |
F404 |
late-future-import |
from future imports must occur at the beginning of the file |
F405 |
undefined-local-with-import-star-usage |
{name} may be undefined, or defined from star imports |
F406 |
undefined-local-with-nested-import-star-usage |
from {name} import * only allowed at module level |
F407 |
future-feature-not-defined |
Future feature {name} is not defined |
F501 |
percent-format-invalid-format |
%-format string has invalid format string: {message} |
F502 |
percent-format-expected-mapping |
%-format string expected mapping but got sequence |
F503 |
percent-format-expected-sequence |
%-format string expected sequence but got mapping |
F504 |
percent-format-extra-named-arguments |
%-format string has unused named argument(s): {message} |
F505 |
percent-format-missing-argument |
%-format string is missing argument(s) for placeholder(s): {message} |
F506 |
percent-format-mixed-positional-and-named |
%-format string has mixed positional and named placeholders |
F507 |
percent-format-positional-count-mismatch |
%-format string has {wanted} placeholder(s) but {got} substitution(s) |
F508 |
percent-format-star-requires-sequence |
%-format string * specifier requires sequence |
F509 |
percent-format-unsupported-format-character |
%-format string has unsupported format character {char} |
F521 |
string-dot-format-invalid-format |
.format call has invalid format string: {message} |
F522 |
string-dot-format-extra-named-arguments |
.format call has unused named argument(s): {message} |
F523 |
string-dot-format-extra-positional-arguments |
.format call has unused arguments at position(s): {message} |
F524 |
string-dot-format-missing-arguments |
.format call is missing argument(s) for placeholder(s): {message} |
F525 |
string-dot-format-mixing-automatic |
.format string mixes automatic and manual numbering |
F541 |
f-string-missing-placeholders |
f-string without any placeholders |
F601 |
multi-value-repeated-key-literal |
Dictionary key literal {name} repeated |
F602 |
multi-value-repeated-key-variable |
Dictionary key {name} repeated |
F621 |
expressions-in-star-assignment |
Too many expressions in star-unpacking assignment |
F622 |
multiple-starred-expressions |
Two starred expressions in assignment |
F631 |
assert-tuple |
Assert test is a non-empty tuple, which is always True |
F632 |
is-literal |
Use == to compare constant literals |
F633 |
invalid-print-syntax |
Use of >> is invalid with print function |
F634 |
if-tuple |
If test is a tuple, which is always True |
F701 |
break-outside-loop |
break outside loop |
F702 |
continue-outside-loop |
continue not properly in loop |
F704 |
yield-outside-function |
{keyword} statement outside of a function |
F706 |
return-outside-function |
return statement outside of a function/method |
F707 |
default-except-not-last |
An except block as not the last exception handler |
F722 |
forward-annotation-syntax-error |
Syntax error in forward annotation: {parse_error} |
F811 |
redefined-while-unused |
Redefinition of unused {name} from {row} |
F821 |
undefined-name |
Undefined name {name}. {tip} |
F822 |
undefined-export |
Undefined name {name} in all |
F823 |
undefined-local |
Local variable {name} referenced before assignment |
F841 |
unused-variable |
Local variable {name} is assigned to but never used |
F842 |
unused-annotation |
Local variable {name} is annotated but never used |
F901 |
raise-not-implemented |
raise NotImplemented should be raise NotImplementedError |
To determine the appropriate settings for each Python file, Ruff looks for the first pyproject.toml, ruff.toml, or .ruff.toml file in the file’s directory or any parent directory.
%%writefile uv-test/ruff.toml
# Set the maximum line length to 79.
line-length = 79
[lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule pycodestyle (E, W)
extend-select = ["E501"]
Writing uv-test/ruff.toml
%%writefile uv-test/even.py
def isEven(number):
#isEven=True
if number % 2==0 :
return True
else:
return False
isEven(2)
Writing uv-test/even.py
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check even.py
All checks passed!
%%writefile uv-test/ruff.toml
# Set the maximum line length to 79.
line-length = 79
[lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule PEP 8 Naming Conventions N
extend-select = ["E501","N"]
Overwriting uv-test/ruff.toml
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check even.py || true
even.py:1:5: N802 Function name `isEven` should be lowercase
|
1 | def isEven(number):
| ^^^^^^ N802
2 | #isEven=True
3 | if number % 2==0 :
|
Found 1 error.
%%writefile uv-test/ruff.toml
# Set the maximum line length to 79.
line-length = 79
[lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule flake8-return (RET)
extend-select = ["E501","N","RET"]
Overwriting uv-test/ruff.toml
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check even.py || true
even.py:1:5: N802 Function name `isEven` should be lowercase
|
1 | def isEven(number):
| ^^^^^^ N802
2 | #isEven=True
3 | if number % 2==0 :
|
even.py:5:5: RET505 [*] Unnecessary `else` after `return` statement
|
3 | if number % 2==0 :
4 | return True
5 | else:
| ^^^^ RET505
6 | return False
7 | isEven(2)
|
= help: Remove unnecessary `else`
Found 2 errors.
[*] 1 fixable with the `--fix` option.
%%writefile uv-test/ruff.toml
# Set the maximum line length to 79.
line-length = 79
[lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule isort (I).
extend-select = ["E501","N","RET", "I"]
Overwriting uv-test/ruff.toml
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
uv add django
Resolved 14 packages in 172ms
Prepared 3 packages in 4.99s
Installed 3 packages in 2.67s
+ asgiref==3.8.1
+ django==5.2
+ sqlparse==0.5.3
%%writefile uv-test/uv_imports.py
from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
import itertools
Writing uv-test/uv_imports.py
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check uv_imports.py || true
uv_imports.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
1 | / from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
2 | | import itertools
| |________________^ I001
|
= help: Organize imports
uv_imports.py:1:25: F401 [*] `django.http.HttpResponse` imported but unused
|
1 | from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
| ^^^^^^^^^^^^ F401
2 | import itertools
|
= help: Remove unused import
uv_imports.py:1:39: F401 [*] `django.http.HttpRequest` imported but unused
|
1 | from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
| ^^^^^^^^^^^ F401
2 | import itertools
|
= help: Remove unused import
uv_imports.py:1:52: F401 [*] `django.http.JsonResponse` imported but unused
|
1 | from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
| ^^^^^^^^^^^^ F401
2 | import itertools
|
= help: Remove unused import
uv_imports.py:1:66: F401 [*] `django.http.HttpResponseRedirect` imported but unused
|
1 | from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
| ^^^^^^^^^^^^^^^^^^^^ F401
2 | import itertools
|
= help: Remove unused import
uv_imports.py:1:80: E501 Line too long (199 > 79)
|
1 | …nseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E501
2 | …
|
uv_imports.py:1:88: F401 [*] `django.http.Http404` imported but unused
|
1 | from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
| ^^^^^^^ F401
2 | import itertools
|
= help: Remove unused import
uv_imports.py:1:97: F401 [*] `django.http.QueryDict` imported but unused
|
1 | from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
| ^^^^^^^^^ F401
2 | import itertools
|
= help: Remove unused import
uv_imports.py:1:108: F401 [*] `django.http.HttpResponseNotFound` imported but unused
|
1 | …onResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpResponseForbidden, HttpResponseBadRequest, HttpRespons…
| ^^^^^^^^^^^^^^^^^^^^ F401
2 | …
|
= help: Remove unused import
uv_imports.py:1:130: F401 [*] `django.http.HttpResponseForbidden` imported but unused
|
1 | …seRedirect, Http404, QueryDict, HttpResponseNotFound, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
| ^^^^^^^^^^^^^^^^^^^^^ F401
2 | …
|
= help: Remove unused import
uv_imports.py:1:153: F401 [*] `django.http.HttpResponseBadRequest` imported but unused
|
1 | …ryDict, HttpResponseNotFound, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
| ^^^^^^^^^^^^^^^^^^^^^^ F401
2 | …
|
= help: Remove unused import
uv_imports.py:1:177: F401 [*] `django.http.HttpResponseServerError` imported but unused
|
1 | …ound, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
| ^^^^^^^^^^^^^^^^^^^^^^^ F401
2 | …
|
= help: Remove unused import
uv_imports.py:2:8: F401 [*] `itertools` imported but unused
|
1 | from django.http import HttpResponse, HttpRequest, JsonResponse, HttpResponseRedirect, Http404, QueryDict, HttpResponseNotFound, HttpRe…
2 | import itertools
| ^^^^^^^^^ F401
|
= help: Remove unused import: `itertools`
Found 13 errors.
[*] 12 fixable with the `--fix` option.
%%writefile uv-test/ruff.toml
# Set the maximum line length to 79.
line-length = 79
[lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule isort (I).
extend-select = ["E501","N","RET", "I"]
ignore= ["F401"]
Overwriting uv-test/ruff.toml
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff format uv_imports.py
1 file reformatted
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check uv_imports.py || true
uv_imports.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
1 | / from django.http import (
2 | | HttpResponse,
3 | | HttpRequest,
4 | | JsonResponse,
5 | | HttpResponseRedirect,
6 | | Http404,
7 | | QueryDict,
8 | | HttpResponseNotFound,
9 | | HttpResponseForbidden,
10 | | HttpResponseBadRequest,
11 | | HttpResponseServerError,
12 | | )
13 | | import itertools
| |________________^ I001
|
= help: Organize imports
Found 1 error.
[*] 1 fixable with the `--fix` option.
%%bash
cd uv-test # navigate to the uv directory
source .venv/bin/activate # activate the virtual environment
ruff check uv_imports.py --fix
Found 1 error (1 fixed, 0 remaining).
exercice¶
Managing Projects with uv and ruff
creat init project with uv
Run ruff check calculate.py
Identify each error type
Fix the errors:
Or remove them if unneeded
%%writefile calc.py
import math
def calculate_circle_area(radius):
return pi * radius ** 2
def calculate_sum(a, b):
result = a + b
return a * b
from os import path
def main():
area = calculate_circle_area(5)
total = calculate_sum(3, 4)
if __name__ == "__main__":
main()
Overwriting calc.py