Featured image of post uv : La meilleure chose qui soit arrivée à Python - et pourquoi vous devriez l'utiliser

uv : La meilleure chose qui soit arrivée à Python - et pourquoi vous devriez l'utiliser

Introduction

Python est sûrement mon langage de programmation préféré. Sa simplicité permet en un rien de temps de développer des scripts, des backends, voire même des sites internet, sans pour autant se faire des nœuds au cerveau.

Mais il y a toujours eu une chose qui me déplaisait chez Python : ses outils de packaging. Pendant longtemps, je me suis tenu à l’écart de tous ces outils comme Poetry, car bien franchement, je n’y comprenais rien !

Et puis, j’ai fait une découverte qui, non seulement m’a montré que packager son code Python est une tâche toute simple, mais qui en plus a complètement changé toutes mes habitudes.

Je veux parler de uv.

Créé par la même équipe talentueuse de chez Astral (qui nous avait déjà régalé avec leur outil ruff), uv est présenté comme un installateur et résolveur de paquets Python “extrêmement rapide”. Cet outil est ce qu’on pourrait appeler un game-changer : une fois que vous y avez goûté, impossible de revenir en arrière !

Alors, qu’est-ce que uv a de si spécial ? Pourquoi cet engouement ? C’est ce que nous allons décortiquer ensemble. Accrochez-vous, vous pourriez bien avoir un nouveau coup de foudre !

Ce guide se base sur mon expérience avec uv et les exemples ont été testés avec la version 0.7.3. Selon votre configuration, notamment derrière certains proxys d’entreprise, vous pourriez avoir besoin d’ajouter l’option --native-tls à certaines commandes uv si vous rencontrez des soucis de connexion SSL.)

uv, c’est quoi au juste ?

En quelques mots, uv est un outil en ligne de commande qui ambitionne de remplacer pip, pip-tools, venv, pyenv, et même une partie de virtualenv et pipx, tout en étant beaucoup, beaucoup plus rapide. Oui, rien que ça !

Comme nombre de nouveaux outils à la mode, il est écrit en Rust. Pardon, je devrais dire, il est écrit en ✨ Rust ✨. Ce qui explique en grande partie ses performances fulgurantes.

Préambule

Avant de nous lancer tête la première dans uv et son fonctionnement, j’aimerais en premier lieu vous indiquer la façon dont uv gère les dépendances, les environnements virtuels, et l’installation des versions de Python. Cela m’a plutôt dérouté la première fois que j’y ai été confronté, et je pense donc qu’il est important de le mentionner avant toute chose.

Gestion des dépendances

uv base toute sa configuration sur votre fichier pyproject.toml. Si vous aviez l’habitude d’utiliser un requirements.txt par exemple, sachez que uv l’ignorera complètement.

Comme je suis sympa, je vous mets ici un gist d’un template de pyproject.toml que j’utilise à chaque fois pour démarrer un nouveau projet Python.

pyproject.toml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
[project]
name = "project-name"
version = "0.1.0"
description = "Project description"
readme = "README.md"
requires-python = ">=3.12"

[[tool.uv.index]]
name = "votre-index"
url = "https://mon-artifactory.corp/api/pypi/mon-repo-virtual"
publish-url = "https://mon-artifactory.corp/api/pypi/mon-repo-local"
default = true

[tool.ruff]
# Exclude common directories that are typically not part of the source code or are generated by tools.
exclude = [
    ".bzr",
    ".cache",
    ".direnv",
    ".eggs",
    ".git",
    ".git-rewrite",
    ".hg",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "venv",
]
 
# Set the maximum line length to 127 characters.
line-length = 127
 
# Define the number of spaces used for indentation, aligning with Black's style.
indent-width = 4
 
# The minimum Python version to target, e.g., when considering automatic code upgrades,
# like rewriting type annotations
target-version = "py312"
 
[tool.ruff.lint]
# Enable Pyflakes (F) and a subset of the pycodestyle (E) codes by default.
# pycodestyle warnings (W)
# Activate Security Rules (S) to replace bandit
# Enable the isort rules (I) to replace isort
# flake8-bugbear (B)
# flake8-simplify (SIM)
select = ["F", "E4", "E7", "E9", "W", "S", "I", "B", "SIM", "PGH004"]
ignore = [] # List any rules to be ignored, currently empty.
 
