Zum Inhalt

Automatische Übersetzung

Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

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

Schnellstart

# Install uv
brew install uv

# For new projects (modern workflow)
uv init                # create project structure
uv add pandas numpy    # add dependencies
uv run train.py        # run your script

# For existing projects (legacy workflow)
uv venv                             # create virtual environment
uv pip install -r requirements.txt  # install dependencies
uv run train.py                     # run your script

# Run tools without installing them
uvx ruff check .       # run linter
uvx black .            # run formatter

# Run a single-file script with its own dependencies (no project needed)
uv run fetch.py        # uv reads the inline deps and runs it

Warum uv für Python auf macOS verwenden?

Wenn Sie Python schon eine Weile nutzen, jonglieren Sie je nach Projekt wahrscheinlich mit pip, virtualenv, pip-tools, pyenv und poetry. uv erledigt all das mit einer einzigen Binärdatei.

Es ist in Rust geschrieben, und auf meinem System installiert es Pakete ungefähr 10- bis 100-mal schneller als der alte Stack. Der größere Gewinn für mich ist, dass ich nicht mehr mitten in einer Aufgabe zwischen Tools wechseln musste.

Das uv-Ökosystem

Es ersetzt pip und pip-tools für das Paketmanagement, pyenv für die Installation von Python-Versionen, virtualenv/venv für Umgebungen, pipx zum Ausführen von Tools und poetry oder pdm für Projekt-Workflows.


uv installieren

Der einfachste Weg, uv auf macOS zu installieren, ist über Homebrew:

brew install uv

uv erkennt die Architektur Ihres Macs (Apple Silicon oder Intel) automatisch, daher ist keine zusätzliche Konfiguration nötig.

So halten Sie es aktuell:

brew upgrade uv
# OR
uv self update

Grundkonzepte

uv deckt drei Anwendungsfälle ab:

  1. Projekte — Aufbau einer Anwendung oder Bibliothek mit Abhängigkeiten.
  2. Skripte — Ausführen eines einzelnen Python-Skripts mit eingebetteten Abhängigkeiten.
  3. Tools — Globales Ausführen von Kommandozeilenwerkzeugen (wie ruff oder httpie).

1. Projektverwaltung

Für neue Projekte verwendet uv den Standard pyproject.toml für die Konfiguration und ein plattformübergreifendes uv.lock für reproduzierbare Builds.

Moderne uv-Projektstruktur

Starten Sie ein neues Projekt:

uv init my-project
cd my-project

Dadurch werden ein pyproject.toml, ein .gitignore und ein hello.py erstellt.

Fügen Sie Abhängigkeiten hinzu:

# Runtime dependencies
uv add pandas requests

# Development dependencies
uv add pytest ruff --dev

Führen Sie Ihren Code aus:

uv run hello.py

uv verwaltet die virtuelle Umgebung in .venv für Sie. Sie müssen sie nicht manuell aktivieren.

2. Python-Versionen verwalten

uv installiert und verwaltet Python-Versionen für Sie und speichert sie in ~/.cache/uv. Wenn Sie dafür bisher pyenv verwendet haben, können Sie es weglassen.

Installieren Sie eine bestimmte Version:

uv python install 3.12

Pinnen Sie eine Version für Ihr Projekt:

uv python pin 3.11

Dadurch wird eine Datei .python-version geschrieben. Beim nächsten uv run wird die gepinnte Version verwendet und bei Bedarf heruntergeladen. Ihr Team und Ihre CI landen ohne zusätzliche Abstimmung auf derselben Python-Version.

3. Tools mit uvx und uv tool install ausführen

uvx (ein Alias für uv tool run) führt Python-Kommandozeilenwerkzeuge aus, ohne sie Ihrer globalen Umgebung oder den Projektabhängigkeiten hinzuzufügen.

# Run a linter
uvx ruff check .

# Run a formatter
uvx black .

# Start a temporary Jupyter server
uvx --from jupyterlab jupyter lab

Jedes Tool läuft in seiner eigenen temporären Umgebung, daher gibt es keine Versionskonflikte mit dem, was in Ihrem Projekt bereits installiert ist.

uvx im Vergleich zu uv tool install

