uv est un gestionnaire de paquets et de projets Python développé par Astral, la société qui édite aussi le linter Ruff. Écrit en Rust, il remplace à lui seul pip, pip-tools, pipx, virtualenv et pyenv, avec des installations plus rapides que pip, selon les benchmarks publiés par Astral.
uv regroupe toute la chaîne (installer pip, créer un virtualenv, gérer les versions de Python avec pyenv, puis jongler entre requirements.txt et poetry) en une seule commande, avec un seul binaire à installer.
Rapide présentation
uv est un gestionnaire de paquets et de projets Python écrit en Rust. Son principal avantage est qu'il gère à la fois :
- l'installation des versions de Python ;
- la création d'environnements virtuels ;
- l'installation des dépendances ;
- la génération de lockfiles reproductibles ;
- et l'exécution d'outils en ligne de commande comme ruff ou black.
Contrairement à pip, qui se contente d'installer des paquets, uv couvre tout le cycle de vie d'un projet Python. C'est pour cette raison qu'on le compare à Cargo, le gestionnaire de projets du langage Rust.
uv regroupe plusieurs outils que les développeurs Python utilisaient jusque-là séparément.
pip: installation de paquets Python ;pip-tools: compilation de fichiers de dépendances verrouillées ;virtualenv: création d'environnements virtuels isolés ;pyenv: installation et gestion de versions Python ;pipx: exécution d'outils en ligne de commande dans des environnements isolés.
Avec ce gestionnaire de paquets, ces cinq besoins passent par un seul binaire, sans dépendance à une installation Python préexistante.
uv est distribué sous double licence MIT et Apache 2.0. Il est gratuit, sans télémétrie et sans offre payante.
Pourquoi uv est-il aussi rapide ?
© Astral
La rapidité de uv vient de trois choix techniques.
D'abord, uv est écrit en Rust, un langage compilé qui évite les coûts d'exécution d'un interpréteur comme Python. Ensuite, il télécharge les paquets en parallèle plutôt que les uns après les autres. Enfin, il s'appuie sur un cache global qui stocke les paquets déjà téléchargés et utilise des liens physiques (hard links) pour les réutiliser instantanément dans n'importe quel projet, sans les recopier.
Sur une résolution de dépendances à froid, uv met environ 1,2 seconde, contre 45,5 secondes pour pip et 38,2 secondes pour Poetry, sur la même machine. Sur un cache déjà rempli, l'écart se creuse encore plus. Ces chiffres varient selon le projet et la machine utilisée, mais l'ordre de grandeur reste constant. uv reste nettement plus rapide dans la quasi-totalité des cas testés.
Installer uv
L'installation se fait en une seule commande :
Sur macOS et Linux :
curl -LsSf https://astral.sh/uv/install.sh | sh
Sur Windows (PowerShell) :
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
On peut aussi l'installer avec pip, pour ceux qui préfèrent rester dans un environnement Python existant, même si cela n'est pas recommandé.
pip install uv
Une fois installé, on peut vérifier la version avec la commandeuv --version.
Premiers pas : créer un projet avec uv
1. Installer une version de Python
uv python install 3.12
uv télécharge et installe l'interpréteur, sans passer par pyenv ni par une installation système.
2. Initialiser un nouveau projet
uv init mon-projet
Cette commande va générer automatiquement le dossier du projet. Ensuite, la commande cd mon-projet vous permet d'accéder à ce dossier dont la structure est la suivante :
mon-projet/
├── .git/
├── .gitignore
├── .python-version
├── README.md
├── main.py
└── pyproject.toml
.git/: uv initialise automatiquement un dépôt Git, sauf si tu es déjà dans un dépôt existant ou si tu utilises l'option--no-vcs..gitignore: préconfiguré pour exclure les fichiers habituels d'un projet Python, comme.venv/ou les fichiers de cache..python-version: fixe la version de Python à utiliser pour ce projet précis. C'est ce fichier que uv lit pour savoir quel interpréteur activer, même si tu as plusieurs versions installées sur ta machine.README.md: un fichier vide, prêt à accueillir la description de ton projet.main.py: un script d'exemple minimal, qui affiche simplement "Hello from mon-projet!" au lancement.pyproject.toml: le fichier central du projet. Il contient le nom du paquet, sa version, sa description et, plus tard, la liste de ses dépendances.
Après la première exécution d'une commande commande comme uv run, uv add ou uv sync, vous verrez dans le dossier du projet deux autres fichiers que sont :
mon-projet/
├── .venv/
└── uv.lock
.venv/: l'environnement virtuel du projet, isolé du reste du système, dans lequel uv installe les dépendances.uv.lock: le lockfile qui fige les versions exactes de chaque dépendance, même les dépendances indirectes. Cela permet d'avoir un environnement identique sur toutes les machines.
Il faut souligner que la commande uv init ne crée que les fichiers de configuration, tant qu'aucune dépendance n'a encore été ajoutée ni aucun script exécuté.
3. Ajouter une dépendance
uv add requests
uv crée automatiquement un environnement virtuel .venv, installe requests dedans et met à jour pyproject.toml ainsi que le fichier uv.lock qui verrouille les versions exactes de chaque dépendance, même les dépendances indirectes.
4. Lancer un script
uv run python mon_script.py
Pas besoin d'activer manuellement l'environnement virtuel, puisque uv run s'en charge et exécute le script dans le bon contexte.
5. Exécuter un outil sans l'installer dans le projet
uvx ruff check .
uvx télécharge et exécute l'outil dans un environnement temporaire, comme le ferait pipx.
Avec ces cinq commandes, vous avez déjà de quoi gérer un projet Python : interpréteur, dépendances, environnement virtuel et outils annexes.
Le fichier pyproject.toml
pyproject.toml est un fichier au format TOML (format de configuration standardisé et lisible) utilisé par la majorité des outils Python modernes.
Juste après uv init, il ressemble à ceci :
[project]
name = "mon-projet"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
La table [project]
Cette table regroupe les métadonnées standard du projet, définies par la spécification Python PEP 621 :
name: le nom du projet, repris du nom du dossier par défaut ;version: la version actuelle du projet, à incrémenter au fil du développement ;description: une courte description, à remplacer par la tienne ;readme: le fichier utilisé comme description longue du projet, généralementREADME.md;requires-python: pour les versions de Python compatibles avec le projet (par exemple>=3.12signifie que le projet nécessite Python 3.12 ou une version plus récente) ;dependencies: la liste des paquets nécessaires au fonctionnement du projet (vide au départ, elle se remplit automatiquement à chaqueuv add).
Quand on exécute uv add requests, la table se met à jour toute seule :
dependencies = [
"requests>=2.32.3",
]
On n'a pas besoin d'éditer cette liste à la main. Les commandes uv add et uv remove s'en chargent puis mettent aussi à jour uv.lock en conséquence.
Séparer les dépendances de développement
Certains paquets ne servent qu'au développement, comme pytest pour les tests ou ruff pour le linting. Ils n'ont pas besoin d'être installés chez les utilisateurs finaux. C'est la raison pour laquelle uv les range dans une table séparée, [dependency-groups], ajoutée automatiquement quand on utilise l'option --dev :
uv add --dev pytest
[dependency-groups]
dev = [
"pytest>=8.3.4",
]
Cette séparation suit la norme PEP 735. Ces dépendances de développement sont installées par défaut avec uv sync, mais ne sont jamais publiées si le projet est distribué sur PyPI, par exemple.
La table [tool.uv], optionnelle
Une dernière table, [tool.uv], peut apparaître pour des réglages propres à uv :
- sources de dépendances alternatives (dépôt Git, chemin local) ;
- configuration d'un workspace regroupant plusieurs projets ;
- ou encore forcer si le projet doit être traité comme un paquet installable.
Elle n'apparaît pas dans un projet qui vient d'être créé et reste facultative pour un usage simple. Pour un premier projet, on ne touche généralment qu'à dependencies et, si nécessaire, à requires-python. Le reste, uv le gère.
Migrer un projet existant de pip et virtualenv vers uv
Si vous avez déjà un projet géré avec pip et virtualenv, la migration vers uv se fait sans réécrire le code. Voici la marche à suivre pour un projet qui utilise un requirements.txt.
1. Se placer dans le projet existant et initialiser uv
Dans le dossier du projet, qui contient déjà le code et le fichier requirements.txt :
uv init
Cette commande crée un pyproject.toml s'il n'en existe pas déjà un. Elle peut aussi ajouter un main.py ou un README.md d'exemple si ces fichiers sont absents.
2. Importer les dépendances existantes en préservant les versions verrouillées
uv add -r requirements.txt
uv lit le fichier, ajoute chaque paquet dans la section dependencies du pyproject.toml, crée l'environnement virtuel .venv, s'il n'existe pas, et génère un uv.lock qui fige les versions exactes installées.
Attention ! Si le fichier requirements.txt a été généré par pip-tools (à partir d'un requirements.in), utiliser directement uv add -r requirements.in peut faire résoudre à uv de nouvelles versions, différentes de celles qui avaient été verrouillées. Pour conserver exactement les versions actuelles pendant la migration, il faut passer l'ancien fichier de versions figées en contrainte avec -c :
uv add -r requirements.in -c requirements.txt
S'il n'y a qu'un seul requirements.txt classique, sans fichier .in séparé, on peut aussi l'utiliser à la fois comme source et comme contrainte, pour être certain de ne rien faire bouger :
uv add -r requirements.txt -c requirements.txt
Dans les deux cas, pyproject.toml reçoit des contraintes larges sur chaque paquet, pendant que uv.lock fige les versions exactes utilisées, en reprenant en priorité celles que tu avais déjà.
3. Séparer les dépendances de développement, s'il y en a
Si le projet a un fichier séparé pour les outils de développement, par exemple requirements-dev.txt :
uv add --dev -r requirements-dev.txt
Ces paquets sont ajoutés dans [dependency-groups] plutôt que dans dependencies, pour ne pas être installés inutilement en production.
4. Remplacer les anciennes habitudes de virtualenv
Avec pip et virtualenv, il fallait activer l'environnement manuellement avant chaque commande :
source .venv/bin/activate
python mon_script.py
Avec uv, cette activation manuelle n'est plus nécessaire. Il suffit de préfixer les commandes avec uv run :
uv run python mon_script.py
uv vérifie que uv.lock correspond bien à pyproject.toml et que l'environnement .venv correspond bien à uv.lock, avant chaque exécution. Si un fichier a changé, il resynchronise l'environnement automatiquement.
5. Garder un requirements.txt si un outil externe en a encore besoin
Certains pipelines de déploiement ou images Docker s'attendent encore à un requirements.txt classique. On peut en régénérer un à tout moment à partir du lockfile, sans le maintenir à la main :
uv export --format requirements-txt > requirements.txt
Cette migration ne casse rien. L'ancien fichier requirements.txt reste utilisable en parallèle si besoin, le temps de valider que tout fonctionne avec uv avant de le retirer définitivement.
Visualiser, construire et publier un projet
Une fois le projet installé et fonctionnel, voici les trois commandes essentielles à connaître.
uv tree : visualiser l'arbre des dépendances
uv tree
Cette commande affiche l'arbre complet des dépendances du projet ainsi que les dépendances indirectes (celles qu'un paquet installe pour son propre fonctionnement). C'est particulièrement utile pour comprendre pourquoi un paquet précis se retrouve installé ou pour repérer l'origine d'un conflit de versions entre deux dépendances qui exigent des versions incompatibles d'un même paquet tiers.
uv build : construire des archives de distribution
uv build
Cette commande construit le projet sous forme d'archives distribuables :
- une distribution source (
.tar.gz) ; - et un wheel (
.whl).
Elle les place par défaut dans un dossier dist/. C'est l'étape nécessaire avant de publier un paquet, que ce soit sur PyPI ou sur un index privé.
Pour que uv build fonctionne correctement, le fichier pyproject.toml a besoin d'une table [build-system], qui précise quel outil sert à construire le paquet. Cette table est ajoutée automatiquement si le projet a été créé avec uv init --package ou uv init --lib ; sinon, il faut l'ajouter manuellement.
uv publish : publier sur un index de paquets
uv publish
Cette commande envoie les archives construites vers un index de paquets, PyPI par défaut. Il faut s'authentifier, par exemple avec un jeton d'API PyPI :
uv publish --token pypi-VOTRE_JETON
Le jeton peut être aussi défini par la variable d'environnement UV_PUBLISH_TOKEN, pour éviter de le taper en clair dans le terminal. Si vous publiez depuis GitHub Actions avec un éditeur de confiance (Trusted Publisher) configuré sur PyPI, aucune information d'identification n'est nécessaire.
Pour finir, il faut souligner que Astral, l'éditeur de uv, a été acquis par OpenAI en mars 2026 et l'équipe a rejoint le projet Codex d'OpenAI. Cette annonce a rassuré une partie de la communauté sur la pérennité du financement de l'outil, même si uv reste en version pré-1.0. Cela signifie que des changements de comportement mineurs peuvent encore survenir d'une version à l'autre. Il est donc conseillé de consulter le changelog avant de mettre à jour uv en production.
Pour en savoir plus, voici la documentation officielle uv.