• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

rolando2424

[Python] [Pyteam] Como usar o Pdb para fazer debugging a um código Python

4 mensagens neste tópico

PYTHON DEBUGGER!

Uma Breve Introdução

O que é isto de Debugger e o acto de fazer debugging, perguntam vocês.

O acto de Debugging é pura e simplesmente o acto de procurar e corrigir bugs num código (e existem sempre bugs, vocês podem é não ter reparado neles ;)).

Primeiro, quero notar que utilizar o debugger do Python não é talvez tão usual como o de, por exemplo, C, pois quando um programa em Python crasha de uma forma não muito bonita, normalmente deita sempre cá para fora as mensagens de erro.

Essas mensagens, por vezes permite-nos saber e corrigir os erros mais básicos.

Exemplo:

numero = 1
numero = numero + 10
print "O Numero e: " + numero

Isto deverá de dar um erro no print, pois a variável "numero" é um integer, e não se pode juntar uma string a um integer, sem antes o converter também para string.

E o Output, certo como um relógio:

Traceback (most recent call last):

File "exemplo1.py", line 5, in <module>

print "O Numero e: " + numero # Vai dar um erro porque "numero" nao e string

TypeError: cannot concatenate 'str' and 'int' objects

O Debugging pode ser feito de várias maneiras, como por exemplo, através de "print variavel".

Exemplo:

numero = 1
print ">> numero = %s" % numero # Neste momento "numero = 1"
numero = numero + 10
print ">> numero = %s" % numero # Neste momento "numero = 11"
print "O Numero final e: %s" % numero # E este e o vosso output final (aquilo que ira ficar no programa final)

O output deste código seria:

>> numero = 1

>> numero = 11

O Numero final e: 11

Neste caso, tomamos como valor a assinalar um print de debugging o símbolo ">>".

No final, quando já tivessem acabado de fazer o debugging, simplesmente teriam que comentar essas linhas, para as mensagens desaparecer. E ficaria assim:

numero = 1
#print ">> numero = %s" % numero
numero = numero + 10
#print ">> numero = %s" % numero
print "O Numero final e: %s" % numero

Sendo que o output passaria a ser simplesmente:

O Numero final e: 11

Até aqui tudo bem.

Mas quando se começa a ter programas cada vez mais complexos, começa a não dar muito jeito ter prints por tudo quando é lado, principalmente se a vossa aplicação for uma aplicação de terminal, em que fica tudo completamente misturado, o output de debugging e o output do programa em si.

Não seria bom se houvesse um programa que podesse facilitar esse trabalho? NÃO SERIA!?!?! (não têm escolha)

Eis que entra o tão famoso debugger.

Um debugger é nada mais, nada menos que um programa que facilita o acto de debugging (óbvio...).

Com um debugger pode-se, por exemplo, parar o programa a qualquer momento, ou alterar um valor de uma variável enquanto o programa está a correr.

O Python tem um debugger incorporado, chamado Pdb (Python DeBugger. obviamente), apesar de existirem outros (como o Pydb). No entanto, não irei falar nestes por este tutorial é apenas dedicado a uma introdução (em relação ao Pydb, têm lá um link para um video de demonstração de 13 minutos (acho eu), do Pydb a correr em Emacs, estando o vídeo alojado Showmedo).

Existem várias formas de invocar o pdb, sendo a mais simples o invocar numa consola, juntamente com o nome do ficheiro com o qual querem fazer o debugging:

rolando@main-computer:~/Programacao/Python/meus_scripts/tutorial_debugging$ pdb exemplo1.py

E se tudo correr bem, isto é o que deverão de ver.

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo1.py(3)<module>()

-> numero = 1

(Pdb)

E o cursor deverá de estar à frente do (Pdb) .

Outro método é invocar o pdb directamente através do código fonte do programa.

Acontece que o pdb contêm um módulo que pode ser usado como outro qualquer, utilizando o comando import pdb .

Esse módulo contêm alguns métodos, mas só vou focar no set_trace() e no run(), que são métodos que vos colocam na prompt do pdb (como está acima).

O run() tem a seguinte sintaxe:

pdb.run(expressão_a_correr [, variáveis_globais [, variáveis_locais]])

Como isto a ideia deste tutorial é para manter as coisas simples, vamos esquecer as partes das variáveis globais e locais.

O que o pdb.run faz é pura e simplesmente uma string como se fosse um comando de Python, dentro do pdb, para poderem analisar, por exemplo, uma função com calma.