# Allow auto-fixing of all enabled rules when using the `--fix` option.
fixable = ["ALL"]
unfixable = [] # Specify rules that cannot be auto-fixed, if any.
 
# Define a regex pattern for allowed unused variables (typically underscore-prefixed).
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
 
[tool.ruff.format]
# Enforce double quotes for strings, following Black's style.
quote-style = "double"
 
# Use spaces for indentation, in line with Black's formatting style.
indent-style = "space"
 
# Keep magic trailing commas, a feature of Black's formatting.
skip-magic-trailing-comma = false
 
# Automatically detect and use the appropriate line ending style.
line-ending = "auto"
 
# Update this with your package name or directory to be scanned by pytest-cov
[tool.pytest.ini_options]
addopts = "-s --no-header --cov=src --cov-fail-under=50" # force cmd flags
testpaths = [ # what directories contain tests
    "tests",
]
pythonpath = [ # what to add to the python path
    "."
]

Au lieu d’ajouter vos dépendances à la main, vous pouvez maintenant utiliser uv add <package_name>, et celui-ci sera ajouté à votre pyproject.toml.

Attention cependant, car certains outils externes se basent toujours sur un fichier requirements.txt. Vous aurez donc peut-être besoin de lancer la commande uv pip compile -o requirements.txt dans votre pipeline CI/CD.

Environnement virtuel

Comme moi, vous aviez peut-être l’habitude d’utiliser la commande source .venv/bin/activate pour activer votre environnement virtuel. Avec uv, c’est un peu différent. Votre .venv sera toujours créé, mais en utilisant uv, il se mettra par défaut dans votre environnement virtuel. Prenons un exemple.

Là où à l’époque vous auriez dû faire ce genre de commandes pour lancer votre script.

1
2
3
4
python -m venv .venv
source .venv/bin/activate
pip install requests
python main.py

Désormais, uv enlève la gestion de l’environnement virtuel de votre travail. Ainsi, les commandes ci-dessus seront remplacées par celles-ci.

1
2
uv add requests
uv run main.py

Lorsque vous ajoutez un nouveau paquet à votre projet (avec la commande uv add), uv se charge automatiquement de créer un environnement virtuel s’il n’existe pas déjà.

Ensuite, en lançant votre script via uv run, uv se mettra automatiquement dans votre environnement virtuel.

Installation de Python

Vous utilisiez peut-être pyenv pour installer vos différentes versions de Python. Avec uv, tout cela est de l’histoire ancienne !

Lorsque vous souhaitez lancer un projet avec une version de Python bien spécifique, vous avez plusieurs moyens de faire :

  • Utiliser le fichier .python-version
  • Créer un environnement virtuel : uv venv --python 3.11.6
  • Lancer une version de Python spécifique : uvx python@3.12

Ce que vous devez retenir, c’est que uv ne s’appuie pas sur une version globale de Python installée, mais essaiera toujours d’utiliser la version spécifique à votre projet.


Bon, je crois que vous en savez assez pour commencer votre initiation. Allons-y !

Installation

L’installation de uv est un jeu d’enfant. Vous avez plusieurs options, choisissez celle qui vous convient le mieux (n’hésitez pas à consulter la documentation officielle sur son installation si besoin) :

1
2
3
4
5
6
7
8
# Sur macOS et Linux - je vous recommande d'utiliser cela si possible
curl -LsSf https://astral.sh/uv/install.sh | sh

# Sur Windows (avec PowerShell)
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

# Avec pip (si vous avez déjà un environnement Python)
pip install uv

Une fois installé, vous pouvez le mettre à jour très simplement :

1
uv self update

Et voilà, uv est prêt à l’emploi !

Gérer les versions de Python avec uv

Une des fonctionnalités sympathiques de uv est sa capacité à installer des versions spécifiques de Python. Plus besoin de passer par le site officiel Python, de jongler avec pyenv ou d’autres outils.

Pour installer la dernière version stable de Python :

1
uv python install

Besoin d’une version particulière ? Pas de problème :

1
2
# Installer Python 3.9
uv python install 3.9

Vous pouvez ensuite utiliser cette version pour exécuter un script :

