O módulo compileall compila arquivos-fonte Python de um diretório ou árvore inteira para bytecode .pyc. Ele é usado em builds, imagens de containers, pacotes de sistema, deploys offline, validação de sintaxe e preparação de ambientes em que a primeira importação não deve pagar o custo da compilação.
O módulo não transforma Python em um executável independente e não protege o código-fonte. Os arquivos .pyc continuam dependentes de uma versão e implementação compatível do Python, podem ser analisados e normalmente são apenas um cache de importação. Use compileall para desempenho operacional e verificação, não como mecanismo de segurança.
Compile um diretório
A função compile_dir() percorre um diretório e compila arquivos reconhecidos.
import compileall
sucesso = compileall.compile_dir(
"meu_pacote",
quiet=1,
)
print(sucesso)
O retorno geral indica se todas as compilações solicitadas terminaram sem falhas. Em um build, trate False como erro.
Compile um arquivo
compile_file() compila um único caminho.
compileall.compile_file(
"meu_pacote/modulo.py",
quiet=1,
)
Para controle mais detalhado de um arquivo, o módulo py_compile é a camada de baixo nível; ele será abordado no próximo artigo.
Use pela linha de comando
O módulo possui uma interface CLI.
python -m compileall meu_pacoteEla é conveniente em Dockerfiles, pipelines e scripts de release. Verifique o código de saída e não confie apenas no texto impresso.
Onde os arquivos .pyc ficam
Por padrão moderno, o bytecode é gravado em diretórios __pycache__ com nomes que incluem a tag da implementação e, quando aplicável, o nível de otimização.
meu_pacote/__pycache__/modulo.cpython-314.pycEssa separação permite que diferentes versões e otimizações coexistam.
O modo legacy
A opção legacy=True grava o arquivo compilado ao lado do fonte com o formato antigo de nome.
Evite esse modo salvo quando uma ferramenta legada exigir. O layout em __pycache__ é o padrão esperado pelo sistema de importação moderno.
Forçar recompilação
Com force=True, os arquivos são compilados mesmo quando o cache parece atualizado.
compileall.compile_dir(
"meu_pacote",
force=True,
quiet=1,
)
Isso é útil em builds reproduzíveis e após mudanças de política. Em árvores grandes, aumenta tempo e escrita em disco.
Validação de sintaxe
Compilar toda a árvore detecta SyntaxError, indentação inválida e algumas incompatibilidades de versão sem executar os módulos.
É uma verificação rápida antes de empacotar, mas não substitui testes, importações reais ou análise de tipos.
Não executa o corpo
A compilação analisa o código e gera code objects, mas não executa imports, decorators, chamadas de nível de módulo ou inicialização de classes.
Um arquivo pode compilar corretamente e falhar ao importar por dependência ausente, variável de ambiente, erro de runtime ou extensão nativa incompatível.
Recursão de diretórios
compile_dir() percorre subdiretórios até um nível configurável.
compileall.compile_dir(
"src",
maxlevels=5,
quiet=1,
)
Defina limites quando a raiz contém mounts, árvores geradas ou links inesperados.
Links simbólicos
O comportamento de links e caminhos deve ser avaliado no ambiente real. Uma árvore de build pode incluir symlinks que apontam para fora da raiz ou criam duplicação.
Prepare uma lista explícita de diretórios e não compile caminhos fornecidos por usuários sem validação.
Exclua arquivos com rx
O parâmetro rx aceita uma expressão regular para excluir caminhos.
import re
compileall.compile_dir(
"projeto",
rx=re.compile(r"/(tests|vendor)/"),
quiet=1,
)
Teste a expressão em Windows e Unix porque separadores e representação do path podem variar.
Quiet
quiet controla a quantidade de saída. Em automação, reduza mensagens normais, mas preserve erros.
Não use silêncio como substituto de observabilidade. Registre duração, quantidade de arquivos e resultado da etapa.
Workers paralelos
O parâmetro workers permite compilar vários arquivos em paralelo.
compileall.compile_dir(
"src",
workers=4,
quiet=1,
)
Escolha um número compatível com CPU, disco e limites do runner. Mais workers podem piorar desempenho em storage lento ou compartilhado.
Workers zero
Em versões compatíveis, valores especiais podem selecionar automaticamente uma quantidade baseada no sistema. Consulte a documentação da versão em execução antes de depender desse comportamento.
Para builds previsíveis, um limite explícito costuma ser mais fácil de controlar.
Níveis de otimização
O parâmetro optimize controla a otimização usada na compilação. Pode representar o nível padrão, -O ou -OO, e APIs recentes aceitam uma sequência de níveis.
compileall.compile_dir(
"src",
optimize=[0, 1, 2],
quiet=1,
)
Isso pode gerar vários arquivos de cache para a mesma fonte.
O que -O altera
O nível otimizado remove asserts e define __debug__ como falso. -OO também pode remover docstrings.
Nunca use assert para validação de entrada, autorização ou invariantes que precisam existir em produção.
hardlink_dupes
Quando vários níveis de otimização geram bytecode idêntico, a opção hardlink_dupes pode usar hard links para economizar espaço, quando o filesystem permite.
Teste em containers, volumes e sistemas de empacotamento. Hard links têm semântica e suporte diferentes entre plataformas.
Invalidation mode
Arquivos .pyc possuem uma estratégia de invalidação, baseada em timestamp ou hash, conforme a política escolhida.
Builds reproduzíveis frequentemente preferem invalidação por hash para não depender de mtimes variáveis. Use py_compile.PycInvalidationMode com a API quando precisar controlar isso.
Timestamp versus hash
O modo por timestamp é rápido e comum, mas depende de tamanho e hora do arquivo. O modo por hash incorpora a identidade do conteúdo e é mais adequado a ambientes herméticos.
Escolha uma política consistente com a forma como o código será instalado e importado.
SOURCE_DATE_EPOCH
Ambientes de build reproduzível podem definir SOURCE_DATE_EPOCH, influenciando padrões relacionados a invalidação e determinismo.
Não basta definir a variável: controle também ordem de arquivos, paths embutidos, permissões e versão do interpretador.
Paths embutidos em tracebacks
O code object armazena um filename que aparece em tracebacks. Em uma máquina de build, um path absoluto pode vazar detalhes e não existir no destino.
Use opções de transformação de path para gerar nomes coerentes com a instalação final.
stripdir
stripdir remove um prefixo do caminho gravado no code object.
compileall.compile_dir(
"/build/work/src",
stripdir="/build/work",
quiet=1,
)
O prefixo precisa corresponder corretamente. Teste tracebacks resultantes.
prependdir
prependdir adiciona um prefixo após a remoção.
compileall.compile_dir(
"/build/work/src",
stripdir="/build/work/src",
prependdir="/opt/app",
quiet=1,
)
Isso ajuda a fazer o filename compilado apontar para o layout final.
ddir
Parâmetros históricos de diretório de destino lógico existem para compatibilidade. Prefira as opções atuais de strip e prepend quando disponíveis e documente a versão mínima.
Não misture opções incompatíveis sem verificar a assinatura da versão executada.
Builds em containers
Compile depois de copiar o código e instalar dependências, usando o mesmo Python que executará a aplicação.
RUN python -m compileall -q /opt/appCompilar em um estágio e executar com outra versão pode produzir caches ignorados ou incompatíveis.
Não copie caches da máquina local
Arquivos .pyc locais podem usar outra versão, otimização, arquitetura de paths ou política de invalidação.
Adicione __pycache__ ao ignore do repositório e gere caches no build final.
Ambientes somente leitura
Pré-compilar é útil quando o diretório da aplicação será read-only e o runtime não poderá criar __pycache__.
Confirme que todos os arquivos necessários foram compilados e que a versão do Python corresponde ao runtime.
Permissões
O usuário de build precisa ler fontes e criar diretórios de cache. Depois, ajuste ownership e permissões para o usuário de execução.
Não execute a aplicação como root apenas para permitir criação de bytecode.
Pacotes instalados
Ferramentas de instalação podem compilar pacotes automaticamente. Evite duplicar trabalho sem medir.
Use compileall explicitamente quando a política de build exige validação, otimizações específicas ou layout read-only.
Namespace packages
Pacotes de namespace podem estar distribuídos por vários diretórios. Compile cada raiz instalada e não presuma uma única árvore.
A compilação não valida se a composição de namespaces em runtime está correta.
Erros de leitura
Arquivos sem permissão, paths quebrados e encoding inválido podem falhar. Preserve o relatório e pare o build quando o código obrigatório não compilar.
Não ignore falhas apenas porque alguns caches foram gerados.
Arquivos gerados
Gere fontes antes de executar compileall. Se um passo posterior altera os arquivos, o cache pode ficar obsoleto ou ser recompilado na primeira importação.
Ordene o pipeline: geração, formatação, validação, compilação, empacotamento.
Segurança de entrada
Não compile árvores enviadas por usuários no processo principal de um serviço. O parser pode consumir CPU e memória, e paths podem explorar mounts ou links.
Use worker isolado, limites, diretório temporário e política de arquivos.
Bytecode não é segredo
Distribuir apenas .pyc dificulta leitura casual, mas ferramentas conseguem inspecionar constantes, nomes e instruções.
Não coloque segredos no código e não prometa proteção intelectual baseada em bytecode.
Compatibilidade
O formato de bytecode muda entre versões. Um .pyc possui um magic number e normalmente é rejeitado por uma versão incompatível.
Compile com o mesmo major/minor e implementação que executarão o pacote.
Use dis para inspeção
O módulo dis mostra instruções do code object e ajuda a comparar otimizações.
Veja dis no Python.
Valide com imports
Após compilar, execute testes de importação em um ambiente limpo. Isso detecta dependências, extensões e efeitos de inicialização ausentes.
python -c "import meu_pacote"CI
Uma etapa útil combina compilação e testes.
python -m compileall -q -f src
pytestUse o mesmo interpretador declarado pelo projeto.
Limpeza
Para remover caches antigos, apague diretórios __pycache__ dentro da árvore controlada.
Valide a raiz antes de uma remoção recursiva e não siga links para fora do workspace.
Testes do processo de build
Teste árvore vazia, erro de sintaxe, arquivo sem permissão, vários níveis de otimização, paths transformados, execução read-only e importação no destino.
Compare hashes de artefatos quando a reprodução for requisito.
Erros comuns
Os erros mais frequentes são tratar bytecode como executável independente, compilar com outra versão, ignorar retorno falso, usar caches locais, depender de asserts em produção, embutir paths da máquina de build, gerar antes de modificar fontes e acreditar que .pyc protege o código.
Conclusão
compileall prepara árvores Python para importação, valida sintaxe e permite controlar otimização, paralelismo, paths e invalidação. Use-o no build com o mesmo interpretador do runtime e trate qualquer falha como problema do artefato.
Gere caches em ambiente limpo, mantenha tracebacks coerentes e valide imports depois. Consulte a documentação oficial de compileall e o guia de sysconfig no Python para inspecionar detalhes do build.