Por exemplo, pdb.run(funcao_teste("string")), permitia correr a função dentro do pdb.

O outro método, o set_trace(), apenas vos colocar dentro do pdb, naquela linha onde está o pdb.set_trace().

Isso é bom, por exemplo, se quisererem só fazer debugging a uma parte qualquer do programa, e não querem ter que caminhar dentro do pdb até essa linha.

Assim, basta só um pdb.set_trace() antes do espaço que querem fazer o debugging, e o pdb abre automaticamente.

Vamos desviar-nos do assunto do set_trace(), e do run(), para se ver como é que utiliza o pdb, depois dele nos colocar na prompt.

Temos aqui este código, que soma todos os números de 0 até um valor que vocês derem (estilo 1+2+3+4+5+6+7+8+9+10=55):

#!/usr/bin/python

#import pdb

def soma_todos_numeros(numero):
#pdb.set_trace()
soma = 0

while numero > 0:
	soma = soma + numero
	numero = numero - 1

return soma

print "Bem-vindo ao programa que soma todos os numeros!"
print "Quer somar todos os numeros de 0 ate?"

n = raw_input("Numero: ")

total = soma_todos_numeros(int(n))
#pdb.run("total = soma_todos_numeros(int(n))")
print "A soma de todos e: %s" % total

Reparem que os comandos do pdb estão comentados (são para os exemplos mais à frente :))

Vamos correr o programa só para ver se está tudo bem.

rolando@main-computer:~/Programacao/Python/meus_scripts/tutorial_debugging$ python exemplo2.py

Bem-vindo ao programa que soma todos os numeros!

Quer somar todos os numeros de 0 ate?

Numero: 10

A soma de todos e: 55

Cá está! Podemos ver que o programa está a funcionar bem. Não seria então necessário fazer debugging a ele, mas vamos fazer na mesma, para ficarem a perceber como é que ele funciona (pensem numa secção de debugging como uma conversa num café, entre vocês, o programa, e o programador que escreveu o programa).

Vamos então executar o pdb na shell:

rolando@main-computer:~/Programacao/Python/meus_scripts/tutorial_debugging$ pdb exemplo2.py

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(5)<module>()

-> def soma_todos_numeros(numero):

(Pdb)

Reparem na linha que está a negrito. Essa linha diz-vos a próxima linha que o pdb vai executar.

A primeira coisa a fazer é saber em que zona do ficheiro estão, utilizando o comando list (ou simplesmente l), para verem em que linha estão, e o que têm à vossa volta. (Podem também ver que na linha por cima da que está a negrito tem lá um "(5)", significando que estão na quinta linha)

Escrevam list no prompt, e eis o que vos aparece:

(Pdb) list

1 #!/usr/bin/python

2

3 #import pdb

4

5 -> def soma_todos_numeros(numero):

6 #pdb.set_trace()

7 soma = 0

8

9 while numero > 0:

10 soma = soma + numero

11 numero = numero - 1

(Pdb)

Observamos que estámos de facto na linha 5 (o sinal -> indica obviamente a linha onde estão).

O comando list imprime no ecrã as 11 linhas que estão à volta da linha onde estão (por default, podem alterar isto se quiserem).

Este comando deverá de ser familiar àqueles que já têm experiência no Gdb (Um debugger de C). De facto, existem comandos que são diferentes aos do Gdb, mas nesta introdução, todos os comandos deverão de reagir de forma semelhante no Gdb (penso eu, não tenho muita experiência com ele).

Para executarem o comando que está na linha onde estão (neste caso, o def soma_todos_numeros(numero):) e passarem para o próximo comando, escrevam next (ou n), ou step (s).

Existe uma diferença entre o next e o step, mas já irei falar disso mais tarde.

(Pdb) next

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(15)<module>()

-> print "Bem-vindo ao programa que soma todos os numeros!"

(Pdb)

Tal como diz no prompt, estamos agora na linha 15 (podem também confirmar usando o list) do ficheiro, que contêm um comando print.

Reparem que o print ainda não foi executado, mas será o próximo comando a ser executado, quando usar o next, ou o step.

Existe um atalho para não terem que andar sempre a escrever o mesmo comando.

Se o prompt do pdb estiver vazio e carregarem no Enter, ele irá repetir o último comando realizado (neste caso, o next), por isso carregem na tecla Enter agora.

(Pdb)

