14 Punkte von GN⁺ 2025-06-25 | 2 Kommentare | Auf WhatsApp teilen
  • Mit dem uv-Paketmanager und PEP 723 lassen sich Python-Skripte ohne Abhängigkeitsprobleme ausführen
  • Die Funktion uvx erstellt automatisch eine Disposable Virtual Environment und beseitigt so den Aufwand bei der Einrichtung der Umgebung
  • Wenn PEP-723-Metadaten in eine Python-Datei eingebettet werden, werden automatische Skriptausführung und Paketverwaltung deutlich einfacher
  • Als Beispielskript lässt sich ein Programm zum Extrahieren von YouTube-Untertiteln schnell umsetzen und verteilen
  • Dadurch ist es nun auch in Python möglich, kompakte einzeln ausführbare Dateien zu schreiben, was die Nutzbarkeit von Skripten deutlich verbessert

Überblick

  • Beim Ausführen von „One-off-Skripten“ in Python musste man bisher jedes Mal die Umgebung einrichten und Pakete installieren; diese Unannehmlichkeit verschwindet mit der Einführung von uv und PEP 723
  • uv ist ein in Rust entwickelter schneller Python-Paket- und Projektmanager und erledigt mit der neuen Funktion uvx die Einrichtung von Disposable Virtual Environments, automatische Paketinstallation und die Verknüpfung mit Python-Versionen sehr schnell und einfach

Vorteile von uv und uvx

  • Die Funktion uvx arbeitet ähnlich wie npx im Nodejs-Ökosystem und erstellt schnell eine Ausführungsumgebung für ein angegebenes Python-Paket, zum Beispiel ruff
  • Disposable Virtual Environments werden als Cache genutzt und ermöglichen eine schnelle Ausführung ohne Overhead
  • Einrichtung der Umgebung und Installation von Abhängigkeiten erfolgen mit einem einzigen Befehl, sodass Entwickler keine separate Umgebungsverwaltung mehr benötigen

Einführung und Nutzung von PEP 723

  • PEP 723 definiert einen Standard, mit dem sich Metadaten zu Abhängigkeiten und Umgebung am Anfang eines Python-Skripts einbetten lassen
  • Beispiel: Am Anfang des Codes können requires-python, dependencies und Ähnliches angegeben werden
  • Externe Tools, die dies erkennen, etwa uv, übernehmen anhand der im Skript hinterlegten Informationen automatisch Installation, Einrichtung der Umgebung und Ausführung

Die Kombination aus uv und PEP 723

  • Werden diese Metadaten am Anfang einer echten Python-Beispieldatei eingetragen und mit dem run-Befehl von uv ausgeführt, werden sofort alle benötigten Pakete automatisch installiert, die angegebene Python-Version eingerichtet und anschließend der Code ausgeführt
  • Der Beispielcode ruft eine Internet-API auf, nämlich die PEP-Liste, und gibt das Ergebnis aus. Zusätzliche Paketinstallation oder Umgebungssetup sind nicht nötig; ein einzelner Befehl genügt

