Aller au contenu
Développement medium

uv Python : remplacer pip et Poetry (uvx, lockfile, Docker)

23 min de lecture

logo uv

Si vous avez déjà travaillé sur un projet Python, vous connaissez probablement ces frustrations :

  • pip est lent : installer des dépendances prend parfois plusieurs minutes
  • Pas de lock file natif : impossible de garantir les mêmes versions entre développeurs sans pip-tools ou poetry
  • Jongler entre les outils : pip pour les paquets, virtualenv pour les environnements, pyenv pour les versions Python, pipx pour les outils CLI…
  • Conflits de dépendances : le fameux “dependency hell” où deux paquets demandent des versions incompatibles

uv résout tous ces problèmes avec un seul outil, écrit en Rust, qui est 10 à 100 fois plus rapide que pip.

Ce guide vous accompagne pas à pas pour :

  1. Comprendre pourquoi uv change la donne face à pip et poetry
  2. Installer uv sur votre système (Linux, macOS, Windows)
  3. Créer votre premier projet avec une structure propre
  4. Gérer vos dépendances de façon reproductible avec le lock file
  5. Exécuter des outils CLI sans polluer votre système (uvx)
  6. Écrire des scripts autonomes avec leurs dépendances intégrées
  7. Optimiser vos images Docker avec uv

Avant de plonger dans l’installation, comprenons ce qui rend uv différent.

Quand vous installez des dépendances avec pip, vous attendez. Sur un projet avec beaucoup de dépendances comme Django ou un projet data science avec pandas, numpy et scikit-learn, l’attente peut durer plusieurs minutes.

Avec uv, la même opération prend quelques secondes. Cette différence vient de plusieurs optimisations :

  • Résolution parallèle : uv télécharge et analyse plusieurs paquets simultanément
  • Cache intelligent : les paquets déjà téléchargés sont réutilisés instantanément
  • Code Rust : pas d’interpréteur Python à démarrer, exécution native

Avant uv, un développeur Python typique devait installer et apprendre plusieurs outils :

  • pip pour installer des paquets
  • pip-tools ou poetry pour verrouiller les versions
  • virtualenv ou venv pour créer des environnements isolés
  • pyenv pour gérer plusieurs versions de Python
  • pipx pour installer des outils CLI globalement

uv remplace tous ces outils. Vous n’avez qu’une seule commande à apprendre, une seule documentation à consulter, et tout fonctionne ensemble de façon cohérente.

L’installation de uv est simple et ne nécessite pas Python au préalable. C’est un binaire autonome qui fonctionne immédiatement.

Cette méthode fonctionne sur Linux et macOS. Le script détecte votre système et télécharge le bon binaire :

Fenêtre de terminal
curl -LsSf https://astral.sh/uv/install.sh | sh

Le script installe uv dans ~/.local/bin. Si ce dossier n’est pas dans votre PATH, ajoutez cette ligne à votre ~/.bashrc ou ~/.zshrc :

Fenêtre de terminal
export PATH="$HOME/.local/bin:$PATH"

Puis rechargez votre shell avec source ~/.bashrc ou ouvrez un nouveau terminal.

Une fois installé, vérifiez que uv fonctionne :

Fenêtre de terminal
uv --version

Vous devriez voir quelque chose comme uv 0.5.x. Si vous obtenez command not found, vérifiez que le dossier d’installation est dans votre PATH.

Un problème courant en Python : votre projet nécessite Python 3.11, mais votre système a Python 3.9. Ou vous travaillez sur plusieurs projets qui demandent des versions différentes.

Traditionnellement, on utilisait pyenv pour gérer ça. Avec uv, plus besoin d’un outil séparé : uv télécharge et gère les versions Python pour vous.

Commencez par lister ce qui est disponible sur votre système et ce que uv peut télécharger :

Fenêtre de terminal
uv python list

La sortie montre deux types d’entrées :

cpython-3.13.0-linux-x86_64-gnu <download available> # Peut être téléchargé
cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3.12 # Déjà installé
cpython-3.11.10-linux-x86_64-gnu <download available> # Peut être téléchargé

uv détecte automatiquement les versions Python déjà présentes sur votre système (installées par le gestionnaire de paquets de votre OS, par exemple). Les versions marquées <download available> peuvent être installées par uv.

Supposons que votre projet nécessite Python 3.11 et que vous ne l’avez pas. Installez-le simplement :