Bem-vindo ao programa que soma todos os numeros!

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(16)<module>()

-> print "Quer somar todos os numeros de 0 ate?"

(Pdb)

Reparem que só depois de terem dito para o pdb avançar, é que o print foi executado.

Continuando...

(Pdb)

Quer somar todos os numeros de 0 ate?

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(18)<module>()

-> n = raw_input("Numero: ")

(Pdb)

Numero: 10

Temos na mesma que fazer todos os inputs para o programa.

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(20)<module>()

-> total = soma_todos_numeros(int(n))

(Pdb)

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(22)<module>()

-> print "A soma de todos e: %s" % total

(Pdb)

(Pequena nota: Reparem que ele não parou nos comments)

Prestem agora atenção para a diferença entre o next e o step.

Quando fizeram next na linha que usava a função soma_todos_numeros, o pdb não vos mostrou o que é que o Python andou a fazer lá para dentro dela.

Se quiserem ver o que se passa por dentro de funções (ou até mesmo de imports), têm que usar o step.

Vamos sair do pdb, utilizando o comando quit (ou q), e voltarmos à linha do total = soma_todos_numeros(int(n)), mas em vez de usar-mos o next, vamos usar o step e ver o que o Python anda para lá a fazer.

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(20)<module>()

-> total = soma_todos_numeros(int(n))

(Pdb) s

--Call--

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(5)soma_todos_numeros()

-> def soma_todos_numeros(numero):

(Pdb)

O pdb regressa novamente à linha 5 (usem o list para verificarem isso).

Agora podem fazer várias coisas.

Podem usar o next e o step da mesma maneira que o têm usado até aqui, ou se quiserem podem automaticamente saltar até à linha de return que vai ocurrer, usando o comando return (ou simplesmente r).

Vamos então usar o return, para ver como a função vai acabar.

(Pdb) return

--Return--

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(13)soma_todos_numeros()->55

-> return soma

(Pdb)

Cá está. Agora estamos na linha 13.

Vamos continuar com o next (também podem usar o step) para finalizar a função.

(Pdb) next

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(22)<module>()

-> print "A soma de todos e: %s" % total

(Pdb)

Que será o valor que a variável total tem?

Para saber isso usamos o comando print (ou só p).

A sintaxe é simples:

print nome_variavel

(Pdb) print total

55

Mas imaginemos que nós queremos enganar o programa, para ver como ele iria reagir, e queremos mudar o valor da variável total?

Para isso tem que se dar um comando directamente ao python, colocando um ! antes do comando. (Nota: Na verdade, qualquer comando não reconhecido pelo pdb é interpretando como sendo um comando de Python, como o comando print atrás. O ! serve só para terem a CERTEZA que o comando vai ser interpretado pelo python e não pelo pdb)

Depois de colocarem o !, é só escrevem o comando como se fosse um comando normal do python (que colocariam, por exemplo, no IDLE).

(Pdb) !total = 1

(Pdb) print total

1

(Pdb)

Podem ver que a variável total passou de 55 a 1.

Se agora executar-mos o print que está no ficheiro, ele vai dar um valor diferente do da primeira vez.

(Pdb) next

A soma de todos e: 1

--Return--

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(22)<module>()->None

-> print "A soma de todos e: %s" % total

(Pdb)

E chegaram ao fim do programa. Para voltarem ao início do programa, para continuarem a fazer o debug, voltem a usar o comando next ou step, e o pdb colocava-vos automaticamente no início.

(Pdb) next

--Return--

> <string>(1)<module>()->None

(Pdb) next

The program finished and will be restarted

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(5)<module>()

-> def soma_todos_numeros(numero):

(Pdb)

E cá está o início.

Agora só vou falar de mais dois comandos, o break (ou só b) e o continue (ou só c).

Vamos começar com o continue. Este comando faz com que o programa que está no pdb corra até ao fim, sem parar, EXCEPTO num breakpoint, que são colocado usando o comando break.

Isto serve para que não tenham de estar sempre a fazer next, até chegarem à parte que querem corrigir. Apenas coloquem um breakpoint, e fazam continue até lá chegar. E depois o programa pára e voltam ao controlo do pdb.

A sintaxe do break é:

break numero_da_linha_do_ficheiro

Podem usar mais algumas coisas como nomes de funções e de ficheiros, mas isso já está fora da parte da introdução.

Se escreverem só break, podem ver uma lista de todos os breakpoints que criaram na secção actual.

