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 requirements.txt)

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