7 pontos por GN⁺ 2025-06-25 | 2 comentários | Compartilhar no WhatsApp
  • Ao migrar para uv, a velocidade de instalação de dependências Python fica cerca de 10 vezes mais rápida do que com pip, e também é possível executar como usuário não root sem um venv separado
  • Com base em pyproject.toml, basta declarar apenas as dependências de nível superior e o uv gerencia automaticamente o arquivo de lock, oferecendo árvore de dependências e controle exato de versões superiores ao pip freeze
  • No Dockerfile, são necessárias mudanças passo a passo, como copiar os binários uv e uvx, usar os arquivos pyproject.toml/uv.lock e configurar variáveis de ambiente
  • Com comandos como uv sync/add/remove e uv:outdated, é possível adicionar, remover e atualizar dependências com facilidade, além de verificar as versões mais recentes dos pacotes
  • Fica mais fácil manter o arquivo de lock e atualizar dependências regularmente, o que ajuda a garantir consistência em colaboração e ambientes de deploy

Instalação de dependências 10x mais rápida, sem venv e com ambiente não root

  • uv é uma ferramenta que melhora bastante a velocidade de instalação de dependências em projetos Python em comparação ao pip tradicional
  • Com a adoção do uv, é possível obter cerca de 10 vezes mais velocidade de instalação em comparação ao pip em vários projetos, como Flask e Django
  • Mesmo sem um ambiente virtual separado (venv), é possível executar com segurança dentro do contêiner como usuário não root

pyproject.toml vs requirements.txt

  • Em vez do tradicional requirements.txt, basta declarar apenas as dependências de nível superior no arquivo pyproject.toml, e o uv gera automaticamente o arquivo uv.lock
    • Adicionar o item [project] dependencies em pyproject.toml
    • Remover o requirements.txt existente
  • O arquivo de lock do uv é semelhante ao resultado de pip freeze, mas inclui árvore de dependências e informações de versão mais precisas

Mudanças na configuração do Dockerfile

  • Usar os binários uv e uvx copiando-os para o contêiner (binários Rust compilados estaticamente)
  • Em vez dos antigos requirements*.txt, copiar os arquivos pyproject.toml e uv.lock*
  • Adicionar variáveis de ambiente:
    • UV_COMPILE_BYTECODE=1: pré-compila para bytecode durante a etapa de build
    • UV_PROJECT_ENVIRONMENT="/home/python/.local": instala os pacotes em um caminho específico sem criar um venv separado
  • O comando de instalação de dependências também muda de pip3-install para uv-install
    • Exemplo: RUN chmod 0755 bin/* && bin/uv-install

Gerenciamento de dependências: adicionar, remover, atualizar e mais

  • É possível executar comandos do uv dentro do contêiner com um script run separado
    • ./run deps:install: instala após construir a imagem e exporta o arquivo de lock para o host
    • ./run deps:install --no-build: atualiza apenas o arquivo de lock sem fazer build
    • ./run uv add mypackage --no-sync: atualiza apenas pyproject.toml e o arquivo de lock; a instalação real é executada separadamente
    • ./run uv remove mypackage --no-sync: remove o pacote
    • ./run uv:outdated: verifica a versão mais recente das dependências atuais

Vídeo e guia prático disponíveis

  • Há demonstrações reais e exemplos de git diff cobrindo adoção do uv, escrita de pyproject.toml, mudanças no Dockerfile, comandos de lock/sync, adição/remoção de dependências e verificação de versões mais recentes
  • Também é possível consultar os diffs de migração de dois projetos, Flask e Django

2 comentários

 
yangeok 2025-06-26

Eu já estava pensando em migrar o que eu implantava com o Poetry, e isso parece estável e simples ^^

 
GN⁺ 2025-06-25
Comentários do Hacker News
  • Vale notar que o uv oferece um fluxo de trabalho que substitui diretamente pyenv, virtualenv e pip. Não é um modelo imposto por lockfile ou pyproject.toml. Com o comando uv python pin <version>, ele cria um arquivo .python-version no diretório atual; com uv virtualenv, baixa a versão do Python como o pyenv e cria o ambiente virtual .venv; com uv pip install -r requirements.txt, instala os pacotes de requirements.txt; e com uv run <command>, executa comandos incluindo variáveis de ambiente do arquivo .env. Só é preciso tomar cuidado com a prioridade das variáveis de ambiente (issue relacionada)

    • A flexibilidade do uv é realmente impressionante. Uma tarefa que leva 10 minutos com pip pode ser feita em 20~30 segundos com uv
    • Foi exatamente por isso que comecei a usar uv. É muito conveniente. Mas às vezes uv pip fica lento e não sei por quê; talvez seja algo do ambiente de rede da empresa
    • Pelo que sei, a informação da versão do Python também fica salva no pyproject.toml, então fico na dúvida se o arquivo .python-version é realmente necessário
  • # Script que garante sempre o lock file mais recente
    if ! test -f uv.lock || ! uv lock --check 2>/dev/null; then
      uv lock
    fi
    

    Esse tipo de abordagem acaba esvaziando o propósito de existir um lock file. Se o arquivo não existe ou está inválido, isso indica um problema sério no lock file e o ideal é que alguém familiarizado com o projeto trate isso manualmente. Caso contrário, não faz sentido manter lock file. No CI, trocar o lock file automaticamente pode gerar confusão

    • (Resposta do autor) Se o lock file estiver inválido, não é que o processo simplesmente segue em frente e cria um novo arquivo em silêncio. O uv lock falha com uma mensagem amigável, e o errexit do shell interrompe tudo imediatamente. O redirecionamento de erro em uv lock --check é para evitar que o mesmo erro apareça duas vezes. Se você corromper o lock file de propósito e rodar o script, o build para com uma mensagem de erro específica. Reescrevi o script com if-else para ficar mais claro. Se o lock file não existir, o fluxo correto é criar um novo. Aí é só gerar e fazer commit
    • Isso já é coberto pela opção uv sync --locked. Se o lock file não existir ou estiver desatualizado, ele falha de forma explícita. Recomendo sempre fazer o build com a opção --locked
    • No mundo Python, muitas vezes o lock file nem vai para o controle de versão, sendo tratado como uma “etapa estranha” do processo de instalação
    • Há um bug sério nessa abordagem. Com a flag --frozen, o lock file não deveria ser atualizado, mas na prática acontece o contrário. Concordo que, se o lock file não existe ou não bate, precisa haver intervenção humana
    • Ainda assim, se o lock file não existe, ou é a primeira execução, ou ele vai acabar sendo sobrescrito pelo upstream do git de qualquer forma. Se ele está quebrado, alguém errou na instalação, e recriá-lo é basicamente a única saída razoável. É uma exceção rara, mas como tratamento simples já basta
  • Sou totalmente contra ferramentas de Python serem desenvolvidas em linguagens que não sejam Python. C já existe, e CPython já é padronizado, então não vejo por que precisar de uma nova linguagem como Rust. O pacote Pendulum demorou mais de 7 meses para oferecer suporte ao 3.13, e acredito que isso aconteceu porque, por ser nativo em Rust, faltavam pessoas capazes de corrigir o problema. Se fosse em C, eu mesmo teria consertado. (issue relacionada) Idealmente, se você quer criar um datetime rápido em uma linguagem externa como Rust, o certo seria fazê-lo via FFI de uma forma reutilizável por várias linguagens. Ainda não gosto muito da base em Rust, e até entendo por que a comunidade Linux resiste a isso

    • Respeito esse ponto de vista, mas acho que fazer uma ferramenta como o uv em Rust é uma boa ideia. Se você cria uma ferramenta de gerenciamento Python em Python, cai num problema de “quem veio primeiro, o ovo ou a galinha”. Para usar a ferramenta Python, o próprio Python já precisa estar instalado antes, e aí entram complicações sobre qual versão de Python está sendo usada, possíveis conflitos entre as bibliotecas da ferramenta e as do app real, gerenciamento de variáveis de ambiente, depuração etc. Já uma ferramenta binária feita em Rust ou outra linguagem é só baixar e usar; ela funciona na hora sem você precisar pensar nisso. No fim, o usuário nem deveria se importar muito com a linguagem em que a ferramenta foi escrita
    • Eu gosto de Python, mas a praticidade e a velocidade do uv são incomparáveis. Quando preciso de Python recente em um servidor em fim de vida, ou só quero instalar rapidamente dependências para um script pequeno, uv é a melhor opção. Concordo em parte com isso: antes eu escrevia tudo em pure Python, depois fui usando extensões em C, e quando bati no limite comecei a querer escrever quase tudo em C. Como C é difícil, ultimamente estou refatorando para Rust. Quando a parte externa fica maior que a interna, é melhor simplesmente mudar tudo para outra linguagem
    • Se a pessoa realmente acha que ferramentas devem ser feitas só em Python, então enquanto ela espera o Pylint lento terminar, eu vou estar dando uma caminhada
    • Suporte a várias linguagens não impõe quase nenhum custo ao usuário. Se a ferramenta é rápida e resolve bem o problema, isso já basta. E na prática ela é muito mais rápida. Ferramentas de gerenciamento são para desenvolvedores, para quem vai usar o sistema
    • Para mim, tanto faz a linguagem em que foi feito, desde que funcione bem. É verdade que usuários de Python podem contribuir mais facilmente com a ferramenta, mas se ela cumpre bem o objetivo, a linguagem não importa. Aliás, quando você esbarra em um problema de ambiente, uma ferramenta feita em Python pode acabar sendo afetada por esse mesmo problema
  • É preciso ter cuidado ao usar uv no lugar de pip. Por padrão, ele não gera arquivos .pyc, então a inicialização do serviço pode ficar mais lenta (referência)

  • Ao usar uv em um contêiner Flask, a diferença no tempo de build não é só grande a ponto de parecer interminável, como o processo de instalação também fica muito mais previsível. Não existe mais aquela frustração de as versões das dependências mudarem com pip. Basta usar pyproject.toml e rodar uv lock. No Docker, se você copiar apenas pyproject.toml e uv.lock (HOT COPY) e executar uv sync --frozen --no-install-project, a camada de instalação pode ser armazenada em cache sem incluir o código do app. Se você já sofreu por precisar reconstruir a camada inteira quando muda um único pacote, entende por que isso é importante. Usando a variável de ambiente UV_PROJECT_ENVIRONMENT=/home/python/.local, dá para pré-aquecer a imagem base sem venv, compartilhar builds e reduzir custos de infraestrutura. Com a opção UV_COMPILE_BYTECODE=1, arquivos .pyc são gerados no build. Isso elimina ambientes mutáveis e força reprodutibilidade; se o build falhar, fica claro que a responsabilidade é do lockfile

  • Mesmo em 2025, empacotamento e gerenciamento de dependências em Python continuam sendo uma bagunça

    • Acho que continua confuso porque nem todo mundo usa uv
    • Isso ensina como é importante acertar esse tipo de coisa desde o início do design da linguagem. Não deixe para depois da v2.0, pense várias vezes antes de colocar metadata em scripts executáveis; algo pode funcionar em certas linguagens e não ser uma boa ideia em Python
    • Eu nunca tive problema com dependências. Usar só requirements.txt e venv já é suficiente
    • O gerenciamento de dependências continua uma bagunça, e agora ainda tem Rust no meio
  • Tenho curiosidade sobre a comparação de segurança entre gerenciadores de pacotes Python como uv, pip e conda. Velocidade é boa, mas considero a segurança do gerenciador muito mais importante

    • O uv é mais seguro que o pip. Ele analisa dependências sem executar código arbitrário, verifica hashes de pacotes por padrão e evita vários riscos como typosquatting. Também é excelente em velocidade e reprodutibilidade (introdução técnica, documentação de compatibilidade)
    • Mas, no fundo, todo gerenciador de pacotes acaba baixando e executando código de terceiros não verificado. Mais importante do que a diferença de segurança de implementação entre uv e pip é o risco de não haver uma política adequada para código externo desde o início
  • Como alguém que publica pacotes no PyPI, pessoalmente quero usar uv pela velocidade, mas não posso migrar facilmente se não houver garantia de que ele se comporta exatamente como o pip. Se um usuário tiver erro com “pip install xxx”, eu também preciso reproduzir e depurar no mesmo ambiente

    • Não funciona de forma 100% idêntica ao pip. As principais diferenças são tratadas na documentação de compatibilidade. Algumas fazem parte da transição para seguir melhor o padrão, outras são escolhas de design próprias do uv
  • Acho que o uv é uma das mudanças mais positivas recentes no empacotamento Python: você simplesmente executa e ele entrega bons resultados

  • Também foi apresentado um excelente guia para usar uv na construção de contêineres de produção (ver guia)