uv unter macOS: Python-Versionen, Projekte und Tools verwalten

Automatische Übersetzung

Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

Diese Anleitung richtet sich an Python-Entwickler unter macOS, die von pip, venv, pip-tools, pyenv oder pipx zu uv wechseln. uv unterstützt vier Arten von Aufgaben: ein Projekt, ein Skript in einer einzelnen Datei, einen einmaligen CLI-Aufruf mit uvx und eine auf PATH installierte CLI. Alle vier können die Python-Versionen nutzen, die uv installiert. Sie lernen die Befehle für jede davon und wie Sie zwischen ihnen wählen.

Ich habe meinen eigenen Python-Workflow auf uv umgestellt. Eine einzige ausführbare Datei deckt jetzt die Arbeit ab, die ich früher auf diese Tools verteilt habe.

Schnellstart

# 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 .

Die Abbildung zeigt, wo jeder der vier Workflows seine Umgebung ablegt, was Sie für jeden behalten und welche verwalteten Python-Installationen sie gemeinsam nutzen.

Vier uv-Workflows mit Speicherbereichen: die Projekt-.venv neben pyproject.toml und uv.lock, Skript- und uvx-Umgebungen im löschbaren uv-Cache, eine installierte Tool-Umgebung im Tool-Verzeichnis, die uvx wiederverwenden kann, und verwaltete Python-Installationen für alle vier. Installierte Befehle benötigen das Verzeichnis der ausführbaren Dateien auf PATHVier uv-Workflows mit Speicherbereichen: die Projekt-.venv neben pyproject.toml und uv.lock, Skript- und uvx-Umgebungen im löschbaren uv-Cache, eine installierte Tool-Umgebung im Tool-Verzeichnis, die uvx wiederverwenden kann, und verwaltete Python-Installationen für alle vier. Installierte Befehle benötigen das Verzeichnis der ausführbaren Dateien auf PATH

uv installieren und aktualisieren

Homebrew ist ein bequemer Installationsweg unter macOS. Die unterstützten Methoden finden Sie in der Installationsanleitung von uv:

brew install uv
uv --version

Wenn Homebrew uv installiert hat, sollte Homebrew es auch aktualisieren:

brew upgrade uv

uv self update ist für die Standalone-Installation von uv gedacht und bei Installationen über einen Paketmanager deaktiviert. Aktualisieren Sie uv mit demselben Tool, mit dem Sie es installiert haben.

So sehen Sie, wo uv, seine Python-Installationen, seine Tools und sein Cache liegen:

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

Verwaltete Python-Installationen, persistente Tools und löschbare Cache-Einträge liegen in getrennten Verzeichnissen. Der Cache darf gelöscht und neu aufgebaut werden. Er ist keine maßgebliche Datenquelle. Unter macOS klont uv Pakete aus dem Cache in jede Umgebung (Copy-on-Write), statt sie zu kopieren. Beim Neuaufbau von .venv verwendet uv benötigte Artefakte wieder, die noch im Cache liegen.

Projekte: Deklarationen, Auflösung und Umgebung

Ein uv-Projekt besteht normalerweise aus drei Teilen:

  • pyproject.toml deklariert die Projektmetadaten und die direkten Abhängigkeiten.
  • uv.lock speichert die plattformübergreifende Auflösung von uv.
  • .venv ist die lokal installierte Umgebung und sollte jederzeit neu aufgebaut werden können.

Die Abbildung sortiert die Projektdateien danach, wer sie schreibt und ob git sie behält.

Sie bearbeiten pyproject.toml, .python-version und den Quellcode; uv lock schreibt uv.lock; alle vier werden committet. uv sync baut .venv aus uv.lock auf, die Pakete werden aus dem uv-Cache geklont, und beides bleibt außerhalb von gitSie bearbeiten pyproject.toml, .python-version und den Quellcode; uv lock schreibt uv.lock; alle vier werden committet. uv sync baut .venv aus uv.lock auf, die Pakete werden aus dem uv-Cache geklont, und beides bleibt außerhalb von git

Erstellen Sie eine Anwendung und fügen Sie Laufzeit- und Testabhängigkeiten hinzu:

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

Die Anwendung gibt Hello from forecast-app! aus; der letzte Befehl prüft, ob pytest installiert ist. Sobald Sie Tests haben, führen Sie sie mit uv run --group test pytest aus.