Praxisbeispiel: ein YouTube-Untertitel-Skript

  • Es wird ein Python-Skript mit shebang (#!/usr/bin/env -S uv run --script) und PEP-723-Metadaten gezeigt
  • Externe Pakete wie youtube-transcript-api werden automatisch installiert und die virtuelle Umgebung wird ebenfalls automatisch eingerichtet
  • Der Nutzer kann einer ausführbaren Datei (ytt) eine YouTube-Video-URL oder ID als Argument übergeben und erhält die Untertitel sofort als Ergebnis
  • Nach dem Setzen der Ausführungsrechte mit chmod lässt sich das Skript einfach im Terminal starten

Verbesserte Developer Experience und erweiterte Möglichkeiten

  • Früher wurden für einfache Skripte oft eher Binärsprachen mit einer einzelnen ausführbaren Datei wie Go bevorzugt, doch nun bietet auch Python ein vergleichbares Maß an Komfort
  • Die Kombination aus uv und PEP 723 erleichtert das Teilen, Verteilen und die automatisierte Ausführung von Python-Skripten erheblich
  • Über das Github-Beispiel cottongeeks/ytt-mcp lassen sich auch komplexere Anwendungsfälle entwickeln

2 Kommentare

 
ndrgrd 2025-06-25

uv ist unglaublich schnell, das gefällt mir sehr. In letzter Zeit verwende ich nach Möglichkeit überall uv.

 
GN⁺ 2025-06-25
Hacker-News-Kommentare
  • Wie der Autor des Beitrags greife ich in letzter Zeit eher zu plattformübergreifenden Python-One-offs und persönlichen Skripten statt zu Go, bin aber unzufrieden damit, dass sich das Python-Type-Checking-System wie pures Chaos anfühlt. Ich hoffe, dass Tools wie ty und pyrefly das wenigstens ein Stück weit verbessern.

  • Inzwischen fühlen sich Python-Skripte so an, als würden sie einfach direkt gut funktionieren, ohne dass man mit virtualenv leiden muss. So eine Erfahrung hätte ich gern auch bei Shell-Skripten. Packaging, Abhängigkeitsmanagement und Reproduzierbarkeit sind dort weiterhin auf Steinzeitniveau. Im Moment ist die Realität immer noch entweder curl | bash und auf Glück hoffen oder eine README-Datei mit 3 fehlenden Abhängigkeiten und 12 manuellen Schritten. Nix? Wirkt wie eine brauchbare Option nur für Leute, die Zeit, Raum und das Nix-Handbuch bereits transzendiert haben. Docker? Eine gute Wahl, falls es für einen vernünftig klingt, eine Linux-Distribution herunterzuladen, nur um einmal sed auszuführen. Es braucht unbedingt einen einfachen, deklarativen Mittelweg, den jeder nutzen kann.

    • Ich bevorzuge ausdrücklich den Ansatz, nur Shell-Skripte zu schreiben, die ausschließlich Binärdateien und Bibliotheken verwenden, die auf dem Ziel-OS bereits standardmäßig vorhanden sind. Shell-Skripte portabel machen zu wollen, ist als Ziel in der Praxis keine besonders passende Wahl. Wenn man ein Shell-Skript mit macOS-spezifischen Befehlen unter Linux nutzen muss, kann das kein Paketmanager lösen.
    • Nix ist für diesen Zweck in der Nutzung gar nicht so schwierig. Wenn man Nix auf einer anderen Distribution installiert, kann man es zum Beispiel recht einfach so verwenden. NixOS und das gesamte Nix-Packaging sind zwar ziemlich anspruchsvoll, aber auf Shell-Skripte begrenzt ist die Einstiegshürde eher niedrig.
    • Ich sehe eigentlich keinen Grund, überhaupt noch neue Shell-Skripte zu schreiben. Wenn man in einer Umgebung arbeiten darf, in der alle Abhängigkeiten installiert werden können, erledigen Tools wie uv alles. Wer Clojure mag, dem kann ich auch babashka empfehlen.
    • Dass Packaging, Abhängigkeitsmanagement und Reproduzierbarkeit bei Shell-Skripten wie aus einer vergangenen Ära wirken, liegt meiner Meinung nach daran, dass Shell ohnehin ungeeignet ist, sobald man so etwas braucht. Shell passt nur für kleine Skripte von vielleicht 20 Zeilen. Die Sprache selbst ist qualitativ nicht gut genug für mehr.
    • Empfehlung für mise. Wir nutzen es im Unternehmen zur Verwaltung der Entwicklungsumgebung, und damit lässt sich die Umgebung viel einfacher aufsetzen als mit Docker oder Nix. Die Unterstützung für parallele Installationen ist gegenüber einer normalen Dockerfile ein großer Vorteil.
  • Ein wirklich großartiger Trend, der sich immer weiter verbreitet. Ich habe zuerst im Blog von simonw davon gelesen und den Zusammenhang dann in einem Blogpost von simonwillison nachgesehen. Im März dieses Jahres gab es dazu auch schon eine Hacker-News-Diskussion zu einem anderen Blogbeitrag. Ich hoffe, dieser Trend bleibt lange genug auf der Startseite, damit ihn noch mehr Leute wahrnehmen.

  • Ich habe uv in kleinen Projekten ausprobiert und die Erfahrung ist wirklich hervorragend. Mit der Kombination aus uv run und uv tool run (uvx) ist das Installieren und direkte Ausführen von Python-Skripten von GitHub auf einer VM extrem einfach. Kein git clone, kein Erstellen oder Betreten eines venv, kein pip install. Vor allem ist uv so schnell, dass ich anfangs dachte, irgendetwas müsse kaputt sein; tatsächlich ist es etwa 10-mal schneller als pip. Tooling und Dokumentation sind zwar noch ein wenig unfertig, aber bei so viel Innovationskraft und praktischem Nutzen ist es trotzdem absolut verwendbar.

    • Ich bin ehrlich beeindruckt, dass uv Abhängigkeiten schneller installiert, als pyenv --help ausgeben kann.
  • Auch Rust entwickelt eine ähnliche Idee von Shell-Skripten mit Single-File-Typ weiter. Dort habe ich so einen Ansatz zuerst gesehen, also die Unterstützung für das Ausführen einzelner Dateien inklusive Abhängigkeitsmanagement. Ich hoffe, dieses Muster setzt sich in noch mehr Sprachen durch; für das Austauschen per Gist oder das schnelle Schreiben kleiner Tools ist es extrem nützlich. Siehe auch das cargo-script-RFC-Dokument.

  • Wenn man uv run --script verwendet und die Metadaten im Skript selbst einbettet, ist es etwas umständlich, direkt aus dem Skript eine Python-REPL zum Bearbeiten und Testen zu öffnen. Zum Beispiel muss man so etwas machen:

    $ uv run --python=3.13 --with-requirements <(uv export --script script.py) -- python
    >>> from script import X
    

    Ich würde mir eine kompaktere Variante wünschen. Zum Beispiel wäre so etwas ideal:

    $ uv run --with-script script.py python
    

    Tatsächlich kann man es aber so ausführen und landet direkt in der passenden Python- und venv-Umgebung für das Skript:

    $ "$(uv python find --script script.py)"
    >>> from script import X
    

    Allerdings muss man das Skript einmal ausführen, damit die Umgebung erzeugt wird.

    • Falls man eine Funktion braucht, die nach dem Setup eine REPL startet, lohnt sich ein Blick auf dieses Gist-Beispiel. Man kann dem Skript etwa ein Flag wie --interactive hinzufügen und als CLI-Option anbieten. Kleine CLIs auf Basis von Typer schreibe ich oft genau so. In einem Dev-Skript bin ich über ein --sql-Flag auch schon in eine DuckDB-SQL-REPL eingestiegen.
    • Noch ein einfacher Ansatz:
      cat ~/.local/bin/uve
      #!/bin/bash
      temp=$(mktemp)
      uv export --script $1 --no-hashes > $temp
      uv run --with-requirements $temp vim $1
      unlink $temp
      
  • Wenn man conda verwendet, kann man in einem Shell-Wrapper für Python-Skripte auch direkt die Umgebung aktivieren. Zum Beispiel so:

    #!/usr/bin/env bash
    eval "$(conda shell.bash hook)"
    conda activate myenv
    python myscript
    

    Das ist allerdings kein so eigenständiger Ansatz wie im Stil von PEP 723.

  • Nachdem ich gestern und heute die HN-Threads gesehen hatte, habe ich mich zum ersten Mal an uv gewagt und war von der Geschwindigkeit und der einfachen Abhängigkeitsverwaltung tief beeindruckt. Mit besserer offizieller Dokumentation wäre es noch besser, besonders mit einer Anleitung für den Umstieg von einem requirements.txt-Workflow auf uv. Auch die projektspezifische Festlegung der Python-Version ist etwas verwirrend, weil sie an zwei Stellen definiert wird: .python-version und pyproject.toml.

    • Ich habe einmal ein E-Book als Explorer für Python-Entwicklertools geschrieben. Dabei habe ich schon auf Lücken in der offiziellen Dokumentation hingewiesen: Migration von requirements.txt und Ändern der Python-Version eines uv-Projekts sind einen Blick wert. Wenn es weitere Themen gibt, die ich behandeln sollte, freue ich mich immer über Vorschläge.
    • Das Feld requires-version in pyproject.toml bezeichnet den kompatiblen Versionsbereich, während .python-version die konkrete Version für die Entwicklung festlegt. Wenn man mit uv init startet, sehen beide anfangs gleich aus, aber mit der Zeit gibt requires-version eher die niedrigste noch unterstützte Version an, die unter .python-version liegen kann. requires-version landet außerdem in den Paketmetadaten und beeinflusst auch die Auflösung von Abhängigkeiten bei anderen, die das veröffentlichte Paket verwenden. Zum Beispiel wenn v1 noch ältere Python-3-Versionen unterstützt, v2 aber nicht mehr.
    • Ich empfinde das ähnlich, halte aber an meinem eigenen Workflow fest: dieselbe Datei per Dropbox auf alle Rechner synchronisieren und plattformunabhängig verwenden. Wie bei npm oder dotnet braucht man beim Plattformwechsel zwar npm update oder dotnet restore, aber ein venv funktioniert danach problemlos weiter. Bei uv wirkt der Umgang mit einem Plattformwechsel dagegen komplizierter und es scheint mehr manuelle Bereinigung nötig zu sein.
    • pyproject.toml dient, unabhängig von uv selbst, dazu, die benötigte Umgebung zu definieren, wenn man Code mit externen Entwicklern und Nutzern teilt. Beim Bauen eines Pakets für PyPI beschreibt es, welche Umgebung erforderlich ist, und der Versionsbereich wird so gesetzt, dass mehr Leute den Code wiederverwenden können. .python-version ist nur für uv gedacht, und zwar nur für das Setup meiner eigenen Entwicklungsumgebung. Wenn bereits eine vorgefertigte Umgebung existiert, muss man das nicht unbedingt neu definieren. uv ist zwar noch kein offizielles Build-Backend, arbeitet aber an dieser Funktion (Issue #3957).
    • Ich habe das nie genauer untersucht, aber ich vermute, dass die Datei .python-version im Wesentlichen der Kompatibilität mit anderen Tools dient, die keinen TOML-Parser haben.
  • Ich wollte früher einmal ein Tool bauen, das Python-Skripte ihre Abhängigkeiten selbst installieren lässt. Das Ziel war ein Tool, das ähnlich wie uvx funktioniert, aber nur Python selbst voraussetzt. Ein Nachteil ist allerdings, dass man am Anfang des Skripts mehrere etwas seltsam aussehende Zeilen einfügen muss. Wer neugierig ist: Auf PyPI ist es unter dem Namen pysolate veröffentlicht.

    • Es gibt auch das ähnliche, aber wenig verbreitete Projekt isolate. Die Vorgehensweise ist etwas anders, aber als Ansatz interessant.
  • Eine von COBOL inspirierte Nachricht im Stil von Grace Hopper. Es sollte die Kultur geben, dass jedes Python-Programm eine ENVIRONMENT division definiert und seine Compile- und Laufzeitumgebung einschließlich Hardware- und Softwareanforderungen klar dokumentiert. So eine Struktur hätte entscheidenden Einfluss darauf, die Portabilität von Programmen zwischen unterschiedlichen Systemen zu verbessern.