O uv reúne tarefas que normalmente exigem várias ferramentas: criar ambientes, gerenciar versões do Python, resolver dependências, gerar lockfile e executar comandos. Ele não muda a forma de escrever Python, mas simplifica a preparação e a reprodução do ambiente de um projeto.
Este guia parte de um projeto novo. Se você ainda não conhece os arquivos envolvidos, leia antes sobre pyproject.toml e ambientes virtuais.
Criar um projeto
Depois de instalar o uv conforme a documentação oficial, crie a aplicação:
uv init analisador
cd analisador
uv add requests
uv run python main.py
uv init cria a base do projeto. uv add registra a dependência em pyproject.toml, resolve versões e atualiza uv.lock. Na primeira execução, o ambiente .venv é criado automaticamente. Não versione .venv; versione os arquivos de configuração e, em uma aplicação, o lockfile.
Sincronizar e executar
Ao clonar o repositório em outra máquina, use:
uv sync
uv run pytest
uv sync faz o ambiente corresponder ao lockfile. uv run verifica a sincronização antes de executar o comando, por isso é uma boa escolha para scripts locais e CI. Para adicionar ferramentas apenas ao grupo de desenvolvimento:
uv add --dev pytest ruff
uv run pytest
Esse fluxo evita instalar ferramentas globalmente e combina bem com GitHub Actions.
Migrar de requirements.txt
Não apague a configuração antiga antes de validar o novo ambiente. Em uma branch separada, inicialize o projeto e importe as dependências:
uv init
uv add -r requirements.txt
uv lock
uv sync
Rode testes e a aplicação. Dependências sem limite de versão podem produzir uma resolução diferente da instalação antiga, então compare o comportamento, não apenas os nomes dos pacotes. A documentação de projetos do uv explica estrutura, lock, execução e build.
Boas práticas
- Declare a faixa de Python em
requires-python. - Use
uv addeuv removeem vez de editar o ambiente manualmente. - Revise mudanças em
uv.lockcomo qualquer alteração de dependência. - Execute testes depois de atualizar pacotes.
- Não misture vários gerenciadores no mesmo fluxo sem uma razão documentada.
O ganho principal do uv não é apenas velocidade. É ter um caminho único e explícito entre a declaração do projeto, o ambiente local e a automação. Comece em um projeto pequeno, confirme o lockfile na CI e só então migre aplicações críticas.
Entender o que cada arquivo representa
Uma adoção segura fica mais fácil quando configuração, resolução e ambiente não são tratados como a mesma coisa. pyproject.toml declara a intenção do projeto: nome, faixa de Python e dependências diretas. uv.lock registra uma solução completa, incluindo dependências transitivas e artefatos compatíveis com diferentes plataformas. .venv é apenas a instalação local produzida a partir desses arquivos.
Essa separação explica por que apagar .venv não apaga o projeto. Se os dois arquivos versionados estiverem íntegros, uv sync reconstrói o ambiente. Também explica por que instalar um pacote diretamente com uma interface compatível com pip não equivale a uv add: a instalação pode mudar o ambiente sem registrar a decisão no projeto.
Para uma aplicação, versionar uv.lock torna deploys e testes mais previsíveis. Em uma biblioteca distribuída para terceiros, o lockfile ainda é útil para desenvolvimento e CI, mas os consumidores resolvem dependências conforme os intervalos publicados pela biblioteca. Portanto, teste também os limites mínimos e máximos de compatibilidade relevantes, não apenas a combinação travada no desenvolvimento.
Dependências comuns, desenvolvimento e grupos
Separe o que a aplicação precisa para funcionar do que serve apenas para desenvolver. Um servidor web ou cliente HTTP pertence às dependências do projeto; pytest, Ruff e ferramentas de documentação normalmente pertencem ao ambiente de desenvolvimento.
uv add fastapi
uv add --dev pytest ruff
uv remove fastapi
Após cada comando, confira tanto pyproject.toml quanto o diff de uv.lock. Uma atualização aparentemente pequena pode alterar várias dependências transitivas. Isso não significa necessariamente um problema, mas exige a mesma revisão aplicada a qualquer mudança de código.
Quando o repositório tem ferramentas opcionais para documentação, testes de integração ou análise, grupos de dependências ajudam a evitar uma instalação única e pesada para todos. Antes de criar muitos grupos, porém, verifique os comandos suportados pela versão de uv adotada e documente quais grupos a CI instala. Uma convenção simples reduz diferenças entre a máquina de quem desenvolve e o pipeline.
Escolher e fixar a versão do Python
O uv pode instalar e selecionar versões de Python, mas o projeto ainda deve declarar sua compatibilidade. Em pyproject.toml, uma aplicação que exige Python 3.12 ou mais recente pode usar:
[project]
requires-python = ">=3.12"
Isso é uma restrição de resolução, não apenas documentação. Dependências incompatíveis com a faixa informada podem ser recusadas. Se a equipe quer uma versão padrão para desenvolvimento, um arquivo .python-version pode registrá-la:
uv python install 3.12
uv python pin 3.12
Não amplie requires-python sem testar. Código que funciona em 3.12 pode usar sintaxe ausente em 3.10, enquanto uma dependência pode ter abandonado uma versão antiga. Para bibliotecas, uma matriz de testes continua sendo necessária. O gerenciador escolhe interpretadores e pacotes, mas não comprova compatibilidade de comportamento.
Executar comandos e scripts com contexto previsível
uv run é útil porque coloca o executável do ambiente do projeto no caminho e sincroniza o ambiente quando necessário. Isso reduz instruções dependentes de ativação:
uv run python -m pytest
uv run ruff check .
uv run python -m analisador
Usar python -m pytest deixa explícito qual interpretador executa o módulo. Em scripts de automação, prefira comandos completos e reproduzíveis. A ativação de .venv ainda pode ser conveniente em um terminal interativo, mas não deve ser um pré-requisito oculto do build.
Para ferramentas avulsas que não pertencem ao projeto, avalie uvx, que executa uma ferramenta em ambiente isolado. Não use essa conveniência para esconder uma dependência necessária ao pipeline. Se uma versão de Ruff ou de um gerador afeta o resultado versionado, declare ou fixe essa ferramenta de modo que a equipe obtenha o mesmo resultado.
Atualizar dependências com controle
Sincronizar e atualizar são ações diferentes. uv sync instala a solução atual do lockfile; não deve ser usado como sinônimo de buscar todas as versões mais recentes. Uma atualização intencional precisa produzir um diff revisável, seguido por testes.
uv lock --upgrade-package requests
uv sync
uv run pytest
Prefira atualizações menores e observe changelog, avisos de segurança e compatibilidade. Se o pacote atravessa uma versão principal, procure mudanças incompatíveis antes de atualizar. Em produção, gere o lockfile em uma alteração revisada, não durante o deploy. O servidor deve consumir a decisão já aprovada, em vez de resolver um conjunto novo sob pressão.
Ao ocorrer um conflito, leia a mensagem do resolvedor procurando três pontos: a faixa de Python, as restrições diretas e as restrições transitivas. Afrouxar todas as versões costuma mascarar o motivo. Identifique qual pacote exige a combinação impossível e decida se deve atualizar, substituir ou manter temporariamente uma versão anterior.
CI e implantação reproduzíveis
No pipeline, instale uv pelo método recomendado para o ambiente de CI, restaure caches apenas como otimização e sincronize de forma que alterações não registradas sejam rejeitadas. A documentação oficial de integração com GitHub Actions mostra o action mantido pelo projeto e as opções atuais.
Um fluxo conceitual é curto: obter o código, instalar a versão correta do Python, instalar uv, sincronizar o lockfile e executar verificações. O cache pode acelerar downloads, mas o build precisa continuar correto quando ele estiver vazio. Não versione .venv nem transporte um ambiente criado em outro sistema operacional.
Em imagens de contêiner, copie primeiro os arquivos de dependência quando quiser aproveitar cache de camadas. Depois copie o código da aplicação. Ainda assim, confirme que o build inclui o pacote local quando necessário e que o comando final usa o ambiente criado. Reproduzível não significa idêntico entre plataformas: dependências podem selecionar artefatos diferentes para Linux, Windows e macOS, todos previstos no lockfile.
Erros comuns ao adotar uv
- Manter
requirements.txt, comandos de Poetry e uv ativos ao mesmo tempo, sem definir qual arquivo é a fonte da verdade. - Editar
uv.lockmanualmente em vez de deixar o resolvedor produzi-lo. - Ignorar um diff grande do lockfile porque apenas uma dependência direta foi solicitada.
- Confundir
uv synccom atualização indiscriminada. - Fixar uma versão de Python local, mas declarar outra faixa em
requires-python. - Usar
uvxsem fixação para gerar arquivos que entram no repositório. - Fazer a primeira resolução no servidor de produção.
Na migração, mantenha o fluxo antigo apenas durante uma janela curta de comparação. Depois de validar testes, comandos de desenvolvimento e deploy, remova instruções obsoletas no mesmo conjunto de mudanças ou em uma etapa claramente planejada. Duas fontes de verdade que envelhecem juntas são mais perigosas que uma migração explícita.
Checklist de adoção
Antes de considerar o projeto migrado, confirme que uma máquina limpa consegue executar uv sync e a suíte de testes; .venv está ignorada; pyproject.toml contém apenas dependências diretas justificadas; uv.lock foi revisado e versionado; a faixa de Python corresponde à realidade; CI e documentação usam os mesmos comandos; e o procedimento de atualização é deliberado.
Por fim, simule o cenário que realmente importa: clone em outro diretório, sem aproveitar o ambiente existente, sincronize e inicie a aplicação. Esse teste simples encontra imports dependentes de instalações globais, arquivos não versionados e comandos que só funcionam na máquina original. É essa reprodução verificável, mais que qualquer comparação de velocidade, que justifica centralizar o fluxo no uv.