Die Vorlage --no-package erstellt main.py, pyproject.toml, README.md und .python-version ohne Build-System. Seit uv 0.12 erstellt ein einfaches uv init eine paketierte Anwendung mit src-Layout und Build-Backend. Verwenden Sie uv init --lib für eine paketierte Bibliothek. Siehe die Anleitung zur Projekterstellung von uv.

uv run prüft das Projekt, aktualisiert bei Bedarf den Lock, synchronisiert die benötigten Abhängigkeiten und führt den Befehl aus. Diesen Ablauf beschreibt die Dokumentation zu Projektlayout und Sync von uv. Die erste Projektoperation erstellt bei Bedarf .venv und uv.lock. uv init selbst erstellt nur die Projektdateien.

uv installiert die Gruppe dev standardmäßig. Andere Gruppen wie test brauchen --group bei jedem Befehl oder default-groups unter [tool.uv]. Um nur eine Gruppe zu installieren, zum Beispiel in einem CI-Job für die Dokumentation, verwenden Sie uv sync --only-group docs.

Gruppen bleiben lokal und werden nie veröffentlicht. Ein veröffentlichtes Extra ist etwas anderes: uv add --optional cli rich fügt ein Extra hinzu, das Nutzer als forecast-app[cli] installieren.

Um das Projekt in Jupyter-Notebooks in VS Code zu nutzen, führen Sie uv add --dev ipykernel aus und wählen .venv als Kernel. Siehe die Jupyter-Anleitung von uv.

Eingaben committen, nicht die Umgebung

Committen Sie pyproject.toml, uv.lock, den Quellcode und eine bewusst gesetzte .python-version. Ignorieren Sie .venv und die Caches von uv.

Wer das Repository klont, bekommt die gelockte Umgebung mit einem einzigen Befehl:

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

So sehen Sie, warum ein Paket installiert ist, und so bringen Sie den Lock auf neuere Versionen:

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

Committen Sie die geänderte uv.lock zusammen mit der Änderung, die sie nötig gemacht hat.

Der Lock hält eine Auflösung über die unterstützten Marker und Plattformen fest. Er macht native Wheels unter macOS, Linux, Intel und Apple Silicon nicht identisch. Testen Sie jede Deployment-Plattform.

Einen Teil davon kann uv erzwingen. required-environments unter [tool.uv] lässt die Auflösung fehlschlagen, wenn ein Paket ohne Source-Distribution kein Wheel für eine aufgeführte Plattform hat:

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

no-build-package verbietet Source-Builds für benannte Pakete. Ein fehlendes Wheel lässt die Installation dann fehlschlagen, statt einen Compiler zu starten. Wenn Sie PyTorch auch unter Linux mit CUDA deployen, folgen Sie für die Index-Einrichtung der PyTorch-Anleitung von uv.

Aktualität des Locks: --locked ist nicht --frozen

Standardmäßig dürfen Projektbefehle uv.lock aktualisieren, wenn sich Deklarationen ändern. Die Flags für Aktualität und Exaktheit beschreibt die Dokumentation zu Locking und Syncing von uv.

Verwenden Sie --locked, um zu verlangen, dass der Lock zu den Projektmetadaten passt:

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

Wenn pyproject.toml und uv.lock nicht übereinstimmen, schlagen diese Befehle fehl, statt einen neuen Lock aufzulösen. Verwenden Sie sie in CI, damit ein veralteter Lock den Build fehlschlagen lässt.

Verwenden Sie --frozen nur, wenn uv bewusst den vorhandenen Lock nutzen soll, ohne zu prüfen, ob er aktuell ist:

uv sync --frozen

Das kann in einer kontrollierten Build-Phase nützlich sein, in der der Lock bereits geprüft wurde. Veraltete Locks erkennt es aber nicht.

Die Abbildung folgt einem veralteten Projekt durch vier Befehle: rich wurde von Hand zu pyproject.toml hinzugefügt, und uv.lock wurde nicht aktualisiert.

Mit rich in pyproject.toml, aber nicht in uv.lock: uv sync schreibt den Lock neu und installiert rich, uv sync --locked und uv lock --check schlagen fehl, und uv sync --frozen installiert httpx ohne rich, während CI grün bleibtMit rich in pyproject.toml, aber nicht in uv.lock: uv sync schreibt den Lock neu und installiert rich, uv sync --locked und uv lock --check schlagen fehl, und uv sync --frozen installiert httpx ohne rich, während CI grün bleibt

