py_compile no Python: gere arquivos pyc

Publicado em: 04/08/2026
Tempo de leitura: 7 minutos
Monitor com código binário representando geração de arquivos pyc com py_compile no Python

Quando um módulo Python é importado, o interpretador pode armazenar seu bytecode em um arquivo .pyc dentro de __pycache__. O módulo py_compile no Python permite gerar esse cache antecipadamente, validar arquivos durante builds e controlar como o interpretador decide se o bytecode ainda corresponde ao código-fonte.

Neste guia, você aprenderá a usar py_compile.compile(), tratar PyCompileError, escolher otimização e configurar invalidação por timestamp ou hash. O conteúdo complementa nossos artigos sobre bytecode com dis, compileall, tracebacks, tokenize e compilação interativa.

O que é um arquivo pyc

Um arquivo .pyc contém bytecode serializado e um cabeçalho com informações usadas para validar o cache. Ele evita recompilar o mesmo código-fonte em determinadas inicializações, mas não transforma Python em um binário independente.

O bytecode continua específico da implementação e da versão. Um arquivo produzido por uma versão do CPython normalmente não deve ser reutilizado por outra tag de cache.

Compilar um arquivo

A função principal recebe o caminho do arquivo-fonte.

import py_compile

caminho = py_compile.compile("meu_modulo.py")
print(caminho)

Por padrão, o resultado segue as convenções PEP 3147 e PEP 488, normalmente dentro de __pycache__ com a tag do interpretador.

Validar sintaxe durante o build

Compilar previamente é uma maneira simples de detectar SyntaxError antes da implantação.

from pathlib import Path
import py_compile

for arquivo in Path("src").rglob("*.py"):
    py_compile.compile(
        str(arquivo),
        doraise=True,
    )

Para árvores inteiras, o módulo compileall oferece uma interface mais conveniente. py_compile é adequado quando a aplicação já possui a lista exata de arquivos.

doraise e PyCompileError

Por padrão, um erro é escrito em stderr e a função retorna None. Com doraise=True, a falha gera PyCompileError.

try:
    py_compile.compile(
        "quebrado.py",
        doraise=True,
    )
except py_compile.PyCompileError as erro:
    print("Compilação falhou:", erro)

Aplicações e pipelines devem preferir a exceção, pois ela permite controlar o status e produzir relatórios estruturados.

Definir o destino com cfile

cfile escolhe explicitamente o arquivo de saída.

py_compile.compile(
    "modulo.py",
    cfile="build/modulo.pyc",
    doraise=True,
)

Crie o diretório de destino antes. Um caminho personalizado pode ser útil em empacotamento, mas as convenções padrão facilitam importação e coexistência entre versões.

Se o destino calculado for um link simbólico ou um arquivo não regular, a função lança FileExistsError.

A documentação oficial de py_compile explica que a gravação segue a semântica de importlib, com escrita temporária e renomeação para reduzir problemas de concorrência. A proteção evita substituir silenciosamente objetos especiais.

Nome de arquivo nos tracebacks

dfile define o nome que aparecerá em tracebacks e mensagens relacionados ao objeto compilado.

py_compile.compile(
    "src/pacote/modulo.py",
    dfile="/app/pacote/modulo.py",
    doraise=True,
)

Isso é útil quando o caminho no ambiente de build difere do caminho de implantação. Use uma localização significativa e não exponha diretórios secretos.

Nível de otimização

optimize é passado ao built-in compile(). O padrão -1 utiliza o nível do interpretador atual.

py_compile.compile(
    "modulo.py",
    optimize=1,
    doraise=True,
)

O nível 1 remove asserts e altera __debug__. O nível 2 também remove docstrings em muitos contextos. Não use otimização para esconder código nem presuma ganho significativo sem medir.

Arquivos diferentes por otimização

A tag do cache inclui a variante de otimização quando o destino padrão é usado. Isso permite que caches de níveis diferentes coexistam em __pycache__.

Ao escolher cfile manualmente, não sobrescreva acidentalmente variantes necessárias. Registre versão, implementação e otimização no processo de build.

