uv op macOS: Python-versies, projecten en tools beheren

Automatische vertaling

Dit artikel is automatisch vertaald vanuit de oorspronkelijke Engelse versie.

Deze gids is bedoeld voor Python-ontwikkelaars op macOS die van pip, venv, pip-tools, pyenv of pipx overstappen naar uv. uv ondersteunt vier soorten werk: een project, een script in één bestand, een eenmalige CLI-uitvoering met uvx en een CLI die op PATH is geïnstalleerd. Alle vier kunnen de Python-versies gebruiken die uv installeert. Je leert de opdrachten voor elk en hoe je ertussen kiest.

Ik heb mijn eigen Python-workflow omgezet naar uv. Eén uitvoerbaar bestand doet nu het werk dat ik vroeger over die tools verdeelde.

Snel starten

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

De figuur toont waar elk van de vier workflows zijn omgeving bewaart, wat je per workflow bewaart en de beheerde Python-installaties die ze delen.

Vier uv-workflows met opslaggebieden: de .venv van het project naast pyproject.toml en uv.lock, script- en uvx-omgevingen in de verwijderbare uv-cache, een geïnstalleerde toolomgeving die uvx kan hergebruiken in de toolmap, en beheerde Python-installaties voor alle vier. Voor geïnstalleerde opdrachten moet de map met uitvoerbare bestanden op PATH staanVier uv-workflows met opslaggebieden: de .venv van het project naast pyproject.toml en uv.lock, script- en uvx-omgevingen in de verwijderbare uv-cache, een geïnstalleerde toolomgeving die uvx kan hergebruiken in de toolmap, en beheerde Python-installaties voor alle vier. Voor geïnstalleerde opdrachten moet de map met uitvoerbare bestanden op PATH staan

uv installeren en updaten

Homebrew is een handige manier om uv op macOS te installeren. In de installatiegids van uv staan de ondersteunde methoden:

brew install uv
uv --version

Als Homebrew uv heeft geïnstalleerd, upgrade je het ook met Homebrew:

brew upgrade uv

uv self update is bedoeld voor de standalone installatiemethode van uv en is uitgeschakeld bij installaties via een package manager. Upgrade uv met dezelfde tool waarmee je het hebt geïnstalleerd.

Zo zie je waar uv, de Python-installaties, de tools en de cache staan:

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

Beheerde Python-installaties, blijvend geïnstalleerde tools en verwijderbare cache-items hebben aparte mappen. Je kunt de cache verwijderen en opnieuw opbouwen. Hij is geen gezaghebbende gegevensbron. Op macOS kloont uv pakketten vanuit de cache naar elke omgeving (copy-on-write), in plaats van ze te kopiëren. Bij het opnieuw opbouwen van .venv hergebruikt uv benodigde artefacten die nog in de cache staan.

Projecten: declaraties, resolutie en omgeving

Een uv-project bestaat normaal uit drie delen:

  • pyproject.toml declareert de projectmetadata en de directe vereisten.
  • uv.lock bevat de cross-platform resolutie van uv.
  • .venv is de lokaal geïnstalleerde omgeving en moet wegwerpbaar zijn.

De figuur sorteert de projectbestanden op wie ze schrijft en of git ze bewaart.

Je bewerkt pyproject.toml, .python-version en de broncode; uv lock schrijft uv.lock; alle vier worden gecommit. uv sync bouwt .venv uit uv.lock, met pakketten gekloond uit de uv-cache, en beide blijven buiten gitJe bewerkt pyproject.toml, .python-version en de broncode; uv lock schrijft uv.lock; alle vier worden gecommit. uv sync bouwt .venv uit uv.lock, met pakketten gekloond uit de uv-cache, en beide blijven buiten git

Maak een applicatie aan en voeg vereisten voor uitvoering en tests toe:

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

De toepassing toont Hello from forecast-app!; de laatste opdracht controleert of pytest is geïnstalleerd. Zodra je tests hebt, voer je ze uit met uv run --group test pytest.

