Ir para o conteúdo

Tradução automática

Este artigo foi traduzido automaticamente a partir da versão original em inglês.

uv no macOS: Gerir versões de Python, projetos e ferramentas

Arranque rápido

# 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

Porquê usar uv para Python no macOS?

Se já usa Python há algum tempo, provavelmente vai alternando entre pip, virtualenv, pip-tools, pyenv e poetry consoante o projeto. uv faz o que todos esses fazem, com um único binário.

Está escrito em Rust e, na minha máquina, instala pacotes cerca de 10–100x mais depressa do que a stack antiga. A maior vantagem para mim é que deixei de alternar entre ferramentas a meio de uma tarefa.

O ecossistema uv

Substitui pip e pip-tools para gestão de pacotes, pyenv para instalar versões de Python, virtualenv/venv para ambientes, pipx para executar ferramentas, e poetry ou pdm para workflows de projeto.


Instalar uv

A forma mais simples de instalar uv no macOS é através do Homebrew:

brew install uv

uv deteta automaticamente a arquitetura do seu Mac (Apple Silicon ou Intel), por isso não é necessária configuração extra.

Para o manter atualizado:

brew upgrade uv
# OR
uv self update

Conceitos principais

uv cobre três casos de uso:

  1. Projetos — desenvolver uma aplicação ou biblioteca com dependências.
  2. Scripts — executar um script Python de ficheiro único com dependências inline.
  3. Ferramentas — executar utilitários de linha de comandos (como ruff ou httpie) globalmente.

1. Gestão de projetos

Para projetos novos, uv usa o padrão pyproject.toml para configuração e um uv.lock multiplataforma para builds reprodutíveis.

Estrutura moderna de projeto com uv

Inicie um novo projeto:

uv init my-project
cd my-project

Isto cria um pyproject.toml, um .gitignore e um hello.py.

Adicione dependências:

# Runtime dependencies
uv add pandas requests

# Development dependencies
uv add pytest ruff --dev

Execute o seu código:

uv run hello.py

uv gere por si o ambiente virtual em .venv. Não precisa de o ativar manualmente.

2. Gerir versões de Python

uv instala e gere versões de Python por si, mantidas em ~/.cache/uv. Se tem usado pyenv para isto, pode deixar de o usar.

Instale uma versão específica:

uv python install 3.12

Fixe uma versão para o seu projeto:

uv python pin 3.11

Isto escreve um ficheiro .python-version. No próximo uv run, usa a versão fixada e descarrega-a se for necessário. A sua equipa e o CI acabam por usar a mesma versão de Python sem coordenação extra.

3. Executar ferramentas com uvx e uv tool install

uvx (um alias para uv tool run) executa ferramentas Python de linha de comandos sem as adicionar ao seu ambiente global nem às dependências do projeto.

# Run a linter
uvx ruff check .

# Run a formatter
uvx black .

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

Cada ferramenta corre no seu próprio ambiente temporário, por isso não há conflitos de versão com o que o seu projeto já tiver instalado.

uvx versus uv tool install

Dois detalhes a que recorro constantemente:

  • Fixar a versão inline com @: uvx ruff@0.6.0 check ., ou forçar a mais recente com uvx ruff@latest.
  • O nome do comando não corresponde ao pacote? Use --from. O pacote jupyterlab disponibiliza o comando jupyter, por isso é uvx --from jupyterlab jupyter lab. O mesmo para --from httpie http.
  • Precisa de uma dependência extra para um plugin? Adicione-a com --with: uvx --with mkdocs-material mkdocs serve.

Se usa uma ferramenta todos os dias, instale-a uma vez em vez de criar um ambiente novo sempre que a executa:

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

A regra prática: uvx para execuções pontuais ou em CI, uv tool install para as ferramentas que usa diariamente.


Projetos legacy (requirements.txt)

requirements.txt teve um bom percurso. É uma lista plana de pacotes sem lockfile, sem separação entre dependências da aplicação e de desenvolvimento, e sem registo de porque é que algo foi fixado. Isso serve até ao dia em que tenta reproduzir uma build de há oito meses e descobre que "fixado" significava o que o PyPI tivesse resolvido nessa altura.

Se é responsável pelo projeto, migre-o. uv lê a sua lista existente diretamente para um pyproject.toml:

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

Passa a ter um pyproject.toml e um verdadeiro uv.lock que fixa as versões resolvidas exatas. Confirme que tudo foi migrado com uv pip freeze, apague o antigo requirements.txt e siga em frente.

Mas talvez tenha usado o mesmo requirements.txt desde o Python 3.6 e não esteja interessado na minha opinião sobre isso. Justo. uv continua a funcionar como substituto direto de pip e venv, sem necessidade de migração:

