uv unter macOS: Python-Versionen, Projekte und Tools verwalten
Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.
Ich habe meinen Python-Workflow auf uv umgestellt. Damit wurden die Wechsel zwischen pip, virtuellen Umgebungen, pip-tools, pipx und Projektmanagern ersetzt, die ich zuvor manuell durchgeführt habe. Eine einzige ausführbare Datei deckt nun den größten Teil dieser Aufgaben ab.
Die Workflows unterscheiden sich weiterhin. Für Python-Entwickler, die unter macOS von pip, virtuellen Umgebungen, pip-tools oder pipx zu uv wechseln, gehören ein Projekt, ein Script mit Inline-Metadaten, ein einmalig ausgeführtes CLI und ein installiertes CLI jeweils zu einer anderen Umgebung und einem anderen Lifecycle. Die folgenden Befehle zeigen, wie man zwischen ihnen wählt.
Schnellstart
# 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 .
TL;DR. In Projekten verwende ich
uv add,uv lock,uv syncunduv run. Für eigenständige Scripts verwende ich PEP-723-Metadaten, für einmalige Toolsuvxund für Befehle, die aufPATHverfügbar bleiben müssen,uv tool install. In CI prüft--locked, ob die Projektmetadaten mituv.lockübereinstimmen.--frozenvertraut auf den vorhandenen Lock, ohne dessen Aktualität zu prüfen.
uv mit einer einzigen zuständigen Installationsmethode installieren
Homebrew ist eine bequeme Installationsmethode unter macOS. Die unterstützten Methoden findest du in uvʼs Installationsanleitung:
brew install uv
uv --version
Wenn Homebrew uv installiert hat, sollte Homebrew auch die Aktualisierung übernehmen:
brew upgrade uv
uv self update ist für uvʼs eigenständige Installationsmethode vorgesehen und bei Installationen über Paketmanager deaktiviert. Verhindere, dass zwei Installer um dieselbe ausführbare Datei konkurrieren.
Nützliche Befehle zur Identitätsprüfung:
command -v uv
uv python dir
uv tool dir
uv cache dir
Verwaltete Python-Installationen, persistente Tools und flüchtige Cache-Einträge liegen in getrennten Verzeichnissen. Der Cache kann gelöscht und neu aufgebaut werden. Er ist keine Source of Truth.
Projekte: Deklarationen, Auflösung und Umgebung
Ein uv-Projekt besteht normalerweise aus drei Artefakten:
pyproject.tomldeklariert Projektmetadaten und direkte Requirements.uv.lockspeichert die plattformübergreifende Auflösung von uv..venvist die lokal installierte Umgebung und sollte wegwerfbar sein.
Erstelle eine Anwendung und füge Runtime- und Test-Requirements hinzu:
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
Aktuelle uv-Anwendungstemplates erstellen main.py, pyproject.toml, README.md und .python-version. Standardmäßig definieren sie kein Build-System. Verwende uv init --lib für eine paketierte Library mit einem src-Layout und Build-Backend.
uv run prüft das Projekt, aktualisiert bei Bedarf den Lock, synchronisiert die erforderlichen Dependencies und führt den Befehl aus. Eine Beschreibung dieses Lifecycles findest du in uvʼs Dokumentation zu Projekt-Layout und Sync. Die erste Projektoperation erstellt bei Bedarf .venv und uv.lock. uv init selbst erstellt nur die Projektdateien.
Inputs committen, nicht die Umgebung
Committe pyproject.toml, uv.lock, den Source-Code und einen bewusst gepflegten .python-version. Ignoriere .venv und die Caches von uv.
Der Lock hält eine Auflösung über unterstützte Marker und Plattformen hinweg fest. Er macht native Wheels für macOS, Linux, Intel und Apple Silicon jedoch nicht identisch. Testen Sie jede Deployment-Plattform.
Aktualität des Locks: --locked ist nicht --frozen
!!! Byte sagt
Ich habe `--frozen` in CI verwendet, um die Auflösung zu überspringen. Dabei wurde auch der Drift-Check übersprungen. Der Lock blieb einen Monat hinter `pyproject.toml` zurück, ohne dass irgendwo ein Fehler signalisiert wurde.
Standardmäßig können Projektbefehle uv.lock aktualisieren, wenn sich Deklarationen ändern. Weitere Informationen zu den Flags für Aktualität und Exaktheit finden Sie in der Dokumentation zum Locking und Synchronisieren von uv.
Verwenden Sie --locked, um zu verlangen, dass der Lock mit den Projektmetadaten aktuell ist:
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, anstatt einen neuen Lock aufzulösen. Dieser Fehler ist normalerweise genau das CI-Gate, das Sie benötigen.
Verwenden Sie --frozen nur, wenn uv den bestehenden Lock absichtlich verwenden soll, ohne dessen Aktualität zu prüfen:
uv sync --frozen
Das kann in einer kontrollierten Build-Phase nützlich sein, in der der Lock bereits validiert wurde. Es ist jedoch kein Detektor für veraltete Locks.
Exaktheit ist eine separate Einstellung von Aktualität. uv sync ist standardmäßig exakt und entfernt überflüssige Packages. uv run führt standardmäßig eine nicht exakte Synchronisierung durch, sofern nicht --exact angefordert wird.
Verwaltetes Python: eine Versionsanforderung, kein universelles Binary
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 kann außerdem System-, Homebrew-, pyenv-, Conda- und andere Interpreter erkennen. Weitere Informationen zu den unterstützten Quellen und zum Erkennungsverhalten finden Sie in der Dokumentation zu Python-Versionen von uv.
.python-version ist eine von uv und anderen kompatiblen Tools erkannte Versionsanforderung. requires-python in pyproject.toml ist der Kompatibilitätsvertrag des Projekts. Legen Sie beide bewusst fest:
[project]
requires-python = ">=3.12,<3.14"
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 benötigen, und lassen Sie CI den Interpreter explizit auswählen und ausgeben.
Verwenden Sie einen anderen Python-Manager, wenn ein Projekt eine Distribution oder Build-Konfiguration benötigt, die uv nicht bereitstellt. uv kann diesen Interpreter weiterhin über --python oder die normale Erkennung verwenden.
Wählen zwischen uv run, uvx und installierten Tools
Projektgebundene Tools: uv run
Wenn pytest, mypy, ein Codegenerator oder ein anderes Tool das Projekt importieren oder dessen gelockte Plugins verwenden muss, deklarieren Sie es in einer Dependency Group:
uv add --group lint ruff
uv run --group lint ruff check .
Wenn Sie dieses Tool über uvx ausführen, wird es vom Projekt isoliert und erkennt möglicherweise das benötigte installierte Package oder die benötigten Plugins nicht.
Ad-hoc Tools: uvx
uvx ist ein Alias für uv tool run. Damit wird eine isolierte Umgebung im temporären uv-Cache erstellt. Die uv-Tools-Dokumentation beschreibt diesen Cache und den Lebenszyklus persistenter Tools:
uvx ruff@0.12.0 check .
uvx --from jupyterlab jupyter lab
uvx --with mkdocs-material mkdocs --help
Fixiere die Tool-Version in CI oder in der Dokumentation. Beim ersten Aufruf ohne Versionsangabe wird eine aktuelle Version ausgewählt; spätere Aufrufe können den Cache-Zustand wiederverwenden.
Persistente Executables: uv tool install
Installiere ein Tool, wenn Skripte außerhalb deiner Kontrolle dessen Kommando auf PATH benötigen oder wenn das Setup-Manifest deines Rechners es verwalten soll:
uv tool install ruff
uv tool list
uv tool upgrade ruff
uv tool uninstall ruff
Auch persistente Tools verwenden isolierte Umgebungen. Verändere diese Umgebungen nicht manuell mit pip.
Skripte: Eine Datei als Release-Einheit
PEP 723 definiert Inline-Skript-Metadaten. Ein kompatibler Runner kann den Kommentarblock lesen und eine isolierte Umgebung erstellen. Weitere Informationen zum Metadatenformat und zum Verhalten des Runners findest du im uv-Skripte-Leitfaden und in PEP 723.
# /// 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ühre das Skript aus und bearbeite die Metadaten mit uv:
uv run fetch.py
uv add --script fetch.py rich
uv remove --script fetch.py rich
Normales python fetch.py ignoriert die Kommentar-Metadaten. Das Skript benötigt daher einen kompatiblen Runner, obwohl die Python-Syntax weiterhin gültig ist.
Wenn ein Skript später reproduzierbar aufgelöst werden muss, erstelle eine benachbarte Lock-Datei:
uv lock --script fetch.py
uv run --script fetch.py
Dadurch wird fetch.py.lock geschrieben. Ein exclude-newer-Zeitstempel kann die Daten der infrage kommenden Distributionen einschränken, ist jedoch schwächer als eine exakt gelockte Auflösung und garantiert nicht, dass ein Artefakt weiterhin verfügbar ist.
Verwende stattdessen ein Projekt, wenn mehrere Dateien Abhängigkeiten gemeinsam nutzen, der Code als Package importierbar ist, Tests den Projektzustand benötigen oder mehrere Skripte gemeinsam ausgeliefert werden müssen.
Requirements-Dateien: Kompatibilität statt Sackgasse
Eine Datei vom Typ requirements.txt kann unspezifizierte Eingaben, exakte Pins, Hashes, Constraints, Indexe, URLs oder einen von einem Resolver erzeugten Requirements-Export enthalten. Ihre Reproduzierbarkeit hängt davon ab, wie sie erstellt und verwendet wurde. Der Dateiname allein sagt nichts aus.
Verwende die pip-kompatible Schnittstelle von uv, ohne zu migrieren:
uv venv
uv pip sync requirements.txt
uv run python app.py
uv pip sync sorgt dafür, dass die Umgebung mit der Datei übereinstimmt. uv pip install -r fügt lediglich weitere Pakete hinzu.
Für eine von dir verwaltete 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ühre den vollständigen Test- und Deployment-Pfad aus, bevor du alte Dateien löschst. Wenn ein nachgelagertes System weiterhin das pip-Format erwartet, leite es mit uvs Export-Kommando aus dem validierten uv-Lock ab:
uv export --format requirements.txt \
--output-file requirements.txt
Verwalte uv.lock und einen manuell bearbeiteten Export nicht als zwei konkurrierende Auflösungen.
Cache, Indexe und Grenzen der Supply Chain
Der uv-Cache beschleunigt wiederholte Installationen, bleibt aber temporär:
uv cache prune
uv cache clean
Bevorzuge prune für die regelmäßige Bereinigung. clean entfernt alle Cache-Einträge und erzwingt bei späteren Aufrufen erneute Downloads und Builds.
Eine Lockfile verbessert die Wiederholbarkeit. Sie macht Dependencies jedoch nicht vertrauenswürdig. Prüfe Paketquellen, Index-Konfiguration, Git-Revisions, Build-Backends, Lizenzen und Credentials. Halte authentifizierte Index-Konfiguration aus committeten Dateien heraus. Eine Ausnahme ist ein nicht geheimes Verweisziel auf einen freigegebenen Credential-Mechanismus.
Bei nativen Packages solltest du die Deployment-Architektur dokumentieren und die Verfügbarkeit von Wheels testen. Andernfalls kann ein Resolver auf einen Source-Build zurückfallen, der Compiler und Systembibliotheken benötigt, die in CI oder der Produktion fehlen.
Eine kompakte Betriebs-Checkliste
Für jedes Projekt:
- Definiere
requires-pythonund direkte Dependencies inpyproject.toml. - Trenne veröffentlichte Extras von lokalen Dependency Groups.
- Committe
uv.lock. Ignoriere.venvund den Cache-Zustand. - Führe projektgebundene Tools über die Projektumgebung aus.
- Verwende
uv lock --checkoder--lockedin CI. - Teste jedes Zielbetriebssystem und jede Zielarchitektur, die durch die Lockfile abgedeckt sind.
- Exportiere Kompatibilitätsformate nur für ausdrücklich benannte nachgelagerte Consumer.
- Prüfe Source- und Credential-Richtlinien unabhängig von der Resolution.
uv ist besonders nützlich, wenn diese Zuständigkeitsgrenzen sichtbar bleiben. Ein einziges Binary kann sie alle verwalten, ohne sie in eine einzige Umgebung zu verwandeln. Weniger Tools – und die vier Workflows bleiben getrennt. Deshalb verwende ich uv unter macOS weiterhin als mein Standard-Tool für Python-Projekte.