O módulo zipapp cria aplicações Python executáveis em um único arquivo ZIP, normalmente com extensão .pyz. O interpretador consegue adicionar o arquivo ao sys.path e executar seu __main__.py. Esse formato é útil para ferramentas internas, scripts de automação, utilitários de linha de comando, protótipos e distribuição em ambientes que já possuem uma versão compatível do Python.
Um zipapp não é um executável nativo independente. Ele não inclui automaticamente o interpretador, não resolve todas as dependências e não transforma extensões compiladas em arquivos importáveis dentro do ZIP. Para distribuição pública, desktop ou ambientes sem Python, ferramentas de empacotamento completas podem ser mais adequadas.
Estrutura mínima
Uma aplicação ZIP precisa de um arquivo __main__.py na raiz.
meu_app/
├── __main__.py
└── pacote/
├── __init__.py
└── cli.py
O conteúdo de __main__.py inicia a aplicação.
from pacote.cli import main
if __name__ == "__main__":
raise SystemExit(main())
Esse padrão permite testar main() separadamente e propagar um código de saída.
Crie pela linha de comando
Use o módulo com -m.
python -m zipapp meu_app -o meu_app.pyz
Depois execute:
python meu_app.pyz --help
O Python trata o arquivo como um diretório importável e executa seu entry point.
Crie pela API
A função zipapp.create_archive() permite automatizar o build.
from zipapp import create_archive
create_archive(
"meu_app",
target="dist/meu_app.pyz",
)
Crie o diretório de destino antes ou use pathlib para organizar o pipeline.
Entry point com main
Quando o diretório não possui __main__.py, você pode indicar um entry point no formato pacote.modulo:funcao.
python -m zipapp meu_app \
-m "pacote.cli:main" \
-o meu_app.pyz
O zipapp gera um __main__.py que importa e chama a função.
Requisitos da função principal
A função deve ser importável e chamável sem argumentos, a menos que ela própria leia sys.argv. Um padrão claro:
def main():
argumentos = parser.parse_args()
executar(argumentos)
return 0
Não coloque toda a lógica no import do módulo. Imports devem configurar definições, não executar o programa.
Shebang e interpreter
A opção --python ou o parâmetro interpreter adiciona uma linha de interpreter ao início do arquivo.
python -m zipapp meu_app \
--python "/usr/bin/env python3" \
-o meu_app.pyz
chmod +x meu_app.pyz
./meu_app.pyz
Isso funciona em sistemas compatíveis com shebang. No Windows, normalmente execute com python meu_app.pyz ou associe a extensão.
Escolha de versão
Um shebang genérico como python3 pode encontrar versões diferentes em cada máquina. Se o código exige uma versão mínima, valide no início.
import sys
if sys.version_info < (3, 12):
raise SystemExit("Python 3.12 ou superior é necessário")
Também documente a versão testada.
Compressão
A opção --compress ou compressed=True comprime arquivos dentro do archive.
create_archive(
"meu_app",
target="dist/meu_app.pyz",
compressed=True,
)
A compressão reduz tamanho, mas aumenta CPU durante build e leitura. Para código pequeno, a diferença pode ser mínima.
Aplicações já compactadas
create_archive() também pode copiar ou ajustar um archive existente em determinados fluxos. Não use essa capacidade como editor genérico de ZIP sem entender o comportamento.
Recrie a aplicação a partir de uma fonte limpa para builds reproduzíveis.
Dependências Python puras
Pacotes escritos apenas em Python podem ser copiados para o diretório antes do build.
python -m pip install \
--target build/app \
--requirement requirements.txt
cp -r src/meu_pacote build/app/
python -m zipapp build/app -o dist/app.pyz
Use um ambiente de build isolado e versões fixadas.
Extensões nativas
Módulos compilados como .so e .pyd normalmente não podem ser importados diretamente de dentro do ZIP, porque o loader do sistema precisa de um arquivo real.
Se uma dependência contém código nativo, mantenha-a instalada fora do zipapp, extraia-a de forma controlada ou escolha outra ferramenta de distribuição.
Descubra dependências nativas
Não confie apenas no nome do pacote. Inspecione a wheel e teste em um ambiente limpo.
Dependências transitivas podem introduzir extensões nativas mesmo quando o pacote principal parece puro.
Recursos do pacote
Código dentro do ZIP não deve presumir que __file__ aponta para um arquivo normal acessível por APIs do sistema.
Use importlib.resources para ler templates, dados e arquivos empacotados.
from importlib.resources import files
texto = (
files("meu_pacote")
.joinpath("dados/config_padrao.json")
.read_text(encoding="utf-8")
)
Recursos que exigem path real
Algumas bibliotecas exigem um pathname. importlib.resources.as_file() pode materializar temporariamente um recurso quando suportado.
Use um context manager e não guarde o path depois da saída.
Arquivos graváveis
O conteúdo do zipapp deve ser tratado como somente leitura. Não grave configuração, cache ou banco ao lado dos módulos internos.
Escolha diretórios de usuário, temporários ou de dados da aplicação e permita configuração explícita.
Diretório atual
Não presuma que o processo começa no diretório do archive. Path.cwd() depende de onde o usuário executou o comando.
Use caminhos absolutos configurados ou recursos internos.
Imports absolutos
Organize a aplicação como pacote e use imports absolutos. Imports relativos frágeis e módulos com nomes iguais aos da biblioteca padrão causam conflitos.
Teste o arquivo final, não apenas a árvore de fontes.
Namespace packages
Namespace packages podem funcionar, mas combinações com partes externas e loaders diferentes exigem testes.
Para uma ferramenta compacta, um pacote normal costuma ser mais previsível.
sys.path
Durante a execução, o archive aparece no caminho de imports. A ordem exata pode interagir com pacotes instalados externamente.
Não dependa de shadowing acidental. Use nomes de pacote únicos e valide versões de dependências.
Conflito com ambiente externo
Se uma dependência não está embutida, o zipapp pode importar uma versão instalada no ambiente do usuário.
Isso reduz o isolamento. Faça validação de versão ou distribua um ambiente virtual junto com instruções claras.
Zipapp e venv
Uma estratégia interna é criar um venv com dependências nativas e executar o .pyz com o Python desse ambiente.
O zipapp contém o código da aplicação; o venv fornece runtime e bibliotecas externas.
Zipapp e pipx
Ferramentas como pipx instalam aplicações Python em ambientes isolados. Para distribuição convencional de CLI, um pacote com entry point pode ser mais fácil de atualizar que um archive manual.
Escolha zipapp quando o arquivo único realmente simplifica a operação.
Metadados de versão
Inclua uma constante ou metadata acessível pelo comando --version.
__version__ = "1.4.0"
Também pode gravar um arquivo de manifest dentro do archive com commit, data e versão de build.
Build reproduzível
ZIPs armazenam timestamps e ordem de entradas, o que pode tornar hashes diferentes. Para reproducibility, controle timestamps, ordene arquivos e fixe dependências.
O zipapp básico pode não atender todos os requisitos de build determinístico sem uma etapa adicional.
Filtro de arquivos
A API aceita um filtro para decidir quais caminhos entram.
def incluir(caminho):
partes = set(caminho.parts)
return not partes.intersection({"__pycache__", ".git", "tests"})
create_archive("build/app", "dist/app.pyz", filter=incluir)
Não exclua arquivos necessários por engano. Teste a aplicação final em um diretório limpo.
Não inclua segredos
Um zipapp é apenas um ZIP e pode ser aberto facilmente. Nunca coloque senhas, tokens, chaves privadas ou credenciais dentro dele.
Leia segredos de um secret manager, ambiente controlado ou arquivo externo protegido.
Assinatura e checksum
Distribua um hash ou assinatura para verificar integridade. O Python não valida automaticamente uma assinatura do zipapp antes de executar.
O processo de distribuição precisa verificar o arquivo antes de invocar o interpretador.
Código não confiável
Executar um .pyz executa código Python com as permissões do usuário. Não baixe e execute archives desconhecidos.
Use HTTPS, assinatura, origem confiável e menor privilégio.
Atualização
Um arquivo único facilita substituição, mas a atualização deve ser atômica. Baixe para um nome temporário, valide e use os.replace().
Não sobrescreva o arquivo enquanto uma verificação ainda está em andamento.
Rollback
Mantenha a versão anterior até o novo arquivo passar por um teste rápido. Um symlink ou launcher pode selecionar a versão ativa.
Não misture migrations irreversíveis com atualização sem plano de rollback.
Execução em containers
Um zipapp pode reduzir arquivos de aplicação em uma imagem, mas ainda precisa do runtime e dependências.
Containers já fornecem isolamento e layers; avalie se o archive realmente agrega valor.
Argumentos e códigos de saída
Use argparse e retorne códigos consistentes. raise SystemExit(main()) preserva o status.
Escreva dados normais em stdout e erros em stderr para integração com shell.
Logging
Não escreva logs dentro do ZIP. Configure stderr, arquivo externo ou logging estruturado.
Inclua versão do archive no início do processo para diagnóstico.
Testes
Crie o archive em CI e execute comandos reais em um ambiente limpo. Teste ajuda, versão, erro de dependência, recurso interno, código de saída, paths com espaços, Windows e POSIX.
Compare o comportamento da fonte e do .pyz.
Erros comuns
Os erros mais frequentes são esquecer __main__.py, incluir extensão nativa dentro do ZIP, usar __file__ como path normal, gravar dentro do archive, depender de pacote externo em versão desconhecida, incluir segredos, não testar o artefato e confundir zipapp com executável autossuficiente.
Conclusão
zipapp oferece uma forma simples de empacotar aplicações Python puras em um arquivo executável pelo interpretador. Organize um entry point limpo, use importlib.resources, fixe dependências, trate o archive como somente leitura e teste no ambiente final.
Consulte a documentação oficial de zipapp e o artigo sobre sysconfig no Python para compreender o runtime de destino.







