uv sur macOS : gérer les versions de Python, les projets et les outils

Traduction automatique

Cet article a été traduit automatiquement depuis la version originale en anglais.

Ce guide s’adresse aux développeurs Python sur macOS qui passent de pip, venv, pip-tools, pyenv ou pipx à uv. uv prend en charge quatre types de travail : un projet, un script dans un seul fichier, une exécution ponctuelle de CLI avec uvx et une CLI installée sur PATH. Les quatre peuvent utiliser les versions de Python installées par uv. Vous apprendrez les commandes de chacun et comment choisir entre eux.

J’ai basculé mon propre workflow Python vers uv. Un seul exécutable couvre désormais le travail que je répartissais auparavant entre ces outils.

Démarrage rapide

# Install uv with Homebrew
brew install uv

# Start a project
uv init --no-package example-app
cd example-app
uv add httpx
uv run python main.py
# Prints "Hello from example-app!"

# Run an isolated CLI without installing it permanently
uvx ruff check .

La figure montre où chacun des quatre workflows conserve son environnement, ce que vous gardez pour chacun, et les installations de Python gérées qu’ils partagent.

Quatre modes de travail uv et leurs zones de stockage : la .venv du projet à côté de pyproject.toml et uv.lock, les environnements de scripts et uvx dans le cache jetable d’uv, un environnement d’outil installé qu’uvx peut réutiliser dans le répertoire des outils, et les installations Python gérées communes aux quatre. Les commandes installées nécessitent le répertoire des exécutables sur PATHQuatre modes de travail uv et leurs zones de stockage : la .venv du projet à côté de pyproject.toml et uv.lock, les environnements de scripts et uvx dans le cache jetable d’uv, un environnement d’outil installé qu’uvx peut réutiliser dans le répertoire des outils, et les installations Python gérées communes aux quatre. Les commandes installées nécessitent le répertoire des exécutables sur PATH

Installer et mettre à jour uv

Homebrew est une méthode d’installation pratique sur macOS. Consultez le guide d’installation de uv pour connaître les méthodes prises en charge :

brew install uv
uv --version

Si Homebrew a installé uv, c’est Homebrew qui doit le mettre à niveau :

brew upgrade uv

uv self update concerne la méthode d’installation autonome de uv et est désactivé pour les installations via un gestionnaire de paquets. Mettez uv à niveau avec l’outil qui l’a installé.

Pour voir où se trouvent uv, ses installations de Python, ses outils et son cache :

command -v uv
uv python dir
uv tool dir
uv cache dir

Les installations Python gérées, les outils persistants et les entrées de cache jetables ont des répertoires distincts. Le cache peut être supprimé et reconstruit. Il ne constitue pas une source de référence. Sur macOS, uv clone les paquets du cache dans chaque environnement (copie à l’écriture) au lieu de les copier. La reconstruction de .venv réutilise les artefacts nécessaires encore présents dans le cache.

Projets : déclarations, résolution et environnement

Un projet uv comporte normalement trois parties :

  • pyproject.toml déclare les métadonnées du projet et ses dépendances directes.
  • uv.lock stocke la résolution multiplateforme de uv.
  • .venv est l’environnement installé localement et doit rester jetable.

La figure classe les fichiers du projet selon qui les écrit et selon que git les conserve ou non.

Vous modifiez pyproject.toml, .python-version et les sources ; uv lock écrit uv.lock ; les quatre sont versionnés. uv sync construit .venv à partir de uv.lock, avec des paquets clonés depuis le cache uv, et les deux restent hors de gitVous modifiez pyproject.toml, .python-version et les sources ; uv lock écrit uv.lock ; les quatre sont versionnés. uv sync construit .venv à partir de uv.lock, avec des paquets clonés depuis le cache uv, et les deux restent hors de git

Créez une application et ajoutez ses dépendances d’exécution et de test :

uv init --no-package forecast-app
cd forecast-app

uv add httpx
uv add --group test pytest
uv run python main.py
uv run --group test pytest --version

L’application affiche Hello from forecast-app! ; la dernière commande vérifie que pytest est installé. Lorsque vous aurez des tests, lancez-les avec uv run --group test pytest.