Exaktheit ist eine von der Aktualität getrennte Einstellung. uv sync ist standardmäßig exakt und entfernt überzählige Pakete. uv run synchronisiert standardmäßig nicht exakt, sofern nicht --exact angegeben ist. Die Abbildung verfolgt ein von Hand installiertes Paket durch uv sync, uv run und uv run --exact.

Eine .venv mit httpx, ipykernel und dem von Hand installierten extra-pkg: uv sync und uv run --exact entfernen extra-pkg, uv run behält esEine .venv mit httpx, ipykernel und dem von Hand installierten extra-pkg: uv sync und uv run --exact entfernen extra-pkg, uv run behält es

Python-Versionen installieren und festlegen

uv kann Python-Distributionen herunterladen und verwalten:

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

Die verwalteten CPython-Builds von uv stammen aus dem Projekt python-build-standalone. uv erkennt außerdem System-, Homebrew-, pyenv-, Conda- und andere Interpreter. Die unterstützten Quellen und das Erkennungsverhalten beschreibt die Dokumentation zu Python-Versionen von uv.

.python-version ist eine Versionsanforderung, die uv und andere kompatible Tools erkennen. uv python pin schreibt diese Datei, und uv init legt eine an. requires-python in pyproject.toml ist der Kompatibilitätsvertrag des Projekts. Setzen Sie beide bewusst:

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

uv nimmt die Versionsanforderung zuerst aus --python, dann aus .python-version, dann aus requires-python.

Das Festlegen von 3.12 garantiert weder dauerhaft denselben Patch-Build noch dasselbe Artefakt auf jedem Betriebssystem. Fordern Sie eine exakte Patch-Version an, wenn Sie eine brauchen. Wählen Sie in CI den Interpreter aus und geben Sie ihn aus, mit uv run --python 3.12.7 python -VV oder indem Sie UV_PYTHON=3.12.7 für den Job setzen.

Verwenden Sie einen anderen Python-Manager, wenn ein Projekt eine Distribution oder Build-Konfiguration braucht, die uv nicht liefert. uv kann diesen Interpreter weiterhin über --python oder die normale Erkennung nutzen.

Wählen zwischen uv run, uvx und installierten Tools

Entscheiden Sie danach, ob das Tool die installierten Pakete des Projekts benötigt, auf PATH bleiben muss oder unabhängig laufen kann. Die Abbildung zeigt, welche Abhängigkeiten verfügbar sind und wie lange jede Umgebung besteht.

uv run verwendet die Projekt-.venv und deren gelockte Abhängigkeiten; uvx verwendet eine separate gecachte oder installierte Tool-Umgebung, ohne Projektabhängigkeiten automatisch zu installieren; uv tool install behält eine Umgebung im Tool-Verzeichnis und stellt den Befehl nach der PATH-Einrichtung bereituv run verwendet die Projekt-.venv und deren gelockte Abhängigkeiten; uvx verwendet eine separate gecachte oder installierte Tool-Umgebung, ohne Projektabhängigkeiten automatisch zu installieren; uv tool install behält eine Umgebung im Tool-Verzeichnis und stellt den Befehl nach der PATH-Einrichtung bereit

Projektgebundene Tools: uv run

Wenn pytest, mypy, ein Codegenerator oder ein anderes Tool das Projekt importieren oder dessen gelockte Plugins nutzen muss, deklarieren Sie es in einer Abhängigkeitsgruppe:

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

uvx installiert das Paket oder die Abhängigkeiten des Projekts nicht automatisch. Ein Tool kann weiterhin lokale Quelldateien importieren, wenn sein Importverhalten das zulässt. Verwenden Sie uv run, wenn es das installierte Projekt oder dessen Abhängigkeiten benötigt.

Ad-hoc-Tools: uvx

uvx ist ein Alias für uv tool run. Es verwendet ein kompatibles, mit uv tool install installiertes Tool wieder; andernfalls nutzt es eine isolierte Umgebung im löschbaren uv-Cache. Die Dokumentation zu uv-Tools beschreibt diesen Cache und den Lebenszyklus persistenter Tools:

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