Modos de invalidação

PycInvalidationMode controla como o interpretador verifica se um .pyc está atualizado.

  • TIMESTAMP: compara timestamp e tamanho;
  • CHECKED_HASH: armazena hash e compara com o conteúdo;
  • UNCHECKED_HASH: armazena hash, mas confia que um sistema externo mantém o cache.

O modo fica registrado no cabeçalho do arquivo.

Invalidação por timestamp

from py_compile import PycInvalidationMode

py_compile.compile(
    "modulo.py",
    doraise=True,
    invalidation_mode=PycInvalidationMode.TIMESTAMP,
)

É rápido e padrão quando SOURCE_DATE_EPOCH não está definido. Filesystems com resolução de tempo limitada podem criar casos em que conteúdo muda, mas metadados parecem equivalentes.

Hash verificado

py_compile.compile(
    "modulo.py",
    doraise=True,
    invalidation_mode=PycInvalidationMode.CHECKED_HASH,
)

O interpretador calcula novamente o hash da fonte durante a importação. Esse modo melhora determinismo e evita depender somente de timestamps, com custo adicional de leitura e hashing.

Hash não verificado

py_compile.compile(
    "modulo.py",
    doraise=True,
    invalidation_mode=PycInvalidationMode.UNCHECKED_HASH,
)

Nesse modo, o runtime assume que o arquivo está correto. Use somente quando um gerenciador de pacotes ou build system controla rigorosamente a atualização dos caches.

SOURCE_DATE_EPOCH

Quando a variável SOURCE_DATE_EPOCH está presente, o padrão do modo de invalidação passa a ser CHECKED_HASH. Isso ajuda builds reproduzíveis que não querem depender do horário real.

Desde Python 3.7.2, a variável determina o padrão, mas não substitui um argumento explícito.

quiet

quiet controla mensagens quando doraise=False.

  • 0 ou 1: comportamento normal de diagnóstico;
  • 2: nenhuma mensagem e doraise deixa de ter efeito.

Em código de automação, prefira doraise=True e captura de exceção em vez de silenciar completamente a falha.

Interface de linha de comando

O módulo pode compilar arquivos informados explicitamente.

python -m py_compile arquivo1.py arquivo2.py

Ele não procura recursivamente por fontes. O status de saída é diferente de zero se algum arquivo não puder ser compilado.

Ler caminhos da entrada padrão

Quando - é o único argumento, os nomes dos arquivos são lidos de stdin.

find src -name '*.py' -print | python -m py_compile -

Caminhos com newline são raros, mas possíveis em sistemas Unix. Para pipelines rigorosos, use uma integração Python que percorra Path diretamente.

Modo quiet na CLI

python -m py_compile -q arquivo1.py arquivo2.py

A opção reduz mensagens, mas o código de saída ainda deve ser verificado pelo pipeline.

Permissões e instalação compartilhada

A compilação antecipada é útil quando usuários finais podem ler o pacote, mas não possuem permissão para gravar em __pycache__.

O processo de instalação executado com privilégios adequados cria os caches. Depois, permissões devem permitir leitura sem conceder escrita desnecessária.

Concorrência

A escrita usa arquivo temporário e renomeação, o que reduz resultados parciais quando vários processos compilam o mesmo destino. Isso não elimina toda disputa de build nem torna seguro escolher o mesmo cfile para fontes diferentes.

Use diretórios separados por versão e evite builds concorrentes que escrevem no mesmo artefato personalizado.

pyc não protege o código

Bytecode pode ser inspecionado e desmontado. Distribuir apenas .pyc não fornece proteção forte de propriedade intelectual, assinatura ou criptografia.

Além disso, código não confiável continua perigoso quando importado, independentemente de estar em fonte ou bytecode.

Remover caches antigos

Não é necessário versionar __pycache__ na maioria dos projetos. Ao trocar interpretador ou criar um build limpo, remova caches e regenere.

from pathlib import Path

