Raising & Chaining
Traduza falhas de baixo nível em erros de domínio sem perder contexto. Use raise ... from, raise sem argumentos e from None intencionalmente para que os tracebacks contem a história completa.
Receita
def load_user(user_id: str) -> dict:
try:
return fetch_row(user_id)
except KeyError as exc:
raise UserNotFoundError(user_id) from excQuando usar isso:
- Envolver exceções de terceiros nos limites da API
- Adicionar contexto de negócio preservando a causa raiz
- Suprimir tracebacks intermediários ruidosos (
from None) - Registrar e depois relançar com
raisesem argumentos - Encadear erros de validação em arquiteturas em camadas
Exemplo de Trabalho
class UserNotFoundError(LookupError):
def __init__(self, user_id: str) -> None:
self.user_id = user_id
super().__init__(f"usuário não encontrado: {user_id}")
_DB: dict[str, dict] = {"1": {"name": "Ada"}}
def fetch_row(user_id: str) -> dict:
return _DB[user_id]
def get_user(user_id: str) -> dict:
try:
return fetch_row(user_id)
except KeyError as exc:
raise UserNotFoundError(user_id) from exc
def parse_id(raw: str) -> str:
try:
value = int(raw)
except ValueError:
raise ValueError(f"id inválido: {raw!r}") from None
if value < 1:
raise ValueError("id deve ser positivo")
return str(value)
try:
get_user("999")
except UserNotFoundError as exc:
print(exc)
print(exc.__cause__)O que isso demonstra:
from excdefine__cause__para depuração de cadeiasfrom Noneoculta os detalhes internos do parser dos consumidores da API- Exceções de domínio carregam identificadores para manipuladores e logs
- Lançamentos de validação sem argumentos ainda produzem mensagens claras
Mergulho Profundo
Como Funciona
raise NewError()inicia uma nova exceção no ponto atual do tracebackraise NewError() from excvinculaexccomo__cause__raisesem argumentos dentro deexceptrelança a exceção capturada em trânsitoraise excde uma variável armazenada pode redefinir o contexto do traceback - prefiraraisesem argumentosException.__context__pode diferir de__cause__quando ocorre encadeamento implícito
Escolhas de Encadeamento
| Sintaxe | Efeito |
|---|---|
raise A from b | Causa explícita mostrada no traceback |
raise (sem argumentos) | A mesma exceção continua |
raise A from None | Suprime o contexto exibido |
Armadilhas
- Perder a causa com
raise New()semfrom- o contexto implícito ainda pode aparecer, mas é ambíguo. Correção: sempre usefrom excao envolver. - Engolir com
from Noneem todos os lugares - oculta as causas raiz em operações. Correção: reservefrom Nonepara limites de análise de entrada do usuário. raise excapós o término do blocoexcept- o traceback aponta para o local do relançamento. Correção:raisesem argumentos apenas dentro deexceptativo.- Loops de encadeamento - A de B de A em wrappers recursivos. Correção: lance o erro de domínio uma vez no limite.
- Registrar
exc_infoe depois lançar um tipo diferente semfrom- os logs e o traceback discordam. Correção: encadeie ou registre dentro do manipulador que relança sem argumentos.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Tipos Result/Either | Núcleo funcional, sem exceções | Limites de serviço Python que esperam lançamentos |
| Códigos de erro | Extensões C, protocolos | Camadas de aplicação Python |
| Agregar em ExceptionGroup | Falhas paralelas | Caminho de falha único |
FAQs
Quando devo usar from None?
Quando a exceção subjacente é um detalhe de implementação para os chamadores - tipicamente análise de entrada em bordas de API públicas.
O que é __context__ vs __cause__?
__cause__ é from explícito. __context__ é um link implícito ao lançar durante o tratamento de outra exceção.
Devo envolver todos os KeyError?
Apenas nos limites do módulo. O código interno pode deixar o KeyError propagar dentro de um pacote.
Como os loggers usam informações de exceção?
log.exception("msg") ou exc_info=True capturam o traceback da exceção ativa no momento do log.
Posso encadear exceções personalizadas?
Sim. Subclasse Exception, lance com from, e adicione campos na classe para tratamento estruturado.
O finally interage com o encadeamento?
finally é executado antes da propagação da exceção; o encadeamento definido em except é preservado.
Como os frameworks mapeiam exceções?
Manipuladores FastAPI/Flask mapeiam tipos de exceção para respostas HTTP - encadeie à causa raiz para logs 500.
Devo relançar após o esgotamento de tentativas?
Sim - raise last_exc from last_exc ou simplesmente lance a falha final com o contexto da contagem de tentativas.
E o assert?
assert se torna AssertionError - não para validação em tempo de execução em produção; use lançamentos explícitos.
Como testo cadeias de exceção?
pytest.raises e afirme que exc.value.__cause__ é o tipo esperado.
Relacionados
- Hierarquias de Exceção Personalizadas - tipos de domínio
- Noções Básicas de Exceções - estrutura try/except
- Retentativas e Backoff - lançar após retentativas
- Melhores Práticas de Erros e Logging - regras operacionais
Versões da Pilha: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.