Fixieren Sie die Tool-Version in CI oder in der Dokumentation. Ohne installiertes Tool wählt der erste Aufruf ohne Versionsangabe das neueste verfügbare Release; spätere Aufrufe können den Cache-Zustand wiederverwenden. @latest ignoriert eine installierte Version und aktualisiert den Cache für das neueste Release. Verwenden Sie --from, wenn sich der Paketname vom Befehlsnamen unterscheidet, wie bei jupyterlab und httpie.

Persistente ausführbare Dateien: uv tool install

Installieren Sie ein Tool, wenn Skripte außerhalb Ihrer Kontrolle seinen Befehl auf PATH brauchen oder wenn das Setup-Manifest Ihres Rechners es verwalten soll:

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

Wenn die Installation meldet, dass das Verzeichnis der ausführbaren Dateien auf PATH fehlt, führen Sie uv tool update-shell aus und starten Sie Ihre Shell neu, bevor Sie den Befehl verwenden. Siehe die Dokumentation zu ausführbaren Tool-Dateien von uv.

Auch persistente Tools nutzen isolierte Umgebungen. Verändern Sie diese Umgebungen nicht manuell mit pip.

Skripte in einer Datei mit PEP 723

PEP 723 definiert Inline-Metadaten für Skripte. Ein kompatibler Runner kann den Kommentarblock lesen und eine isolierte Umgebung aufbauen. Das Metadatenformat und das Verhalten des Runners beschreiben die Skript-Anleitung von uv und PEP 723.

Die Abbildung markiert den Metadatenblock in fetch.py und zeigt, was uv run und einfaches python mit der Datei tun.

fetch.py mit hervorgehobenem PEP-723-Kommentarblock: uv run liest den Block, findet Python 3.12 oder neuer, installiert httpx in eine isolierte Umgebung und führt den Code aus; python fetch.py führt nur den Code ausfetch.py mit hervorgehobenem PEP-723-Kommentarblock: uv run liest den Block, findet Python 3.12 oder neuer, installiert httpx in eine isolierte Umgebung und führt den Code aus; python fetch.py führt nur den Code aus

uv kann den Metadatenblock für Sie schreiben:

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

Ein fertiges Skript sieht so aus:

# /// 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)

Führen Sie das Skript mit uv aus und bearbeiten Sie die Metadaten damit:

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

Ein einfaches python fetch.py ignoriert die Metadaten im Kommentar. Das Skript braucht deshalb einen kompatiblen Runner, obwohl die Python-Syntax gültig bleibt. uv ist ein solcher Runner. pipx run, Hatch und PDM lesen den Block ebenfalls.

Um das Skript als ./fetch.py auszuführen, fügen Sie eine Shebang-Zeile hinzu, die uv aufruft:

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

Das Flag -S lässt env die Zeile in einzelne Argumente aufteilen, sodass --script bei uv ankommt.

Für ein schnelles Experiment übergeben Sie Abhängigkeiten auf der Kommandozeile, statt Metadaten zu schreiben. Die zweite Zeile startet eine Python-REPL mit verfügbarem pandas:

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

Für ein Skript, dessen Auflösung später reproduzierbar sein muss, erstellen Sie daneben einen Lock:

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

Das schreibt fetch.py.lock. Ein exclude-newer-Zeitstempel im Metadatenblock lässt uv Paketversionen ignorieren, die nach diesem Datum veröffentlicht wurden:

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

Das ist schwächer als ein Lock. Es fixiert keine exakten Versionen und kann keine Datei wiederherstellen, die der Index später entfernt.

Verwenden Sie stattdessen ein Projekt, wenn mehrere Dateien Abhängigkeiten teilen, der Code als Paket importierbar ist, Tests Projektzustand brauchen oder mehrere Skripte gemeinsam geändert werden müssen.

Bestehende requirements.txt-Projekte

Eine requirements.txt kann lose Eingaben, exakte Pins, Hashes, Constraints, Indexe, URLs oder einen von einem Resolver erzeugten Export enthalten. Ihre Reproduzierbarkeit hängt davon ab, wie sie erzeugt und verwendet wurde. Der Dateiname allein sagt nichts aus.

Sie haben zwei Möglichkeiten: das pip-Format behalten und mit uv pip ausführen oder, bei einer eigenen Anwendung, zu uv.lock migrieren.

Für eine vollständig aufgelöste Datei mit direkten und transitiven Abhängigkeiten nutzen Sie die pip-kompatible Schnittstelle von uv, ohne zu migrieren:

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