for pasta in Path(".").rglob("__pycache__"):
    for arquivo in pasta.iterdir():
        arquivo.unlink()
    pasta.rmdir()

Use ferramentas de limpeza com cuidado para não apagar ambientes compartilhados.

Exemplo de compilador controlado

from pathlib import Path
import py_compile


def compilar(arquivo: Path) -> Path:
    destino = py_compile.compile(
        str(arquivo),
        doraise=True,
        optimize=0,
        invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
    )
    return Path(destino)

O chamador pode registrar tamanho, hash do artefato e versão do Python.

Testes

Crie arquivos temporários válidos e inválidos e confirme destino, exceções e modo escolhido.

from tempfile import TemporaryDirectory

with TemporaryDirectory() as pasta:
    fonte = Path(pasta) / "ok.py"
    fonte.write_text("valor = 42\n", encoding="utf-8")
    pyc = compilar(fonte)
    assert pyc.exists()

Teste também symlink de destino em plataformas que oferecem suporte.

Erros frequentes

  • Presumir que pyc é portátil entre versões.
  • Ignorar o retorno None quando doraise=False.
  • Escolher um cfile compartilhado por várias fontes.
  • Usar hash não verificado sem build confiável.
  • Distribuir pyc como proteção do código.
  • Confundir compilação com execução de testes.
  • Não registrar o nível de otimização.
  • Versionar caches gerados localmente sem necessidade.

Boas práticas

  • Use doraise=True em automação.
  • Prefira o caminho padrão de __pycache__.
  • Escolha explicitamente o modo em builds reproduzíveis.
  • Separe artefatos por versão e otimização.
  • Compile antes da implantação para detectar sintaxe inválida.
  • Execute testes além da compilação.
  • Não importe bytecode não confiável.
  • Limpe caches ao trocar de ambiente.

Conclusão

O módulo py_compile no Python compila arquivos-fonte em caches .pyc, permitindo validar sintaxe e preparar instalações onde o runtime não pode escrever no diretório do pacote. Ele oferece controle sobre destino, nome em traceback, otimização e invalidação.

A escolha entre timestamp e hash depende do processo de build. Com exceções explícitas, caminhos padronizados e isolamento por versão, py_compile torna a geração de bytecode previsível sem confundir cache com binário portátil, proteção de código ou teste funcional.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Editor de código representando correção de tabs e espaços com tabnanny no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tabnanny no Python: corrija indentação

    Aprenda tabnanny no Python para detectar tabs e espaços ambíguos, verificar projetos e evitar TabError e IndentationError.

    Ler mais

    Tempo de leitura: 7 minutos
    04/08/2026
    Código-fonte e sintaxe representando análise lexical com tokenize no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tokenize no Python: analise código-fonte

    Aprenda tokenize no Python para analisar tokens, comentários, encoding, posições e reconstruir código-fonte com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    04/08/2026
    Terminal de programação representando compilação de entradas interativas com codeop no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    codeop no Python: compile entradas interativas

    Aprenda codeop no Python para detectar entradas completas, compilar comandos de REPL e preservar __future__ com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    04/08/2026
    Desenvolvedor analisando estrutura de código e tabelas de símbolos com symtable no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    symtable no Python: escopos e símbolos

    Aprenda symtable no Python para analisar escopos, símbolos, globals, nonlocals, closures, imports, annotations e type parameters.

    Ler mais

    Tempo de leitura: 7 minutos
    03/08/2026
    Monitor com código binário representando análise de bytecode com dis no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    dis no Python: entenda o bytecode

    Aprenda dis no Python para desmontar bytecode, analisar instruções, caches adaptativos, posições, tracebacks e detalhes do CPython.

    Ler mais

    Tempo de leitura: 7 minutos
    03/08/2026
    Tela de erro representando diagnóstico de crashes e deadlocks com faulthandler no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    faulthandler no Python: diagnostique travamentos

    Aprenda faulthandler no Python para diagnosticar crashes, deadlocks e timeouts com pilhas de threads e código nativo.

    Ler mais

    Tempo de leitura: 8 minutos
    03/08/2026