De template --no-package maakt main.py, pyproject.toml, README.md en .python-version zonder buildsysteem. Sinds uv 0.12 maakt gewoon uv init een toepassing als package met een src-indeling en een build-backend. Gebruik uv init --lib voor een bibliotheek als package. Zie de gids voor het maken van projecten met uv.

uv run controleert het project, werkt de lock bij als dat nodig is, synchroniseert de vereiste afhankelijkheden en voert de opdracht uit. Zie de documentatie van uv over projectlayout en sync voor deze lifecycle. De eerste projectoperatie maakt .venv en uv.lock aan waar nodig. uv init zelf maakt alleen de projectbestanden aan.

uv installeert de groep dev standaard. Andere groepen, zoals test, hebben bij elke opdracht --group nodig, of default-groups onder [tool.uv]. Gebruik uv sync --only-group docs om één groep los te installeren, bijvoorbeeld in een docs-job in CI.

Groepen blijven lokaal en worden nooit gepubliceerd. Een gepubliceerde extra is iets anders: uv add --optional cli rich voegt een extra toe die gebruikers installeren als forecast-app[cli].

Om het project in Jupyter-notebooks in VS Code te gebruiken, voer je uv add --dev ipykernel uit en selecteer je .venv als kernel. Zie de Jupyter-gids van uv.

Commit de invoer, niet de omgeving

Commit pyproject.toml, uv.lock, de broncode en een bewust gekozen .python-version. Negeer .venv en de caches van uv.

Een teamgenoot die de repository kloont, krijgt de omgeving met vastgelegde versies met één opdracht:

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

Zo zie je waarom een pakket geïnstalleerd is en zet je de lock op nieuwere versies:

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

Commit de gewijzigde uv.lock samen met de wijziging die dat nodig maakte.

De lock legt een resolutie vast over de ondersteunde markers en platforms heen. Hij maakt native wheels niet identiek op macOS, Linux, Intel en Apple Silicon. Test elk platform waarop je deployt.

uv kan een deel hiervan afdwingen. Met required-environments onder [tool.uv] mislukt de resolutie als een pakket zonder brondistributie geen wheel heeft voor een van de genoemde platforms:

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

no-build-package verbiedt bouwen uit broncode voor de genoemde pakketten, zodat een ontbrekende wheel de installatie laat mislukken in plaats van een compiler te starten. Deploy je ook PyTorch op Linux met CUDA, volg dan de PyTorch-gids van uv voor de index-configuratie.

Actualiteit van de lock: --locked is niet --frozen

Standaard mogen projectopdrachten uv.lock bijwerken als de declaraties veranderen. Zie de documentatie van uv over locking en syncing voor de flags voor actualiteit en exactheid.

Gebruik --locked om te eisen dat de lock actueel is ten opzichte van de projectmetadata:

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

Als pyproject.toml en uv.lock niet overeenkomen, mislukken deze opdrachten in plaats van nieuwe versies vast te leggen. Gebruik ze in CI, zodat een verouderde lock de build laat mislukken.

Gebruik --frozen alleen als je bewust wilt dat uv de bestaande lock gebruikt zonder te controleren of die actueel is:

uv sync --frozen

Dat kan nuttig zijn in een gecontroleerde build-stap waarin de lock al gevalideerd is, maar het detecteert geen verouderde lock.

De figuur volgt één verouderd project door vier opdrachten: rich is met de hand aan pyproject.toml toegevoegd en uv.lock is niet bijgewerkt.

Met rich in pyproject.toml maar niet in uv.lock: uv sync herschrijft de lock en installeert rich, uv sync --locked en uv lock --check falen, en uv sync --frozen installeert httpx zonder rich terwijl CI groen blijftMet rich in pyproject.toml maar niet in uv.lock: uv sync herschrijft de lock en installeert rich, uv sync --locked en uv lock --check falen, en uv sync --frozen installeert httpx zonder rich terwijl CI groen blijft