Zwei Details, zu denen ich ständig greife:

  • Version direkt inline pinnen mit @: uvx ruff@0.6.0 check ., oder mit uvx ruff@latest immer die neueste erzwingen.
  • Der Kommandoname entspricht nicht dem Paket? Verwenden Sie --from. Das Paket jupyterlab liefert das Kommando jupyter aus, also ist es uvx --from jupyterlab jupyter lab. Dasselbe gilt für --from httpie http.
  • Zusätzliche Abhängigkeit für ein Plugin nötig? Fügen Sie sie mit --with hinzu: uvx --with mkdocs-material mkdocs serve.

Wenn Sie ein Tool täglich verwenden, installieren Sie es einmal, statt jedes Mal eine neue Umgebung hochzufahren:

uv tool install ruff      # now `ruff` is on your PATH
uv tool list              # see what's installed
uv tool upgrade --all     # update everything
uv tool uninstall ruff

Die Faustregel: uvx für einmalige oder CI-Läufe, uv tool install für Ihre täglichen Standardwerkzeuge.


Legacy-Projekte (requirements.txt)

requirements.txt hat gute Dienste geleistet. Es ist eine flache Liste von Paketen ohne Lockfile, ohne Trennung zwischen App- und Dev-Abhängigkeiten und ohne Dokumentation, warum etwas gepinnt wurde. Das ist in Ordnung — bis zu dem Tag, an dem Sie versuchen, einen Build von vor acht Monaten zu reproduzieren, und feststellen, dass „gepinned“ damals einfach bedeutete, was PyPI gerade aufgelöst hat.

Wenn Ihnen das Projekt gehört, migrieren Sie es. uv liest Ihre bestehende Liste direkt in ein pyproject.toml ein:

uv init --bare                        # minimal pyproject.toml, no scaffolding
uv add -r requirements.txt            # import runtime dependencies
uv add --dev -r requirements-dev.txt  # and dev ones, if you split them

Sie haben jetzt ein pyproject.toml und ein echtes uv.lock, das exakt aufgelöste Versionen pinnt. Bestätigen Sie mit uv pip freeze, dass alles übernommen wurde, löschen Sie das alte requirements.txt und schauen Sie nicht zurück.

Aber vielleicht betreiben Sie seit Python 3.6 dasselbe requirements.txt und interessieren sich nicht für meine Meinung dazu. Fair. uv funktioniert weiterhin als Drop-in-Ersatz für pip und venv, ganz ohne Migration:

# Create a virtual environment
uv venv

# Install dependencies
uv pip install -r requirements.txt

# Run it
uv run python app.py

Dieselben Kommandos, die Sie schon kennen, nur schneller. Am alten Workflow wird nichts schlechter; Sie müssen ihn nur nicht mehr verwenden.


Einzelne Skripte mit eingebetteten Abhängigkeiten

Diese Funktion hat verändert, wie ich Wegwerf- und Glue-Code schreibe. Ein Python-Skript kann seine eigenen Abhängigkeiten innerhalb der Datei in einem Kommentarblock deklarieren, wie in PEP 723 definiert. Kein pyproject.toml, kein requirements.txt, keine virtuelle Umgebung, die erst erstellt werden muss — uv run liest den Block, erstellt eine gecachte Umgebung und führt die Datei aus.

Wichtig ist, klar zu sagen, was hier die Arbeit macht: Das ist keine Sprachfunktion von Python und keine Erfindung von uv. Der Block ist nur ein Kommentar, daher ignoriert plain python fetch.py ihn und scheitert an den fehlenden Imports. PEP 723 ist ein gemeinsamer Standard, daher liest jeder kompatible Runner denselben Block — pipx run, Hatch und PDM unterstützen ihn ebenfalls. uv ist zufällig der schnellste Weg, ihn zu nutzen, deshalb verwendet der Rest dieses Abschnitts uv run.

Anatomie eines in sich geschlossenen Skripts

Hier ist das Ganze in einer Datei, fetch.py:

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "httpx",
#     "rich",
# ]
# ///
import httpx
from rich import print

print(httpx.get("https://api.github.com/repos/astral-sh/uv").json())

Führen Sie es aus:

uv run fetch.py

Beim ersten Lauf werden httpx und rich in eine gecachte Umgebung aufgelöst und installiert; spätere Läufe verwenden sie wieder und starten sofort.

Sie müssen den Metadatenblock nicht von Hand schreiben. uv erstellt und bearbeitet ihn für Sie:

# Create a new script with the block pre-filled
uv init --script fetch.py --python 3.12