Führen Sie hier den installierten Interpreter direkt aus. Wenn eine pyproject.toml vorhanden ist, kann uv run deren Anforderungen vor der Ausführung auflösen und synchronisieren und dabei die gerade installierten Pakete ersetzen. Siehe die Skript-Anleitung von uv.

uv pip sync bringt die Umgebung in Übereinstimmung mit einer vollständig aufgelösten Datei. Enthält requirements.txt nur direkte Anforderungen, verwenden Sie uv pip install -r requirements.txt, um deren Abhängigkeiten aufzulösen und zu installieren. uv pip install -r fügt Pakete hinzu. Siehe die Anleitung zur pip-kompatiblen Versionsfixierung von uv.

Für eine eigene Anwendung kann eine schrittweise Migration sinnvoll sein:

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

Führen Sie den vollständigen Test- und Deployment-Pfad aus, bevor Sie alte Dateien löschen. Wenn ein nachgelagertes System weiterhin das pip-Format erwartet, leiten Sie es mit dem Export-Befehl von uv aus dem geprüften uv-Lock ab:

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

Pflegen Sie uv.lock und einen von Hand bearbeiteten Export nicht als zwei konkurrierende Auflösungen.

Cache und private Indexe

Der uv-Cache beschleunigt wiederholte Installationen, bleibt aber wegwerfbar:

uv cache prune
uv cache clean

Bevorzugen Sie prune für die regelmäßige Bereinigung. clean entfernt alle Cache-Einträge und erzwingt bei späteren Aufrufen erneute Downloads und Builds.

Eine Lock-Datei verbessert die Wiederholbarkeit. Sie macht Abhängigkeiten nicht vertrauenswürdig.

Für einen privaten Index tragen Sie Name und URL in pyproject.toml ein und halten die Zugangsdaten aus der Datei heraus:

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

Übergeben Sie die Zugangsdaten über UV_INDEX_INTERNAL_USERNAME und UV_INDEX_INTERNAL_PASSWORD oder über Keyring. uv bildet die Variablennamen aus dem Namen des Index. Siehe die Dokumentation zur Index-Authentifizierung von uv.

Alte Befehle und ihre uv-Entsprechungen

Wenn Sie von den älteren Tools wechseln, ordnet diese Tabelle jedem vertrauten Befehl den uv-Befehl zu, der dieselbe Aufgabe erledigt.

AufgabeVorheruv
Python installierenpyenv install 3.12uv python install 3.12
Neues Projekt und Umgebungmkdir proj && cd proj && python -m venv .venvuv init proj && cd proj && uv sync
Paket hinzufügenpip install pandas && pip freeze > requirements.txtuv add pandas
Pins kompilierenpip-compile requirements.inuv lock
Ein Paket aktualisierenpip-compile --upgrade-package httpxuv lock --upgrade-package httpx
In der Umgebung ausführensource .venv/bin/activate && python app.pyuv run app.py
Skript mit Abhängigkeiteneine manuell erstellte venv für eine DateiPEP-723-Block + uv run
Tool einmal ausführenpipx run blackuvx black
Tool installierenpipx install ruffuv tool install ruff
Reproduzierbare CI-Installationpip install -r requirements.txtuv sync --locked

Eine kompakte Checkliste für den Alltag

Für jedes Projekt:

  1. Definieren Sie requires-python und die direkten Abhängigkeiten in pyproject.toml.
  2. Trennen Sie veröffentlichte Extras (--optional) von lokalen Abhängigkeitsgruppen (--group).
  3. Committen Sie uv.lock. Ignorieren Sie .venv und den Cache-Zustand.
  4. Führen Sie projektgebundene Tools über die Projektumgebung aus.
  5. Verwenden Sie uv lock --check oder --locked in CI.
  6. Testen Sie jedes Zielbetriebssystem und jede Zielarchitektur, die der Lock abdeckt.
  7. Exportieren Sie Kompatibilitätsformate nur für benannte nachgelagerte Abnehmer.
  8. Halten Sie Index-Zugangsdaten aus committeten Dateien heraus. Übergeben Sie sie über Umgebungsvariablen oder Keyring.

Ich verwende uv unter macOS als mein Standard-Python-Tool, weil eine einzige ausführbare Datei diese Aufgaben abdeckt und Projekt- und Tool-Abhängigkeiten getrennt hält.

Referenzen