Fenêtre de terminal
uv python install 3.11

uv télécharge une version précompilée depuis le registre Python. C’est beaucoup plus rapide que de compiler depuis les sources comme le fait pyenv.

Pour garantir que tous les développeurs utilisent la même version de Python, fixez-la dans le projet :

Fenêtre de terminal
cd mon-projet
uv python pin 3.11

Cette commande crée un fichier .python-version contenant simplement 3.11. Quand quelqu’un clone le projet et exécute uv sync ou uv run, uv utilise automatiquement cette version (et la télécharge si nécessaire).

C’est un fichier standard reconnu aussi par pyenv et d’autres outils, donc même si vos collègues n’utilisent pas uv, ils sauront quelle version utiliser.

Maintenant que uv est installé, créons un vrai projet Python. uv génère une structure de fichiers propre et moderne.

  1. Créer le projet

    Placez-vous dans le dossier où vous rangez vos projets et lancez :

    Fenêtre de terminal
    uv init mon-projet
    cd mon-projet

    uv crée cette structure :

    mon-projet/
    ├── pyproject.toml # Configuration du projet et dépendances
    ├── README.md # Documentation
    ├── .python-version # Version Python à utiliser
    └── hello.py # Script d'exemple
  2. Comprendre pyproject.toml

    Ce fichier est le cœur de votre projet. Regardons son contenu :

    Fenêtre de terminal
    cat pyproject.toml
    [project]
    name = "mon-projet"
    version = "0.1.0"
    description = "Add your description here"
    readme = "README.md"
    requires-python = ">=3.11"
    dependencies = []

    Le fichier pyproject.toml est un standard Python (PEP 621) que comprennent pip, poetry, et uv. Vos dépendances seront listées dans dependencies.

  3. Tester que tout fonctionne

    Exécutez le script d’exemple :

    Fenêtre de terminal
    uv run hello.py

    Vous devriez voir Hello from mon-projet!. uv a créé automatiquement l’environnement virtuel et exécuté le script dedans.

uv propose trois templates selon ce que vous construisez :

Application simple (par défaut) — pour un script ou un service :

Fenêtre de terminal
uv init mon-script

C’est le choix pour la plupart des cas : un outil en ligne de commande, un bot, un service web, etc.

Application packagée — pour une application à distribuer :

Fenêtre de terminal
uv init --package mon-app

Crée une structure avec un dossier src/ et un système de build. Utile si vous voulez installer votre application comme un paquet ou la publier.

Bibliothèque — pour un package à publier sur PyPI :

Fenêtre de terminal
uv init --lib ma-lib

Similaire à --package, mais configuré pour être importé par d’autres projets plutôt qu’exécuté directement.

Les dépendances sont les bibliothèques externes dont votre code a besoin. Par exemple, si vous faites des requêtes HTTP, vous utiliserez probablement requests. Si vous faites du web, peut-être flask ou fastapi.

Pour ajouter une bibliothèque à votre projet :

Fenêtre de terminal
uv add requests

Que se passe-t-il ? uv fait trois choses :

  1. Télécharge le paquet et ses sous-dépendances
  2. Met à jour pyproject.toml avec la nouvelle dépendance
  3. Génère/met à jour uv.lock avec les versions exactes

Vous verrez requests apparaître dans votre pyproject.toml :

[project]
dependencies = [
"requests>=2.32.0",
]

Certaines dépendances ne sont utiles que pendant le développement : les outils de test, les linters, les formateurs de code. Elles ne doivent pas être installées en production.

uv les distingue avec l’option --dev :

Fenêtre de terminal
uv add --dev pytest ruff mypy

Ces dépendances apparaissent dans une section séparée et ne seront pas installées quand vous déployez en production avec uv sync --no-dev.

Parfois vous avez besoin d’une version précise, par exemple pour éviter un bug ou assurer la compatibilité :

Fenêtre de terminal
# Au moins la version 2.28
uv add 'requests>=2.28'
# Exactement cette version
uv add 'requests==2.28.0'
# Entre deux versions
uv add 'requests>=2.28,<3.0'

Pour retirer une dépendance devenue inutile :

Fenêtre de terminal
uv remove requests

uv met à jour pyproject.toml, uv.lock, et désinstalle le paquet de l’environnement.