Le modèle --no-package crée main.py, pyproject.toml, README.md et .python-version sans système de construction. Depuis uv 0.12, un simple uv init crée une application empaquetée avec une structure src et un backend de construction. Utilisez uv init --lib pour une bibliothèque empaquetée. Consultez le guide de création de projets d’uv.

uv run vérifie le projet, met à jour le lock si nécessaire, synchronise les dépendances requises et exécute la commande. Consultez la documentation de uv sur la structure des projets et la synchronisation pour ce cycle de vie. La première opération sur le projet crée .venv et uv.lock si nécessaire. uv init crée uniquement les fichiers du projet.

uv installe le groupe dev par défaut. Les autres groupes, comme test, exigent --group à chaque commande, ou default-groups sous [tool.uv]. Pour installer un seul groupe, par exemple dans un job CI de documentation, utilisez uv sync --only-group docs.

Les groupes restent locaux et ne sont jamais publiés. Un extra publié, c’est autre chose : uv add --optional cli rich ajoute un extra que les utilisateurs installent sous la forme forecast-app[cli].

Pour utiliser le projet dans des notebooks Jupyter sous VS Code, exécutez uv add --dev ipykernel et sélectionnez .venv comme kernel. Consultez le guide Jupyter de uv.

Versionner les entrées, pas l’environnement

Versionnez pyproject.toml, uv.lock, le code source et un .python-version choisi délibérément. Ignorez .venv et les caches de uv.

Un collègue qui clone le dépôt obtient l’environnement verrouillé avec une seule commande :

git clone https://github.com/example/forecast-app.git
cd forecast-app
uv sync --locked

Pour voir pourquoi un paquet est installé et faire passer le lock à des versions plus récentes :

uv tree                            # show the dependency graph
uv tree --outdated                 # show newer versions where they exist
uv lock --upgrade-package httpx    # move one package, keep the rest
uv lock --upgrade                  # re-resolve everything

Versionnez le uv.lock modifié avec le changement qui l’a rendu nécessaire.

Le lock enregistre une résolution couvrant les marqueurs et les plateformes prises en charge. Il ne rend pas les wheels natifs identiques sur macOS, Linux, Intel et Apple Silicon. Testez chaque plateforme de déploiement.

uv peut en imposer une partie. required-environments sous [tool.uv] fait échouer la résolution lorsqu’un paquet sans distribution source n’a pas de wheel pour une plateforme listée :

[tool.uv]
required-environments = [
    "sys_platform == 'darwin' and platform_machine == 'arm64'",
    "sys_platform == 'linux' and platform_machine == 'x86_64'",
]

no-build-package interdit les builds depuis les sources pour les paquets nommés : un wheel manquant fait alors échouer l’installation au lieu de lancer un compilateur. Si vous déployez aussi PyTorch sur Linux avec CUDA, suivez le guide PyTorch de uv pour configurer l’index.

Fraîcheur du lock : --locked n’est pas --frozen

Par défaut, les commandes de projet peuvent mettre à jour uv.lock lorsque les déclarations changent. Consultez la documentation de uv sur le verrouillage et la synchronisation pour les options de fraîcheur et d’exactitude.

Utilisez --locked pour exiger que le lock soit à jour par rapport aux métadonnées du projet :

uv lock --check
uv sync --locked --group test
uv run --locked --group test pytest

Si pyproject.toml et uv.lock divergent, ces commandes échouent au lieu de résoudre un nouveau lock. Utilisez-les en CI pour qu’un lock obsolète fasse échouer le build.

Utilisez --frozen uniquement lorsque vous voulez délibérément que uv utilise le lock existant sans vérifier qu’il est à jour :

uv sync --frozen

Cela peut être utile dans une étape de build contrôlée, lorsque le lock a déjà été validé, mais ce n’est pas un détecteur de lock obsolète.

La figure suit un projet dont le lock est obsolète à travers quatre commandes : rich a été ajouté à la main à pyproject.toml, et uv.lock n’a pas été mis à jour.

