O módulo zipimport no Python permite importar módulos e pacotes diretamente de arquivos ZIP. Na maioria das aplicações, você não precisa instanciá-lo manualmente: o mecanismo padrão de importação reconhece caminhos ZIP presentes em sys.path e usa zipimporter automaticamente.
Esse recurso é útil para distribuir coleções de módulos em um único arquivo, carregar plugins empacotados, reduzir a quantidade de pequenos arquivos e entender como aplicações .pyz funcionam. Entretanto, importar de ZIP não transforma código externo em seguro. O módulo importado é executado no processo atual com os mesmos privilégios da aplicação.
Como a importação por ZIP funciona
Um item de sys.path normalmente aponta para um diretório, mas também pode apontar para um arquivo ZIP. O importador procura arquivos .py e .pyc dentro do arquivo, respeitando a estrutura de pacotes.
import sys
sys.path.insert(0, "plugins.zip")
import meu_plugin
print(meu_plugin.__file__)
# plugins.zip/meu_plugin.pyNão é necessário escrever import zipimport para esse cenário. O sistema de imports seleciona o finder apropriado por meio dos hooks de caminho.
Estrutura de pacotes no arquivo
Pacotes tradicionais precisam manter sua estrutura, incluindo __init__.py quando aplicável.
plugins.zip
├── meu_plugin/
│ ├── __init__.py
│ ├── comandos.py
│ └── validacao.py
└── util.pyDepois de adicionar o ZIP ao caminho, você pode usar from meu_plugin import comandos. O arquivo deve preservar os nomes e diretórios esperados pelo sistema de importação.
Importe somente de um subdiretório
O caminho pode apontar para uma pasta interna do arquivo:
sys.path.insert(0, "pacote.zip/lib")
import bibliotecaNesse caso, apenas lib/ é tratada como raiz de importação. Isso ajuda quando o ZIP inclui documentação, recursos, metadados e código em diretórios separados.
Use zipimporter diretamente
Para ferramentas de diagnóstico e loaders customizados, crie uma instância de zipimport.zipimporter.
from zipimport import zipimporter, ZipImportError
try:
importer = zipimporter("plugins.zip")
except ZipImportError as erro:
print(f"Arquivo inválido: {erro}")
else:
spec = importer.find_spec("meu_plugin")
print(spec)O construtor aceita o arquivo completo ou um caminho interno, como plugins.zip/lib. Um arquivo inexistente ou inválido gera ZipImportError, que também é uma subclasse de ImportError.
Prefira find_spec
APIs antigas como find_module() e find_loader() foram removidas. Use find_spec(), que devolve um ModuleSpec compatível com importlib.
spec = importer.find_spec("meu_plugin.comandos")
if spec is None:
raise ModuleNotFoundError("Plugin não encontrado")
modulo = __import__("meu_plugin.comandos", fromlist=["*"])Na maior parte dos casos, ainda é melhor deixar a instrução import cuidar de todo o ciclo.
Leia código sem executar
get_source() retorna a fonte de um módulo quando ela está presente.
fonte = importer.get_source("meu_plugin")
if fonte is not None:
print(fonte[:200])Isso evita executar o módulo, mas não garante que o conteúdo seja confiável. Trate a fonte como dado externo, limite o tamanho e não a publique sem sanitização.
Para listar classes e funções sem importar, o guia de pyclbr no Python apresenta uma alternativa voltada a código-fonte Python.
Obtenha objetos de código
get_code() compila ou recupera o objeto de código correspondente.
objeto = importer.get_code("meu_plugin")
print(objeto.co_filename)Executar esse objeto com exec() possui os mesmos riscos de importar o módulo. Não use essa API para tentar criar uma sandbox.
Leia arquivos auxiliares
get_data() devolve bytes de um caminho existente no arquivo.
conteudo = importer.get_data("plugins.zip/meu_plugin/config.json")A montagem exata do caminho depende do arquivo e do prefixo do importador. Para recursos de pacotes, importlib.resources costuma oferecer uma interface mais estruturada. Veja o guia de importlib.resources no Python.
Descubra arquivo e tipo de módulo
get_filename() mostra o valor que seria usado em __file__. is_package() informa se o nome corresponde a pacote.
print(importer.get_filename("meu_plugin"))
print(importer.is_package("meu_plugin"))Essas funções ajudam navegadores, diagnósticos e sistemas de plugins a validar a estrutura antes da importação.
Limitação das extensões nativas
O importador aceita código-fonte e bytecode Python, mas não carrega módulos dinâmicos como .so e .pyd diretamente do ZIP. O carregador do sistema operacional precisa acessar binários como arquivos reais.
Se uma dependência contém extensão C, distribua-a instalada no ambiente ou extraia-a de maneira controlada para um diretório compatível com a plataforma. O guia de zipapp no Python explica a mesma limitação em aplicações .pyz.
Fonte versus bytecode
Quando o ZIP contém somente .py, Python compila o código durante a importação, mas não modifica o arquivo ZIP para adicionar .pyc. Importações repetidas em processos novos podem ser mais lentas.
Você pode incluir bytecode criado pela versão correta do Python, mas o formato não é estável entre versões. Em distribuições portáteis, manter a fonte geralmente é mais seguro. Nunca trate .pyc como criptografia.
Crie o arquivo corretamente
Use zipfile para gerar o pacote:
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
with ZipFile("plugins.zip", "w", ZIP_DEFLATED) as zipf:
for arquivo in Path("plugins").rglob("*.py"):
zipf.write(arquivo, arquivo.relative_to("plugins"))Normalize os caminhos, exclua caches e não inclua segredos, chaves privadas ou arquivos de ambiente. Para extração e validação segura, consulte zipfile no Python.
Plugins empacotados
Um sistema de plugins pode colocar arquivos ZIP em um diretório aprovado, verificar assinatura e hash, adicionar o caminho e importar um entry point conhecido. Não descubra nem execute qualquer módulo automaticamente.
from importlib import import_module
PERMITIDOS = {"relatorios", "exportacao"}
nome = validar_nome_solicitado()
if nome not in PERMITIDOS:
raise PermissionError("Plugin não autorizado")
plugin = import_module(f"plugins.{nome}")Uma allowlist reduz o risco, mas não substitui isolamento. Plugins de terceiros devem rodar em processo separado ou ambiente controlado.
Conflitos de nomes e ordem de sys.path
Inserir o ZIP na posição zero pode sobrescrever módulos legítimos com o mesmo nome. Um arquivo chamado json.py, por exemplo, pode sombrear a biblioteca padrão.
Use namespaces exclusivos, prefira adicionar o caminho após entradas confiáveis e registre de onde cada módulo foi carregado. Para investigar caminhos de runtime, veja site no Python e pkgutil no Python.
Cache do importador
invalidate_caches() limpa informações internas sobre os arquivos do ZIP.
importer.invalidate_caches()Se o arquivo é substituído durante a execução, invalide caches e evite trocas concorrentes. Uma estratégia mais segura é publicar um arquivo imutável com nome versionado e reiniciar os workers.
Atualizações atômicas
Não edite o ZIP no lugar enquanto outros threads ou processos importam. Crie um novo arquivo em localização temporária, valide sua integridade, sincronize a gravação e faça uma substituição atômica. Mesmo assim, módulos já presentes em sys.modules continuam carregados.
ZIP64 e arquivos grandes
Versões atuais suportam ZIP64, permitindo arquivos maiores e grande quantidade de entradas. Isso não significa que arquivos enormes sejam ideais. Um índice grande aumenta tempo de abertura, consumo de memória e superfície para ataques de negação de serviço.
Segurança
- Importar executa código com os privilégios do processo.
- Valide origem, assinatura e hash do arquivo.
- Restrinja caminhos e nomes de módulos.
- Não aceite ZIP enviado por usuário para importação direta.
- Evite colisões em
sys.path. - Limite tamanho, entradas e profundidade.
- Execute plugins não confiáveis em isolamento real.
- Registre arquivo, versão e módulo carregado.
Testes importantes
Teste pacotes, subpacotes, módulos apenas com fonte, módulos apenas com bytecode, caminhos internos, arquivo corrompido, comentários no ZIP, ZIP64 e colisões de nomes. Verifique também comportamento em Windows e Linux.
Confirme que dependências nativas produzem uma mensagem clara e que o sistema não cai para um módulo inesperado instalado fora do arquivo.
Conclusão
O zipimport no Python integra arquivos ZIP ao mecanismo padrão de imports e permite distribuir módulos Python em um único contêiner. Ele oferece acesso a specs, fonte, bytecode, dados e metadados do módulo sem exigir extração prévia.
Use o recurso com políticas explícitas de origem, caminhos e isolamento. Consulte a documentação oficial do zipimport e a PEP 273 sobre imports de ZIP.







