💻 Exemplos: Capítulo 11: Tratamento Robusto de Exceções, Exceções de Domínio e ExceptionGroup
Especialização em Sistemas com Python 3.12+ • Capítulo 11
🎯 Demonstração Prática: Hierarquia de Exceções de Domínio, Encadeamento e ExceptionGroup
O exemplo abaixo demonstra o tratamento corporativo de erros segundo os padrões modernos de Python 3.12+:
- Construção de hierarquia semântica de exceções com status HTTP e metadados de auditoria.
- Encadeamento explícito de exceções via
raise ... frompreservando o histórico de causa original. - Utilização de
add_note()(PEP 678) para enriquecer o contexto de depuração em tempo de execução. - Agrupamento de erros múltiplos em lotes via
ExceptionGroup(PEP 654) e desempacotamento seletivo com a sintaxeexcept*.
"""
demonstracao_excecoes_e_exception_group.py
Exceções de Domínio Ricas, Encadeamento, add_note() e ExceptionGroup.
Compatível com Python 3.12+ (PEP 654 e PEP 678).
"""
from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal
import sys
from typing import Final
# ==============================================================================
# 1. HIERARQUIA CORPORATIVA DE EXCEÇÕES DE DOMÍNIO
# ==============================================================================
class DominioFinanceiroError(Exception):
"""Classe base para todas as exceções de regras de negócio financeiras."""
def __init__(self, mensagem: str, codigo_http: int = 400) -> None:
super().__init__(mensagem)
self.mensagem: str = mensagem
self.codigo_http: int = codigo_http
class SaldoInsuficienteError(DominioFinanceiroError):
def __init__(self, saldo_disponivel: Decimal, valor_solicitado: Decimal) -> None:
msg = f"Saldo disponível (R$ {saldo_disponivel:,.2f}) insuficiente para a operação de R$ {valor_solicitado:,.2f}."
super().__init__(msg, codigo_http=422) # 422 Unprocessable Entity
self.saldo_disponivel: Decimal = saldo_disponivel
self.valor_solicitado: Decimal = valor_solicitado
class ContaBloqueadaError(DominioFinanceiroError):
def __init__(self, motivo: str) -> None:
super().__init__(f"Conta corrente bloqueada por segurança: {motivo}", codigo_http=403)
class FalhaInfraestruturaError(DominioFinanceiroError):
def __init__(self, detalhe: str) -> None:
super().__init__(f"Instabilidade no serviço de persistência: {detalhe}", codigo_http=503)
# ==============================================================================
# 2. SERVIÇO COM TRY-EXCEPT-ELSE-FINALLY E ENCADEAMENTO
# ==============================================================================
def simular_debito_com_encadeamento(
saldo: Decimal,
valor_debito: Decimal,
conta_bloqueada: bool = False
) -> Decimal:
"""Executa débito e demonstra captura, encadeamento e adição de notas."""
recurso_aberto: bool = True
print(f"\n[SISTEMA] Alocando lock transacional na conta...")
try:
if conta_bloqueada:
raise ContaBloqueadaError("Detecção de acessos concorrentes em locais discrepantes.")
if valor_debito > saldo:
raise SaldoInsuficienteError(saldo_disponivel=saldo, valor_solicitado=valor_debito)
# Sucesso
saldo_restante = saldo - valor_debito
return saldo_restante
except SaldoInsuficienteError as err:
# Enriquecimento com PEP 678 add_note()
err.add_note(f"Auditoria Interna: Tentativa registrada com delta negativo de R$ {valor_debito - saldo:.2f}")
err.add_note("Ação Recomendada: Notificar cliente via Push Notification.")
raise
except Exception as err_inesperado:
# Encadeamento com raise ... from
novo_erro = FalhaInfraestruturaError("Não foi possível liquidar o débito na base relacional.")
raise novo_erro from err_inesperado
finally:
# Incondicional
recurso_aberto = False
print(f"[SISTEMA] Lock transacional liberado determinística e seguramente no finally.")
# ==============================================================================
# 3. VALIDAÇÃO EM LOTE COM EXCEPTIONGROUP (PEP 654)
# ==============================================================================
def validar_lote_transferencias(lote: list[dict[str, object]]) -> None:
"""Acumula todos os erros de validação e dispara um ExceptionGroup consolidado."""
erros_acumulados: list[Exception] = []
for idx, item in enumerate(lote, start=1):
if not str(item.get("conta", "")).strip():
erros_acumulados.append(ValueError(f"Linha {idx}: Número de conta ausente."))
valor = item.get("valor", Decimal("0.00"))
if not isinstance(valor, Decimal) or valor <= Decimal("0.00"):
erros_acumulados.append(TypeError(f"Linha {idx}: Valor inválido ou não-positivo ({valor})."))
if erros_acumulados:
raise ExceptionGroup("Falhas de Conformidade no Lote de Pagamentos", erros_acumulados)
# ==============================================================================
# 4. EXECUÇÃO DEMONSTRATIVA
# ==============================================================================
if __name__ == "__main__":
print("=" * 80)
print(" TRATAMENTO DE EXCEÇÕES CORPORATIVO, ENCADEAMENTO E NOTAS")
print("=" * 80)
# 1. Demonstração de Exceção de Domínio com add_note()
try:
simular_debito_com_encadeamento(saldo=Decimal("500.00"), valor_debito=Decimal("1200.00"))
except SaldoInsuficienteError as err:
print(f"\n>> EXCEÇÃO DE NEGÓCIO CAPTURADA:")
print(f" Mensagem : {err}")
print(f" Status : HTTP {err.codigo_http}")
if hasattr(err, "__notes__"):
print(" Notas de Depuração Anexadas:")
for nota in err.__notes__:
print(f" * {nota}")
# 2. Demonstração de ExceptionGroup e Desempacotamento com except*
print("\n" + "=" * 80)
print(" DEMONSTRAÇÃO DE EXCEPTIONGROUP E SINTAXE EXCEPT* (PEP 654)")
print("=" * 80)
lote_inconsistente = [
{"conta": "12345-6", "valor": Decimal("500.00")}, # Válido
{"conta": "", "valor": Decimal("300.00")}, # Erro ValueError
{"conta": "99988-1", "valor": Decimal("-50.00")}, # Erro TypeError
{"conta": " ", "valor": "invalido"}, # Dois erros na mesma linha
]
try:
validar_lote_transferencias(lote_inconsistente)
except* ValueError as eg_val:
print(f">> [except* ValueError] Tratando {len(eg_val.exceptions)} erros de formato cadastral:")
for sub_err in eg_val.exceptions:
print(f" - {sub_err}")
except* TypeError as eg_type:
print(f">> [except* TypeError] Tratando {len(eg_type.exceptions)} erros de tipagem monetária:")
for sub_err in eg_type.exceptions:
print(f" - {sub_err}")
print("\n" + "=" * 80)
print(">> Demonstração concluída com sucesso e tratamento 100% resiliente.")
print("=" * 80)