Avec rich dans pyproject.toml mais pas dans uv.lock : uv sync réécrit le lock et installe rich, uv sync --locked et uv lock --check échouent, et uv sync --frozen installe httpx sans rich pendant que la CI reste verteAvec rich dans pyproject.toml mais pas dans uv.lock : uv sync réécrit le lock et installe rich, uv sync --locked et uv lock --check échouent, et uv sync --frozen installe httpx sans rich pendant que la CI reste verte

L’exactitude est un réglage distinct de la fraîcheur. uv sync est exact par défaut et supprime les paquets superflus. uv run effectue par défaut une synchronisation non exacte, sauf si --exact est demandé. La figure suit un paquet installé à la main à travers uv sync, uv run et uv run --exact.

Un .venv avec httpx, ipykernel et un extra-pkg installé à la main : uv sync et uv run --exact suppriment extra-pkg, uv run le conserveUn .venv avec httpx, ipykernel et un extra-pkg installé à la main : uv sync et uv run --exact suppriment extra-pkg, uv run le conserve

Installer et épingler des versions de Python

uv peut télécharger et gérer des distributions Python :

uv python install 3.12 3.13
uv python list --only-installed
uv python pin 3.12

Les builds CPython gérés par uv proviennent du projet python-build-standalone. uv peut également détecter les interpréteurs système, Homebrew, pyenv, Conda et d’autres. Consultez la documentation de uv sur les versions de Python pour les sources prises en charge et le comportement de détection.

.python-version est une demande de version que uv et d’autres outils compatibles détectent. uv python pin écrit ce fichier, et uv init en crée un. requires-python dans pyproject.toml est le contrat de compatibilité du projet. Choisissez les deux délibérément :

[project]
requires-python = ">=3.12,<3.14"

uv prend la demande de version d’abord dans --python, puis dans .python-version, puis dans requires-python.

Épingler 3.12 ne garantit ni le même build de patch pour toujours, ni le même artefact sur tous les systèmes d’exploitation. Demandez un patch exact lorsque vous en avez besoin. En CI, sélectionnez l’interpréteur et affichez-le, avec uv run --python 3.12.7 python -VV ou en définissant UV_PYTHON=3.12.7 pour le job.

Utilisez un autre gestionnaire Python lorsqu’un projet a besoin d’une distribution ou d’une configuration de build que uv ne fournit pas. uv peut tout de même utiliser cet interpréteur via --python ou la détection normale.

Choisir entre uv run, uvx et les outils installés

Choisissez selon que l’outil a besoin des paquets installés du projet, doit rester sur PATH ou peut fonctionner indépendamment. La figure montre les dépendances disponibles et la durée de vie de chaque environnement.

uv run utilise la .venv du projet et ses dépendances verrouillées ; uvx utilise un environnement d’outil distinct, en cache ou installé, sans installer automatiquement les dépendances du projet ; uv tool install conserve un environnement dans le répertoire des outils et rend la commande disponible après configuration de PATHuv run utilise la .venv du projet et ses dépendances verrouillées ; uvx utilise un environnement d’outil distinct, en cache ou installé, sans installer automatiquement les dépendances du projet ; uv tool install conserve un environnement dans le répertoire des outils et rend la commande disponible après configuration de PATH

Outils couplés au projet : uv run

Si pytest, mypy, un générateur de code ou un autre outil doit importer le projet ou utiliser ses plugins verrouillés, déclarez-le dans un groupe de dépendances :

uv add --group lint ruff
uv run --group lint ruff check .

uvx n’installe pas automatiquement le paquet ni les dépendances du projet. Un outil peut tout de même importer des fichiers source locaux si son fonctionnement le permet ; utilisez uv run lorsqu’il a besoin du projet installé ou de ses dépendances.

Outils ponctuels : uvx

uvx est un alias de uv tool run. Il réutilise un outil compatible installé avec uv tool install ; sinon, il utilise un environnement isolé stocké dans le cache jetable d’uv. La documentation des outils uv décrit ce cache et le cycle de vie des outils persistants :

uvx ruff@0.12.0 check .
uvx ruff@latest check .
uvx --from jupyterlab jupyter lab
uvx --from httpie http --version
uvx --with mkdocs-material mkdocs --help

