- Même pour un petit script de test d’API, il faut généralement aligner l’environnement d’exécution à cause des dépendances. Avec le shebang d’exécution uv, on peut en faire un script exécutable immédiatement, sans procédure d’installation.
- L’exemple
jam_users.py teste l’endpoint /users d’une API Go : il supprime et recrée des utilisateurs avec httpx, IPython et loguru, puis ouvre un REPL.
- Avec la méthode classique, il faut soit installer les paquets globalement dans le Python système, soit préparer manuellement un environnement virtuel, ce qui rend le partage et la réexécution du script fastidieux.
- En déclarant
dependencies = ["ipython", "httpx", "loguru"] dans l’en-tête # /// script, puis en lançant uv run jam_users.py, uv se charge de l’environnement isolé et de l’installation des dépendances.
- En ajoutant le shebang
#!/usr/bin/env -S uv run --script et les droits d’exécution, on peut l’exécuter sur un système Unix avec seulement uv installé, via ./jam_users.py.
Déclarer les dépendances dans le script
- L’exemple
jam_users.py prépare des données utilisateur de test pour l’API locale http://localhost:4000/v1/users.
- Envoie des requêtes API avec httpx
- Ouvre le REPL de IPython pour inspecter les réponses et poursuivre les tests
- Journalise les suppressions et créations avec
loguru
- Le script de base suit un flux qui réinitialise la liste des utilisateurs avant de recharger les données de test.
- Récupère la liste existante avec
GET /v1/users
- Appelle
DELETE /v1/users/{id} pour chaque utilisateur
- Envoie la liste d’utilisateurs préparée en JSON vers
POST /v1/users
- Lance ensuite un REPL avec
IPython.embed()
- Pour l’exécuter avec
python jam_users.py, httpx, IPython et loguru doivent déjà être installés dans l’environnement d’exécution.
- On peut aussi les installer globalement dans le Python système ou créer un environnement virtuel séparé, mais les deux approches demandent une préparation avant l’exécution.
- L’installation globale peut encombrer le Python système avec des paquets
- Avec un environnement virtuel, il faut gérer soi-même la création, l’activation, l’installation et l’exécution
- Dans les deux cas, il faut aussi disposer d’un Python système compatible avec les paquets requis
Créer un script exécutable directement avec uv
- En plaçant le tag
# /// script de uv en haut du script, on peut déclarer les dépendances directement dans le fichier.
# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
- Une fois cet en-tête présent, on peut lancer le script avec
uv run jam_users.py.
- uv crée un environnement virtuel isolé pour le script
- Télécharge et installe les dépendances nécessaires
- Exécute ensuite le script dans le contexte de cet environnement virtuel
- Un shebang Python classique comme
#!/usr/bin/env python ne permet pas d’exploiter l’en-tête de script de uv, car Python ignore les commentaires # /// script.
- En appelant uv directement dans le shebang, on peut traiter le script comme un exécutable.
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
- Le flag
-S de env indique que la chaîne qui suit doit être découpée en arguments distincts et transmise à env.
- Après avoir accordé les droits d’exécution avec
chmod +x jam_users.py, on peut l’exécuter directement ainsi :
./jam_users.py
- Avec cette méthode, tant que uv est installé sur le système Unix, le script peut être exécuté sans installation séparée des dépendances ni gestion manuelle d’un environnement virtuel.
- Lorsqu’on transmet un script Python complexe à d’autres utilisateurs, cela réduit la charge consistant à devoir expliquer en détail toute la préparation du système avant l’exécution.
1 commentaires
Avis Hacker News
Ce n’est pas UV en particulier, mais plus généralement la façon de contrôler l’exécution du code via des commentaires qui me dérange.
Utiliser des commentaires pour des directives de linter ou des notes de développeur, très bien, mais s’il s’agit de configuration ou de données liées à l’exécution, une forme comme
UV_ENV = { "dependencies": { "requests": "2.32.3", "pandas": "2.2.3" } }me semble bien meilleure.Cette approche est une syntaxe Python valide, utilise des structures de données standard plutôt qu’un parsing arbitraire de commentaires, ce qui facilite la génération et la validation, et surtout respecte le principe selon lequel le code doit s’exécuter de la même manière même si l’on supprime tous les commentaires.
La méthode proposée reste une constante magique qui ne fait rien au runtime et n’est parsée que par analyse statique ; un autre outil pourrait aussi la voir comme du code inutilisé et la supprimer au lieu de la laisser à uv.
À la place, on pourrait faire un appel direct à uv pour lui dire quoi faire, par exemple
import uvsuivi deuv.exec(dependencies=["clown"], python=">=3.10").La première exécution se ferait avec n’importe quel runtime Python capable de trouver ce paquet
uvhypothétique ; le paquetuvpréparerait ensuite l’environnement virtuel et le runtime Python, puis ferait unre-exec(3)avec un indicateur, par exemple une variable d’environnement.Lors du second runtime,
uv.execdétecterait cet indicateur et ne ferait rien.https://peps.python.org/pep-0723/
Et la ligne shebang elle-même fonctionne en fait comme un commentaire.
Elle est tellement profondément ancrée depuis 45 ans que les gens ne pensent plus vraiment au fait qu’il s’agit d’un commentaire shell.
Si les mêmes dépendances sont installées, le code s’exécute de la même manière.
Cela ne change pas vraiment le sens du code lui-même, mais plutôt l’environnement dans lequel il s’exécute ; en ce sens, ce n’est pas différent du commentaire
#!/bin/bashen tête d’un script shell.Cela dit, uv ne voudra probablement pas exécuter le code pour déterminer les dépendances ; il faudra donc que ce soit un sous-ensemble très limité de la syntaxe Python.
Le simple fait d’avoir besoin de ce genre de chose révèle une faiblesse du langage.
L’instruction
importelle-même devrait pouvoir transmettre toutes les informations relatives aux dépendances.Ces derniers mois, ce sujet est revenu très souvent sur HN ; voici des exemples récents :
https://news.ycombinator.com/item?id=43500124
https://news.ycombinator.com/item?id=42463975
J’aime bien uv, mais j’ai du mal avec l’expression self-contained pour deux raisons.
Premièrement, pour exécuter le script, uv doit déjà être installé.
On peut certes faire un script shell qui vérifie si uv est installé et, sinon, l’installe via curlpipe, mais cela ajoute pas mal de boilerplate, et la méthode curlpipe elle-même n’est pas idéale.
Deuxièmement, créer automatiquement un environnement virtuel quelque part dans le répertoire personnel n’est pas vraiment self-contained.
Même si on l’exécute une seule fois puis qu’on supprime le script, cet environnement virtuel reste là et occupe de l’espace ; je n’ai pas trouvé dans la documentation d’uv de garantie indiquant que ces environnements virtuels temporaires sont nettoyés automatiquement.
curl | shest valable.Cela dit, à mesure que les gestionnaires de paquets commenceront à inclure uv dans leurs dépôts, ce sera de moins en moins un problème.
Par exemple, uv est déjà disponible dans Alpine Linux et Homebrew : https://repology.org/project/uv/versions
De plus, les métadonnées de script inline font partie du standard Python.
Si uv n’est pas présent sur le système et n’est pas empaqueté, mais qu’une version de Python compatible avec le script est disponible, on peut l’exécuter avec pipx : https://pipx.pypa.io/stable/examples/#pipx-run-examples
pipx est empaqueté beaucoup plus largement : https://repology.org/project/pipx/versions
Sur une machine de développeur quelconque, Docker peut être plus courant qu’uv, et comme l’article parle aussi d’un projet d’entreprise, ça paraissait plausible.
En revanche, pour l’instant il ne met rien en cache, donc il retélécharge à chaque exécution, ce qui est maladroit ; cela devrait pouvoir se corriger avec des volumes.
Cela ressemble à ceci : https://hugojosefson.github.io/docker-shebang/#python
Cela dit, si l’exécution télécharge depuis Internet des éléments dont on ne connaît pas vraiment la nature, il est difficile d’appeler cela self-contained ; le vrai self-contained complet ressemble davantage à une AppImage.
Avec quelque chose comme py2exe, on peut créer un « script Python » self-contained.
Cela crée beaucoup de problèmes pour le développeur, mais en minimise pour l’utilisateur.
On peut faire la même chose avec Nix, avec une ligne shebang qui ressemble à ceci
#! nix-shell -i python3 -p "python312.withPackages (pkgs: [ pkgs.boto3 pkgs.click ])"Ainsi, la seule chose nécessaire sur le système est Nix ; Python n’a même pas besoin d’être installé
L’exemple le plus obvious est un script bash avec toutes ses dépendances déclarées, et j’ai déjà fait de petits scripts Rust mono-fichier rapides avec un shebang Nix
https://nixos.wiki/wiki/Nix-shell_shebang
nix shell, plutôt qu’avecnix-shellComme le disent d’autres commentaires, l’affirmation « self-contained » repose sur le fait que
uvsoit installéSi vous voulez vraiment un script Python self-contained, le compilateur Nuitka vaut le détour
Nous l’utilisons sans problème en production pour des services gRPC, et il suffit de lancer
nuitka --onefile run.pyComme c’est un compilateur, le binaire obtenu peut aussi être plus rapide que le programme Python d’origine empaqueté avec PyInstaller
La page GitHub de l’auteur dit : « La mission de ma vie est d’essayer, jusqu’à mourir de vieillesse, de créer le meilleur Python Compiler possible »
https://nuitka.net/
https://github.com/kayhayen
J’aime beaucoup ce pattern, mais malheureusement je n’ai pas réussi à le faire fonctionner correctement avec le LSP
J’utilise pyright dans Helix, et même lancer l’éditeur avec
uv run hx script.pyne marche pasOn peut faire quelque chose comme
uv run --with whatever-it-is-i-need hx script.py, mais les duplications s’accumulent viteuvefait maison$ cat ~/.local/bin/uve#!/bin/bashtemp=$(mktemp)uv export --script $1 --no-hashes > $tempuv run --with-requirements $temp vim $1unlink $tempJ’aimerais que l’éditeur prenne bientôt en charge
uv python find --scriptÇa a l’air assez utile
Je me demande si uv est un choix plus sûr pour distribuer des projets basés sur Python sur le long terme
Je repense au rug pull d’Anaconda : il y a environ 5 ans, nous l’utilisions pour la gestion des dépendances, puis les règles ont changé et les clients appartenant à des organisations de plus de 200 employés ne pouvaient plus l’utiliser gratuitement ; ils devaient payer une licence commerciale
Ils peuvent arrêter le développement ou déplacer les travaux futurs vers un fork sous une autre licence, mais ils ne peuvent pas modifier rétroactivement la licence passée ; ce qui existe aujourd’hui est donc garanti open source
Si cela vous inquiète vraiment, vous pouvez créer un fork et continuer à le synchroniser
En pratique, c’est presque la même chose pour les autres projets OSS, donc je ne m’en inquiéterais pas trop
À ma connaissance, conda n’a jamais été open source et distribuait des binaires
https://github.com/astral-sh/uv?tab=readme-ov-file#license
Les paquets de conda-forge restent utilisables gratuitement
uv utilise simplement PyPI, donc en cas de problème on peut passer de uv à pip, Poetry ou autre, et continuer à récupérer les paquets au même endroit
Ce propriétaire finira forcément par être racheté par le pire milliardaire imaginable
J’ai parcouru le guide de contribution et les issues d’uv, mais je n’ai pas vu de CLA ; PyTorch, lui, avait un CLA tout en haut de son guide de contribution
Il aurait tout de même dû y avoir un fork communautaire de la dernière version FOSS d’Anaconda
C’est ce qui s’est passé avec Redis, qui utilise aussi un CLA : https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md#redis-software-grant-and-contributor-license-agreement
Il ne faut jamais signer de CLA
À mon avis, autant ne contribuer qu’à des projets copyleft
Nous sommes trop bien payés pour travailler gratuitement
Pour de petits utilitaires, cela ressemble à une bonne alternative de packaging à la conteneurisation
Il faut maintenant que je convainque tous mes collègues d’installer uv
Ça ressemble à bundler/inline côté Ruby
Je suis content de voir quelque chose de similaire arriver en Python, et c’est vraiment très pratique
https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html
Je me demande s’il y a des gens qui ont réussi à faire fonctionner ça sous Windows
Je voulais utiliser cette astuce pour un outil destiné à un mod de jeu sur lequel je travaille, mais je n’ai pas réussi à faire fonctionner l’astuce du shebang
$> uv init --script .py$> uv add --script .py ...$> uv add --script .py --dev ...$> uv run .pyJ’espère que ça aidera
Source : https://docs.astral.sh/uv/guides/scripts/
.pyLe py launcher prend en charge les lignes shebang
C’était aussi abordé dans un billet de blog sur le même sujet publié il y a quelques jours, et d’après celui-ci il faut retirer
-S: https://thisdavej.com/share-python-scripts-like-a-pro-uv-and-pep-723-for-easy-deployment/https://news.ycombinator.com/item?id=43500124
Je n’ai pas essayé moi-même ; à la place, j’ai changé l’association de fichiers pour que tous les fichiers
.pys’ouvrent par défaut avecuv runhttps://docs.python.org/3/using/windows.html#python-launcher-for-windows
https://peps.python.org/pep-0397/
https://pyinstaller.org
https://onor.io/2025/01/more-scripting-with-racket.html
Ce cas d’usage m’a fait aimer uv, mais le fait qu’une PEP officielle et très utile ne soit pas prise en charge par les outils Python officiels me semble contraire au Zen of Python
De mon point de vue, c’est la première fois que Python n’était pas “batteries included”
Maintenant, j’ai aussi deux gestionnaires de dépendances Python sur mon système
Je sais qu’il y a beaucoup à dire sur la gestion des dépendances en Python, mais ces dernières années, quand un projet avait seulement un
requirements.txt, je m’en sortais avec lepip+venvde baseSi je me souviens bien,
pyproject.tomlest arrivé avant la bibliothèquetomllibDonc, pendant quelques versions, il fallait spécifier les modules dans un langage que Python ne pouvait pas lire nativement
C’est pire que d’avoir une méthode par défaut qui inclut des métadonnées inutilisées
C’est justement pour ça que ce sont des métadonnées au départ ; sinon, ce serait simplement de la syntaxe Python