Introducción
En este artículo vamos a explicar cómo depurar un programa Python usando el módulo pdb de la biblioteca estándar. Si no sabes qué es exactamente depurar un programa o para qué te puede servir, sigue leyendo.
Depurar consiste en seguir el flujo de un programa a medida que se ejecuta, de forma que podemos monitorizar qué es lo que está sucediendo en cada momento. Es un método muy efectivo para encontrar fallos, porque:
- Permite detener momentáneamente la ejecución del programa usando puntos de ruptura (breakpoints).
- Permite examinar en cada momento las variables que se están utilizando (no necesitas llenar tu código de
print
). - Permite cambiar el valor de una variable mientras está detenida la ejecución.
Si es la primera vez que oyes hablar de esto, en seguida descubrirás el mundo de posibilidades que ofrece la depuración.
Puedes leer online la documentación del módulo pdb
.
En esta entrada se ha usado python 3.3.2.
Primeros pasos
Vamos a utilizar este programa extraído del libro «Dive into Python 3»:
SUFFIXES = { 1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] } def approximate_size(size, a_kilobyte_is_1024_bytes=True): '''Convert a file size to human-readable form. Keyword arguments: size -- file size in bytes a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 if False, use multiples of 1000 Returns: string ''' if size < 0: raise ValueError('number must be non-negative') multiple = 1024 if a_kilobyte_is_1024_bytes else 1000 for suffix in SUFFIXES[multiple]: size /= multiple if size < multiple: return '{0:.1f} {1}'.format(size, suffix) raise ValueError('number too large') if __name__ == '__main__': print(approximate_size(1000000000000, False)) print(approximate_size(1000000000000))
Que simplemente produce la siguiente salida:
$ python example.py 1.0 TB 931.3 GiB
La forma más directa de iniciar el depurador es con la línea:
$ python -m pdb example.py > /home/juanlu/Development/Python/test/pdb/example.py(1)() -> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], (Pdb)
De acuerdo, ¿qué acaba de suceder? Nos encontramos con un intérprete interactivo esperando órdenes, y la línea que empieza por ->
es la línea donde se encuentra detenida la ejecución ahora mismo.
Lo primero que observamos es que podemos ejecutar cualquier instrucción Python que queramos:
(Pdb) 2 * 8 16 (Pdb) "Hello".lower() 'hello' (Pdb) import numpy as np; np.sqrt(2) 1.4142135623730951
Para mostrar una lista de comandos disponibles, escribimos help
. También podemos mostrar la ayuda de cada comando individial:
(Pdb) help Documented commands (type help ): EOF cl disable interact next return u where a clear display j p retval unalias alias commands down jump pp run undisplay args condition enable l print rv unt b cont exit list q s until break continue h ll quit source up bt d help longlist r step w c debug ignore n restart tbreak whatis Miscellaneous help topics: exec pdb (Pdb) help list l(ist) [first [,last] | .] List source code for the current file. Without arguments, list 11 lines around the current line or continue the previous listing. With . as argument, list 11 lines around the current line. With one argument, list 11 lines starting at that line. With two arguments, list the given range; if the second argument is less than the first, it is a count. The current line in the current frame is indicated by "->". If an exception is being debugged, the line where the exception was originally raised or propagated is indicated by ">>", if it differs from the current line. (Pdb)
Como se puede leer, podemos usar el comando list
para examinar el código fuente del archivo que estamos ejecutando. La primera vez que lo ejecutemos sin argumentos mostrará las 11 primeras líneas y marcará con ->
la línea actual, y si seguimos ejecutándolo proseguirá avanzando. Veamos:
(Pdb) list 1 -> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 2 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 3 'YiB']} 4 5 def approximate_size(size, a_kilobyte_is_1024_bytes=True): 6 '''Convert a file size to human-readable form. 7 8 Keyword arguments: 9 size -- file size in bytes 10 a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 11 if False, use multiples of 1000 (Pdb) list 12 13 Returns: string 14 15 ''' 16 if size < 0: 17 raise ValueError('number must be non-negative') 18 19 multiple = 1024 if a_kilobyte_is_1024_bytes else 1000 20 for suffix in SUFFIXES[multiple]: 21 size /= multiple 22 if size < multiple:
Ahora, un par de trucos:
- ¿Has visto que, al mostrar la lista de comandos, había muchos con una sola letra? Lo que sucede es que son atajos: por ejemplo,
l
es un atajo paralist
, así que no tienes que escribir el comando entero. - Para repetir el último comando introducido, simplemente presiona Enter. Si quieres repetir el comando list tres veces, introduce
l
una vez y Enter otras dos. Más fácil imposible 🙂
Por ejemplo, para mostrar dónde estamos detenidos podemos usar el comando where
o simplemente w
:
(Pdb) w /usr/lib/python3.3/bdb.py(405)run() -> exec(cmd, globals, locals) (1)() > /home/juanlu/Development/Python/test/pdb/example.py(1)() -> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
Si queremos cerrar el depurador usaríamos quit
, y si queremos que el programa continue hasta el final, usaremos continue
o c
:
(Pdb) c 1.0 TB 931.3 GiB The program finished and will be restarted > /home/juanlu/Development/Python/test/pdb/example.py(1)() -> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], (Pdb)
El programa termina, se reinicia y estamos en el mismo punto que antes. De momento no hemos hecho nada demasiado interesante: comencemos 😉
Flujo e inspección de variables
Ahora, ¿cómo conseguimos ir avanzando en el flujo de ejecución? Para ello tenemos dos comandos: step
(s
) y next
(n
). Ambos ejecutan una línea del programa y avanzan a la siguiente, con la diferencia de que step
se introduce dentro de las funciones cuando se invoca alguna. Volviendo a nuestro ejemplo:
The program finished and will be restarted > /home/juanlu/Development/Python/test/pdb/example.py(1)() -> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], (Pdb) n > /home/juanlu/Development/Python/test/pdb/example.py(2)<module>() -> 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', (Pdb) # Presionamos Enter, lo mismo que ejecutar next otra vez > /home/juanlu/Development/Python/test/pdb/example.py(3)<module>() (Pdb) > /home/juanlu/Development/Python/test/pdb/example.py(5)<module>() -> def approximate_size(size, a_kilobyte_is_1024_bytes=True): (Pdb) > /home/juanlu/Development/Python/test/pdb/example.py(27)<module>() -> if name == 'main': (Pdb) > /home/juanlu/Development/Python/test/pdb/example.py(28)<module>() -> print(approximate_size(1000000000000, False)) (Pdb) # next ejecuta la línea pero no se introduce en approximate_size 1.0 TB > /home/juanlu/Development/Python/test/pdb/example.py(29)<module>() -> print(approximate_size(1000000000000)) (Pdb)
Hemos ido ejecutando las líneas una a una, y al llegar a la línea print
y usar next
se ha ejecutado también y ha saltado a la siguiente. Si para la siguiente línea print
usamos step
, nos introduciremos en el cuerpo de la función approximate_size
y seguiremos depurando desde ahí:
> /home/juanlu/Development/Python/test/pdb/example.py(29)<module>() -> print(approximate_size(1000000000000)) (Pdb) s --Call-- > /home/juanlu/Development/Python/test/pdb/example.py(5)approximate_size() -> def approximate_size(size, a_kilobyte_is_1024_bytes=True): (Pdb) > /home/juanlu/Development/Python/test/pdb/example.py(16)approximate_size() -> if size < 0: (Pdb) l . # Para ver dónde estamos 11 if False, use multiples of 1000 12 13 Returns: string 14 15 ''' 16 -> if size < 0: 17 raise ValueError('number must be non-negative') 18 19 multiple = 1024 if a_kilobyte_is_1024_bytes else 1000 20 for suffix in SUFFIXES[multiple]: 21 size /= multiple (Pdb)
Estamos ahora en esa línea y no sabemos lo que va a suceder. ¿Cómo averiguamos el valor de una variable? Usando el comando print
(p
):
(Pdb) p size 1000000000000 (Pdb) p a_kilobyte_is_1024_bytes True (Pdb)
Y si ahora nos apetece salir del cuerpo de la función y volver al programa principal, escribimos return
(r
):
(Pdb) r --Return-- > /home/juanlu/Development/Python/test/pdb/example.py(23)approximate_size()->'931.3 GiB' -> return '{0:.1f} {1}'.format(size, suffix) (Pdb) 931.3 GiB --Return-- > /home/juanlu/Development/Python/test/pdb/example.py(29)<module>()->None -> print(approximate_size(1000000000000)) (Pdb)
Puntos de ruptura
Imagina que tienes un programa muy largo y no quieres ir línea por línea desde el principio hasta el punto que te interesa. Para eso existen los puntos de ruptura: si estableces un punto de ruptura en una línea, el comando continue
ejecutará el programa sin depuración hasta que encuentre uno, y entonces se detendrá. Si vuelves a usar continue
el depurador se volverá a detener en el siguiente punto de ruptura, y así sucesivamente hasta que ya no queden y el programa finalice, como vimos al principio del artículo.
Para establecer un punto de ruptura se utiliza el comando break
(b
):
$ python -m pdb example.py > /home/juanlu/Development/Python/test/pdb/example.py(1)<module>() -> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], (Pdb) l 1 -> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 2 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 3 'YiB']} 4 5 def approximate_size(size, a_kilobyte_is_1024_bytes=True): 6 '''Convert a file size to human-readable form. 7 8 Keyword arguments: 9 size -- file size in bytes 10 a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 11 if False, use multiples of 1000 (Pdb) 12 13 Returns: string 14 15 ''' 16 if size < 0: 17 raise ValueError('number must be non-negative') 18 19 multiple = 1024 if a_kilobyte_is_1024_bytes else 1000 20 for suffix in SUFFIXES[multiple]: 21 size /= multiple 22 if size < multiple: (Pdb) b 21 Breakpoint 1 at /home/juanlu/Development/Python/test/pdb/example.py:21 (Pdb) c > /home/juanlu/Development/Python/test/pdb/example.py(21)approximate_size() -> size /= multiple (Pdb)
Hemos establecido un punto de ruptura en la línea 21 del programa, y a continuación el depurador lo ha ejecutado hasta llegar a dicha línea. Y a partir de ahí todo funciona igual que antes 🙂
Otra forma de establecer un punto de ruptura en tu programa es incluir la siguiente línea:
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000 for suffix in SUFFIXES[multiple]: import pdb; pdb.set_trace() size /= multiple if size < multiple:
De este modo, al ejecutarlo saltará el depurador directamente:
$ python example.py > /home/juanlu/Development/Python/test/pdb/example.py(22)approximate_size() -> size /= multiple (Pdb)
¿Te ha ayudado esto a encontrar ese error que se te resistía? Cuéntanos en los comentarios 🙂
¡Un saludo!
Muy buen articulo! muy completo .. muchos IDE traen un alias para cuando escribes “debug” te agregue la linea de pdb… en vim (mi editor) yo la bindee asi en el .vimrc:
au FileType python iab debug import pdb; pdb.set_trace()
¡Gracias angvp! Y gracias por el truco de vim, seguro que a algunos de nuestros lectores les viene bien 🙂 ¡Un saludo!
Reblogueó esto en El Blog de FoxCarlosy comentado:
Ya le echaba de menos a un depurador de codigo #python que por cuestiones de tiempo no habia tenido tiempom de buscar uno, pues aqui la gente de #pybonacci escribió este esxcelente post sobre #depuracion de codigo con PDB
¡Gracias por el reblogging! 🙂
Muchas gracias por el artículo, me sirve para empezar a usar herramientas mas poderosas que un montón de prints 🙂 se agradece el tiempo en escribir artículos tan útiles!
¡Gracias a ti por el comentario! Sin duda la depuración es una poderosa herramienta 🙂 ¡Si tienes dudas ya sabes dónde encontrarnos!
Excelente esto es lo que me faltaba =) gracias
Me alegro mucho 🙂 ¡Gracias a ti por leer!
Hermano buen aporte, siempre me había negado a usar el depurador de python por lo complejo que se veía y me conformaba con los print, pero se hizo muy tedioso poner y quitar print aqui y allá ademas que consume más tiempo, con esta intro que has dado, me doy cuenta que es muy fácil depurar con pdb, gracias, saludo desde Venezuela.
Muchas gracias por tu comentario Antony 🙂 Lo bueno de aprender a usar pdb es que los mismos conceptos te valen para depurar casi todos los lenguajes que existen. Por cierto, ¿sabes que está resurgiendo la comunidad Python en Venezuela? http://www.python2.org.ve/ ¡Saludos desde España!