Fixez la version de l’outil dans CI ou la documentation. Sans outil installé, le premier appel sans version sélectionne la version disponible la plus récente ; les appels suivants peuvent réutiliser l’état du cache. @latest ignore une version installée et actualise le cache pour la version la plus récente. Utilisez --from lorsque le nom du paquet diffère de celui de la commande, comme pour jupyterlab et httpie.

Exécutables persistants : uv tool install

Installez un outil lorsque des scripts que vous ne contrôlez pas ont besoin de sa commande sur PATH, ou lorsque le manifeste de configuration de votre machine doit le gérer :

uv tool install ruff
uv tool list
uv tool upgrade ruff
uv tool upgrade --all
uv tool uninstall ruff

Si l’installation signale que le répertoire des exécutables est absent de PATH, lancez uv tool update-shell et redémarrez votre shell avant d’utiliser la commande. Consultez la documentation des exécutables d’outils uv.

Les outils persistants utilisent toujours des environnements isolés. Ne modifiez pas ces environnements à la main avec pip.

Scripts en un seul fichier avec PEP 723

PEP 723 définit les métadonnées inline des scripts. Un runner compatible peut lire le bloc de commentaires et créer un environnement isolé. Consultez le guide des scripts uv et PEP 723 pour le format des métadonnées et le comportement du runner.

La figure repère le bloc de métadonnées dans fetch.py et montre ce que uv run et un simple python font du fichier.

fetch.py avec son bloc de commentaires PEP 723 mis en évidence : uv run lit le bloc, trouve Python 3.12 ou plus récent, installe httpx dans un environnement isolé et exécute le code ; python fetch.py n’exécute que le codefetch.py avec son bloc de commentaires PEP 723 mis en évidence : uv run lit le bloc, trouve Python 3.12 ou plus récent, installe httpx dans un environnement isolé et exécute le code ; python fetch.py n’exécute que le code

uv peut écrire le bloc de métadonnées à votre place :

uv init --script fetch.py --python 3.12

Un script terminé ressemble à ceci :

# /// script
# requires-python = ">=3.12"
# dependencies = [
#   "httpx>=0.27,<1",
# ]
# ///
import httpx

response = httpx.get("https://example.com", timeout=10)
response.raise_for_status()
print(response.status_code)

Exécutez le script et modifiez ses métadonnées avec uv :

uv run fetch.py
uv add --script fetch.py rich
uv remove --script fetch.py rich

Un simple python fetch.py ignore les métadonnées des commentaires. Le script dépend donc d’un runner compatible, même si sa syntaxe Python reste valide. uv est l’un de ces runners. pipx run, Hatch et PDM lisent aussi ce bloc.

Pour exécuter le script avec ./fetch.py, ajoutez un shebang qui appelle uv :

#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["httpx"]
# ///
import httpx
chmod +x fetch.py
./fetch.py

Avec l’option -S, env découpe la ligne en arguments séparés, si bien que --script parvient à uv.

Pour une expérience rapide, passez les dépendances en ligne de commande au lieu d’écrire des métadonnées. La deuxième ligne lance un REPL Python avec pandas disponible :

uv run --with httpx scratch.py
uv run --with pandas python

Pour un script qui doit reproduire une résolution plus tard, créez un lock adjacent :

uv lock --script fetch.py
uv run --script fetch.py

Cela écrit fetch.py.lock. Un horodatage exclude-newer dans le bloc de métadonnées fait ignorer à uv les versions de paquets publiées après cette date :

# /// script
# dependencies = ["httpx"]
# [tool.uv]
# exclude-newer = "2025-04-17T00:00:00Z"
# ///

C’est plus faible qu’un lock. Cela n’épingle pas de versions exactes et ne peut pas restaurer un fichier que l’index supprime ensuite.

Utilisez plutôt un projet lorsque plusieurs fichiers partagent des dépendances, que le code est importable comme un paquet, que les tests ont besoin de l’état du projet ou que plusieurs scripts doivent évoluer ensemble.

Projets existants avec requirements.txt

Un fichier requirements.txt peut contenir des entrées souples, des versions fixées exactement, des empreintes, des contraintes, des index, des URLs ou un export de dépendances généré par un résolveur. Sa reproductibilité dépend de la manière dont il a été produit et consommé. Le nom du fichier seul n’en dit rien.