Exactheid staat los van actualiteit. uv sync is standaard exact en verwijdert overbodige pakketten. uv run gebruikt standaard een niet-exacte sync, tenzij je --exact opgeeft. De figuur volgt één met de hand geïnstalleerd pakket door uv sync, uv run en uv run --exact.

Een .venv met httpx, ipykernel en een met de hand geïnstalleerd extra-pkg: uv sync en uv run --exact verwijderen extra-pkg, uv run houdt hetEen .venv met httpx, ipykernel en een met de hand geïnstalleerd extra-pkg: uv sync en uv run --exact verwijderen extra-pkg, uv run houdt het

Python-versies installeren en pinnen

uv kan Python-distributies downloaden en beheren:

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

De beheerde CPython-builds van uv komen uit het project python-build-standalone. uv kan ook interpreters van het systeem, Homebrew, pyenv, Conda en andere bronnen vinden. Zie de documentatie van uv over Python-versies voor de ondersteunde bronnen en het zoekgedrag.

.python-version is een versieverzoek dat uv en andere compatibele tools vinden. uv python pin schrijft dit bestand en uv init maakt er een aan. requires-python in pyproject.toml is het compatibiliteitscontract van het project. Kies beide bewust:

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

uv neemt het versieverzoek eerst uit --python, dan uit .python-version en dan uit requires-python.

Het pinnen van 3.12 garandeert niet voor altijd dezelfde patch-build, en ook niet hetzelfde artifact op elk besturingssysteem. Vraag een exacte patchversie aan als je die nodig hebt. Selecteer in CI de interpreter en print hem, met uv run --python 3.12.7 python -VV of door UV_PYTHON=3.12.7 voor de job in te stellen.

Gebruik een andere Python-manager als een project een distributie of buildconfiguratie nodig heeft die uv niet levert. uv kan die interpreter dan nog steeds gebruiken via --python of de normale zoekvolgorde.

Kiezen tussen uv run, uvx en geïnstalleerde tools

Kies op basis van de vraag of de tool de geïnstalleerde pakketten van het project nodig heeft, op PATH moet blijven of onafhankelijk kan draaien. De figuur toont welke afhankelijkheden beschikbaar zijn en hoe lang elke omgeving bestaat.

uv run gebruikt de .venv van het project en de vastgelegde afhankelijkheden; uvx gebruikt een aparte toolomgeving in de cache of van een geïnstalleerde tool, zonder automatisch projectafhankelijkheden te installeren; uv tool install bewaart een omgeving in de toolmap en maakt de opdracht beschikbaar nadat PATH is ingestelduv run gebruikt de .venv van het project en de vastgelegde afhankelijkheden; uvx gebruikt een aparte toolomgeving in de cache of van een geïnstalleerde tool, zonder automatisch projectafhankelijkheden te installeren; uv tool install bewaart een omgeving in de toolmap en maakt de opdracht beschikbaar nadat PATH is ingesteld

Tools die aan het project gekoppeld zijn: uv run

Als pytest, mypy, een codegenerator of een andere tool het project moet importeren of de gelockte plugins ervan moet gebruiken, declareer die tool dan in een afhankelijkhedengroep:

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

uvx installeert niet automatisch het pakket of de afhankelijkheden van het project. Een tool kan nog steeds lokale bronbestanden importeren als zijn importgedrag dat toestaat; gebruik uv run wanneer hij het geïnstalleerde project of de afhankelijkheden daarvan nodig heeft.

Ad-hoc tools: uvx

uvx is een alias voor uv tool run. Het hergebruikt een compatibele tool die met uv tool install is geïnstalleerd; anders gebruikt het een geïsoleerde omgeving in de verwijderbare uv-cache. De documentatie voor uv-tools beschrijft deze cache en de levensduur van blijvend geïnstalleerde 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