La commande uv sync est fondamentale. Elle garantit que votre environnement correspond exactement à ce qui est déclaré dans uv.lock :

Fenêtre de terminal
uv sync

Concrètement, uv sync :

  • Crée l’environnement virtuel .venv/ s’il n’existe pas
  • Installe les dépendances aux versions exactes du lock file
  • Supprime les paquets qui ne sont plus déclarés

C’est la commande à lancer quand vous clonez un projet ou après un git pull qui a modifié les dépendances.

Fenêtre de terminal
# Regénérer le lock file
uv lock --upgrade
# Mettre à jour un paquet spécifique
uv lock --upgrade-package requests

Pour installer un paquet uniquement sur certaines plateformes :

Fenêtre de terminal
uv add 'pywin32; sys_platform == "win32"'
uv add 'uvloop; sys_platform != "win32"'

uv run exécute une commande dans l’environnement du projet :

Fenêtre de terminal
# Exécuter un script
uv run python script.py
# Lancer un module
uv run python -m http.server
# Exécuter un outil installé en dépendance dev
uv run pytest
uv run ruff check .

Définissez des raccourcis dans pyproject.toml :

[tool.uv.scripts]
serve = "python -m http.server"
test = "pytest -v"
lint = "ruff check ."
format = "ruff format ."

Puis exécutez-les :

Fenêtre de terminal
uv run serve
uv run test

Il y a des outils Python que vous utilisez occasionnellement : un formateur de code pour un projet ponctuel, un générateur de documentation, un outil de migration. Les installer globalement encombre votre système et crée des conflits de versions.

uvx résout ce problème. Il télécharge l’outil, l’exécute, et le garde en cache pour la prochaine fois. Rien n’est installé globalement.

Vous voulez formater du code Python avec Black, mais vous ne l’avez pas installé :

Fenêtre de terminal
uvx black mon_fichier.py

La première fois, uvx télécharge Black. Les fois suivantes, il utilise le cache et l’exécution est instantanée.

Vous pouvez demander une version précise avec @ :

Fenêtre de terminal
uvx ruff@0.5.0 check .

C’est utile pour reproduire exactement le comportement d’une CI ou comparer le résultat entre deux versions.

Parfois le nom du paquet PyPI n’est pas le même que la commande. Par exemple, le paquet httpie fournit la commande http. Utilisez --from :

Fenêtre de terminal
uvx --from httpie http GET https://httpbin.org/get

Voici quelques outils que vous pouvez lancer sans les installer :

Fenêtre de terminal
# Formater du code
uvx black .
uvx isort .
# Analyser la qualité du code
uvx ruff check .
uvx mypy .
# Générer un projet depuis un template
uvx cookiecutter gh:user/mon-template
# Servir de la documentation
uvx mkdocs serve

uv permet de définir les dépendances directement dans un script Python, sans projet ni pyproject.toml. Idéal pour les scripts autonomes.

#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests",
# "rich",
# ]
# ///
import requests
from rich import print
response = requests.get("https://httpbin.org/get")
print(response.json())
Fenêtre de terminal
# Rendre exécutable
chmod +x mon_script.py
# Exécuter (uv installe les dépendances automatiquement)
./mon_script.py
# ou
uv run mon_script.py
#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx",
# "pandas",
# "tabulate",
# ]
# ///
import httpx
import pandas as pd
# Récupérer des données
response = httpx.get("https://api.github.com/repos/astral-sh/uv/releases")
releases = response.json()
# Créer un DataFrame
df = pd.DataFrame([
{"version": r["tag_name"], "date": r["published_at"][:10]}
for r in releases[:5]
])
print(df.to_markdown(index=False))

Les builds Docker Python sont souvent lents : pip télécharge et installe les dépendances à chaque build, même si rien n’a changé. Avec uv, les builds sont beaucoup plus rapides, et on peut optimiser le cache Docker.

Deux avantages majeurs :

  1. Vitesse : uv installe les dépendances en quelques secondes au lieu de minutes
  2. Cache Docker optimisé : en copiant uv.lock avant le code source, Docker réutilise le cache tant que les dépendances ne changent pas

Voici un Dockerfile qui tire parti de ces avantages :

