Traduction automatique
Cet article a été traduit automatiquement depuis la version originale en anglais.
uv sur macOS : gérer les versions de Python, les projets et les outils
Démarrage rapide
# Install uv
brew install uv
# For new projects (modern workflow)
uv init # create project structure
uv add pandas numpy # add dependencies
uv run train.py # run your script
# For existing projects (legacy workflow)
uv venv # create virtual environment
uv pip install -r requirements.txt # install dependencies
uv run train.py # run your script
# Run tools without installing them
uvx ruff check . # run linter
uvx black . # run formatter
# Run a single-file script with its own dependencies (no project needed)
uv run fetch.py # uv reads the inline deps and runs it
Pourquoi utiliser uv pour Python sur macOS ?
Si vous utilisez Python depuis un moment, vous jonglez probablement entre pip, virtualenv, pip-tools, pyenv et poetry selon le projet. uv fait tout ce que tous ces outils font, avec un seul binaire.
Il est écrit en Rust et, sur ma machine, il installe les paquets environ 10 à 100 fois plus vite que l’ancienne pile. Le vrai gain pour moi, c’est que j’ai arrêté de changer d’outil en plein milieu d’une tâche.
Il remplace pip et pip-tools pour la gestion des paquets, pyenv pour installer des versions de Python, virtualenv/venv pour les environnements, pipx pour lancer des outils, et poetry ou pdm pour les workflows de projet.
Installation de uv
La façon la plus simple d’installer uv sur macOS est de passer par Homebrew :
brew install uv
uv détecte automatiquement l’architecture de votre Mac (Apple Silicon ou Intel), donc aucune configuration supplémentaire n’est nécessaire.
Pour le garder à jour :
brew upgrade uv
# OR
uv self update
Concepts de base
uv couvre trois cas d’usage :
- Projets — construire une application ou une bibliothèque avec des dépendances.
- Scripts — exécuter un script Python en un seul fichier avec des dépendances inline.
- Outils — exécuter globalement des utilitaires en ligne de commande (comme
ruffouhttpie).
1. Gestion de projet
Pour les nouveaux projets, uv utilise le standard pyproject.toml pour la configuration et un uv.lock multiplateforme pour des builds reproductibles.
Démarrez un nouveau projet :
uv init my-project
cd my-project
Cela crée un pyproject.toml, un .gitignore et un hello.py.
Ajoutez des dépendances :
# Runtime dependencies
uv add pandas requests
# Development dependencies
uv add pytest ruff --dev
Exécutez votre code :
uv run hello.py
uv gère pour vous l’environnement virtuel dans .venv. Vous n’avez pas besoin de l’activer manuellement.
2. Gérer les versions de Python
uv installe et gère les versions de Python pour vous, conservées dans ~/.cache/uv. Si vous utilisiez pyenv pour ça, vous pouvez vous en passer.
Installez une version spécifique :
uv python install 3.12
Épinglez une version pour votre projet :
uv python pin 3.11
Cela écrit un fichier .python-version. Au prochain uv run, il utilise la version épinglée et la télécharge si nécessaire. Votre équipe et votre CI se retrouvent ainsi sur la même version de Python sans coordination supplémentaire.
3. Lancer des outils avec uvx et uv tool install
uvx (un alias de uv tool run) exécute des outils Python en ligne de commande sans les ajouter à votre environnement global ni aux dépendances de votre projet.
# Run a linter
uvx ruff check .
# Run a formatter
uvx black .
# Start a temporary Jupyter server
uvx --from jupyterlab jupyter lab
Chaque outil s’exécute dans son propre environnement temporaire, donc il n’y a pas de conflit de version avec ce qui est déjà installé dans votre projet.
Deux détails que j’utilise en permanence :
- Épinglez la version inline avec
@:uvx ruff@0.6.0 check ., ou forcez la plus récente avecuvx ruff@latest. - Le nom de la commande ne correspond pas au paquet ? Utilisez
--from. Le paquetjupyterlabfournit la commandejupyter, donc c’estuvx --from jupyterlab jupyter lab. Pareil pour--from httpie http. - Besoin d’une dépendance supplémentaire pour un plugin ? Ajoutez-la avec
--with:uvx --with mkdocs-material mkdocs serve.
Si vous utilisez un outil tous les jours, installez-le une fois au lieu de recréer un environnement à chaque exécution :
uv tool install ruff # now `ruff` is on your PATH
uv tool list # see what's installed
uv tool upgrade --all # update everything
uv tool uninstall ruff
Règle générale : uvx pour les exécutions ponctuelles ou en CI, uv tool install pour vos outils du quotidien.
Projets legacy (requirements.txt)
requirements.txt a eu une belle carrière. C’est une liste plate de paquets, sans lockfile, sans séparation entre dépendances applicatives et de développement, et sans trace de la raison pour laquelle quoi que ce soit est épinglé. Ça va très bien jusqu’au jour où vous essayez de reproduire un build d’il y a huit mois et découvrez que « épinglé » voulait dire ce que PyPI résolvait à ce moment-là.
Si le projet vous appartient, migrez-le. uv lit directement votre liste existante dans un pyproject.toml :
uv init --bare # minimal pyproject.toml, no scaffolding
uv add -r requirements.txt # import runtime dependencies
uv add --dev -r requirements-dev.txt # and dev ones, if you split them
Vous avez maintenant un pyproject.toml et un vrai uv.lock qui épingle les versions résolues exactes. Vérifiez que tout a bien été importé avec uv pip freeze, supprimez l’ancien requirements.txt et ne regardez plus en arrière.
Mais peut-être que vous exécutez le même requirements.txt depuis Python 3.6 et que mon avis sur le sujet ne vous intéresse pas. Très bien. uv fonctionne toujours comme remplaçant direct de pip et venv, sans migration nécessaire :
# Create a virtual environment
uv venv
# Install dependencies
uv pip install -r requirements.txt
# Run it
uv run python app.py
Les mêmes commandes que vous connaissez déjà, simplement plus rapides. Rien dans l’ancien workflow ne devient pire ; c’est juste que vous n’êtes plus obligé de l’utiliser.
Scripts en un seul fichier avec dépendances inline
C’est la fonctionnalité qui a changé ma façon d’écrire du code jetable et du glue code. Un script Python peut déclarer ses propres dépendances dans le fichier, dans un bloc de commentaires défini par PEP 723. Pas de pyproject.toml, pas de requirements.txt, pas d’environnement virtuel à créer — uv run lit le bloc, construit un environnement mis en cache et exécute le fichier.
Il faut être clair sur ce qui fait le travail ici : ce n’est pas une fonctionnalité du langage Python, et ce n’est pas une invention de uv. Le bloc n’est qu’un commentaire, donc python fetch.py seul l’ignore et échoue sur les imports manquants. PEP 723 est un standard partagé, donc n’importe quel exécuteur compatible lit le même bloc — pipx run, Hatch et PDM le prennent aussi en charge. uv se trouve simplement être la manière la plus rapide de l’utiliser, c’est pourquoi le reste de cette section utilise uv run.
Voici l’ensemble dans un seul fichier, fetch.py :
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx",
# "rich",
# ]
# ///
import httpx
from rich import print
print(httpx.get("https://api.github.com/repos/astral-sh/uv").json())
Exécutez-le :
uv run fetch.py
Au premier lancement, httpx et rich sont résolus et installés dans un environnement mis en cache ; les lancements suivants le réutilisent et démarrent instantanément.
Vous n’avez pas besoin d’écrire le bloc de métadonnées à la main. uv le génère et l’édite pour vous :
# Create a new script with the block pre-filled
uv init --script fetch.py --python 3.12
# Add or remove dependencies
uv add --script fetch.py httpx rich
uv remove --script fetch.py rich
Pour une expérience rapide, vous pouvez même ignorer complètement le bloc et passer les dépendances en ligne de commande :
uv run --with httpx --with rich fetch.py
Pourquoi c’est excellent pour les scripts de skill et d’automatisation
Je m’en sers pour les petits scripts qui ne méritent pas un projet : un correctif de données ponctuel, un helper CI, un script de Claude Code skill, une tâche cron. Le script est l’unité. Vous pouvez le mettre dans un gist, le committer dans n’importe quel dépôt ou le donner à un collègue, et uv run est la seule chose dont il a besoin pour l’exécuter correctement.
Rendez-le directement exécutable avec un shebang :
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["httpx"]
# ///
import httpx
...
chmod +x fetch.py
./fetch.py # uv handles the environment behind the scenes
Le -S découpe le shebang en arguments séparés afin que env transmette --script à uv.
Verrouiller et épingler les scripts
Pour les scripts qui doivent continuer à fonctionner dans plusieurs mois, vous avez deux options.
Verrouillez la résolution exacte à côté du script :
uv lock --script fetch.py # writes fetch.py.lock
Ou épinglez un point dans le temps pour que uv ne considère que les paquets publiés avant une date donnée — pratique pour la reproductibilité sans lockfile :
# /// script
# dependencies = ["httpx"]
# [tool.uv]
# exclude-newer = "2025-04-17T00:00:00Z"
# ///
Astuces et points utiles à connaître
Voici quelques autres choses que j’utilise régulièrement et qui ne sont pas évidentes dans le démarrage rapide.
Synchroniser depuis le lockfile en CI. uv sync installe exactement ce qui se trouve dans uv.lock. Ajoutez --frozen pour échouer bruyamment si le lockfile n’est plus à jour au lieu de relancer silencieusement la résolution — exactement ce qu’il faut en CI et dans les builds Docker.
uv sync --frozen
Regroupez vos dépendances de développement. Au-delà de --dev, vous pouvez définir des groupes de dépendances nommés dans pyproject.toml (docs, lint, test) et ne synchroniser que ce dont vous avez besoin :
uv add --group docs mkdocs-material
uv sync --only-group docs
Inspectez l’arbre des dépendances. uv tree montre qui a amené quoi — et --outdated signale les mises à jour :
uv tree
uv tree --outdated
Exportez vers requirements.txt quand un outil en aval en attend encore un :
uv export --format requirements-txt > requirements.txt
Exécutez un Python ponctuel avec des paquets supplémentaires, sans projet requis :
uv run --with pandas --with matplotlib python
Gérez le cache quand l’espace disque commence à manquer ou qu’un build tourne mal :
uv cache clean # wipe the whole cache
uv cache prune # remove only unused entries
Pourquoi c’est rapide
Trois choses comptent ici. C’est écrit en Rust, donc il n’y a pas de surcoût de démarrage Python à chaque invocation. Il met en cache globalement les wheels construites — une fois que numpy est installé dans un projet, l’installer dans un autre est quasiment instantané (sur macOS, il utilise des liens copy-on-write). Et il télécharge et installe les paquets en parallèle au lieu de le faire un par un.
Résumé
| Tâche | Ancienne méthode | Méthode uv |
|---|---|---|
| Installer Python | pyenv install 3.12 |
uv python install 3.12 |
| Nouveau projet | mkdir proj && cd proj && python -m venv .venv |
uv init proj |
| Installer un paquet | pip install pandas && pip freeze > requirements.txt |
uv add pandas |
| Exécuter un script | source .venv/bin/activate && python script.py |
uv run script.py |
| Script + dépendances | un projet, ou un venv manuel juste pour un fichier | bloc PEP 723 inline + uv run |
| Lancer un outil une fois | pipx run black |
uvx black |
| Installer un outil | pipx install ruff |
uv tool install ruff |
| CI reproductible | pip install -r requirements.txt |
uv sync --frozen |
J’ai basculé mon workflow Python vers uv peu après sa sortie et je ne suis jamais revenu en arrière. Si vous utilisez encore l’ancienne pile, testez-le sur votre prochain projet — c’est comme ça que je l’ai évalué avant de tout migrer.