Leg de toolversie vast in CI of de documentatie. Zonder geïnstalleerde tool kiest de eerste aanroep zonder versie de nieuwste beschikbare release; latere aanroepen kunnen de cachestatus hergebruiken. @latest negeert een geïnstalleerde versie en vernieuwt de cache voor de nieuwste release. Gebruik --from als de pakketnaam verschilt van de opdrachtnaam, zoals bij jupyterlab en httpie.

Blijvend beschikbare uitvoerbare bestanden: uv tool install

Installeer een tool wanneer scripts buiten jouw beheer de opdracht op PATH nodig hebben, of wanneer het setup-manifest van je machine de tool moet beheren:

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

Als de installatie waarschuwt dat de map met uitvoerbare bestanden niet op PATH staat, voer je uv tool update-shell uit en start je je shell opnieuw voordat je de opdracht gebruikt. Zie de documentatie voor uitvoerbare toolbestanden van uv.

Persistente tools gebruiken nog steeds geïsoleerde omgevingen. Pas die omgevingen niet met de hand aan met pip.

Scripts van één bestand met PEP 723

PEP 723 definieert inline scriptmetadata. Een compatibele runner kan het commentaarblok lezen en een geïsoleerde omgeving opbouwen. Zie de scriptgids van uv en PEP 723 voor het metadataformaat en het gedrag van de runner.

De figuur markeert het metadatablok in fetch.py en toont wat uv run en gewone python met het bestand doen.

fetch.py met het PEP 723-commentaarblok gemarkeerd: uv run leest het blok, vindt Python 3.12 of nieuwer, installeert httpx in een geïsoleerde omgeving en draait de code; python fetch.py draait alleen de codefetch.py met het PEP 723-commentaarblok gemarkeerd: uv run leest het blok, vindt Python 3.12 of nieuwer, installeert httpx in een geïsoleerde omgeving en draait de code; python fetch.py draait alleen de code

uv kan het metadatablok voor je schrijven:

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

Een afgerond script ziet er zo uit:

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

Draai het script en bewerk de metadata met uv:

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

Een gewone python fetch.py negeert de metadata in het commentaar. Het script is daardoor afhankelijk van een compatibele runner, ook al blijft de Python-syntax geldig. uv is één runner. pipx run, Hatch en PDM lezen het blok ook.

Om het script als ./fetch.py te draaien, voeg je een shebang toe die uv aanroept:

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

De flag -S laat env de regel opsplitsen in losse argumenten, zodat --script bij uv aankomt.

Voor een snel experiment geef je afhankelijkheden op de opdrachtregel mee in plaats van metadata te schrijven. De tweede regel start een Python-REPL waarin pandas beschikbaar is:

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

Maak voor een script dat later dezelfde resolutie moet reproduceren een lock ernaast aan:

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

Dit schrijft fetch.py.lock. Met een exclude-newer-tijdstempel in het metadatablok negeert uv packageversies die na die datum zijn gepubliceerd:

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

Dit is zwakker dan een lock. Het pint geen exacte versies en kan geen bestand terughalen dat de index later verwijdert.

Gebruik in plaats daarvan een project als meerdere bestanden afhankelijkheden delen, de code als package importeerbaar is, tests projectstatus nodig hebben of meerdere scripts samen moeten meebewegen.

Bestaande projecten met requirements.txt

Een requirements.txt-bestand kan niet-vastgelegde vereisten, exacte pins, hashes, constraints, indexes, URL’s of een door een resolver gegenereerde requirements-export bevatten. Hoe reproduceerbaar het is, hangt af van hoe het is gemaakt en gebruikt. De bestandsnaam alleen zegt niets.

Je hebt twee opties: het pip-formaat behouden en uitvoeren met uv pip, of, voor een applicatie die van jou is, migreren naar uv.lock.

