|
| 1 | +title: Debugging em python (sem IDE) |
| 2 | +Slug: debugging-em-python-sem-ide |
| 3 | +Date: 2015-02-11 11:30 |
| 4 | +Tags: python,pdb,ipython,ipdb,debugging |
| 5 | +Author: Diego Garcia |
| 6 | + |
| 7 | +Github: drgarcia1986 |
| 8 | +Site: http://www.codeforcloud.info |
| 9 | +Twitter: drgarcia1986 |
| 10 | +Linkedin: drgarcia1986 |
| 11 | +Category: debugging |
| 12 | + |
| 13 | + |
| 14 | +<figure style="float:right;"> |
| 15 | +<img src="/images/drgarcia1986/debugging.png"> |
| 16 | +</figure> |
| 17 | +</br> |
| 18 | +Um dos principais motivos que ainda levam desenvolvedores Python a recorrerem a IDEs pesadas e que requerem instalação é o **debugging**. |
| 19 | +Devs que vieram de linguagens como _DotNet_, _Java_ e _Delphi_ por exemplo, estão acostumados a IDEs super pesadas e inchadas que no final das contas, além do debugging, só servem para drenar memória RAM. |
| 20 | +Brincadeiras a parte, não a motivos para você não dar uma chance ao **VIM** ou ao **Sublime**, pois para fazer debugging em scripts python, tudo que você precisa é o **PDB**. |
| 21 | + |
| 22 | +<!-- MORE --> |
| 23 | + |
| 24 | +# PDB |
| 25 | +O `pdb` é um módulo _buit-in_ que funciona como um console interativo, onde é posssível realizar debug de códigos python. |
| 26 | +Nele é possível fazer um _step-by-step_ do código, verificando o valor de variaveis, definindo breakpoints, manipulando valores, etc. |
| 27 | +É possível inclusive realizer _step-into_ em métodos. Ou seja, tudo que uma boa ferramenta de debug precisa ter. |
| 28 | + |
| 29 | +## Comandos |
| 30 | +Antes de partirmos para prática, é importante conhecer alguns comandos básicos para já começar o uso do pdb de forma efetiva. |
| 31 | + |
| 32 | +Durante o debugging, eventualmente seu script irá _estacionar_ em pontos de paradas, possívelmente definidos por você, neste momento, os comandos a seguir poderão ser utilizados. |
| 33 | + |
| 34 | +### q (quit) |
| 35 | +Sai da execução do script. |
| 36 | + |
| 37 | +### n (next) |
| 38 | +Avança para a próxima linha do script. |
| 39 | + |
| 40 | +### p (print) |
| 41 | +Executa o comando `print` do python, por exemplo: |
| 42 | +```python |
| 43 | +> /script.py(1)<module>() |
| 44 | +-> foo = "foo var" |
| 45 | +(Pdb) p foo |
| 46 | +'foo var' |
| 47 | +``` |
| 48 | +> Vale ressaltar que no exemplo acima, não é necessário utilizar o comando `p`, basta digitar o nome da variável e pressionar `enter`, o efeito seria o mesmo. |
| 49 | +### c (continue) |
| 50 | +Avança o debug até o próximo **breakpoint** ou até ocorrer uma **exception**. |
| 51 | + |
| 52 | +### l (list) |
| 53 | +Lista algumas linhas do código que estão em volta da linha atual. |
| 54 | +Por padrão serão apresentadas 11 linhas (5 a cima e 5 a baixo). |
| 55 | + |
| 56 | +### s (step into) |
| 57 | +Ao realizar a navegação através do comando `n` o debug **não** irá _entrar_ em métodos que possívelmente forem invocados. |
| 58 | +Para que o debug entre no método que está sendo invocado na linha corrente, basta trocar o comando `n`, pelo comando `s`. |
| 59 | +```python |
| 60 | +> /home/user/foo.py(20)<module>() |
| 61 | +-> foo.bar('barz') |
| 62 | +(Pdb) s |
| 63 | +--Call-- |
| 64 | +> /home/user/foo.py(3)bar() |
| 65 | +-> def bar(self, the_bar): |
| 66 | +(Pdb) |
| 67 | +``` |
| 68 | + |
| 69 | +### r (return) |
| 70 | +Já o comando `r` libera a execução do script até sair da função atual. |
| 71 | + |
| 72 | +### b (breakpoint) |
| 73 | +Cria um breakpoint em uma determinada linha ou método, por exemplo. |
| 74 | +```python |
| 75 | +> /script.py(1)<module>() |
| 76 | +(Pdb) b 21 |
| 77 | +Breakpoint 1 at /script.py:21 |
| 78 | +``` |
| 79 | +No comando a cima, setamos um breakpoint na linha 21 de nosso script. |
| 80 | +```python |
| 81 | +> /script.py(1)<module>() |
| 82 | +(Pdb) b foo |
| 83 | +Breakpoint 1 at /script.py:30 |
| 84 | +``` |
| 85 | +Já no exemplo acima, setamos o breakpoint para o método `foo`. |
| 86 | +O pdb informa qual linha ele setou o breakpoint, em nosso exemplo o método `foo` está na linha 30 do script. |
| 87 | + |
| 88 | +### a (arguments) |
| 89 | +O comando `a` mostra os argumentos que foram passados para a função atual. |
| 90 | + |
| 91 | +```python |
| 92 | +> /home/user/foo.py(20)<module>() |
| 93 | +-> foo.bar('barz') |
| 94 | +(Pdb) s |
| 95 | +--Call-- |
| 96 | +> /home/user/foo.py(3)bar() |
| 97 | +-> def bar(self, the_bar): |
| 98 | +(Pdb) a |
| 99 | +the_bar = "barz" |
| 100 | +``` |
| 101 | + |
| 102 | +### ENTER |
| 103 | +Se você precionar o `ENTER` sem nenhum comando no pdb, ele irá repetir o último comando executado. |
| 104 | + |
| 105 | +## Debug na prática |
| 106 | +Vamos utilizar um script python simples e didático como exemplo. |
| 107 | + |
| 108 | +```python |
| 109 | +class NumberList(object): |
| 110 | + def __init__(self): |
| 111 | + self.numbers = list() |
| 112 | + |
| 113 | + def add(self, number): |
| 114 | + if not isinstance(number, (int, float)): |
| 115 | + raise TypeError |
| 116 | + self.numbers.append(number) |
| 117 | + |
| 118 | + def sum(self): |
| 119 | + result = 0 |
| 120 | + for i in self.numbers: |
| 121 | + result += i |
| 122 | + return result |
| 123 | + |
| 124 | + |
| 125 | +if "__main__" == __name__: |
| 126 | + numbers = NumberList() |
| 127 | + |
| 128 | + numbers.add(5) |
| 129 | + assert numbers.sum() == 5 |
| 130 | + |
| 131 | + numbers.add(10) |
| 132 | + assert numbers.sum() == 15 |
| 133 | + |
| 134 | + print "The End" |
| 135 | +``` |
| 136 | +Esse script possui uma classe chamada `NumberList` que armazena uma lista de numeros e retorna a soma deles. |
| 137 | +Além destas classe, esse script também realiza algumas operações como instaciar essa classe e realizar alguns testes de asserção. |
| 138 | +Salve esse script em um arquivo chamado `numbers.py` para ser utilizado em nossos exemplos. |
| 139 | + |
| 140 | +## Modos de uso do pdb |
| 141 | + |
| 142 | +Na prática o pdb se assemelha bastante ao prompt interativo do python, com a diferença dos caracteres identificadores. |
| 143 | +Enquanto que no prompt interativo do python o identificador é o `>>>`, no pdb o identificador é `(Pdb)`. |
| 144 | +Existem algumas maneiras de usar o pdb, depende da forma como você pretende realizer o debbug. |
| 145 | + |
| 146 | +### pdb.py |
| 147 | +Uma delas é através da chamada do script `pdb.py` passando como paramêtro o script para ser feito do debug, por exemplo: |
| 148 | + |
| 149 | +```bash |
| 150 | +python -m pdb numbers.py |
| 151 | +``` |
| 152 | +Isso fará com o pdb seja iniciado na primeira linha do script `numbers.py`, no caso, a declaração da classe `NumberList()`. |
| 153 | +Caso você execute o comando `n`, a próxima linha será o `if "__main__" == __name__:` e assim por diante. |
| 154 | +Utilizando desta maneira, você pode verificar linha a linha do script ou _setar_ um breakpoint assim que entrar no debug, por exemplo, se você quer criar um breakpoint na execução do método `sum()` de uma instância da classe `NumberList()`, basta executar o comando `b numbers.sum`. |
| 155 | + |
| 156 | +```python |
| 157 | +(venv)user@machine:~/$ python -m pdb numbers.py |
| 158 | +> /home/user/numbers.py(4)<module>() |
| 159 | +-> class NumberList(object): |
| 160 | +(Pdb) n |
| 161 | +> /home/user/numbers.py(20)<module>() |
| 162 | +-> if __name__ == "__main__": |
| 163 | +(Pdb) n |
| 164 | +> /home/user/numbers.py(21)<module>() |
| 165 | +-> numbers = NumberList() |
| 166 | +(Pdb) n |
| 167 | +> /home/user/numbers.py(23)<module>() |
| 168 | +-> numbers.add(5) |
| 169 | +(Pdb) b numbers.sum |
| 170 | +Breakpoint 1 at /home/user/numbers.py:13 |
| 171 | +(Pdb) |
| 172 | +``` |
| 173 | + |
| 174 | +Ou para simplificar, também poderiamos setar o breakpoint pelo número da linha. |
| 175 | + |
| 176 | +```python |
| 177 | +(venv)user@machine:~/$ python -m pdb numbers.py |
| 178 | +> /home/user/numbers.py(4)<module>() |
| 179 | +-> class NumberList(object): |
| 180 | +(Pdb) b 13 |
| 181 | +Breakpoint 1 at /home/user/numbers.py:13 |
| 182 | +(Pdb) |
| 183 | +``` |
| 184 | +### pdb.set_trace() |
| 185 | +Outra forma é utilizando o método `set_trace()` do pacote `pdb`. |
| 186 | +Com o `pdb.set_trace()` você pode definir onde será o seu breakpoint via código, por exemplo, faremos uma alteração em nosso script para setar um breakpoint no método `NumberList().sum()`. |
| 187 | +```python |
| 188 | +import pdb |
| 189 | + |
| 190 | + |
| 191 | +class NumberList(object): |
| 192 | + def __init__(self): |
| 193 | + self.numbers = list() |
| 194 | + |
| 195 | + def add(self, number): |
| 196 | + if not isinstance(number, (int, float)): |
| 197 | + raise TypeError |
| 198 | + self.numbers.append(number) |
| 199 | + |
| 200 | + def sum(self): |
| 201 | + pdb.set_trace() |
| 202 | + |
| 203 | + result = 0 |
| 204 | + for i in self.numbers: |
| 205 | + result += i |
| 206 | + return result |
| 207 | + |
| 208 | +""" |
| 209 | +Resto do script omitido |
| 210 | +""" |
| 211 | +``` |
| 212 | +Dessa forma, ao executar o script (sem a necessidade de ser via pdb) e passar pelo método `pdb.set_trace()` será iniciado um prompt interativo do pdb. |
| 213 | + |
| 214 | +```python |
| 215 | +(venv)user@machine:~/$ python numbers.py |
| 216 | +> /home/user/numbers.py(16)sum() |
| 217 | +-> result = 0 |
| 218 | +(Pdb) |
| 219 | +``` |
| 220 | + |
| 221 | +## ipdb |
| 222 | +Uma das desvantagens do prompt interativo do python é a falta de _syntax highlighting_ e _code completion_, com o pdb não é diferente, porém, assim como podemos recorrer ao [ipython](http://ipython.org/) para isso, também podemos utilizar o [ipdb](https://github.com/gotcha/ipdb). |
| 223 | +O `ipdb` é uma espécie de wrapper para o pdb que faz uso das rotinas de debug do `IPython`. |
| 224 | +A maneira de uso se assemelha bastante ao pdb, bastando trocar o pacote `pdb` pelo pacote `ipdb`. |
| 225 | + |
| 226 | +```python |
| 227 | +import ipdb |
| 228 | + |
| 229 | +foo = "foo" |
| 230 | +ipdb.set_trace() |
| 231 | +bar = "bar" |
| 232 | +``` |
| 233 | + |
| 234 | +Para instalar o ipdb basta utilizar o `pip` |
| 235 | + |
| 236 | +``` |
| 237 | +pip install ipdb |
| 238 | +``` |
| 239 | +Com certeza recomendo o uso do `ipdb` principalmente por ser mais intuitivo. |
| 240 | + |
| 241 | +**Referências**<br \> |
| 242 | +[Documentação Oficial](https://docs.python.org/2/library/pdb.html)<br /> |
| 243 | +[ipdb](https://github.com/gotcha/ipdb) |
0 commit comments