Debugging com pdb e ferramentas modernas
1. Introdução ao pdb: o debugger embutido do Python
O pdb (Python Debugger) é a ferramenta de depuração padrão que acompanha toda instalação do Python. Desde a versão 3.7, o Python introduziu a função embutida breakpoint(), que simplifica significativamente a ativação do debugger.
Para ativar o pdb, existem duas abordagens principais:
# Abordagem 1: Executar o script diretamente com pdb
# python -m pdb meu_script.py
# Abordagem 2 (Python 3.7+): Inserir breakpoint() no código
def calcular_media(numeros):
total = sum(numeros)
breakpoint() # Pausa a execução aqui
return total / len(numeros)
resultado = calcular_media([10, 20, 30, 40])
Uma vez dentro do pdb, você precisa dominar alguns comandos essenciais:
# Exemplo prático de uso dos comandos
def processar_dados(lista):
resultado = []
for i, item in enumerate(lista):
resultado.append(item * 2)
return resultado
# Ao encontrar um breakpoint, use:
# (Pdb) n - Avança para a próxima linha
# (Pdb) s - Entra dentro de uma função
# (Pdb) c - Continua até o próximo breakpoint
# (Pdb) q - Sai do debugger
Para inspecionar variáveis, o pdb oferece comandos poderosos:
def calcular_desconto(preco, desconto):
valor_desconto = preco * (desconto / 100)
preco_final = preco - valor_desconto
breakpoint()
return preco_final
# Durante a depuração:
# (Pdb) p preco # Imprime o valor de preco
# (Pdb) pp locals() # Pretty print de todas as variáveis locais
# (Pdb) whatis desconto # Mostra o tipo da variável
# (Pdb) p preco > 100 # Avalia expressões condicionais
2. Configurando breakpoints e navegação no código
Breakpoints estáticos são fundamentais para controlar onde a execução deve parar:
def calcular_fatorial(n):
if n <= 1:
return 1
resultado = n * calcular_fatorial(n - 1)
return resultado
# Exemplo de breakpoints no pdb:
# (Pdb) b 5 # Breakpoint na linha 5
# (Pdb) b calcular_fatorial # Breakpoint na entrada da função
Breakpoints condicionais permitem pausar apenas quando uma condição específica é satisfeita:
def buscar_usuario(usuarios, id_alvo):
for i, usuario in enumerate(usuarios):
# No pdb: b 4, usuario['id'] == id_alvo
if usuario['id'] == id_alvo:
return usuario
return None
# (Pdb) b 4, usuario['id'] == 42 # Pausa apenas quando id == 42
Gerenciar múltiplos breakpoints é essencial em scripts complexos:
# Comandos de gerenciamento:
# (Pdb) b # Lista todos os breakpoints
# (Pdb) cl 1 # Limpa o breakpoint 1
# (Pdb) disable 2 # Desabilita temporariamente o breakpoint 2
# (Pdb) enable 2 # Reabilita o breakpoint 2
# (Pdb) ignore 1 5 # Ignora o breakpoint 1 nas próximas 5 execuções
3. Pós-mortem e debugging remoto com pdb
O debugging pós-mortem é uma técnica valiosa para analisar exceções após sua ocorrência:
# Execução com pós-mortem automático
# python -m pdb -c continue script_que_falha.py
import pdb
def dividir_numeros(a, b):
try:
return a / b
except ZeroDivisionError:
pdb.post_mortem() # Inicia pós-mortem interativo
# Agora podemos inspecionar o estado no momento da exceção
O comando pm é particularmente útil para análise pós-exceção:
def processar_arquivo(caminho):
with open(caminho, 'r') as arquivo:
dados = arquivo.read()
return eval(dados) # Isso pode lançar uma exceção
# Após uma exceção não tratada:
# >>> import pdb
# >>> pdb.pm() # Inicia debugging no frame da exceção
Para debugging remoto, podemos utilizar sockets:
import pdb
import socket
def iniciar_debug_remoto():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 4444))
sock.listen(1)
print("Aguardando conexão do debugger remoto...")
conn, addr = sock.accept()
# Redireciona stdin/stdout para o socket
import sys
sys.stdin = conn.makefile('r')
sys.stdout = conn.makefile('w')
pdb.set_trace() # Inicia sessão remota
4. Ferramentas modernas: ipdb e pudb
O ipdb oferece uma experiência superior ao pdb tradicional com integração IPython:
# Instalação: pip install ipdb
import ipdb
def analisar_dados(series):
ipdb.set_trace() # Interface com autocomplete e syntax highlighting
media = sum(series) / len(series)
variancia = sum((x - media) ** 2 for x in series) / len(series)
return media, variancia
# Recursos exclusivos do ipdb:
# - Tab completion para nomes de variáveis
# - Syntax highlighting colorido
# - Histórico de comandos persistente
# - Suporte a magias IPython como %timeit
O pudb oferece uma interface TUI (Text User Interface) completa:
# Instalação: pip install pudb
import pudb
def processar_transacoes(transacoes):
pudb.set_trace() # Abre interface TUI completa
saldo = 0
for transacao in transacoes:
saldo += transacao['valor']
if saldo < 0:
print(f"Alerta: saldo negativo: {saldo}")
return saldo
# Características do pudb:
# - Visualização dividida: código, variáveis, stack
# - Navegação por teclas de atalho
# - Exploração interativa de estruturas de dados
Comparação prática:
- Use pdb quando precisar de debugging básico sem dependências externas
- Use ipdb para desenvolvimento local com necessidade de autocomplete e análise rápida
- Use pudb para debugging visual em terminais, especialmente útil em servidores remotos
5. Debugging visual com IDEs e editores
VS Code
O VS Code oferece debugging visual integrado com suporte nativo a Python:
# Configuração de breakpoints via interface gráfica
def calcular_estatisticas(dados):
media = sum(dados) / len(dados)
# Clique na margem esquerda para adicionar breakpoint visual
variancia = sum((x - media) ** 2 for x in dados) / len(dados)
desvio = variancia ** 0.5
return {'media': media, 'desvio': desvio}
# Recursos do VS Code:
# - Watch expressions para monitorar variáveis
# - Call stack visual com navegação entre frames
# - Debug Console para expressões arbitrárias
# - Logpoints que registram mensagens sem pausar execução
PyCharm
O PyCharm oferece um debugger gráfico avançado:
# PyCharm suporta step filtering para ignorar código de bibliotecas
import pandas as pd
def analisar_dataframe(df):
# Breakpoint condicional: clique direito no breakpoint
resultado = df.groupby('categoria').agg({
'valor': ['sum', 'mean', 'count']
})
return resultado
# Diferenciais do PyCharm:
# - Evaluate expression com preview de tipos
# - Set Value para modificar variáveis durante debugging
# - Frame stepping automático
# - Breakpoints condicionais com expressões complexas
6. Debugging avançado com bibliotecas especializadas
O módulo faulthandler é essencial para capturar crashes em produção:
import faulthandler
import signal
# Ativa o faulthandler para capturar segfaults
faulthandler.enable()
faulthandler.register(signal.SIGUSR1) # Registra sinal personalizado
def operacao_perigosa():
import ctypes
# Simula um crash que seria capturado pelo faulthandler
ctypes.string_at(0) # Isso causaria um segfault
O módulo trace permite rastreamento linha a linha:
import trace
def rastrear_execucao():
tracer = trace.Trace(count=True, trace=True)
tracer.run('print("Olá mundo")')
# Gera relatório de cobertura
resultados = tracer.results()
resultados.write_results(show_missing=True, coverdir=".")
# Útil para:
# - Análise de cobertura de código
# - Rastreamento de fluxo de execução
# - Identificação de código morto
O icecream revoluciona o debugging com logs instantâneos:
# Instalação: pip install icecream
from icecream import ic
def calcular_comissao(vendas, taxa):
ic() # Loga localização e timestamp
total_vendas = sum(vendas)
ic(total_vendas) # Mostra: ic| total_vendas: 15000
comissao = total_vendas * taxa
ic(comissao) # ic| comissao: 750.0
# ic() retorna o valor, permitindo uso inline
resultado = ic(comissao * 0.8) # ic| comissao * 0.8: 600.0
return resultado
# Recursos do icecream:
# - Cores para melhor visualização
# - Contexto automático (arquivo, linha, função)
# - Prefixo personalizável
# - Fácil desativação global
7. Boas práticas e armadilhas comuns
Evite breakpoints em produção usando variáveis de ambiente:
import os
# Em desenvolvimento:
# PYTHONBREAKPOINT=pdb.set_trace
# Em produção:
os.environ['PYTHONBREAKPOINT'] = '0'
# Isso desativa todos os breakpoint() sem modificar o código
def funcao_critica():
breakpoint() # Será ignorado em produção
# ... lógica importante
Debugging de código multithread requer atenção especial:
import threading
import pdb
def worker_thread(id):
try:
# pdb não lida bem com múltiplas threads
# Cada thread pode pausar em breakpoints diferentes
resultado = processar_dados(id)
return resultado
except Exception as e:
# Melhor prática: logging em vez de pdb em threads
import logging
logging.error(f"Thread {id} falhou: {e}")
# Alternativa: usar logging com tracebacks completos
import traceback
def worker_seguro(id):
try:
return processar_dados(id)
except:
traceback.print_exc() # Imprime stack trace completo
Para debugging de bibliotecas C e pacotes externos:
# Estratégias para debugging de código nativo:
# 1. Use gdb para debugging de extensões C
# 2. Configure PYTHONFAULTHANDLER para capturar segfaults
# 3. Use ctypes.util.find_library() para localizar bibliotecas
import ctypes
import os
# Habilita faulthandler para capturar crashes em C
os.environ['PYTHONFAULTHANDLER'] = '1'
def chamar_biblioteca_externa():
try:
lib = ctypes.CDLL('./minha_biblioteca.so')
lib.funcao_perigosa()
except Exception as e:
print(f"Erro ao chamar biblioteca: {e}")
8. Conclusão: montando seu arsenal de debugging
Montar um arsenal eficiente de debugging significa escolher a ferramenta certa para cada cenário:
Fluxo recomendado:
1. Desenvolvimento local: ipdb ou IDE (VS Code/PyCharm) para conforto visual
2. Análise rápida: icecream com ic() para logs instantâneos
3. Produção: faulthandler + logging estruturado
4. Servidores remotos: pudb para debugging visual via terminal
5. CI/CD: pdb com scripts automatizados
Checklist de ferramentas por cenário:
| Cenário | Ferramenta Recomendada |
|---|---|
| Desenvolvimento | IDE + ipdb |
| Produção | faulthandler + logging |
| Análise pós-exceção | pdb.pm() |
| Código multithread | logging + traceback |
| Bibliotecas C | gdb + faulthandler |
| CI/CD | pdb scriptado |
Próximos passos: Combine debugging com profiling (cProfile) para identificar gargalos de performance, e logging estruturado (structlog) para monitoramento contínuo em produção. Essas técnicas complementares transformam debugging de uma atividade reativa em uma prática proativa de qualidade de código.
Referências
- Documentação oficial do pdb — Guia completo do debugger embutido do Python, incluindo todos os comandos e opções
- Real Python: Python Debugging With Pdb — Tutorial prático e abrangente sobre debugging com pdb, com exemplos do mundo real
- ipdb no PyPI — Página oficial do ipdb com instruções de instalação e integração com IPython
- pudb no GitHub — Repositório oficial do pudb com documentação da interface TUI e exemplos de uso
- Documentação do faulthandler — Guia oficial para captura de falhas e crashes em produção
- icecream no GitHub — Repositório oficial com exemplos de debugging instantâneo usando ic()
- VS Code Python Debugging — Documentação oficial da Microsoft sobre debugging Python no VS Code
- PyCharm Debugging Guide — Guia completo do debugger gráfico do PyCharm com técnicas avançadas