Gerenciadores de contexto delimitam aquisição e liberação de recursos. contextlib ajuda a escrever essa garantia com menos código, especialmente quando um try/finally seria correto, mas repetitivo.

Criar um contexto com generator

from contextlib import contextmanager
from pathlib import Path
from typing import Iterator, TextIO


@contextmanager
def abrir_relatorio(caminho: Path) -> Iterator[TextIO]:
    arquivo = caminho.open("w", encoding="utf-8")
    try:
        yield arquivo
    finally:
        arquivo.close()


with abrir_relatorio(Path("relatorio.txt")) as relatorio:
    relatorio.write("processamento concluido\n")

O valor entregue por yield aparece depois de as. Um generator decorado com @contextmanager deve executar exatamente um yield. Se a preparação falhar antes dele, o corpo nem começa. Se o corpo falhar, a exceção reaparece na expressão yield, permitindo registrar, converter ou propagar o erro.

@contextmanager
def transacao(conexao):
    cursor = conexao.cursor()
    try:
        yield cursor
    except Exception:
        conexao.rollback()
        raise
    else:
        conexao.commit()
    finally:
        cursor.close()

O else garante commit somente depois de um corpo bem-sucedido; uma falha causa rollback, e o cursor sempre fecha. Capturar sem relançar informa ao with que a exceção foi tratada. Isso pode ser intencional em um caso estreito e documentado, mas é um padrão perigoso.

Usar os adaptadores com precisão

closing(recurso) chama close() na saída. Use apenas para um objeto que precisa ser fechado e ainda não implementa o protocolo. Arquivos e muitos recursos da biblioteca padrão já funcionam diretamente com with.

nullcontext(valor) não faz limpeza e entrega o próprio valor. Ele ajuda quando o chamador pode fornecer um recurso opcional:

from contextlib import nullcontext


def ler_linhas(origem=None):
    contexto = (
        open("entrada.txt", encoding="utf-8")
        if origem is None
        else nullcontext(origem)
    )
    with contexto as arquivo:
        return arquivo.readlines()

suppress(FileNotFoundError) expressa que uma ausência específica é aceitável, como ao apagar um cache opcional. Mantenha o bloco protegido curto. Uma supressão ampla transforma defeitos não relacionados em falso sucesso.

redirect_stdout() e redirect_stderr() trocam temporariamente streams globais do processo. Podem capturar saída de código que não aceita um stream, sobretudo em testes, mas não combinam bem com concorrência ou bibliotecas. Quando você controla a função, prefira receber o destino ou usar logging estruturado.

Compor recursos dinâmicos com ExitStack

Para contextos fixos, uma única instrução with basta. ExitStack é útil quando a quantidade surge em runtime ou callbacks de limpeza precisam ser combinados.

from contextlib import ExitStack
from pathlib import Path


def concatenar(caminhos: list[Path], destino: Path) -> None:
    with ExitStack() as stack:
        entradas = [
            stack.enter_context(p.open(encoding="utf-8"))
            for p in caminhos
        ]
        saida = stack.enter_context(destino.open("w", encoding="utf-8"))
        for entrada in entradas:
            saida.write(entrada.read())

Se o terceiro arquivo falhar ao abrir, os dois primeiros já estão registrados e fecham automaticamente. A limpeza ocorre na ordem inversa, como em blocos with aninhados. stack.callback(funcao, *args) registra uma função comum; pop_all() transfere as limpezas para outra pilha quando a posse precisa mudar após uma validação.

Não use ExitStack para esconder dois recursos estáveis, pois um with direto é mais legível. A abstração se justifica quando a aquisição é condicional, iterativa ou mistura callbacks.

Contextos assíncronos e testes

Trate a limpeza como parte do contrato público. Documente o recurso adquirido, o valor entregue, as exceções convertidas e qualquer supressão intencional. Registre a limpeza imediatamente após cada aquisição, pois uma falha durante a preparação também precisa liberar o que já foi obtido. Se o próprio fechamento puder falhar, decida qual erro deverá permanecer visível.

Use nos testes um recurso falso que registre chamadas. Confira a ordem abrir, usar e fechar; lance uma exceção dentro do corpo e confirme o fechamento. Com ExitStack, falhe no meio da aquisição e verifique que todos os itens anteriores foram liberados em ordem inversa. Para transações, teste commit e rollback separadamente.

Não envolva cálculo comum em contexto apenas por simetria. O protocolo funciona melhor quando representa posse de recurso ou mudança temporária de estado com restauração clara. Faça também um teste com retorno antecipado, pois sair do corpo por return ainda deve executar a limpeza. Em código concorrente, evite utilitários que alteram estado global.

asynccontextmanager aplica a mesma estrutura de um yield a async with. AsyncExitStack combina gerenciadores assíncronos e callbacks que aguardam corrotinas. Escolha-os quando aquisição ou liberação realmente exigir await, não apenas porque a função chamadora é assíncrona.

Uma instância criada pelo generator é de uso único; a função decorada produz uma nova a cada chamada. Se o contexto precisar ser reentrante ou expuser uma API pública com muito estado, uma classe com __enter__ e __exit__ pode mostrar melhor as transições.

Teste o caminho normal, uma exceção dentro do corpo e aquisição parcial com pilhas. A limpeza deve preservar o erro original sempre que possível, pois uma nova exceção durante close() pode esconder a causa que iniciou o encerramento.

O código antes de yield adquire o recurso e o bloco finally garante a limpeza, inclusive quando o corpo do with lança uma exceção. Não esconda erros sem uma decisão explícita: registrar e relançar costuma ser mais seguro do que fingir sucesso.

closing() adapta objetos que oferecem close(), mas não implementam o protocolo de contexto. suppress() é adequado apenas para exceções esperadas e estreitas. Evite suppress(Exception), que pode mascarar falhas de programação.

Quando os recursos vêm de uma lista, ExitStack permite entrar em contextos durante um loop e desfazê-los na ordem inversa. Essa composição é mais segura do que manter uma lista manual de arquivos e tentar fechar cada um depois.

O guia de tratamento de exceções em Python complementa esse fluxo. A documentação oficial de contextlib, consultada em 22 de julho de 2026, descreve versões síncronas e assíncronas, ExitStack e utilitários de redirecionamento.