Vous avez deux options : garder le format pip et l’exécuter avec uv pip, ou, pour une application dont vous êtes responsable, migrer vers uv.lock.

Pour un fichier entièrement résolu qui contient les dépendances directes et transitives, utilisez l’interface d’uv compatible avec pip sans migrer :

uv venv
uv pip sync requirements.txt
.venv/bin/python app.py

Exécutez ici directement l’interpréteur installé. Si un pyproject.toml est présent, uv run peut verrouiller et synchroniser ses dépendances avant l’exécution, en remplaçant les paquets que vous venez d’installer. Consultez le guide des scripts d’uv.

uv pip sync fait correspondre l’environnement à un fichier entièrement résolu. Si requirements.txt ne contient que des dépendances directes, utilisez uv pip install -r requirements.txt pour résoudre et installer leurs dépendances. uv pip install -r ajoute des paquets. Consultez le guide de verrouillage compatible avec pip d’uv.

Pour une application dont vous êtes responsable, une migration progressive peut être utile :

uv init --bare
uv add -r requirements.in
uv add --group test -r requirements-test.in
uv lock
uv sync --locked

Exécutez l’intégralité du parcours de test et de déploiement avant de supprimer les anciens fichiers. Si un système en aval attend toujours le format pip, dérivez-le du lock uv validé avec la commande d’export de uv :

uv export --format requirements.txt \
  --output-file requirements.txt

Ne maintenez pas uv.lock et un export édité à la main comme deux résolutions concurrentes.

Cache et index privés

Le cache uv accélère les installations répétées, mais reste jetable :

uv cache prune
uv cache clean

Préférez prune pour le nettoyage courant. clean supprime toutes les entrées du cache et force de nouveaux téléchargements et de nouvelles constructions.

Un lockfile améliore la reproductibilité. Il ne rend pas les dépendances dignes de confiance.

Pour un index privé, placez son nom et son URL dans pyproject.toml et gardez les identifiants hors du fichier :

[[tool.uv.index]]
name = "internal"
url = "https://pypi.example.com/simple"

Passez les identifiants via UV_INDEX_INTERNAL_USERNAME et UV_INDEX_INTERNAL_PASSWORD, ou via keyring. uv construit les noms de variables à partir du nom de l’index. Consultez la documentation de uv sur l’authentification des index.

Anciennes commandes et leurs équivalents uv

Si vous quittez les anciens outils, ce tableau fait correspondre chaque commande familière à la commande uv qui fait le même travail.

TâcheAvantuv
Installer Pythonpyenv install 3.12uv python install 3.12
Nouveau projet et environnementmkdir proj && cd proj && python -m venv .venvuv init proj && cd proj && uv sync
Ajouter un paquetpip install pandas && pip freeze > requirements.txtuv add pandas
Fixer les versionspip-compile requirements.inuv lock
Mettre à niveau un paquetpip-compile --upgrade-package httpxuv lock --upgrade-package httpx
Exécuter dans l’environnementsource .venv/bin/activate && python app.pyuv run app.py
Script avec dépendancesun venv manuel pour un seul fichierbloc PEP 723 + uv run
Exécuter un outil une foispipx run blackuvx black
Installer un outilpipx install ruffuv tool install ruff
Installation reproductible en CIpip install -r requirements.txtuv sync --locked

Liste de contrôle opérationnelle

Pour chaque projet :

  1. Définissez requires-python et les dépendances directes dans pyproject.toml.
  2. Séparez les extras publiés (--optional) des groupes de dépendances locaux (--group).
  3. Versionnez uv.lock. Ignorez .venv et l’état du cache.
  4. Exécutez les outils couplés au projet dans l’environnement du projet.
  5. Utilisez uv lock --check ou --locked en CI.
  6. Testez chaque système d’exploitation et chaque architecture cible représentés par le lock.
  7. N’exportez les formats de compatibilité que pour des consommateurs en aval explicitement identifiés.
  8. Gardez les identifiants d’index hors des fichiers versionnés. Passez-les via des variables d’environnement ou keyring.

J’utilise uv comme outil Python par défaut sur macOS, car un seul exécutable couvre ces tâches et sépare les dépendances des projets de celles des outils.

Références