Voltando ao código inicial, digamos que queremos parar o programa duas vezes, na linha 7 (dentro da função soma_todos_numeros) e na linha 20.

(Pdb) break 7

Breakpoint 1 at /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py:7

(Pdb) break 20

Breakpoint 2 at /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py:20

(Pdb) break

Num Type Disp Enb Where

1 breakpoint keep yes at /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py:7

2 breakpoint keep yes at /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py:20

(Pdb)

Podem ver que temos agora 2 breakpoints colocados no ficheiro. Agora se usar-mos o continue, ele irá parar só nessas linhas.

(Pdb) continue

Bem-vindo ao programa que soma todos os numeros!

Quer somar todos os numeros de 0 ate?

Numero: 10

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(20)<module>()

-> total = soma_todos_numeros(int(n))

(Pdb)

Como podem ver, o pdb correu o programa como se ele tivesse a correr normalmente (não o parou a todas a linhas).

No entanto, quando chegou à linha 20, atingiu o primeiro breakpoint e parou a execução do programa (não parou na linha 7, porque esta está dentro de uma função, e esta ainda não foi executada).

Continuando:

(Pdb) continue

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(7)soma_todos_numeros()

-> soma = 0

(Pdb)

Podem ver que parou na linha 7, dentro da função (usem o list para confirmarem se quiserem).

Se voltarem a usar continue, ele vai correr o programa até ao fim vistos ele não encontrar mais nenhum breakpoint (se por acaso esta função fosse chamada mais à frente no programa, o pdb voltaria a parar na linha 7).

E isto acaba a introdução de como usar o pdb.

Agora é só ver como se pode chamar o pdb dentro de um programa de Python.

Nesta introdução só vou falar do método set_trace() e do run().

O set_trace(), faz com que o programa pare e entre no pdb sempre que chega a essa linha.

O run() avalia uma string como se fosse um comando de python, e corre-o no pdb.

Vamos descomentar a linha 6 do programa (a que diz pdb.set_trace()) e a linha 3 (import pdb).

rolando@main-computer:~/Programacao/Python/meus_scripts/tutorial_debugging$ python exemplo2.py

Bem-vindo ao programa que soma todos os numeros!

Quer somar todos os numeros de 0 ate?

Numero: 10

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(7)soma_todos_numeros()

-> soma = 0

(Pdb)

E estão de volta ao pdb, na linha a seguir à linha do set_trace().

Para verem o run(), voltem a comentar a linha 6, comentando também a linha 20, e descomentando a linha 21.

rolando@main-computer:~/Programacao/Python/meus_scripts/tutorial_debugging$ python exemplo2.py

Bem-vindo ao programa que soma todos os numeros!

Quer somar todos os numeros de 0 ate?

Numero: 10

> <string>(1)<module>()

(Pdb) step

--Call--

> /home/rolando/Programacao/Python/meus_scripts/tutorial_debugging/exemplo2.py(5)soma_todos_numeros()

-> def soma_todos_numeros(numero):

(Pdb)

Usem o step para entrarem na função.

(Lembrem-se, se quiserem continuar com a execução do programa, têm de usar o comando continue).

E é tudo por hoje. Sei que parece muito, mas com a prática vão ver que é fácil.

Ainda à algumas coisas que não falei, como temporary breakpoints (o continue só pára lá uma vez), mas já têm muito para trabalhar.

Só mais uma coisa.

Se acharem que não estão com paciência para aprender estes comandos todos, podem sempre tentar o modo Debug do IDLE (está lá no ménu Debug).

Se não quiserem usar o pdb, lembrem-se que existem outros (talvez melhores e com mais opções, e mais parecidos com o Gdb), como o Pydb, que pode ser usado num ambiente mais gráfico com o programa DDD (este tem também a vantagem de suportar outros debuggers como o Gdb).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Fixe, não conhecia o pdb :biggrin:

Pois, eu também só tive conhecimento dele à uns tempos (sabia só que o IDLE tinha um debugger, mas nunca dei muita atenção àquilo).

E acho que o descobri assim por acaso (já não me lembro como :P).

E como não tinha visto muita coisa sobre ele, fiz esta introdução (que por acaso ficou um pouco para o grande :))

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Bom trabalho pelo pouco que li! :P Já tinha ouvido falar nisto, mas nunca precisei nem/ou prestei atenção.

PyTeam's on Fire!

(Quando tiver tempo leio-o todo)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora