- Mesmo pequenos scripts de teste de API exigem ajustar o ambiente de execução por causa das dependências, mas, usando um shebang de execução do uv, é possível deixá-los em um formato executável diretamente, sem etapas de instalação
- O exemplo
jam_users.py entra em um REPL após excluir e criar usuários com httpx, IPython e loguru para testar o endpoint /users de uma API em Go
- No método tradicional, é preciso instalar pacotes globalmente no Python do sistema ou preparar manualmente um ambiente virtual, o que torna o compartilhamento e a reexecução de scripts trabalhosos
- Ao declarar
dependencies = ["ipython", "httpx", "loguru"] no cabeçalho # /// script e executar uv run jam_users.py, o uv cuida do ambiente isolado e da instalação das dependências
- Ao adicionar o shebang
#!/usr/bin/env -S uv run --script e permissão de execução, em sistemas Unix é possível executar como ./jam_users.py tendo apenas o uv instalado
Declarando dependências dentro do script
- O exemplo
jam_users.py prepara dados de usuários de teste para a API local http://localhost:4000/v1/users
- Envia requisições à API com httpx
- Entra no REPL do IPython para inspecionar as respostas e continuar os testes
- Registra logs de exclusão e criação com
loguru
- O fluxo do script básico é inicializar a lista de usuários e depois repopulá-la com dados de teste
- Busca a lista de usuários existente com
GET /v1/users
- Chama
DELETE /v1/users/{id} para cada usuário
- Envia a lista de usuários preparada em JSON para
POST /v1/users
- Em seguida, abre um REPL com
IPython.embed()
- Para executar com
python jam_users.py, httpx, IPython e loguru já precisam estar instalados no ambiente de execução
- Também é possível instalar globalmente no Python do sistema ou criar um ambiente virtual separado, mas ambos exigem preparação antes da execução
- A instalação global pode bagunçar o Python do sistema com pacotes
- A abordagem com ambiente virtual exige gerenciar manualmente criação, ativação, instalação e execução
- Em ambos os casos, é preciso ter um Python do sistema compatível com os pacotes necessários
Criando um script executável diretamente com uv
- Ao inserir a tag
# /// script do uv no topo do script, é possível declarar as dependências dentro do próprio arquivo
# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
- Com esse cabeçalho, é possível executar com
uv run jam_users.py
- O uv cria um ambiente virtual isolado para o script
- Baixa e instala as dependências necessárias
- Executa o script no contexto desse ambiente virtual
- Um shebang comum de Python, como
#!/usr/bin/env python, não aproveita o cabeçalho de script do uv, pois o Python ignora o comentário # /// script
- Ao colocar a chamada ao uv diretamente no shebang, é possível tratar o script como um arquivo executável
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
- A flag
-S do env faz com que a string seguinte seja dividida em argumentos separados e passada ao env
- Depois de conceder permissão de execução com
chmod +x jam_users.py, é possível executar diretamente assim
./jam_users.py
- Com essa abordagem, se o uv estiver instalado em um sistema Unix, o script pode ser executado sem instalação separada de dependências nem gerenciamento de ambiente virtual
- Ao entregar scripts Python complexos a outros usuários, isso reduz o peso de ter que explicar longos procedimentos de preparação do sistema antes da execução
1 comentários
Opiniões no Hacker News
Não é algo específico do UV, mas, de modo geral, me incomoda a forma de controlar a execução do código por meio de comentários
Acho ok usar comentários para diretivas de linters ou notas de desenvolvedores, mas, se forem dados relacionados a configuração ou execução, algo como
UV_ENV = { "dependencies": { "requests": "2.32.3", "pandas": "2.2.3" } }me parece muito melhorEssa abordagem é sintaxe Python válida, usa estruturas de dados padrão em vez de parsing arbitrário de comentários, o que facilita geração e validação, e, acima de tudo, preserva o princípio de que mesmo removendo todos os comentários, o código deve executar da mesma forma
A abordagem sugerida também é uma constante mágica que não faz nada em runtime e só é analisada por análise estática; em vez de ser lida pelo uv, outra ferramenta poderia vê-la como código não usado e removê-la
Melhor seria fazer uma chamada direta ao uv dizendo o que fazer, como
import uvseguido deuv.exec(dependencies=["clown"], python=">=3.10")A primeira execução aconteceria em qualquer runtime Python capaz de encontrar esse pacote
uvhipotético; o pacoteuvprepararia o ambiente virtual e o runtime Python e então fariare-exec(3)com algum sinalizador, como uma variável de ambienteNo segundo runtime,
uv.execdetectaria o sinalizador e não faria nadahttps://peps.python.org/pep-0723/
Além disso, a própria linha shebang também funciona, na prática, como um comentário
Ela está tão profundamente arraigada há 45 anos que as pessoas simplesmente não se dão muito conta de que aquilo é um comentário do shell
Se as mesmas dependências estiverem instaladas, o código executa da mesma forma
Em vez de mudar o significado do código em si, isso muda o ambiente em que o código é executado; nesse sentido, não é diferente do comentário
#!/bin/bashno topo de um script de shellDito isso, é bem provável que o uv não queira executar o código para descobrir as dependências, então teria de ser um subconjunto muito limitado da sintaxe Python
O próprio fato de algo assim ser necessário expõe uma fraqueza da linguagem
A instrução
importem si deveria ser capaz de transmitir todas as informações sobre dependênciasNos últimos meses, esse assunto apareceu com muita frequência no HN, e exemplos recentes são estes
https://news.ycombinator.com/item?id=43500124
https://news.ycombinator.com/item?id=42463975
Eu gosto do uv, mas tenho dificuldade em concordar com o termo self-contained por dois motivos
Primeiro, para executar o script, o uv já precisa estar instalado
Dá para fazer um script de shell verificar se o uv está instalado e, se não estiver, instalá-lo via curlpipe, mas isso aumenta bastante o boilerplate, e o próprio método curlpipe não é bom
Segundo, criar automaticamente um ambiente virtual em algum lugar no diretório home não é realmente self-contained
Mesmo que você execute uma única vez e apague o script, esse ambiente virtual fica para trás ocupando espaço, e não encontrei na documentação do uv nenhuma garantia de que esses ambientes virtuais temporários sejam limpos automaticamente
curl | shé válidaMas, à medida que gerenciadores de pacotes começarem a incluir o uv em seus repositórios, isso deve se tornar cada vez menos problemático
Por exemplo, o uv já é fornecido no Alpine Linux e no Homebrew: https://repology.org/project/uv/versions
Além disso, metadados inline de scripts são um padrão do Python
Se o sistema não tiver uv e ele não estiver empacotado, mas houver uma versão do Python compatível com o script, dá para executá-lo com pipx: https://pipx.pypa.io/stable/examples/#pipx-run-examples
O pipx é empacotado de forma muito mais ampla: https://repology.org/project/pipx/versions
Em uma máquina de desenvolvimento qualquer, Docker pode ser mais comum do que uv, e o texto original também dizia que era um projeto de empresa, então parecia plausível
No estado atual, porém, não há cache nenhum, então downloads acontecem a cada execução, o que é meio estranho; talvez dê para corrigir isso com volumes
É algo assim: https://hugojosefson.github.io/docker-shebang/#python
Ainda assim, se, na hora da execução, ele baixa da internet algo de identidade desconhecida, fica difícil chamar isso de self-contained; algo realmente totalmente self-contained está mais próximo de um AppImage
Com algo como py2exe, dá para criar um “script Python” self-contained
Isso causa muitos problemas para o desenvolvedor, mas minimiza os problemas para o usuário
Com Nix também se usa a mesma abordagem, e a linha shebang fica assim
#! nix-shell -i python3 -p "python312.withPackages (pkgs: [ pkgs.boto3 pkgs.click ])"Assim, a única coisa necessária no sistema é o Nix; nem é preciso ter Python instalado
O exemplo mais óbvio é um bash com todas as dependências especificadas, e eu já cheguei a criar scripts Rust rápidos em arquivo único com shebang do Nix
https://nixos.wiki/wiki/Nix-shell_shebang
nix shell, em vez denix-shellComo outros comentários disseram, a alegação de “self-contained” depende de
uvestar instaladoSe você quer um script Python realmente self-contained, vale olhar o compilador Nuitka
Uso em produção em um serviço gRPC sem problemas, e basta executar
nuitka --onefile run.pyPor ser um compilador, o binário resultante também pode ser mais rápido do que o programa Python original empacotado com PyInstaller
Na página do autor no GitHub há a frase: “a missão da minha vida é tentar, até morrer de velhice, criar o melhor Python Compiler possível”
https://nuitka.net/
https://github.com/kayhayen
Gosto muito desse padrão, mas infelizmente não consegui fazê-lo funcionar direito com LSP
Uso pyright no Helix, e nem executando o editor com
uv run hx script.pyfuncionaDá para fazer algo como
uv run --with whatever-it-is-i-need hx script.py, mas a duplicação vai aumentandouve$ cat ~/.local/bin/uve#!/bin/bashtemp=$(mktemp)uv export --script $1 --no-hashes > $tempuv run --with-requirements $temp vim $1unlink $tempEspero que os editores passem a oferecer suporte a
uv python find --scriptem breveParece bastante útil
Fico me perguntando se o uv é uma opção mais segura para distribuição de longo prazo de projetos baseados em Python
Lembro do rug pull de cerca de 5 anos atrás, quando usamos Anaconda para gerenciamento de dependências e depois as regras mudaram: clientes organizacionais com mais de 200 funcionários não podiam mais usar Anaconda gratuitamente e precisavam pagar por uma licença comercial
Eles podem parar o desenvolvimento ou mover o trabalho futuro para um fork com outra licença, mas não podem alterar retroativamente a licença anterior; portanto, o que existe hoje é garantidamente open source
Se isso realmente preocupa você, basta criar um fork e mantê-lo sincronizado
Na prática, isso vale quase da mesma forma para outros projetos OSS, então eu não me preocuparia muito
Até onde sei, o conda nunca foi open source e distribuía binários
https://github.com/astral-sh/uv?tab=readme-ov-file#license
Os pacotes do conda-forge continuam gratuitos
Como o uv simplesmente usa o PyPI, se houver problema dá para trocar o uv por pip, Poetry etc., e continuar obtendo os pacotes do mesmo lugar
Esse dono acabará sendo adquirido pela pior pessoa rica que se possa imaginar
Passei os olhos no guia de contribuição e nas issues do uv, mas não vi CLA; já o PyTorch tinha um CLA logo no topo do guia de contribuição
Ainda assim, deveria ter havido um fork comunitário da última versão FOSS do Anaconda
No Redis isso aconteceu, e o Redis também usa CLA: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md#redis-software-grant-and-contributor-license-agreement
Nunca se deve assinar um CLA
Eu diria que é melhor contribuir apenas com projetos copyleft
Somos bem pagos demais para trabalhar de graça
Para pequenos utilitários, parece uma boa alternativa de empacotamento em vez de conteinerização
Agora preciso convencer todos os meus colegas a instalar o uv
Parece semelhante ao bundler/inline do Ruby
É bom ver algo parecido surgindo no Python, e na prática é muito conveniente
https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html
Fico curioso para saber se alguém conseguiu fazer isso funcionar no Windows
Eu queria usar esse truque em uma ferramenta para um mod de jogo em que estou trabalhando, mas não consegui fazer o truque do shebang funcionar
$> uv init --script .py$> uv add --script .py ...$> uv add --script .py --dev ...$> uv run .pyEspero que ajude
Fonte: https://docs.astral.sh/uv/guides/scripts/
.pyO py launcher dá suporte a linhas shebang
Isso também foi abordado em um post de blog sobre o mesmo tema publicado há alguns dias e, segundo ele, é preciso remover o
-S: https://thisdavej.com/share-python-scripts-like-a-pro-uv-and-pep-723-for-easy-deployment/https://news.ycombinator.com/item?id=43500124
Não testei diretamente; em vez disso, alterei a associação de arquivos para que todos os arquivos
.pysejam abertos por padrão comuv runhttps://docs.python.org/3/using/windows.html#python-launcher-for-windows
https://peps.python.org/pep-0397/
https://pyinstaller.org
https://onor.io/2025/01/more-scripting-with-racket.html
Passei a gostar do uv por causa desse caso de uso, mas acho que o fato de uma PEP oficial e muito útil não ser suportada pelas ferramentas oficiais do Python vai contra o Zen of Python
Do meu ponto de vista, foi a primeira vez que Python deixou de vir com “batteries included”
Agora também tenho dois gerenciadores de dependências de Python no sistema
Sei que há muito a dizer sobre gerenciamento de dependências em Python, mas, nos últimos anos, quando um projeto tinha apenas
requirements.txt, consegui me virar com opip+venvpadrãoPelo que me lembro,
pyproject.tomlveio antes da bibliotecatomllibEntão, por algumas versões, era preciso especificar módulos em uma linguagem que o Python não conseguia ler por padrão
É uma situação pior do que haver uma forma padrão de incluir metadados que não são usados
Afinal, é por isso que são metadados; caso contrário, seriam apenas sintaxe de Python