Gebruik voor een volledig opgelost bestand met directe en transitieve afhankelijkheden de pip-compatibele interface van uv zonder te migreren:

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

Voer hier de geïnstalleerde interpreter rechtstreeks uit. Als er een pyproject.toml aanwezig is, kan uv run de vereisten ervan vóór uitvoering vastleggen en synchroniseren, waardoor het de zojuist geïnstalleerde pakketten vervangt. Zie de scriptgids van uv.

uv pip sync laat de omgeving overeenkomen met een volledig opgelost bestand. Als requirements.txt alleen directe vereisten bevat, gebruik je uv pip install -r requirements.txt om hun afhankelijkheden op te lossen en te installeren. uv pip install -r voegt pakketten toe. Zie de pip-lockinggids van uv.

Voor een applicatie die je zelf beheert, kan een gefaseerde migratie nuttig zijn:

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

Doorloop het volledige test- en deploymentpad voordat je oude bestanden verwijdert. Als een downstream systeem nog het pip-formaat verwacht, leid het dan af van de gevalideerde uv-lock met het export-command van uv:

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

Onderhoud uv.lock en een met de hand bewerkte export niet als twee concurrerende resoluties.

Cache en privé-indexen

De cache van uv versnelt herhaalde installaties, maar blijft wegwerpbaar:

uv cache prune
uv cache clean

Gebruik bij voorkeur prune voor routinematig opruimen. clean verwijdert alle cache-items en dwingt latere downloads en builds af.

Een lockfile maakt installaties beter herhaalbaar. Het maakt afhankelijkheden niet betrouwbaar.

Zet voor een privé-index de naam en URL in pyproject.toml en houd de inloggegevens buiten het bestand:

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

Geef de inloggegevens mee via UV_INDEX_INTERNAL_USERNAME en UV_INDEX_INTERNAL_PASSWORD, of via keyring. uv leidt de namen van de variabelen af van de indexnaam. Zie de documentatie van uv over index-authenticatie.

Oude opdrachten en hun uv-equivalenten

Stap je over van de oudere tools, dan koppelt deze tabel elke bekende opdracht aan de uv-opdracht die hetzelfde doet.

TaakVoorheenuv
Python installerenpyenv install 3.12uv python install 3.12
Nieuw project en omgevingmkdir proj && cd proj && python -m venv .venvuv init proj && cd proj && uv sync
Een pakket toevoegenpip install pandas && pip freeze > requirements.txtuv add pandas
Pins compilerenpip-compile requirements.inuv lock
Eén pakket bijwerkenpip-compile --upgrade-package httpxuv lock --upgrade-package httpx
Draaien in de omgevingsource .venv/bin/activate && python app.pyuv run app.py
Script met afhankelijkhedeneen handmatige venv voor één bestandPEP 723-blok + uv run
Een tool één keer draaienpipx run blackuvx black
Een tool installerenpipx install ruffuv tool install ruff
Reproduceerbare installatie in CIpip install -r requirements.txtuv sync --locked

Een compacte checklist voor het dagelijks gebruik

Per project:

  1. Definieer requires-python en de directe afhankelijkheden in pyproject.toml.
  2. Houd gepubliceerde extras (--optional) gescheiden van lokale afhankelijkhedengroepen (--group).
  3. Commit uv.lock. Negeer .venv en de cachestatus.
  4. Draai tools die aan het project gekoppeld zijn via de projectomgeving.
  5. Gebruik uv lock --check of --locked in CI.
  6. Test elk doel-OS en elke architectuur die de lock dekt.
  7. Exporteer compatibiliteitsformaten alleen voor benoemde downstream gebruikers.
  8. Houd inloggegevens voor indexen buiten gecommitte bestanden. Geef ze mee via omgevingsvariabelen of keyring.

Ik gebruik uv als mijn standaard Python-tool op macOS, omdat één uitvoerbaar bestand deze taken afhandelt en project- en toolafhankelijkheden gescheiden houdt.

Referenties