tempfile em Python: arquivos temporários seguros na prática
Aprenda tempfile em Python com TemporaryDirectory, NamedTemporaryFile, SpooledTemporaryFile, testes, permissões e limpeza automática em exemplos práticos.
O módulo tempfile é a forma recomendada de criar arquivos e diretórios temporários em Python sem inventar nomes, disputar o mesmo caminho com outro processo ou esquecer resíduos no servidor. Ele faz parte da biblioteca padrão e oferece APIs de alto nível como TemporaryDirectory, NamedTemporaryFile, TemporaryFile e SpooledTemporaryFile.
A recomendação direta é: use um context manager (with) sempre que o temporário só for necessário durante uma operação. Escolha TemporaryDirectory para fluxos com vários artefatos; NamedTemporaryFile quando outra biblioteca precisa receber um caminho; TemporaryFile quando apenas seu código usa o objeto de arquivo; e SpooledTemporaryFile quando dados pequenos podem ficar em memória, mas os grandes devem migrar automaticamente para o disco.
Isso aparece em tarefas reais como gerar PDFs, descompactar documentos fiscais, converter imagens, preparar anexos, receber uploads e chamar programas externos. Neste guia, você vai aprender as diferenças entre as APIs, as armadilhas no Windows, como testar com pytest e como montar um processamento seguro de relatórios.
Por que não criar um arquivo com nome fixo?
Um script pequeno pode parecer funcionar assim:
from pathlib import Path
caminho = Path("/tmp/relatorio.pdf")
caminho.write_bytes(b"conteudo")
O problema aparece quando duas execuções usam relatorio.pdf ao mesmo tempo. Uma pode sobrescrever a outra, ler conteúdo incompleto ou apagar um arquivo que ainda está em uso. Em aplicações web, um nome previsível também pode abrir espaço para ataques envolvendo links simbólicos ou troca do arquivo entre a verificação e a abertura.
Acrescentar a data ou um número aleatório manualmente não resolve bem:
# Evite criar sua própria estratégia de nome temporário
caminho = Path(f"/tmp/relatorio-{cliente_id}.pdf")
Além de expor identificadores, o nome ainda pode colidir e costuma deixar a responsabilidade de limpeza espalhada pelo código. tempfile centraliza esses detalhes, escolhe a pasta temporária adequada ao sistema e cria nomes exclusivos com permissões apropriadas.
Temporário não significa sem importância. Um arquivo pode conter dados pessoais, notas fiscais, contratos ou credenciais. Trate seu conteúdo com o mesmo cuidado aplicado ao arquivo definitivo.
TemporaryDirectory: a melhor opção para uma tarefa completa
TemporaryDirectory cria uma pasta exclusiva e devolve seu caminho. Ao sair do bloco with, o Python remove a pasta e seu conteúdo.
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as pasta:
temporaria = Path(pasta)
entrada = temporaria / "entrada.csv"
saida = temporaria / "relatorio.txt"
entrada.write_text("pedido,total\n101,199.90\n", encoding="utf-8")
saida.write_text("Relatório processado", encoding="utf-8")
print(saida.read_text(encoding="utf-8"))
# A pasta e os dois arquivos já foram removidos aqui
A limpeza ocorre mesmo quando uma exceção interrompe a operação, porque o context manager executa sua finalização. Isso torna a API especialmente útil para pipelines com várias etapas:
- baixar ou receber um documento;
- extrair arquivos;
- transformar o conteúdo;
- enviar apenas o resultado final;
- descartar os intermediários.
Você pode personalizar o prefixo para facilitar o diagnóstico sem tornar o nome previsível:
with TemporaryDirectory(prefix="nfe-processamento-") as pasta:
print(pasta)
O sistema ainda acrescenta uma parte aleatória. Não dependa do formato exato do nome, pois ele é detalhe de implementação.
Mantendo o resultado que realmente importa
Tudo dentro da pasta será apagado. Se um artefato precisa sobreviver, copie ou mova-o antes de sair do bloco:
import shutil
from pathlib import Path
from tempfile import TemporaryDirectory
DESTINO = Path("relatorios/relatorio-final.csv")
DESTINO.parent.mkdir(parents=True, exist_ok=True)
with TemporaryDirectory() as pasta:
temporario = Path(pasta) / "relatorio.csv"
temporario.write_text("id,status\n1,ok\n", encoding="utf-8")
shutil.copy2(temporario, DESTINO)
O artigo sobre zipfile e shutil explica quando usar copy, copy2, move e extração segura.
NamedTemporaryFile: quando você precisa de um caminho
Algumas bibliotecas aceitam um objeto de arquivo. Outras exigem um caminho no sistema, normalmente como str ou Path. NamedTemporaryFile atende ao segundo caso e expõe o nome no atributo .name.
from pathlib import Path
from tempfile import NamedTemporaryFile
with NamedTemporaryFile(
mode="w",
suffix=".json",
prefix="pedido-",
encoding="utf-8",
) as arquivo:
arquivo.write('{"pedido_id": 101, "status": "aprovado"}')
arquivo.flush()
caminho = Path(arquivo.name)
print(caminho.name)
print(caminho.read_text(encoding="utf-8"))
O flush() envia o conteúdo do buffer do Python ao sistema operacional antes que outro leitor abra o caminho. Isso não é a mesma coisa que garantir persistência física em disco, mas normalmente é o necessário para uma biblioteca ler os bytes recém-gravados.
Use suffix quando a ferramenta identifica o formato pela extensão, como .pdf, .png ou .csv. O sufixo ajuda na interoperabilidade; ele não valida o conteúdo.
A armadilha do Windows
Reabrir um NamedTemporaryFile enquanto ele continua aberto funciona de maneira diferente entre sistemas. No Windows, o compartilhamento e a exclusão de arquivos abertos seguem regras mais restritivas. Em versões modernas do Python, as opções delete e delete_on_close permitem controlar quando o nome será removido.
Uma estratégia portátil quando uma ferramenta externa precisa abrir o caminho é fechar o arquivo antes do uso e fazer a limpeza explicitamente:
from pathlib import Path
from tempfile import NamedTemporaryFile
caminho: Path | None = None
try:
with NamedTemporaryFile(
mode="wb",
suffix=".pdf",
delete=False,
) as arquivo:
arquivo.write(b"%PDF-conteudo-de-exemplo")
caminho = Path(arquivo.name)
# A biblioteca ou o subprocesso abre o caminho já fechado.
print(f"Processar: {caminho}")
finally:
if caminho is not None:
caminho.unlink(missing_ok=True)
Ao usar delete=False, a limpeza passa a ser sua responsabilidade. Mantenha-a em um finally, não apenas no caminho de sucesso. Para uma sequência com vários arquivos, TemporaryDirectory costuma ser mais simples e menos propenso a vazamentos.
TemporaryFile: um arquivo privado para a operação
TemporaryFile devolve um objeto de arquivo que é removido automaticamente ao fechar. Em alguns sistemas, o arquivo pode nem ter uma entrada de diretório visível. Use-o quando seu código só precisa ler e escrever pelo próprio objeto e ninguém exige um caminho.
from tempfile import TemporaryFile
with TemporaryFile(mode="w+b") as arquivo:
arquivo.write(b"linha 1\nlinha 2\n")
arquivo.seek(0)
conteudo = arquivo.read()
print(conteudo.decode("utf-8"))
Após escrever, seek(0) reposiciona o cursor no início. Sem isso, read() começa no fim e devolve bytes vazios.
Para texto, informe modo e encoding:
from tempfile import TemporaryFile
with TemporaryFile(mode="w+t", encoding="utf-8") as arquivo:
arquivo.write("São Paulo; aprovado\n")
arquivo.seek(0)
print(arquivo.read())
Prefira essa API a NamedTemporaryFile quando o nome não tem utilidade. Menos componentes conseguem localizar ou manipular o arquivo por caminho.
SpooledTemporaryFile: memória primeiro, disco depois
SpooledTemporaryFile mantém o conteúdo em memória até atingir max_size. Depois desse limite — ou se rollover() for chamado — ele migra para um arquivo em disco.
from tempfile import SpooledTemporaryFile
with SpooledTemporaryFile(max_size=1_000_000, mode="w+b") as arquivo:
arquivo.write(b"dados do relatorio")
arquivo.seek(0)
enviar_para_api(arquivo.read())
Essa opção é útil quando a maioria dos objetos é pequena, mas alguns podem crescer. Você evita acesso desnecessário ao disco nos casos comuns sem correr o risco de manter todo arquivo grande na RAM.
Um exemplo típico é preparar um CSV para upload:
import csv
import io
from tempfile import SpooledTemporaryFile
with SpooledTemporaryFile(max_size=2_000_000, mode="w+b") as binario:
texto = io.TextIOWrapper(binario, encoding="utf-8", newline="")
escritor = csv.DictWriter(texto, fieldnames=["pedido", "total"])
escritor.writeheader()
escritor.writerows(
[
{"pedido": 101, "total": "199,90"},
{"pedido": 102, "total": "49,00"},
]
)
texto.flush()
binario.seek(0)
payload = binario.read()
print(len(payload))
Não escolha um limite gigantesco sem medir. Em um servidor que processa muitos uploads simultâneos, alguns megabytes por requisição podem virar vários gigabytes de memória. Limites de concorrência e tamanho continuam necessários.
Como o Python escolhe a pasta temporária?
tempfile.gettempdir() mostra o diretório padrão selecionado para a execução:
import tempfile
print(tempfile.gettempdir())
O resultado varia conforme sistema operacional e variáveis de ambiente. Em Linux, é comum encontrar /tmp; no Windows, uma pasta dentro do perfil do usuário. Não grave lógica de negócio assumindo um caminho específico.
Se você precisa apenas descobrir a localização, gettempdir() é suficiente. Se precisa de um arquivo, não monte manualmente gettempdir() / nome: use as funções de criação do módulo.
mkstemp e mkdtemp são APIs de baixo nível
mkstemp() e mkdtemp() criam recursos com nomes seguros, mas não oferecem limpeza automática. mkstemp() ainda devolve um descritor de arquivo que precisa ser fechado.
import os
from pathlib import Path
from tempfile import mkstemp
fd, nome = mkstemp(suffix=".txt")
caminho = Path(nome)
try:
with os.fdopen(fd, mode="w", encoding="utf-8") as arquivo:
arquivo.write("resultado")
finally:
caminho.unlink(missing_ok=True)
Na maioria dos projetos, prefira as APIs de alto nível. Use mkstemp apenas quando você realmente precisa controlar descritor, abertura e ciclo de vida.
Exemplo prático: gerar e publicar um relatório sem deixar resíduos
Imagine um serviço que recebe registros, gera um PDF em uma pasta de trabalho, compacta os comprovantes e copia apenas o pacote final para uma área de saída. Em vez de misturar intermediários com arquivos publicados, cada execução ganha uma pasta temporária exclusiva.
import shutil
from pathlib import Path
from tempfile import TemporaryDirectory
def gerar_pdf(registros: list[dict], destino: Path) -> None:
# Substitua pela biblioteca de PDF usada no projeto.
linhas = ["RELATÓRIO DE PEDIDOS", ""]
linhas.extend(
f"Pedido {item['id']}: R$ {item['total']}"
for item in registros
)
destino.write_text("\n".join(linhas), encoding="utf-8")
def publicar_relatorio(registros: list[dict], saida: Path) -> Path:
saida.mkdir(parents=True, exist_ok=True)
pacote_final = saida / "relatorio-pedidos.zip"
with TemporaryDirectory(prefix="relatorio-pedidos-") as pasta:
trabalho = Path(pasta)
artefatos = trabalho / "artefatos"
artefatos.mkdir()
pdf = artefatos / "relatorio.pdf"
metadados = artefatos / "LEIA-ME.txt"
gerar_pdf(registros, pdf)
metadados.write_text(
"Arquivo gerado automaticamente. Valide antes de distribuir.",
encoding="utf-8",
)
base_zip = trabalho / "pacote"
zip_criado = Path(
shutil.make_archive(
str(base_zip),
format="zip",
root_dir=artefatos,
base_dir=".",
)
)
temporario_publicacao = saida / ".relatorio-pedidos.zip.tmp"
shutil.copy2(zip_criado, temporario_publicacao)
temporario_publicacao.replace(pacote_final)
return pacote_final
if __name__ == "__main__":
resultado = publicar_relatorio(
registros=[
{"id": 101, "total": "199,90"},
{"id": 102, "total": "49,00"},
],
saida=Path("saida"),
)
print(f"Publicado em: {resultado}")
O exemplo separa três ciclos de vida:
- os intermediários vivem apenas dentro de
TemporaryDirectory; - o
.tmpde publicação reduz o risco de expor um ZIP pela metade; - o arquivo final permanece em
saida/.
Em uma aplicação real, use uma biblioteca apropriada para o PDF, conforme o guia de relatórios PDF com Python. Se a geração depende de LibreOffice, FFmpeg ou outra ferramenta, chame-a com argumentos em lista e timeout, seguindo as práticas de subprocess.
Uploads: o que tempfile resolve e o que não resolve
Arquivos enviados por usuários são dados não confiáveis. tempfile ajuda a criar um destino sem colisões e sem usar diretamente o nome fornecido pelo navegador, mas não substitui uma política de upload.
Aplique pelo menos estes controles:
- limite de tamanho antes e durante a leitura;
- nome gerado pelo servidor, não pelo usuário;
- validação de conteúdo, não apenas da extensão ou
Content-Type; - armazenamento fora de diretórios executáveis ou públicos;
- timeout e limite de memória para conversões;
- verificação antimalware, quando o risco exigir;
- remoção garantida em sucesso e falha;
- logs sem conteúdo sensível.
Não extraia um ZIP recebido diretamente em uma pasta sem validar os caminhos internos. Entradas como ../../arquivo podem tentar escapar do diretório de destino. O guia de zipfile citado anteriormente mostra a proteção contra path traversal.
Para dados sensíveis, lembre que apagar um arquivo não garante sobrescrita física em SSDs, sistemas com snapshots ou armazenamento em nuvem. Se o requisito é forte, minimize a gravação, use criptografia e siga a política de retenção da organização.
Testando com pytest e tmp_path
Se sua função aceita um diretório como argumento, o fixture tmp_path do pytest costuma ser melhor do que chamar tempfile dentro do teste. Ele entrega um Path exclusivo e facilita inspeções.
Código da aplicação:
from pathlib import Path
def salvar_resumo(pasta: Path, texto: str) -> Path:
pasta.mkdir(parents=True, exist_ok=True)
destino = pasta / "resumo.txt"
destino.write_text(texto, encoding="utf-8")
return destino
Teste:
from pathlib import Path
def test_salvar_resumo(tmp_path: Path) -> None:
destino = salvar_resumo(tmp_path / "saida", "concluído")
assert destino.exists()
assert destino.read_text(encoding="utf-8") == "concluído"
assert destino.parent == tmp_path / "saida"
Evite afirmar que o caminho começa com /tmp ou contém um nome específico. Isso quebra no Windows, em containers configurados de outra forma e até entre versões do Python.
Para testar uma função que usa TemporaryDirectory internamente, verifique o resultado que sai da função e, se relevante, que os intermediários não permaneceram. O tutorial de testes unitários com pytest cobre fixtures, parametrização e simulação de falhas.
Boas práticas e erros comuns
Use este checklist em automações e backends:
- Prefira
with. A limpeza fica ligada ao escopo que usa o recurso. - Use
TemporaryDirectorypara múltiplos artefatos. É mais simples que gerenciar vários arquivos isolados. - Escolha
NamedTemporaryFileapenas quando o caminho for necessário. Caso contrário,TemporaryFilereduz exposição. - Chame
flush()antes de outro leitor abrir o arquivo ainda ativo. Ou feche-o primeiro para maior portabilidade. - Ao usar
delete=False, limpe emfinally. Uma exceção não pode abandonar o arquivo. - Não confie na extensão.
.pdfno nome não transforma bytes arbitrários em PDF válido. - Não use nomes enviados pelo usuário como caminhos. Preserve o nome original apenas como metadado sanitizado, se necessário.
- Defina limites de tamanho e tempo. Temporários também podem esgotar disco, memória ou inodes.
- Não registre conteúdo sensível. Prefira um identificador interno e métricas de tamanho/duração.
- Monitore a pasta temporária em workers de longa duração. Resíduos indicam encerramento abrupto,
delete=Falsesem limpeza ou processos externos travados. - Use
Pathpara manipular caminhos. Ele combina bem comtempfilee torna o código mais legível; veja o guia depathlib. - Trate erros específicos. Disco cheio, permissão e arquivo em uso devem aparecer em logs e alertas úteis, como explicado em tratamento de erros e logging.
Qual API escolher?
| Necessidade | API recomendada |
|---|---|
| Vários arquivos de uma única tarefa | TemporaryDirectory |
| Outra biblioteca precisa de um caminho | NamedTemporaryFile |
| Só seu código usa o objeto de arquivo | TemporaryFile |
| Conteúdo pequeno em memória, grande no disco | SpooledTemporaryFile |
| Teste que recebe uma pasta | pytest com tmp_path |
| Controle de baixo nível do descritor | mkstemp, com fechamento e limpeza manuais |
Se estiver em dúvida, comece com TemporaryDirectory. Uma pasta de trabalho exclusiva deixa claro o que é intermediário, funciona bem com ferramentas externas e simplifica a limpeza de fluxos complexos.
Perguntas frequentes
O módulo tempfile precisa ser instalado com pip?
Não. tempfile faz parte da biblioteca padrão do Python. Basta importar TemporaryDirectory, NamedTemporaryFile, TemporaryFile ou SpooledTemporaryFile. Bibliotecas externas só são necessárias para o restante do fluxo, como gerar PDF, processar imagens ou enviar arquivos a um serviço de nuvem.
Qual a diferença entre TemporaryFile e NamedTemporaryFile?
TemporaryFile cria um arquivo que pode não ter um caminho reutilizável por outro programa, dependendo da plataforma. NamedTemporaryFile garante um nome acessível pelo atributo .name, sendo a escolha comum quando uma biblioteca ou subprocesso exige um caminho. No Windows, reabrir um arquivo ainda aberto exige atenção às opções de exclusão.
Quando devo usar TemporaryDirectory?
Use TemporaryDirectory quando uma tarefa produz vários arquivos relacionados, como páginas extraídas de um PDF, XMLs descompactados, miniaturas ou artefatos de uma ferramenta externa. O context manager cria uma pasta exclusiva e remove a árvore ao sair do bloco, inclusive quando ocorre uma exceção.
tempfile torna qualquer upload seguro?
Não. O módulo reduz riscos de nomes previsíveis e colisões, mas o conteúdo recebido ainda precisa de limites de tamanho, validação de tipo e tratamento como dado não confiável. Não execute o arquivo, não use o nome enviado pelo usuário como comando e não confie apenas na extensão.
Como testar código que cria arquivos temporários?
Para funções que recebem um diretório, use o fixture tmp_path do pytest, pois ele fornece um pathlib.Path isolado por teste. Para testar o uso interno de tempfile, valide o resultado observável e a limpeza após o bloco. Não dependa do caminho exato da pasta temporária.
Conclusão
tempfile resolve uma parte invisível, mas importante, do software profissional: criar espaço de trabalho sem colisões e encerrá-lo sem deixar resíduos. A regra prática é combinar TemporaryDirectory ou um arquivo temporário com with, copiar apenas o resultado que deve sobreviver e manter validação, limites e tratamento de erros ao redor do fluxo.
A API correta depende de quem consumirá o temporário. Se uma ferramenta exige caminho, use NamedTemporaryFile ou uma pasta temporária; se apenas seu código precisa de leitura e escrita, TemporaryFile é suficiente; se o tamanho varia muito, considere SpooledTemporaryFile. Com pathlib, testes em tmp_path, logs úteis e limpeza em finally nos casos manuais, seus scripts ficam mais portáteis e confiáveis — exatamente o tipo de cuidado valorizado em automações, backends e vagas de Python.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português