[!NOTE] Traduction automatique Cet article a été traduit automatiquement depuis la version originale en anglais.
uv sur macOS : Gestion des versions de Python, des projets et des outils
J’ai migré mon flux de travail en Python vers uv, car il remplace l’alternance de outils que je devais effectuer entre pip, des environnements virtuels, pip-tools, pipx et des gestionnaires de projet. Un seul exécutable suffit désormais pour gérer la majeure partie de ces tâches.
Les flux de travail restent distincts. Un projet, un script de métadonnées intégrées, une commande CLI ponctuelle et une commande CLI installée possèdent chacun leur propre environnement et leur propre cycle de vie. Ce guide présente les commandes que j’utilise ainsi que les limites qui définissent le champ d’action de chacune d’elles.
Début rapide
# Install uv with Homebrew
brew install uv
# Start a project
uv init example-app
cd example-app
uv add httpx
uv run python main.py
# Run an isolated CLI without installing it permanently
uvx ruff check .
# Run a script that declares PEP 723 dependencies
uv run fetch.py
En résumé. J’utilise
uv add,uv lock,uv sync, etuv runà l’intérieur des projets ; les métadonnées PEP 723 pour les scripts autocontenus ;uvxpour les outils ponctuels ; etuv tool installpour les commandes qui doivent rester en lignePATH. Dans les pipelines CI,--lockedvérifie que les métadonnées du projet correspondent auxuv.lock;--frozenIl fait confiance au verrou existant sans vérifier sa fraîcheur.
Installer uv avec un propriétaire unique
Homebrew constitue un chemin d’installation pratique sous macOS :
brew install uv
uv --version
Si Homebrew a installé uv, il devrait le mettre à jour :
brew upgrade uv
uv self update Il s’agit de la méthode d’installation autonome des UV et est désactivé pour les installations via un gestionnaire de paquets. Il ne faut pas permettre à deux installateurs de concurrencer pour le même exécutable.
Commandes d’identité utiles :
command -v uv
uv python dir
uv tool dir
uv cache dir
Les installations Python gérées, les outils persistants ainsi que les entrées de cache temporaires disposent de répertoires distincts. Le cache peut être supprimé puis reconstruit ; il ne constitue pas une source fiable d’information.
Projets : déclarations, résolution et environnement
Un projet UV comporte normalement trois artefacts distincts :
pyproject.tomlDéclare les métadonnées du projet ainsi que ses exigences directes.uv.lockStocke la résolution multiplateforme des UV..venvIl s’agit de l’environnement installé localement et doit être réutilisable.
Créez une application, ajoutez runtime ainsi que les exigences de test :
uv init forecast-app
cd forecast-app
uv add httpx
uv add --group test pytest
uv run python main.py
uv run --group test pytest
Les modèles actuels d’application UV génèrent main.py, pyproject.toml, README.md, et .python-version. Par défaut, ils ne définissent pas de système de compilation. Utilisez uv init --lib pour une bibliothèque emballée disposant d’un src Mise en page et compilation backend.
uv run vérifie le projet, met à jour le verrou lorsque c’est nécessaire, synchronise les dépendances requises, et exécute la commande. La première opération sur le projet crée .venv et uv.lock selon les besoins ; uv init Il ne crée que les fichiers du projet lui-même.
Soumettre les entrées, et non l’environnement
Valider pyproject.toml, uv.lock, source, ainsi qu’un intentionnel .python-version. Ignorer .venv ainsi que les caches des UV.
Le verrou enregistre une résolution valable pour les marqueurs et plateformes pris en charge ; il ne rend pas les modules natifs identiques sur macOS, Linux, Intel et Apple Silicon. Tester chaque plateforme de déploiement.
Verrouillage de la fraîcheur : --locked n’est pas --frozen
Par défaut, les commandes de projet peuvent être mises à jour uv.lock lorsque les déclarations changent.
Utiliser --locked pour exiger que le verrou soit à jour par rapport aux métadonnées du projet :
uv lock --check
uv sync --locked
uv run --locked pytest
Si pyproject.toml et uv.lock Je ne suis pas d’accord : ces commandes échouent au lieu de résoudre un nouveau verrouillage. Or, c’est généralement cette étape du pipeline CI qui s’avère utile.
Utiliser --frozen uniquement lorsque l’on souhaite délibérément que le UV utilise le verrou existant sans vérifier s’il est actuel :
uv sync --frozen
Cela peut s’avérer utile lors d’une étape de construction contrôlée où le verrouillage a déjà été validé, mais il ne s’agit pas d’un détecteur de verrouillage obsolète. uv sync Il est par défaut précis et supprime les paquets superflus ; uv run utilise une synchronisation imparfaite par défaut, sauf si --exact Cela est demandé.
Python géré : une demande de version, et non un binaire universel
uv permet de télécharger et de gérer des distributions Python :
uv python install 3.12 3.13
uv python list --only-installed
uv python pin 3.12
les builds gérées de CPython fournies par uv proviennent du python-build-standalone projet. uv peut également détecter les interpréteurs système, Homebrew, pyenv, Conda et d’autres.
.python-version Il s’agit d’une demande de version détectée par UV ainsi que par d’autres outils compatibles. requires-python dans pyproject.toml Il s’agit du contrat de compatibilité du projet. Il convient de préserver les deux aspects intentionnels :
[project]
requires-python = ">=3.12,<3.14"
Fixation 3.12 Cela ne garantit pas une version du patch identique indéfiniment, ni le même artefact sur tous les systèmes d’exploitation. Demandez un patch spécifique uniquement lorsque c’est réellement nécessaire, et faites en sorte que les pipelines CI sélectionnent et indiquent explicitement l’interpréteur utilisé.
Utiliser un autre gestionnaire Python lorsque le projet nécessite une configuration de distribution ou de compilation que uv ne fournit pas. uv peut néanmoins continuer à utiliser cet interpréteur via --python ou découverte normale.
Choisissez parmi uv run, uvx, ainsi que les outils installés
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 .
Exécuter cet outil en passant par uvx cela le isolerait du projet et pourrait cacher le paquet ou les plugins installés dont il a besoin.
Outils ad hoc : uvx
uvx est un alias pour uv tool run. Il crée un environnement isolé stocké dans le cache UV éphémère :
uvx ruff@0.12.0 check .
uvx --from jupyterlab jupyter lab
uvx --with mkdocs-material mkdocs --help
Fixez la version de l’outil dans le CI ou dans la documentation. Une première exécution sans spécification de version sélectionne une version en cours d’utilisation, et les exécutions ultérieures peuvent réutiliser l’état du cache.
Exécutables persistants : uv tool install
Installez un outil lorsque des scripts hors de votre contrôle ont besoin de sa commande. PATH, ou lorsque le manifeste de machine en est délibérément propriétaire :
uv tool install 'ruff==0.12.0'
uv tool list
uv tool upgrade ruff
uv tool uninstall ruff
Les outils persistants utilisent toujours des environnements isolés. Ne modifiez pas manuellement ces environnements à l’aide de pip.
Scripts : faire d’un seul fichier l’unité de déploiement
PEP 723 définit les métadonnées de script en ligne de commande. Un exécuteur compatible peut lire le bloc de commentaires afin de créer un environnement isolé.
# /// 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écuter et modifier les métadonnées à l’aide de uv :
uv run fetch.py
uv add --script fetch.py rich
uv remove --script fetch.py rich
Texte brut python fetch.py Il ignore les métadonnées de commentaire. Par conséquent, le script dépend d’un exécuteur compatible, même si la syntaxe Python reste valide.
Pour un script qui doit reproduire une résolution ultérieurement, créez un verrou adjacent :
uv lock --script fetch.py
uv run --script fetch.py
Ce texte est écrit. fetch.py.lock. Un exclude-newer La date de timestamp peut restreindre les dates de distribution des candidats, mais son efficacité est moindre par rapport à une résolution verrouillée avec précision, et elle ne garantit pas que l’artefact reste accessible.
Préférez un projet lorsque plusieurs fichiers partagent des dépendances, que le code peut être importé en tant que paquet, que les tests nécessitent l’état du projet, ou encore lorsque plusieurs scripts doivent être exécutés ensemble.
Fichiers de spécifications : compatibilité, et non échec
A requirements.txt Un fichier peut contenir des entrées brutes, des broches précises, des hachages, des contraintes, des index, des URL, ou encore un environnement compilé. Sa reproductibilité dépend de la manière dont il a été généré et consommé ; le simple nom du fichier ne renseigne en rien à ce sujet.
Utilisez l’interface compatible avec pip d’uv sans migration :
uv venv
uv pip sync requirements.txt
uv run python app.py
uv pip sync fait en sorte que l’environnement corresponde au fichier, tandis que uv pip install -r Il est additif.
Pour une application propriétaire, une migration par étapes peut s’avérer 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’ensemble du parcours de test et de déploiement avant de supprimer les fichiers anciens. Si un système intermédiaire attend toujours le format Pip, dérivez-le à partir du fichier uv lock déjà validé :
uv export --format requirements.txt \
--output-file requirements.txt
Ne pas maintenir uv.lock ainsi qu’une exportation manuellement modifiée sous deux résolutions concurrentes.
Cache, indexations et limites de la chaîne d’approvisionnement
Le cache UV améliore les installations répétées, mais reste éphémère :
uv cache prune
uv cache clean
Préférer prune pour le nettoyage de routine. clean Supprime toutes les entrées de cache et force des téléchargements ainsi que des compilations ultérieurs.
Un fichier de verrouillage améliore la reproductibilité ; il ne rend pas les dépendances fiables pour autant. Vérifiez les sources des paquets, la configuration de l’index, les révisions Git, les backends de compilation, les licences ainsi que les identifiants. Gardez la configuration de l’index authentifiée hors des fichiers commités, sauf si le dépôt ne contient que des références non secrètes vers un mécanisme d’authentification approuvé.
Pour les paquets natifs, enregistrer l’architecture de déploiement ainsi que la disponibilité du wheel de test. Sinon, un résolveur pourrait être contraint d’utiliser une compilation locale qui nécessite des compilateurs et des bibliothèques système absents dans l’environnement CI ou en production.
Une liste de contrôle opérationnelle compacte
Pour chaque projet :
- Définir
requires-pythonainsi que les dépendances directes danspyproject.toml. - Séparer les extras publiés des groupes de dépendances locaux.
- Faire un commit
uv.lock; ignorer.venvet l’état du cache. - Exécuter les outils couplés au projet à l’intérieur de son environnement.
- Utiliser
uv lock --checkou--lockeddans le CI. - Tester chaque système d’exploitation et architecture cibles indiqués par le lock.
- Exporter les formats de compatibilité uniquement pour les consommateurs désignés en aval.
- Évaluer la politique de sources et de identifiants de manière indépendante de la résolution.
UV est particulièrement utile lorsque ces limites de propriété restent visibles. Un seul binaire peut gérer l’ensemble sans les fusionner en un seul environnement. Cette combinaison — moins d’outils, sans prétendre que chaque flux de travail est identique — explique pourquoi je continue à l’utiliser comme outil par défaut pour mes projets Python sous macOS.