# Create a virtual environment
uv venv

# Install dependencies
uv pip install -r requirements.txt

# Run it
uv run python app.py

Os mesmos comandos que já conhece, apenas mais rápidos. Nada no workflow antigo piora; a única diferença é que já não tem de o usar.


Scripts de ficheiro único com dependências inline

Esta foi a funcionalidade que mudou a forma como escrevo código descartável e de integração. Um script Python pode declarar as suas próprias dependências dentro do ficheiro, num bloco de comentários definido pela PEP 723. Sem pyproject.toml, sem requirements.txt, sem ambiente virtual para criar — uv run lê o bloco, cria um ambiente em cache e executa o ficheiro.

Vale a pena clarificar o que está realmente a fazer o trabalho aqui: isto não é uma funcionalidade da linguagem Python, nem uma invenção de uv. O bloco é apenas um comentário, por isso python fetch.py normal ignora-o e falha quando faltam os imports. A PEP 723 é um padrão partilhado, por isso qualquer runner compatível lê o mesmo bloco — pipx run, Hatch e PDM também suportam isto. uv é simplesmente a forma mais rápida de o usar, e é por isso que o resto desta secção usa uv run.

Anatomia de um script autocontido

Aqui está tudo num único ficheiro, 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())

Execute-o:

uv run fetch.py

Na primeira execução, resolve e instala httpx e rich num ambiente em cache; nas execuções seguintes reutiliza-o e arranca instantaneamente.

Não tem de escrever o bloco de metadados à mão. uv gera e edita-o por si:

# 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

Para uma experiência rápida, pode até ignorar totalmente o bloco e passar as dependências na linha de comandos:

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

Porque isto é excelente para scripts de skill e automação

Uso isto muito para scripts pequenos que não justificam um projeto: uma correção pontual de dados, um helper de CI, um script de Claude Code skill, uma tarefa cron. O script é a unidade. Pode colocá-lo num gist, fazer commit para qualquer repositório ou passá-lo a um colega, e uv run é a única coisa de que ele precisa para o executar corretamente.

Torne-o executável diretamente com um shebang:

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

O -S divide o shebang em argumentos separados para que env passe --script para uv.

Bloquear e fixar scripts

Para scripts que precisam de continuar a funcionar daqui a vários meses, tem duas opções.

Bloqueie a resolução exata ao lado do script:

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

Ou fixe um ponto no tempo para que uv só considere pacotes lançados antes de uma determinada data — útil para reprodutibilidade sem lockfile:

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

Dicas e truques que vale a pena conhecer

Mais algumas coisas que uso regularmente e que não são óbvias no arranque rápido.

Sincronizar a partir do lockfile em CI. uv sync instala exatamente o que está em uv.lock. Adicione --frozen para falhar explicitamente se o lockfile estiver desatualizado em vez de voltar a resolver silenciosamente — exatamente o que quer em builds de CI e Docker.

uv sync --frozen

Agrupe as dependências de desenvolvimento. Além de --dev, pode definir grupos de dependências com nome em pyproject.toml (docs, lint, test) e sincronizar apenas o que precisa:

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

Inspecione a árvore de dependências. uv tree mostra o que puxou o quê — e --outdated sinaliza upgrades:

uv tree
uv tree --outdated

Exporte para requirements.txt quando uma ferramenta a jusante ainda precisar desse formato:

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

Execute um Python pontual com pacotes extra, sem projeto necessário:

uv run --with pandas --with matplotlib python

Gerir a cache quando o espaço em disco aperta ou uma build corre mal:

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

Porque é rápido

Há três coisas que contam aqui. Está escrito em Rust, por isso não há overhead de arranque do Python em cada invocação. Faz cache global das wheels construídas — depois de numpy estar instalado num projeto, instalá-lo noutro é praticamente instantâneo (no macOS usa ligações copy-on-write). E descarrega e instala pacotes em paralelo em vez de um de cada vez.


Resumo

Tarefa Forma antiga Forma com uv
Instalar Python pyenv install 3.12 uv python install 3.12
Novo projeto mkdir proj && cd proj && python -m venv .venv uv init proj
Instalar pacote pip install pandas && pip freeze > requirements.txt uv add pandas
Executar script source .venv/bin/activate && python script.py uv run script.py
Script + deps um projeto, ou um venv manual apenas para um ficheiro bloco inline PEP 723 + uv run
Executar ferramenta uma vez pipx run black uvx black
Instalar uma ferramenta pipx install ruff uv tool install ruff
CI reprodutível pip install -r requirements.txt uv sync --frozen

Mudei o meu workflow de Python para uv pouco depois de ter saído e não voltei atrás. Se ainda está na stack antiga, experimente-o no próximo projeto — foi assim que o testei antes de migrar tudo.