Debugando no Django

Bem que você tentou, releu o código diversas vezes, escreveu um monte de prints, mas não conseguiu descobrir o que está causando aquele bug indesejado. É chegada a hora de debugar seu código, a seguir darei algumas dicas de como debugar com pdb seu projeto em Django[1].

Primeiramente, debugar um projeto em Python[2] e debugar um projeto em Django não tem muita diferença, a coisa toda é muito parecida, você só precisa do pontapé inicial para se virar dali pra frente.

Para aprender como utilizar o pdb[3] (vulgo Python Debugger) crie um novo projeto chamado djdebug, logo em seguida crie um arquivo vazio chamado views.py na própria raiz do projeto, ficando com a seguinte estrutura de arquivos:

djdebug
  - __init__.py
  - manage.py
  - settings.py
  - urls.py
  - views.py

No arquivo views.py cole o seguinte código:

from django.shortcuts import HttpResponse

def soma(a,b):
    arg1 = a
    arg2 = b
    soma = arg1 + arg2
    return soma

def hello(request):
    a = 2
    b = 3
    c = [1,2,3]
    c.pop()
    d = soma(3,4)
    response = HttpResponse("Hello World")
    return response

Como podemos ver o arquivo contém uma função soma, que não faz nada de muito complexo e uma função hello que retorna uma response com o conteúdo "Hello World".

No arquivo urls.py importe a view hello e adicione uma URL para podemos acessá-la:

from views import hello

urlpatterns = patterns('',
    ...
    (r'^hello', hello),
)

Execute o servidor de testes, navegue até http://localhost:8000/hello e verifique que está tudo funcionando:

./manage runserver

Suponha que há um bug em alguma parte da view hello e então adicione o código para possibilitar o debug:

def hello(request):
    a = 2
    import pdb
    pdb.set_trace()
    ...

Apenas para esclarecer, o import pdb importa o Python debugger, já o pdb.set_trace() serve pra colocar um breakpoint no código, há maneiras de se colocar um breakpoint no código sem alterá-lo através do próprio pdb[3].

Execute o servidor de testes mas desta vez adicione o parâmetro necessário para evitar recarregamento automático do código-fonte:

./manage runserver --noreload

Abra novamente a view hello no seu navegador, ela ficará parada e no terminal que você iniciou o servidor o pdb[3] estará parado esperando por comandos:

> /home/.../djdebug/views.py(13)hello()
-> b = 3
(Pdb)

Vamos começar, temos um comando interessante que é o l (de list, lista o código na região do debugger) que lista onde se encontra o debugger:

(Pdb) l
  8
  9  	def hello(request):
 10  	    a = 2
 11  	    import pdb
 12  	    pdb.set_trace()
 13      ->  b = 3
 14  	    c = [1,2,3]
 15  	    c.pop()
 16  	    d = soma(3,4)
 17  	    response = HttpResponse("Hello World")
 18  	    return response

É importante notar que o debugger se encontra no exato momento antes de executar a linha apontada pela seta, isto é, se executarmos p (de print, imprime a expressão passada como argumento) para inspecionarmos a variável b, ela não estará definida:

(Pdb) p a
2
(Pdb) p b
*** NameError: NameError("name 'b' is not defined",)

Vamos para próxima linha com n (de next, segue para a próxima linha):

(Pdb) n
> /home/.../djdebug/views.py(14)hello()
-> c = [1,2,3]
(Pdb) p b
3

Agora sim b já possui valor, vamos avançar até a chamada da função somar:

(Pdb) n
> /home/.../djdebug/views.py(15)hello()
-> c.pop()
(Pdb) <enter>
> /home/.../djdebug/views.py(16)hello()
-> d = soma(3,4)

A partir do primeiro n, enter executará n novamente. Como nós não sabemos se o problema da view hello é a função somar, vamos entrar na função somar com s (de step into, entra na função, ou seja, passo a dentro) e verificar se está tudo certo:

(Pdb) s
--Call--
> /home/.../djdebug/views.py(3)soma()
-> def soma(a,b):
(Pdb) l
  1  	  from django.shortcuts import HttpResponse
  2
  3     -> def soma(a,b):
  4  	      arg1 = a
  5  	      arg2 = b
  6  	      soma = arg1 + arg2
  7  	      return soma
  8
  9  	  def hello(request):
 10  	      a = 2
 11  	      import pdb

Como podemos ver o debugger se encontra na entrada da função soma, damos uma “olhada por cima” e saímos da função com up (de up, sobe uma chamada na pilha):

> /home/.../djdebug/views.py(4)soma()
-> arg1 = a
(Pdb) n
> /home/.../djdebug/views.py(5)soma()
-> arg2 = b
(Pdb) n
> /home/.../djdebug/views.py(6)soma()
-> soma = arg1 + arg2
(Pdb) u
> /home/.../djdebug/views.py(16)hello()
-> d = soma(3,4)

Como podemos ver, voltamos a view hello, vamos avançar até antes de retornar a resposta da requisição e atestar umas coisinhas:

(Pdb) n
> /home/.../djdebug/views.py(17)hello()
-> response = HttpResponse("Hello World")
(Pdb) n
> /home/.../djdebug/views.py(18)hello()
-> return response
(Pdb) dir(response)
['__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_charset', '_container', '_convert_to_ascii', '_get_content', '_headers', '_is_string', '_set_content', 'close', 'content', 'cookies', 'delete_cookie', 'flush', 'get', 'has_header', 'items', 'next', 'set_cookie', 'status_code', 'tell', 'write']
(Pdb) response.content
'Hello World'
(Pdb) response.content = 'Django debugado'
(Pdb) c

Como podemos verificar, o pdb[3] fornece um shell do Python[2] você pode executar dir, verificar valores de variáveis e até mudar conteúdo de variáveis (poder e perigo andando juntos), por fim executamos c (de continue, para continuar a execução), veja o resultado no seu navegador, a mensagem "Hello World" foi trocada por "Django debugado".

Recomendo a leitura do paper “Debugging in Python”[4] para mais alguns detalhes do pdb[3]. É muito interessante saber “brincar” com o shell do Python e do Django, com essas ferramentas fica mais fácil rastrear o chato do bug.

[1] - http://www.djangoproject.com/
[2] – http://www.python.org/
[3] – http://docs.python.org/library/pdb.html
[4] – http://www.ferg.org/papers/debugging_in_python.html

Esta entrada foi publicada em Sem categoria e marcada com a tag , , . Adicione o link permanenteaos seus favoritos.

2 respostas a Debugando no Django

  1. Opa ! Beleza..
    Obrigado pela dica ! Vai ser muito bem aproveitada…

  2. Igor Sobreira disse:

    Muito bom o post Danilo!

    Uma dica: ipdb
    http://pypi.python.org/pypi/ipdb/0.1dev-r1716

    pdb usando ipython :-)

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">