Debugging
Nesta aula, vamos explorar técnicas e ferramentas essenciais para depurar código Python, desde o uso do depurador interativo pdb até a função breakpoint() e boas práticas de logging. Você aprenderá a identificar e corrigir erros de forma eficiente, substituindo o uso indiscriminado de print por abordagens mais profissionais.
Depurar código é uma habilidade fundamental para qualquer programador. Nesta aula, vamos mergulhar no mundo do debugging em Python, explorando desde o depurador interativo pdb até a moderna função breakpoint(), além de discutir a diferença entre usar print para depuração e a prática recomendada de logging. Ao final, você terá um arsenal de ferramentas para encontrar e corrigir bugs com muito mais eficiência.
O debugging não é apenas sobre corrigir erros; é sobre entender o comportamento do seu programa. Muitas vezes, o problema não está onde pensamos, e ferramentas como pdb nos permitem inspecionar o estado do programa em tempo de execução. Vamos começar com o básico e evoluir para técnicas mais avançadas.
pdb
O pdb (Python Debugger) é um módulo da biblioteca padrão que fornece um depurador interativo para Python. Ele permite que você pause a execução do programa em pontos específicos, inspecione variáveis, execute código passo a passo e até mesmo modifique o estado do programa enquanto ele está em execução. Isso é extremamente útil para entender o fluxo do programa e identificar onde algo está dando errado.
Para usar o pdb, você pode inseri-lo no código com import pdb; pdb.set_trace() ou usar a linha de comando com python -m pdb script.py. No entanto, a forma mais simples e moderna é usar a função breakpoint(), que discutiremos em seguida. Quando o pdb é acionado, você entra em um prompt interativo com comandos como n (next), s (step), c (continue), p (print), l (list) e q (quit).
Vamos a um exemplo prático. Suponha que temos um programa que calcula a média de uma lista, mas queremos entender por que está retornando um valor incorreto:
import pdb
def calcular_media(numeros):
total = 0
for i in range(len(numeros)):
total += numeros[i]
pdb.set_trace() # Pausa a execução aqui
return total / len(numeros)
notas = [7, 8, 9]
media = calcular_media(notas)
print(f"A média é {media}")
Ao executar o código, o programa pausará na primeira iteração, e você pode inspecionar i, total e numeros. Use n para avançar para a próxima linha, p total para imprimir o valor de total, e c para continuar até o próximo breakpoint ou até o final. Isso permite que você veja exatamente como os valores mudam a cada iteração.
breakpoint()
A função breakpoint() foi introduzida no Python 3.7 como uma forma mais simples e flexível de acionar o depurador. Por padrão, ela chama pdb.set_trace(), mas você pode alterar o comportamento definindo a variável de ambiente PYTHONBREAKPOINT. Por exemplo, se você definir PYTHONBREAKPOINT=0, a função breakpoint() não faz nada, o que é útil para desativar todos os breakpoints em produção sem precisar remover as chamadas.
Outra vantagem é que você pode substituir o depurador padrão por outras ferramentas, como o pudb ou ipdb, definindo a variável de ambiente para o módulo desejado. Por exemplo, PYTHONBREAKPOINT=ipdb.set_trace fará com que breakpoint() use o IPython debugger.
Veja como usar breakpoint() no exemplo anterior:
def calcular_media(numeros):
total = 0
for i in range(len(numeros)):
total += numeros[i]
breakpoint() # Pausa a execução aqui
return total / len(numeros)
notas = [7, 8, 9]
media = calcular_media(notas)
print(f"A média é {media}")
O comportamento é idêntico ao pdb.set_trace(), mas com a vantagem de ser mais limpo e configurável. É recomendável usar breakpoint() em vez de pdb.set_trace() em código novo.
Logging vs print
Muitos programadores iniciantes usam print() para depurar, inserindo prints em vários pontos do código para ver o valor de variáveis. Embora isso funcione em programas pequenos, torna-se problemático em projetos maiores: você precisa remover os prints depois, eles poluem a saída padrão e não oferecem níveis de severidade ou formatação flexível.
A biblioteca padrão logging é a solução profissional. Ela permite que você registre mensagens com diferentes níveis de severidade (DEBUG, INFO, WARNING, ERROR, CRITICAL), direcione as mensagens para diferentes destinos (console, arquivo, etc.) e configure formatação. Além disso, você pode desativar facilmente as mensagens de debug em produção, sem precisar remover o código.
Exemplo básico de logging:
import logging
# Configura o logging para exibir mensagens a partir do nível DEBUG
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
def calcular_media(numeros):
total = 0
for i in range(len(numeros)):
total += numeros[i]
logging.debug(f"i={i}, total={total}")
media = total / len(numeros)
logging.info(f"Média calculada: {media}")
return media
notas = [7, 8, 9]
media = calcular_media(notas)
print(f"A média é {media}")
Ao executar, você verá as mensagens de debug e info no console, com timestamp e nível. Em produção, você pode definir o nível para WARNING ou ERROR, e as mensagens de debug não aparecerão. Isso é muito mais limpo e controlável do que usar print.
Comparando: print é útil para scripts rápidos, mas logging é a escolha certa para aplicações reais. Sempre que você sentir a necessidade de adicionar um print para depurar, considere usar logging.debug() em vez disso.
Ferramentas
Além do pdb e breakpoint(), existem outras ferramentas que podem ajudar no debugging de código Python. Aqui estão algumas delas:
- IPython / ipdb: O IPython oferece um shell interativo aprimorado, e
ipdbé o depurador integrado que fornece recursos como autocompletar, destaque de sintaxe e melhor visualização de objetos. Você pode instalá-lo compip install ipdbe usá-lo como depurador padrão definindo a variável de ambiente. - pudb: Um depurador visual em modo texto que oferece uma interface mais amigável, com painéis para código, variáveis e pilha de chamadas. Instale com
pip install pudbe usepudb.set_trace(). - IDE Debuggers: IDEs como PyCharm, VS Code e Eclipse+PyDev têm depuradores gráficos integrados que permitem definir breakpoints, inspecionar variáveis e executar passo a passo com uma interface visual. Esses são extremamente poderosos e muitas vezes preferidos por desenvolvedores profissionais.
- trace: O módulo
tracepermite rastrear a execução do programa, mostrando quais linhas são executadas. Pode ser útil para análise de cobertura de código. - cProfile: Para problemas de desempenho, o
cProfileé um profiler que mostra quanto tempo é gasto em cada função. Embora não seja exatamente um debugger, ajuda a identificar gargalos.
Escolher a ferramenta certa depende do contexto. Para debugging interativo rápido, pdb/breakpoint() são ótimos. Para debugging mais visual, um IDE é melhor. Para análise de desempenho, use um profiler.
Boas práticas e observações finais
Quando você encontrar um bug, tente reproduzi-lo de forma isolada, com um exemplo mínimo. Isso facilita a identificação da causa. Use breakpoints em pontos estratégicos, como antes de uma operação que você suspeita estar causando o problema. Não tenha medo de explorar o estado do programa: variáveis, pilha de chamadas, valores de argumentos.
Além disso, lembre-se de que o debugging não é apenas sobre corrigir o erro imediato, mas também sobre entender o sistema. Depois de corrigir, escreva um teste de regressão para garantir que o bug não volte. E, se possível, reflita sobre como o bug surgiu e se há uma maneira de preveni-lo no futuro.
Por fim, não use print como sua principal ferramenta de debugging. Invista tempo em aprender logging e as ferramentas de depuração da sua IDE. Isso pagará dividendos em produtividade e qualidade de código.
Referências
- Documentação oficial do pdb
- Documentação da função breakpoint()
- Guia de Logging em Python (HowTo)
- Documentação do módulo logging
- ipdb no PyPI
- pudb no PyPI
- Documentação do módulo trace
- Documentação do cProfile
Exercícios
- Escreva um programa que use
breakpoint()para inspecionar o valor de uma variável dentro de um loop. Execute e pratique os comandos do pdb (n, s, p, c, q).✓ Resposta: Um exemplo simples:
Ao executar, o programa pausa e você pode usar comandos comofor i in range(5): breakpoint() print(i)p ipara ver o valor dei,npara avançar, etc. - Converta um programa que usa
printpara depuração em um que useloggingcom nível DEBUG. Mostre como configurar o logging para exibir as mensagens no console.✓ Resposta: Antes:
Depois:def soma(a, b): print(f"a={a}, b={b}") return a + bimport logging logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s') def soma(a, b): logging.debug(f"a={a}, b={b}") return a + b - Explique a diferença entre
pdb.set_trace()ebreakpoint()e por que você deve preferirbreakpoint()em código novo.✓ Resposta:pdb.set_trace()é uma chamada explícita ao módulo pdb, enquantobreakpoint()é uma função integrada que por padrão chamapdb.set_trace(), mas pode ser configurada para usar outros depuradores ou ser desativada via variável de ambientePYTHONBREAKPOINT. Isso oferece mais flexibilidade e é mais limpo, por isso é recomendado para código novo. - Dado o código abaixo, use
breakpoint()para encontrar o erro (a variáveltotalnão está acumulando corretamente).def calcular_total(precos): total = 0 for preco in precos: total = preco # Erro: deveria ser total += preco return total✓ Resposta: Adicionebreakpoint()dentro do loop para inspecionartotaleprecoem cada iteração. Você verá quetotalé sobrescrito em vez de acumulado. A correção étotal += preco. - Pesquise sobre o depurador visual
pudbe escreva um pequeno tutorial de como instalá-lo e usá-lo em um programa simples.✓ Resposta: Instale compip install pudb. No código, importepudbe chamepudb.set_trace(). Ao executar, você verá uma interface interativa com navegação por teclado. Por exemplo:
Use as setas para navegar,import pudb pudb.set_trace() print("Olá")npara avançar,spara entrar em funções,cpara continuar, etc. Consulte a documentação oficial para mais detalhes.