1
uv run --python 3.9 python mon_script.py

Comme je vous l’expliquais un peu plus haut, les versions de Python installées par uv ne sont pas disponibles “globalement” sur votre système via la simple commande python. Pour les utiliser, il faut passer par uv run --python <version>, ou les activer dans un environnement virtuel créé avec cette version spécifique.

uvx pour exécuter des packages à la volée

Vous connaissez peut-être npx dans le monde JavaScript ? uvx (le shortcut de uv tool run) est l’équivalent proposé par uv. Il permet d’exécuter une commande CLI Python (comme ruff, black, ipython, etc.) dans un environnement temporaire avec les dépendances spécifiées, sans polluer votre projet ou votre système.

Par exemple, pour lancer une version spécifique de ruff :

1
uvx --python 3.12 ruff@0.9.6 check main.py

Ou pour lancer ipython avec requests disponible temporairement :

1
uvx --with requests -p 3.13 ipython

Extrêmement pratique pour des one-shots ou pour tester des outils !

La gestion des projets avec uv

Bon, toutes ces commandes, c’est très sympa, mais j’imagine que vous vous demandez comment intégrer uv dans un nouveau projet, ou dans un projet existant.

Laissez-moi donc vous montrer toute la puissance de uv pour créer et gérer un projet Python.

Démarrer un nouveau projet

Pour initialiser un nouveau projet avec uv :

1
uv init

Cette commande va créer quelques fichiers pour vous :

  • .python-version : Spécifie la version de Python à utiliser pour ce projet (par exemple, 3.11).
  • main.py : Un fichier Python d’exemple.
  • pyproject.toml : Le fichier central pour la configuration de votre projet, y compris ses dépendances. C’est le standard moderne en Python.

Maintenant, voyons comment gérer nos dépendances.

Avec uv, le pyproject.toml devient votre source de vérité pour les dépendances.

Pour ajouter une nouvelle dépendance à votre projet :

1
uv add requests

Pour ajouter une dépendance de développement (uniquement pour le dev, comme ruff ou pytest) :

1
2
uv add ruff --dev
uv add pytest --dev

⚠️ Très important : La première fois que vous ajoutez une dépendance, uv va générer un fichier uv.lock. Ce fichier contient les versions exactes de toutes vos dépendances (directes et indirectes) qui ont été résolues. Ce fichier uv.lock est crucial et DOIT être commité dans votre repository GitHub. Il garantit que les versions de vos paquets soient les mêmes pour tout le monde, selon votre environnement de travail.

Une fois vos dépendances (notamment de dev) ajoutées, vous pouvez les utiliser avec uv run :

1
2
uv run ruff format .
uv run pytest

Pour supprimer un paquet :

1
2
3
4
5
# Pour une dépendance normale
uv remove requests

# Pour une dépendance de développement (notez le --group dev)
uv remove ruff --group dev

Dans le cas où vous utiliseriez uv en entreprise avec un index privé, vous pouvez aussi configurer cela !

Par défaut, uv utilise PyPI. Si vous travaillez dans un environnement d’entreprise (avec un Artifactory ou un autre index privé), vous pouvez configurer uv pour l’utiliser via le fichier pyproject.toml.

1
2
3
4
5
[[tool.uv.index]]
name = "votre-index"
url = "https://mon-artifactory.corp/api/pypi/mon-repo-virtual"
publish-url = "https://mon-artifactory.corp/api/pypi/mon-repo-local"
default = true

Ensuite, vous pouvez par exemple exporter des variables d’environnement pour vous authentifier.

1
2
3
4
# Les variables d'environnement doivent respecter le format suivant : UV_INDEX_{name}_USERNAME
# où {name} est le nom de l'index que vous avez défini dans votre fichier pyproject.toml
export UV_INDEX_VOTRE_INDEX_USERNAME="delia, antoine"
export UV_INDEX_VOTRE_INDEX_PASSWORD="cmVmdG**************************FJUjNw"

N’hésitez pas à consulter la documentation uv sur les index pour plus de détails.

Migrer un projet existant vers uv