# Étape 1 : Construction
FROM python:3.12-slim AS builder
# Récupérer le binaire uv depuis l'image officielle
# Pas besoin de l'installer avec pip !
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
WORKDIR /app
# Copier SEULEMENT les fichiers de dépendances d'abord
# Astuce : si le code change mais pas les dépendances,
# Docker réutilise cette couche en cache
COPY pyproject.toml uv.lock ./
# Installer les dépendances (sans le code source)
# --frozen : échoue si uv.lock n'est pas à jour (sécurité)
# --no-dev : pas de dépendances de développement en production
RUN uv sync --frozen --no-dev --no-install-project
# Maintenant copier le code source
COPY src ./src
# Installer le projet lui-même
RUN uv sync --frozen --no-dev
# Étape 2 : Image finale (plus légère)
FROM python:3.12-slim
WORKDIR /app
# Copier uniquement l'environnement virtuel, pas uv ni les sources
COPY --from=builder /app/.venv /app/.venv
# Ajouter l'environnement au PATH
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "mon_app"]

--frozen fait échouer le build si uv.lock n’est pas synchronisé avec pyproject.toml. C’est une sécurité : vous savez exactement ce qui est installé.

--no-dev exclut pytest, ruff et autres outils de développement. Votre image de production est plus légère et plus sécurisée.

--no-install-project dans la première commande uv sync installe uniquement les dépendances, pas votre code. Cela maximise l’utilisation du cache Docker.

Fenêtre de terminal
# Initialiser le projet
uv init
# Importer les dépendances existantes
uv add $(cat requirements.txt | grep -v '^#' | tr '\n' ' ')
# Ou utiliser le mode compatibilité pip
uv pip install -r requirements.txt
Fenêtre de terminal
# uv lit directement pyproject.toml de poetry
uv sync

uv comprend le format pyproject.toml de poetry et génère son propre uv.lock.

Fenêtre de terminal
uv build

Génère dans dist/ :

dist/
├── mon_projet-0.1.0-py3-none-any.whl
└── mon_projet-0.1.0.tar.gz
Fenêtre de terminal
# Distribution source uniquement
uv build --sdist
# Wheel uniquement
uv build --wheel

Pour les projets avec plusieurs packages liés, uv supporte les workspaces (inspiré de Cargo).

Dans le pyproject.toml racine :

[tool.uv.workspace]
members = ["packages/*"]
exclude = ["packages/experimental"]
[project]
dependencies = ["mon-package-interne"]
[tool.uv.sources]
mon-package-interne = { workspace = true }

Le système ne trouve pas uv. Le binaire est probablement installé mais pas dans votre PATH.

Solution : Ajoutez le dossier d’installation au PATH. Si vous avez utilisé le script officiel :

Fenêtre de terminal
export PATH="$HOME/.local/bin:$PATH"

Ajoutez cette ligne à votre ~/.bashrc ou ~/.zshrc pour que ce soit permanent.

Le fichier uv.lock ne correspond plus à pyproject.toml. Quelqu’un a probablement modifié les dépendances sans régénérer le lock.

Solution : Régénérez le lock file :

Fenêtre de terminal
uv lock

Puis commitez le nouveau uv.lock.

Deux dépendances demandent des versions incompatibles d’un même paquet.

Solution : Essayez de mettre à jour toutes les dépendances vers leurs dernières versions compatibles :

Fenêtre de terminal
uv lock --upgrade

Si le conflit persiste, uv affiche quels paquets sont en conflit. Vous devrez peut-être choisir une version différente d’une de vos dépendances directes.

Rarement, le cache de uv peut se corrompre (coupure pendant un téléchargement, par exemple).

Solution : Videz le cache et recommencez :

Fenêtre de terminal
uv cache clean
uv sync
Fenêtre de terminal
# Voir la version de uv et où il est installé
uv --version
which uv
# Voir l'arbre complet des dépendances
# (utile pour comprendre d'où vient une sous-dépendance)
uv tree
# Lister ce qui est installé dans l'environnement
uv pip list
  1. uv remplace pip, pip-tools, virtualenv, pyenv et pipx en un seul outil
  2. 10-100x plus rapide que pip grâce à Rust et au cache intelligent
  3. uv sync installe exactement ce qui est dans uv.lock
  4. uvx exécute des outils CLI sans les installer globalement
  5. Scripts inline avec # /// script pour des scripts autonomes
  6. Docker : copier le binaire depuis ghcr.io/astral-sh/uv:latest
  7. Migration facile depuis pip ou poetry

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.