# Add or remove dependencies
uv add --script fetch.py httpx rich
uv remove --script fetch.py rich

Für ein schnelles Experiment können Sie den Block sogar ganz weglassen und Abhängigkeiten auf der Kommandozeile übergeben:

uv run --with httpx --with rich fetch.py

Warum das großartig für Skill- und Automatisierungsskripte ist

Ich nutze das für kleine Skripte, die kein Projekt verdienen: ein einmaliger Daten-Fix, ein CI-Helfer, ein Skript für einen Claude Code Skill, ein Cron-Job. Das Skript ist die Einheit. Sie können es in ein Gist legen, in jedes Repository committen oder an ein Teammitglied weitergeben, und uv run ist das Einzige, was zum korrekten Ausführen benötigt wird.

Machen Sie es mit einem Shebang direkt ausführbar:

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

-S teilt den Shebang in separate Argumente auf, sodass env --script an uv weiterreicht.

Skripte sperren und pinnen

Für Skripte, die auch in Monaten noch funktionieren müssen, haben Sie zwei Optionen.

Sperren Sie die exakte Auflösung neben dem Skript:

uv lock --script fetch.py   # writes fetch.py.lock

Oder pinnen Sie einen Zeitpunkt, sodass uv nur Pakete berücksichtigt, die vor einem bestimmten Datum veröffentlicht wurden — praktisch für Reproduzierbarkeit ohne Lockfile:

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

Tipps und Tricks, die man kennen sollte

Noch ein paar Dinge, die ich regelmäßig nutze und die im Schnellstart nicht sofort offensichtlich sind.

Aus dem Lockfile in CI synchronisieren. uv sync installiert exakt das, was in uv.lock steht. Fügen Sie --frozen hinzu, damit bei einem veralteten Lockfile laut fehlgeschlagen wird, statt stillschweigend neu aufzulösen — genau das, was Sie in CI- und Docker-Builds wollen.

uv sync --frozen

Dev-Abhängigkeiten gruppieren. Zusätzlich zu --dev können Sie in pyproject.toml benannte Abhängigkeitsgruppen definieren (docs, lint, test) und nur das synchronisieren, was Sie brauchen:

uv add --group docs mkdocs-material
uv sync --only-group docs

Abhängigkeitsbaum prüfen. uv tree zeigt, was wodurch hereingezogen wurde — und --outdated markiert Upgrades:

uv tree
uv tree --outdated

Nach requirements.txt exportieren, wenn ein nachgelagertes Tool weiterhin eines erwartet:

uv export --format requirements-txt > requirements.txt

Ein einmaliges Python mit zusätzlichen Paketen ausführen, ganz ohne Projekt:

uv run --with pandas --with matplotlib python

Den Cache verwalten, wenn der Speicherplatz knapp wird oder ein Build schiefläuft:

uv cache clean      # wipe the whole cache
uv cache prune       # remove only unused entries

Warum es schnell ist

Drei Dinge sind hier entscheidend. Es ist in Rust geschrieben, daher gibt es bei jedem Aufruf keinen Python-Startup-Overhead. Es cached gebaute Wheels global — sobald numpy in einem Projekt installiert wurde, ist die Installation in einem anderen praktisch sofort erledigt (auf macOS verwendet es Copy-on-Write-Links). Und es lädt Pakete parallel herunter und installiert sie parallel statt nacheinander.


Zusammenfassung

Aufgabe Alter Weg Der uv-Weg
Python installieren pyenv install 3.12 uv python install 3.12
Neues Projekt mkdir proj && cd proj && python -m venv .venv uv init proj
Paket installieren pip install pandas && pip freeze > requirements.txt uv add pandas
Skript ausführen source .venv/bin/activate && python script.py uv run script.py
Skript + Abhängigkeiten ein Projekt oder ein manuelles venv nur für eine Datei inline PEP 723-Block + uv run
Tool einmal ausführen pipx run black uvx black
Ein Tool installieren pipx install ruff uv tool install ruff
Reproduzierbare CI pip install -r requirements.txt uv sync --frozen

Ich habe meinen Python-Workflow kurz nach der Veröffentlichung auf uv umgestellt und bin nicht mehr zurückgegangen. Wenn Sie noch auf dem alten Stack sind, probieren Sie es im nächsten Projekt aus — genau so habe ich es getestet, bevor ich alles umgestellt habe.