Vous avez un projet avec un bon vieux requirements.txt ? La migration est assez simple :

  1. Si vous n’avez pas de pyproject.toml, créez-en un (n’hésitez pas à utiliser celui fourni plus haut).
  2. Lancez les commandes suivantes pour ajouter vos requirements dans votre pyproject.toml : uv add -r requirements.txt et uv add --dev -r requirements-dev.txt.
  3. Une fois toutes les dépendances migrées, vous pouvez supprimer vos anciens fichiers requirements.txt.

Si vous utilisiez d’autres outils comme Poetry, il existe des outils de migration (comme par exemple : uvx migrate-to-uv).

Et voilà, votre projet est propulsé par uv !

Rejoindre un projet qui utilise déjà uv

Si vous clonez un projet qui est déjà géré par uv (il devrait donc y avoir un pyproject.toml et un uv.lock), la mise en place est d’une simplicité enfantine. Vous n’avez qu’à lancer :

1
uv sync

Cette commande magique va lire le fichier uv.lock et installer exactement les mêmes versions de tous les paquets qui y sont listés. N’est-ce pas beau tout ça ?

Build et publier votre projet

uv ne s’arrête pas là et propose aussi des commandes pour le build et la publication.

Pour construire votre package :

1
uv build

Pour publier sur PyPI (ou un index privé) :

1
2
3
uv publish
# Pour un index privé, vous pourriez avoir besoin de spécifier l'URL (sauf si déjà spécifiée dans votre pyproject.toml)
# uv publish --repository-url https://mon-artifactory.corp/api/pypi/mon-repo-local

Et pour tester rapidement si votre package fraîchement buildé s’installe et s’importe correctement :

1
2
# Remplacez <MON_PACKAGE> par le nom de votre paquet
uv run --with <MON_PACKAGE>-<VERSION>.whl --no-project --python -c "import <MON_PACKAGE>"

Et voilà ! Vous avez publié votre package en un clin d’oeil !

Nettoyer votre cache

Avec le temps, uv (tout comme pip) accumule un cache de paquets téléchargés. Alors certes, cela permet rapidement d’installer vos dépendances sur plusieurs projets, mais à la longue, cela pourrait prendre pas mal de place sur votre ordinateur. Pour le nettoyer, il suffit de lancer la commande suivante :

1
uv cache clean

Conclusion

Vous l’aurez compris, uv n’est pas juste “encore un autre gestionnaire de paquets”. C’est une véritable bouffée d’air frais dans l’écosystème Python.

  • La vitesse : C’est le premier argument qui frappe lorsqu’on l’utilise. Les installations, les résolutions, tout est incroyablement plus rapide que pip. Sur de gros projets, le gain de temps est phénoménal.
  • L’unification : uv regroupe des fonctionnalités qui nécessitaient auparavant plusieurs outils (pip, venv, pip-tools, voire pyenv pour des besoins basiques). Avoir une seule interface cohérente simplifie grandement le workflow.
  • La conformité : Les développeurs d’uv basent tous leurs choix sur les guidelines Python (ces fameux PEP). Vous pouvez donc être sûr que votre pyproject.toml respecte les standards modernes de packaging Python.
  • La fiabilité : Le système de lockfile (uv.lock) assure des builds reproductibles, un point essentiel pour le travail en équipe et l’intégration continue.

Depuis que j’utilise uv, mes interactions avec la gestion des dépendances Python sont devenues plus rapides, plus simples et plus agréables. C’est le genre d’outil qui, une fois adopté, vous fait vous demander comment vous faisiez avant.

Alors, si vous cherchez à moderniser votre utilisation de Python et à gagner en productivité (et en sérénité !), je ne peux que vous encourager à donner sa chance à uv. L’essayer, c’est très souvent l’adopter !

Références

Bonus : uv cheatsheet

Je vous mets ici une liste de commandes uv ultra pratiques. Une sorte de cheatsheet si vous préférez. N’hésitez pas à revenir la consulter au besoin !

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Update uv
uv self update

# Run a script with a specific Python version
uv run --python 3.12.3 main.py

# Quickly run a tool with a specific version
uvx --python 3.12 ruff@0.9.6 check

# Quickly run ipython with requests installed
uvx --with requests -p 3.13 ipython

# Update your project's version
uv version --bump [major/minor/patch]

# Clean the cache
uv cache clean
Généré avec Hugo
Thème Stack conçu par Jimmy