pdb — Depurador interactivo¶
Purpose: | Depurador interactivo de Python |
---|
pdb
implementa un entorno de depuración interactivo para los programas de
Python. Incluye funciones para pausar un programa, observar los valores de las
variables y observar la ejecución del programa paso a paso, para que puedas
comprender lo que hace el programa y encontrar errores en la lógica.
Iniciar del depurador¶
El primer paso para usar pdb
es hacer que el intérprete ingrese al
depurador en el momento correcto. Hay algunas formas diferentes de hacerlo,
dependiendo de las condiciones de inicio y de lo que se está depurando.
Desde la línea de comando¶
La forma más sencilla de usar el depurador es ejecutarlo desde la línea de comandos, dándole el programa como entrada para que sepa qué ejecutar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
class MyObj:
def __init__(self, num_loops):
self.count = num_loops
def go(self):
for i in range(self.count):
print(i)
return
if __name__ == '__main__':
MyObj(5).go()
|
La ejecución del depurador desde la línea de comandos hace que cargue el
archivo fuente y detenga la ejecución en la primera instrucción que encuentre.
En este caso, se detiene antes de evaluar la definición de la clase MyObj
en la línea 8.
$ python3 -m pdb pdb_script.py
> .../pdb_script.py(8)<module>()
-> class MyObj(object):
(Pdb)
Nota
Normalmente, pdb
incluye la ruta completa a cada módulo en la salida al
imprimir un nombre de archivo. Para mantener ejemplos claros, la ruta en la
salida de muestra en esta sección ha sido reemplazada por puntos suspensivos
(...
).
Dentro del intérprete¶
Muchos desarrolladores de Python trabajan con el intérprete interactivo
mientras desarrollan las primeras versiones de los módulos porque les permite
experimentar de forma más iterativa sin el ciclo de guardar/ejecutar/repetir
necesario al crear secuencias de comandos independientes. Para ejecutar el
depurador desde un intérprete interactivo, usa run()
o runeval()
.
$ python3
Python 3.5.1 (v3.5.1:37a07cee5969, Dec 5 2015, 21:12:44)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb_script
>>> import pdb
>>> pdb.run('pdb_script.MyObj(5).go()')
> <string>(1)<module>()
(Pdb)
El argumento de run()
es una expresión de cadena que puede ser evaluada por
el intérprete de Python. El depurador lo analizará, luego pausará la ejecución
justo antes de que se evalúe la primera expresión. Los comandos del depurador
descritos aquí se pueden usar para navegar y controlar la ejecución.
Desde dentro de un programa¶
Los dos ejemplos anteriores inician el depurador al comienzo de un programa.
Para un proceso de larga duración en el que el problema aparece mucho más tarde
en la ejecución del programa, será más conveniente iniciar el depurador desde
dentro del programa usando set_trace()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
import pdb
class MyObj:
def __init__(self, num_loops):
self.count = num_loops
def go(self):
for i in range(self.count):
pdb.set_trace()
print(i)
return
if __name__ == '__main__':
MyObj(5).go()
|
La línea 17 de la secuencia de comandos de muestra activa el depurador en ese punto de la ejecución, deteniéndolo en la línea 18.
$ python3 ./pdb_set_trace.py
> .../pdb_set_trace.py(18)go()
-> print(i)
(Pdb)
set_trace()
es solo una función de Python, por lo que se puede invocar en
cualquier punto de un programa. Esto permite ingresar al depurador en función
de las condiciones dentro del programa, incluso desde un controlador de
excepciones o mediante una rama específica de una declaración de control.
Después de una falla¶
La depuración de una falla después de que un programa termina se llama
depuración post-mortem. pdb
admite la depuración post-mortem a través de
las funciones pm()
y post_mortem()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
class MyObj:
def __init__(self, num_loops):
self.count = num_loops
def go(self):
for i in range(self.num_loops):
print(i)
return
|
Aquí el nombre de atributo incorrecto en la línea 14 desencadena una excepción
AttributeError
, lo que hace que la ejecución se detenga. pm()
busca el
rastreo activo e inicia el depurador en el punto de la pila de llamadas donde
ocurrió la excepción.
$ python3
Python 3.5.1 (v3.5.1:37a07cee5969, Dec 5 2015, 21:12:44)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from pdb_post_mortem import MyObj
>>> MyObj(5).go()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".../pdb_post_mortem.py", line 14, in go
for i in range(self.num_loops):
AttributeError: 'MyObj' object has no attribute 'num_loops'
>>> import pdb
>>> pdb.pm()
> .../pdb/pdb_post_mortem.py(14)go()
-> for i in range(self.num_loops):
(Pdb)
Controlar el depurador¶
La interfaz para el depurador es un pequeño lenguaje de comandos que le permite
moverse por la pila de llamadas, examinar y cambiar los valores de las
variables y controlar cómo el depurador ejecuta el programa. El depurador
interactivo utiliza readline
para aceptar comandos, y admite la
complementación de comandos, nombres de archivos y nombres de funciones por
tabulador. Al ingresar una línea en blanco, se vuelve a ejecutar el comando
anterior, a menos que se trate de una operación de list
.
Examinar variables en la pila¶
Cada cuadro en la pila mantiene un conjunto de variables, incluidos los valores
locales de la función que se ejecuta y la información de estado global. pdb
proporciona varias formas de examinar el contenido de esas variables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
import pdb
def recursive_function(n=5, output='to be printed'):
if n > 0:
recursive_function(n - 1)
else:
pdb.set_trace()
print(output)
return
if __name__ == '__main__':
recursive_function()
|
El comando args
(abreviado a
) imprime todos los argumentos en la
función activa en el marco actual. Este ejemplo también utiliza una función
recursiva para mostrar cómo se ve una pila más profunda cuando se imprime con
where
.
$ python3 pdb_function_arguments.py
> .../pdb_function_arguments.py(15)recursive_function()
-> print(output)
(Pdb) where
.../pdb_function_arguments.py(19)<module>()
-> recursive_function()
.../pdb_function_arguments.py(12)recursive_function()
-> recursive_function(n - 1)
.../pdb_function_arguments.py(12)recursive_function()
-> recursive_function(n - 1)
.../pdb_function_arguments.py(12)recursive_function()
-> recursive_function(n - 1)
.../pdb_function_arguments.py(12)recursive_function()
-> recursive_function(n - 1)
.../pdb_function_arguments.py(12)recursive_function()
-> recursive_function(n - 1)
> .../pdb_function_arguments.py(15)recursive_function()
-> print(output)
(Pdb) args
n = 0
output = to be printed
(Pdb) up
> .../pdb_function_arguments.py(12)recursive_function()
-> recursive_function(n - 1)
(Pdb) args
n = 1
output = to be printed
El comando p
evalúa una expresión dada como argumento e imprime el
resultado. La función print()
de Python también está disponible, pero se
pasa al intérprete para que se ejecute en lugar de ejecutarse como un comando
en el depurador.
(Pdb) p n
1
(Pdb) print(n)
1
Del mismo modo, anteponer una expresión con !
lo pasa al intérprete de
Python para su evaluación. Esta característica se puede utilizar para ejecutar
sentencias arbitrarias de Python, incluida la modificación de variables. Este
ejemplo cambia el valor de output
antes de permitir que el depurador
continúe ejecutando el programa. La siguiente declaración después de la
llamada a set_trace()
imprime el valor de output
, mostrando el valor
modificado.
$ python3 pdb_function_arguments.py
> .../pdb_function_arguments.py(14)recursive_function()
-> print(output)
(Pdb) !output
'to be printed'
(Pdb) !output='changed value'
(Pdb) continue
changed value
Para valores más complicados, como estructuras de datos anidados o grandes, usa
pp
para una «impresión bonita». Este programa lee varias líneas de texto
de un archivo.
1 2 3 4 5 6 7 8 9 10 11 12 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
import pdb
with open('lorem.txt', 'rt') as f:
lines = f.readlines()
pdb.set_trace()
|
Imprimir la variable lines
con p
da como resultado una salida que es
difícil de leer porque puede quedar incómoda. pp
usa pprint
para
formatear el valor para una impresión limpia.
$ python3 pdb_pp.py
> .../pdb_pp.py(12)<module>()->None
-> pdb.set_trace()
(Pdb) p lines
['Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
\n', 'Donec egestas, enim et consecte tuer ullamcorper, lect
us \n', 'ligula rutrum leo, a elementum el it tortor eu quam
.\n']
(Pdb) pp lines
['Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \n',
'Donec egestas, enim et consectetuer ullamcorper, lectus \n',
'ligula rutrum leo, a elementum elit tortor eu quam.\n']
(Pdb)
Para la exploración y experimentación interactivas, es posible pasar del depurador a una solicitud interactiva estándar de Python con los globales y locales del marco actual ya poblado.
$ python3 -m pdb pdb_interact.py
> .../pdb_interact.py(7)<module>()
-> import pdb
(Pdb) break 14
Breakpoint 1 at .../pdb_interact.py:14
(Pdb) continue
> .../pdb_interact.py(14)f()
-> print(l, m, n)
(Pdb) p l
['a', 'b']
(Pdb) p m
9
(Pdb) p n
5
(Pdb) interact
*interactive*
>>> l
['a', 'b']
>>> m
9
>>> n
5
Los objetos mutables como las listas se pueden cambiar desde el intérprete interactivo. Los objetos inmutables no pueden, y los nombres no pueden ser devueltos a nuevos valores.
>>> l.append('c')
>>> m += 7
>>> n = 3
>>> l
['a', 'b', 'c']
>>> m
16
>>> n
3
Usa la secuencia de fin de archivo Ctrl-D para salir de la solicitud
interactiva y regresar al depurador. En este ejemplo, la lista l
ha
cambiado pero los valores de m
y n
no lo han hecho.
>>> ^D
(Pdb) p l
['a', 'b', 'c']
(Pdb) p m
9
(Pdb) p n
5
(Pdb)
Atravesar un programa¶
Además de navegar hacia arriba y hacia abajo en la pila de llamadas cuando el programa está en pausa, también es posible avanzar por la ejecución del programa más allá del punto donde ingresa al depurador.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
import pdb
def f(n):
for i in range(n):
j = i * n
print(i, j)
return
if __name__ == '__main__':
pdb.set_trace()
f(5)
|
Usa el step
(abreviado s
) para ejecutar la línea actual y luego
detente en el siguiente punto de ejecución, ya sea la primera instrucción
dentro de una función que se llama o la siguiente línea de la función actual.
$ python3 pdb_step.py
> .../pdb_step.py(18)<module>()
-> f(5)
El intérprete hace una pausa después de la llamada a set_trace()
y le da el
control al depurador. El primer step
hace que la ejecución ingrese
f()
.
(Pdb) step
--Call--
> .../pdb_step.py(10)f()
-> def f(n):
Un step
más mueve la ejecución a la primera línea de f()
e inicia el
ciclo.
(Pdb) step
> .../pdb_step.py(11)f()
-> for i in range(n):
Al retroceder nuevamente se mueve a la primera línea dentro del bucle donde se
define j
.
(Pdb) step
> .../pdb_step.py(12)f()
-> j = i * n
(Pdb) p i
0
El valor de i
es 0
, por lo que después de un paso más, el valor de
j
también debería ser 0
.
(Pdb) step
> .../pdb_step.py(13)f()
-> print(i, j)
(Pdb) p j
0
(Pdb)
Pasar una línea a la vez de esta manera puede volverse tedioso si hay mucho código que cubrir antes del punto donde ocurre el error, o si se llama repetidamente a la misma función.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
import pdb
def calc(i, n):
j = i * n
return j
def f(n):
for i in range(n):
j = calc(i, n)
print(i, j)
return
if __name__ == '__main__':
pdb.set_trace()
f(5)
|
En este ejemplo, no hay nada malo con calc()
, por lo que recorrerlo cada
vez que se llama en el bucle en f()
oscurece la salida útil al mostrar
todas las líneas de calc()
a medida que se ejecutan.
$ python3 pdb_next.py
> .../pdb_next.py(23)<module>()
-> f(5)
(Pdb) step
--Call--
> .../pdb_next.py(15)f()
-> def f(n):
(Pdb) step
> .../pdb_next.py(16)f()
-> for i in range(n):
(Pdb) step
> .../pdb_next.py(17)f()
-> j = calc(i, n)
(Pdb) step
--Call--
> .../pdb_next.py(10)calc()
-> def calc(i, n):
(Pdb) step
> .../pdb_next.py(11)calc()
-> j = i * n
(Pdb) step
> .../pdb_next.py(12)calc()
-> return j
(Pdb) step
--Return--
> .../pdb_next.py(12)calc()->0
-> return j
(Pdb) step
> .../pdb_next.py(18)f()
-> print(i, j)
(Pdb) step
0 0
> .../pdb_next.py(16)f()
-> for i in range(n):
(Pdb)
El comando next
(abreviado n
) es como step
, pero no ingresa a
funciones llamadas desde la instrucción que se está ejecutando. En efecto,
recorre todo el camino a través de la llamada de función a la siguiente
instrucción en la función actual en una sola operación.
> .../pdb_next.py(16)f()
-> for i in range(n):
(Pdb) step
> .../pdb_next.py(17)f()
-> j = calc(i, n)
(Pdb) next
> .../pdb_next.py(18)f()
-> print(i, j)
(Pdb)
El comando until
es como next
, excepto que continúa explícitamente
hasta que la ejecución alcanza una línea en la misma función con un número de
línea más alto que el valor actual. Eso significa, por ejemplo, que until
se puede usar para pasar el final de un ciclo.
$ python3 pdb_next.py
> .../pdb_next.py(23)<module>()
-> f(5)
(Pdb) step
--Call--
> .../pdb_next.py(15)f()
-> def f(n):
(Pdb) step
> .../pdb_next.py(16)f()
-> for i in range(n):
(Pdb) step
> .../pdb_next.py(17)f()
-> j = calc(i, n)
(Pdb) next
> .../pdb_next.py(18)f()
-> print(i, j)
(Pdb) until
0 0
1 5
2 10
3 15
4 20
> .../pdb_next.py(19)f()
-> return
(Pdb)
Antes de ejecutar el comando until
, la línea actual era 18, la última línea
del bucle. Después de ejecutar until
, la ejecución estaba en la línea 19 y
el bucle se había agotado.
Para permitir que la ejecución se ejecute hasta una línea específica, pasa el
número de línea al comando until
. A diferencia de cuando se establece un
punto de interrupción, el número de línea pasado a until
debe ser mayor que
el número de línea actual, por lo que es más útil para navegar dentro de una
función para saltar bloques largos.
$ python3 pdb_next.py
> .../pdb_next.py(23)<module>()
-> f(5)
(Pdb) list
18 print(i, j)
19 return
20
21 if __name__ == '__main__':
22 pdb.set_trace()
23 -> f(5)
[EOF]
(Pdb) until 18
*** "until" line number is smaller than current line number
(Pdb) step
--Call--
> .../pdb_next.py(15)f()
-> def f(n):
(Pdb) step
> .../pdb_next.py(16)f()
-> for i in range(n):
(Pdb) list
11 j = i * n
12 return j
13
14
15 def f(n):
16 -> for i in range(n):
17 j = calc(i, n)
18 print(i, j)
19 return
20
21 if __name__ == '__main__':
(Pdb) until 19
0 0
1 5
2 10
3 15
4 20
> .../pdb_next.py(19)f()
-> return
(Pdb)
El comando return
es otro atajo para omitir partes de una función.
Continúa ejecutándose hasta que la función está a punto de ejecutar una
declaración de return
, y luego se detiene, proporcionando tiempo para mirar
el valor de retorno antes de que la función regrese.
$ python3 pdb_next.py
> .../pdb_next.py(23)<module>()
-> f(5)
(Pdb) step
--Call--
> .../pdb_next.py(15)f()
-> def f(n):
(Pdb) step
> .../pdb_next.py(16)f()
-> for i in range(n):
(Pdb) return
0 0
1 5
2 10
3 15
4 20
--Return--
> .../pdb_next.py(19)f()->None
-> return
(Pdb)
Puntos de interrupción¶
A medida que los programas crecen, incluso el uso de next
y until
se
volverá lento y engorroso. En lugar de recorrer el programa a mano, una mejor
solución es dejar que se ejecute normalmente hasta que llegue al punto en que
el depurador deba interrumpirlo. set_trace()
puede iniciar el depurador,
pero eso solo funciona si hay un solo punto en el programa donde debería
detenerse. Es más conveniente ejecutar el programa a través del depurador,
pero dile dónde debe detenerse con anticipación usando puntos de
interrupción. El depurador supervisa el programa y, cuando alcanza la
ubicación descrita por un punto de interrupción, el programa se detiene antes
de ejecutar la línea.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
def calc(i, n):
j = i * n
print('j =', j)
if j > 0:
print('Positive!')
return j
def f(n):
for i in range(n):
print('i =', i)
j = calc(i, n) # noqa
return
if __name__ == '__main__':
f(5)
|
Hay varias opciones para el comando break
(abreviado b
) que se utiliza
para establecer puntos de interrupción, incluido el número de línea, el archivo
y la función donde el procesamiento debe detenerse. Para establecer un punto
de interrupción en una línea específica del archivo actual, usa break
lineno
.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 12
Breakpoint 1 at .../pdb_break.py:12
(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb_break.py(12)calc()
-> print('Positive!')
(Pdb)
El comando continue
(abreviado c
) le dice al depurador que siga
ejecutando el programa hasta el próximo punto de interrupción. En este caso,
se recorre la primera iteración del bucle for
en f()
y se detiene
dentro de calc()
durante la segunda iteración.
Los puntos de interrupción también se pueden establecer en la primera línea de
una función especificando el nombre de la función en lugar de un número de
línea. Este ejemplo muestra lo que sucede si se agrega un punto de
interrupción para la función calc()
.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:8
(Pdb) continue
i = 0
> .../pdb_break.py(9)calc()
-> j = i * n
(Pdb) where
.../pdb_break.py(23)<module>()
-> f(5)
.../pdb_break.py(19)f()
-> j = calc(i, n)
> .../pdb_break.py(9)calc()
-> j = i * n
(Pdb)
Para especificar un punto de interrupción en otro archivo, prefija el argumento de línea o función con un nombre de archivo.
1 2 3 4 5 6 | #!/usr/bin/env python3
# encoding: utf-8
from pdb_break import f
f(5)
|
Aquí se establece un punto de interrupción para la línea 12 de pdb_break.py
después de iniciar el programa principal pdb_break_remote.py
.
$ python3 -m pdb pdb_break_remote.py
> .../pdb_break_remote.py(4)<module>()
-> from pdb_break import f
(Pdb) break pdb_break.py:12
Breakpoint 1 at .../pdb_break.py:12
(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb_break.py(12)calc()
-> print('Positive!')
(Pdb)
El nombre de archivo puede ser una ruta completa al archivo de origen, o una
ruta relativa a un archivo disponible en sys.path
.
Para enumerar los puntos de interrupción establecidos actualmente, usa
break
sin ningún argumento. El resultado incluye el archivo y el número de
línea de cada punto de interrupción, así como información sobre cuántas veces
se ha encontrado.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 12
Breakpoint 1 at .../pdb_break.py:12
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:12
(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb/pdb_break.py(12)calc()
-> print('Positive!')
(Pdb) continue
Positive!
i = 2
j = 10
> .../pdb_break.py(12)calc()
-> print('Positive!')
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:12
breakpoint already hit 2 times
(Pdb)
Administrar puntos de interrupción¶
A medida que se agrega cada nuevo punto de interrupción, se le asigna un
identificador numérico. Estos números de identificación se utilizan para
habilitar, deshabilitar y eliminar los puntos de interrupción de forma
interactiva. Apagar un punto de interrupción con disable
le dice al
depurador que no se detenga cuando se alcanza esa línea. El punto de ruptura
se recuerda, pero se ignora.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:8
(Pdb) break 12
Breakpoint 2 at .../pdb_break.py:12
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:8
2 breakpoint keep yes at .../pdb_break.py:12
(Pdb) disable 1
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep no at .../pdb_break.py:8
2 breakpoint keep yes at .../pdb_break.py:12
(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> .../pdb_break.py(12)calc()
-> print('Positive!')
(Pdb)
La siguiente sesión de depuración establece dos puntos de interrupción en el
programa, luego deshabilita uno. El programa se ejecuta hasta que se encuentra
el punto de interrupción restante, y luego el otro punto de interrupción se
vuelve a habilitar con enable
antes de que continúe la ejecución.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:8
(Pdb) break 18
Breakpoint 2 at .../pdb_break.py:18
(Pdb) disable 1
(Pdb) continue
> .../pdb_break.py(18)f()
-> print('i =', i)
(Pdb) list
13 return j
14
15
16 def f(n):
17 for i in range(n):
18 B-> print('i =', i)
19 j = calc(i, n)
20 return
21
22 if __name__ == '__main__':
23 f(5)
(Pdb) continue
i = 0
j = 0
> .../pdb_break.py(18)f()
-> print('i =', i)
(Pdb) list
13 return j
14
15
16 def f(n):
17 for i in range(n):
18 B-> print('i =', i)
19 j = calc(i, n)
20 return
21
22 if __name__ == '__main__':
23 f(5)
(Pdb) p i
1
(Pdb) enable 1
Enabled breakpoint 1 at .../pdb_break.py:8
(Pdb) continue
i = 1
> .../pdb_break.py(9)calc()
-> j = i * n
(Pdb) list
4 # Copyright (c) 2010 Doug Hellmann. All rights reserved.
5 #
6
7
8 B def calc(i, n):
9 -> j = i * n
10 print('j =', j)
11 if j > 0:
12 print('Positive!')
13 return j
14
(Pdb)
Las líneas con el prefijo B
en la salida de list
muestran dónde se
establecen los puntos de interrupción en el programa (líneas 8 y 18).
Usa clear
para eliminar un punto de interrupción por completo.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at .../pdb_break.py:8
(Pdb) break 12
Breakpoint 2 at .../pdb_break.py:12
(Pdb) break 18
Breakpoint 3 at .../pdb_break.py:18
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:8
2 breakpoint keep yes at .../pdb_break.py:12
3 breakpoint keep yes at .../pdb_break.py:18
(Pdb) clear 2
Deleted breakpoint 2
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:8
3 breakpoint keep yes at .../pdb_break.py:18
(Pdb)
Los otros puntos de interrupción conservan sus identificadores originales y no se vuelven a numerar.
Puntos de interrupción temporales¶
Un punto de interrupción temporal se borra automáticamente la primera vez que la ejecución del programa lo alcanza. El uso de un punto de interrupción temporal hace que sea fácil alcanzar rápidamente un punto particular en el flujo del programa, al igual que con un punto de interrupción regular, pero como se borra de inmediato, no interfiere con el progreso posterior si esa parte del programa se ejecuta repetidamente.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) tbreak 12
Breakpoint 1 at .../pdb_break.py:12
(Pdb) continue
i = 0
j = 0
i = 1
j = 5
Deleted breakpoint 1 at .../pdb_break.py:12
> .../pdb_break.py(12)calc()
-> print('Positive!')
(Pdb) break
(Pdb) continue
Positive!
i = 2
j = 10
Positive!
i = 3
j = 15
Positive!
i = 4
j = 20
Positive!
The program finished and will be restarted
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb)
Una vez que el programa llega a la línea 12 por primera vez, se elimina el punto de interrupción y la ejecución no se detiene nuevamente hasta que finaliza el programa.
Puntos de interrupción condicionales¶
Las reglas se pueden aplicar a los puntos de interrupción para que la ejecución
solo se detenga cuando se cumplan las condiciones. El uso de puntos de
interrupción condicionales brinda un control más preciso sobre cómo el
depurador detiene el programa que habilitar y deshabilitar los puntos de
interrupción manualmente. Los puntos de interrupción condicionales se pueden
establecer de dos maneras. La primera es especificar la condición cuando el
punto de interrupción se establece usando break
.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 10, j>0
Breakpoint 1 at .../pdb_break.py:10
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:10
stop only if j>0
(Pdb) continue
i = 0
j = 0
i = 1
> .../pdb_break.py(10)calc()
-> print('j =', j)
(Pdb)
El argumento de la condición debe ser una expresión que use valores visibles en el marco de la pila donde se define el punto de interrupción. Si la expresión se evalúa como verdadera, la ejecución se detiene en el punto de interrupción.
Una condición también se puede aplicar a un punto de interrupción existente
utilizando el comando condition
. Los argumentos son la identificación del
punto de interrupción y la expresión.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 10
Breakpoint 1 at .../pdb_break.py:10
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:10
(Pdb) condition 1 j>0
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:10
stop only if j>0
(Pdb)
Ignorar los puntos de interrupción¶
Los programas que repiten o utilizan una gran cantidad de llamadas recursivas a
la misma función a menudo son más fáciles de depurar saltando adelante en la
ejecución, en lugar de observar cada llamada o punto de interrupción. El
comando ignore
le dice al depurador que pase un punto de interrupción
sin detenerse. Cada vez que el procesamiento encuentra el punto de
interrupción, disminuye el contador de ignorar. Cuando el contador es cero, el
punto de interrupción se reactiva.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 19
Breakpoint 1 at .../pdb_break.py:19
(Pdb) continue
i = 0
> .../pdb_break.py(19)f()
-> j = calc(i, n)
(Pdb) next
j = 0
> .../pdb_break.py(17)f()
-> for i in range(n):
(Pdb) ignore 1 2
Will ignore next 2 crossings of breakpoint 1.
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:19
ignore next 2 hits
breakpoint already hit 1 time
(Pdb) continue
i = 1
j = 5
Positive!
i = 2
j = 10
Positive!
i = 3
> .../pdb_break.py(19)f()
-> j = calc(i, n)
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:19
breakpoint already hit 4 times
Restablecer explícitamente el recuento de ignorados a cero vuelve a habilitar el punto de interrupción inmediatamente.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 19
Breakpoint 1 at .../pdb_break.py:19
(Pdb) ignore 1 2
Will ignore next 2 crossings of breakpoint 1.
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:19
ignore next 2 hits
(Pdb) ignore 1 0
Will stop next time breakpoint 1 is reached.
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_break.py:19
Activar acciones en un punto de interrupción¶
Además del modo puramente interactivo, pdb
admite secuencias básicas de
comandos. Usando commands
, se puede ejecutar una serie de comandos de
intérprete, incluyendo las declaraciones de Python, cuando se encuentra un
punto de interrupción específico. Después de ejecutar commands
con el
número de punto de interrupción como argumento, el indicador del depurador
cambia a (com)
. Ingresa los comandos uno por vez y termine la lista con
end
para guardar la secuencias de comandos y regresar al indicador
principal del depurador.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 10
Breakpoint 1 at .../pdb_break.py:10
(Pdb) commands 1
(com) print('debug i =', i)
(com) print('debug j =', j)
(com) print('debug n =', n)
(com) end
(Pdb) continue
i = 0
debug i = 0
debug j = 0
debug n = 5
> .../pdb_break.py(10)calc()
-> print('j =', j)
(Pdb) continue
j = 0
i = 1
debug i = 1
debug j = 5
debug n = 5
> .../pdb_break.py(10)calc()
-> print 'j =', j
(Pdb)
Esta característica es especialmente útil para depurar código que utiliza muchas estructuras de datos o variables, ya que se puede hacer que el depurador imprima todos los valores automáticamente, en lugar de hacerlo manualmente cada vez que se encuentra el punto de interrupción.
Vigilando cambio de datos¶
También es posible observar cómo cambian los valores durante el curso de la
ejecución del programa sin necesidad de escribir comandos explícitos de print
utilizando el comando display
.
$ python3 -m pdb pdb_break.py
> .../pdb_break.py(8)<module>()
-> def calc(i, n):
(Pdb) break 18
Breakpoint 1 at .../pdb_break.py:18
(Pdb) continue
> .../pdb_break.py(18)f()
-> print('i =', i)
(Pdb) display j
display j: ** raised NameError: name 'j' is not defined **
(Pdb) next
i = 0
> .../pdb_break.py(19)f()
-> j = calc(i, n) # noqa
(Pdb) next
j = 0
> .../pdb_break.py(17)f()
-> for i in range(n):
display j: 0 [old: ** raised NameError: name 'j' is not defined **]
(Pdb)
Cada vez que la ejecución se detiene en el marco, se evalúa la expresión y, si
cambia, el resultado se imprime junto con el valor anterior. El comando
display
sin argumento imprime una lista de las pantallas activas para el
cuadro actual.
(Pdb) display
Currently displaying:
j: 0
(Pdb) up
> .../pdb_break.py(23)<module>()
-> f(5)
(Pdb) display
Currently displaying:
(Pdb)
Elimina una expresión de visualización con undisplay
.
(Pdb) display
Currently displaying:
j: 0
(Pdb) undisplay j
(Pdb) display
Currently displaying:
(Pdb)
Cambio de flujo de ejecución¶
El comando jump
altera el flujo del programa en tiempo de ejecución, sin
modificar el código. Puede saltar hacia adelante para evitar ejecutar algún
código, o hacia atrás para volver a ejecutarlo. Este programa de muestra
genera una lista de números.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
def f(n):
result = []
j = 0
for i in range(n):
j = i * n + j
j += n
result.append(j)
return result
if __name__ == '__main__':
print(f(5))
|
Cuando se ejecuta sin interferencia, la salida es una secuencia de números
crecientes divisibles por 5
.
$ python3 pdb_jump.py
[5, 15, 30, 50, 75]
Saltar hacia adelante¶
Saltar hacia adelante mueve el punto de ejecución más allá de la ubicación
actual sin evaluar ninguna de las declaraciones intermedias. Al saltarse la
línea 13 en el ejemplo, el valor de j
no se incrementa y todos los valores
posteriores que dependen de él son un poco más pequeños.
$ python3 -m pdb pdb_jump.py
> .../pdb_jump.py(8)<module>()
-> def f(n):
(Pdb) break 13
Breakpoint 1 at .../pdb_jump.py:13
(Pdb) continue
> .../pdb_jump.py(13)f()
-> j += n
(Pdb) p j
0
(Pdb) step
> .../pdb_jump.py(14)f()
-> result.append(j)
(Pdb) p j
5
(Pdb) continue
> .../pdb_jump.py(13)f()
-> j += n
(Pdb) jump 14
> .../pdb_jump.py(14)f()
-> result.append(j)
(Pdb) p j
10
(Pdb) disable 1
(Pdb) continue
[5, 10, 25, 45, 70]
The program finished and will be restarted
> .../pdb_jump.py(8)<module>()
-> def f(n):
(Pdb)
Saltar hacia atrás¶
Los saltos también pueden mover la ejecución del programa a una declaración que
ya se ha ejecutado, para ejecutarla nuevamente. Aquí, el valor de j
se
incrementa un tiempo adicional, por lo que los números en la secuencia de
resultados son todos más grandes de lo que serían de otra manera.
$ python3 -m pdb pdb_jump.py
> .../pdb_jump.py(8)<module>()
-> def f(n):
(Pdb) break 14
Breakpoint 1 at .../pdb_jump.py:14
(Pdb) continue
> .../pdb_jump.py(14)f()
-> result.append(j)
(Pdb) p j
5
(Pdb) jump 13
> .../pdb_jump.py(13)f()
-> j += n
(Pdb) continue
> .../pdb_jump.py(14)f()
-> result.append(j)
(Pdb) p j
10
(Pdb) disable 1
(Pdb) continue
[10, 20, 35, 55, 80]
The program finished and will be restarted
> .../pdb_jump.py(8)<module>()
-> def f(n):
(Pdb)
Saltos Ilegales¶
Entrar y salir de ciertas declaraciones de control de flujo es peligroso o indefinido y, por lo tanto, el depurador no lo permite.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
def f(n):
if n < 0:
raise ValueError('Invalid n: {}'.format(n))
result = []
j = 0
for i in range(n):
j = i * n + j
j += n
result.append(j)
return result
if __name__ == '__main__':
try:
print(f(5))
finally:
print('Always printed')
try:
print(f(-5))
except:
print('There was an error')
else:
print('There was no error')
print('Last statement')
|
jump
se puede usar para ingresar una función, pero los argumentos no están
definidos y es poco probable que el código funcione.
$ python3 -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(8)<module>()
-> def f(n):
(Pdb) break 22
Breakpoint 1 at .../pdb_no_jump.py:22
(Pdb) jump 9
> .../pdb_no_jump.py(9)<module>()
-> if n < 0:
(Pdb) p n
*** NameError: NameError("name 'n' is not defined",)
(Pdb) args
(Pdb)
jump
no entrará en el medio de un bloque, como un bucle for
o una
declaración try:except
.
$ python3 -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(8)<module>()
-> def f(n):
(Pdb) break 22
Breakpoint 1 at .../pdb_no_jump.py:22
(Pdb) continue
> .../pdb_no_jump.py(22)<module>()
-> print(f(5))
(Pdb) jump 27
*** Jump failed: can't jump into the middle of a block
(Pdb)
El código en un bloque finally
debe ejecutarse, por lo que jump
no
abandonará el bloque.
$ python3 -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(8)<module>()
-> def f(n):
(Pdb) break 24
Breakpoint 1 at .../pdb_no_jump.py:24
(Pdb) continue
[5, 15, 30, 50, 75]
> .../pdb_no_jump.py(24)<module>()
-> print 'Always printed'
(Pdb) jump 26
*** Jump failed: can't jump into or out of a 'finally' block
(Pdb)
Y la restricción más básica es que el salto está restringido al marco inferior de la pila de llamadas. Después de subir la pila para examinar las variables, el flujo de ejecución no se puede cambiar en ese punto.
$ python3 -m pdb pdb_no_jump.py
> .../pdb_no_jump.py(8)<module>()
-> def f(n):
(Pdb) break 12
Breakpoint 1 at .../pdb_no_jump.py:12
(Pdb) continue
> .../pdb_no_jump.py(12)f()
-> j = 0
(Pdb) where
.../lib/python3.5/bdb.py(
431)run()
-> exec cmd in globals, locals
<string>(1)<module>()
.../pdb_no_jump.py(22)<module>()
-> print(f(5))
> .../pdb_no_jump.py(12)f()
-> j = 0
(Pdb) up
> .../pdb_no_jump.py(22)<module>()
-> print(f(5))
(Pdb) jump 25
*** You can only jump within the bottom frame
(Pdb)
Reiniciar un programa¶
Cuando el depurador llega al final del programa, lo reinicia automáticamente, pero también se puede reiniciar explícitamente sin salir del depurador y perder los puntos de interrupción actuales u otras configuraciones.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann. All rights reserved.
#
import sys
def f():
print('Command-line args:', sys.argv)
return
if __name__ == '__main__':
f()
|
Ejecutar este programa hasta su finalización dentro del depurador imprime el nombre del archivo de la secuencia de comandos, ya que no se dieron otros argumentos en la línea de comando.
$ python3 -m pdb pdb_run.py
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb) continue
Command line args: ['pdb_run.py']
The program finished and will be restarted
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb)
El programa se puede reiniciar usando run
. Los argumentos pasados a
run
se analizan con shlex
y se pasan al programa como si fueran
argumentos de línea de comandos, por lo que el programa se puede reiniciar con
diferentes configuraciones.
(Pdb) run a b c "this is a long value"
Restarting pdb_run.py with arguments:
a b c this is a long value
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb) continue
Command line args: ['pdb_run.py', 'a', 'b', 'c',
'this is a long value']
The program finished and will be restarted
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb)
run
también se puede usar en cualquier otro punto del procesamiento para
reiniciar el programa.
$ python3 -m pdb pdb_run.py
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb) break 11
Breakpoint 1 at .../pdb_run.py:11
(Pdb) continue
> .../pdb_run.py(11)f()
-> print('Command line args:', sys.argv)
(Pdb) run one two three
Restarting pdb_run.py with arguments:
one two three
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb)
Personalizar el depurador con alias¶
Evita escribir comandos complejos repetidamente utilizando alias
para
definir un acceso directo. La expansión de alias se aplica a la primera
palabra de cada comando. El cuerpo del alias puede consistir en cualquier
comando que sea legal escribir en el indicador del depurador, incluidos otros
comandos del depurador y expresiones puras de Python. La recursión está
permitida en las definiciones de alias, por lo que un alias puede incluso
invocar a otro.
$ python3 -m pdb pdb_function_arguments.py
> .../pdb_function_arguments.py(7)<module>()
-> import pdb
(Pdb) break 11
Breakpoint 1 at .../pdb_function_arguments.py:11
(Pdb) continue
> .../pdb_function_arguments.py(11)recursive_function()
-> if n > 0:
(Pdb) pp locals().keys()
dict_keys(['output', 'n'])
(Pdb) alias pl pp locals().keys()
(Pdb) pl
dict_keys(['output', 'n'])
Ejecutar alias
sin ningún argumento muestra la lista de alias definidos. Se
supone que un solo argumento es el nombre de un alias y se imprime su
definición.
(Pdb) alias
pl = pp locals().keys()
(Pdb) alias pl
pl = pp locals().keys()
(Pdb)
Los argumentos del alias se referencian usando %n
donde n
se reemplaza
con un número que indica la posición del argumento, comenzando con 1
. Para
consumir todos los argumentos, usa %*
.
$ python3 -m pdb pdb_function_arguments.py
> .../pdb_function_arguments.py(7)<module>()
-> import pdb
(Pdb) alias ph !help(%1)
(Pdb) ph locals
Help on built-in function locals in module builtins:
locals()
Return a dictionary containing the current scope's local
variables.
NOTE: Whether or not updates to this dictionary will affect
name lookups in the local scope and vice-versa is
*implementation dependent* and not covered by any backwards
compatibility guarantees.
Borra la definición de un alias con unalias
.
(Pdb) unalias ph
(Pdb) ph locals
*** SyntaxError: invalid syntax (<stdin>, line 1)
(Pdb)
Guardar ajustes de configuración¶
La depuración de un programa implica muchas repeticiones: ejecutar el código,
observar la salida, ajustar el código o las entradas y volver a ejecutarlo.
pdb
intenta reducir la cantidad de repetición necesaria para controlar la
experiencia de depuración, para permitir que te concentres en el código en
lugar del depurador. Para ayudar a reducir la cantidad de veces que emite los
mismos comandos al depurador, pdb
puede leer una configuración guardada de
los archivos de texto interpretados cuando comienza.
El archivo ~/.pdbrc
se lee primero, lo que permite preferencias personales
globales para todas las sesiones de depuración. Luego, ./.pdbrc
se lee del
directorio de trabajo actual, para establecer las preferencias locales para un
proyecto en particular.
$ cat ~/.pdbrc
# Show python help
alias ph !help(%1)
# Overridden alias
alias redefined p 'home definition'
$ cat .pdbrc
# Breakpoints
break 11
# Overridden alias
alias redefined p 'local definition'
$ python3 -m pdb pdb_function_arguments.py
Breakpoint 1 at .../pdb_function_arguments.py:11
> .../pdb_function_arguments.py(7)<module>()
-> import pdb
(Pdb) alias
ph = !help(%1)
redefined = p 'local definition'
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../pdb_function_arguments.py:11
(Pdb)
Cualquier comando de configuración que se pueda escribir en el indicador del
depurador se puede guardar en uno de los archivos de inicio. Algunos comandos
que controlan la ejecución (continue
, next
, etc.) también pueden
hacerlo.
$ cat .pdbrc
break 11
continue
list
$ python3 -m pdb pdb_function_arguments.py
Breakpoint 1 at .../pdb_function_arguments.py:11
6
7 import pdb
8
9
10 def recursive_function(n=5, output='to be printed'):
11 B-> if n > 0:
12 recursive_function(n - 1)
13 else:
14 pdb.set_trace()
15 print(output)
16 return
> .../pdb_function_arguments.py(11)recursive_function()
-> if n > 0:
(Pdb)
Especialmente útil es run
, lo que significa que los argumentos de la línea
de comandos para una sesión de depuración se pueden establecer en ./.pdbrc
para que sean consistentes en varias ejecuciones.
$ cat .pdbrc
run a b c "long argument"
$ python3 -m pdb pdb_run.py
Restarting pdb_run.py with arguments:
a b c "long argument"
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb) continue
Command-line args: ['pdb_run.py', 'a', 'b', 'c',
'long argument']
The program finished and will be restarted
> .../pdb_run.py(7)<module>()
-> import sys
(Pdb)
Ver también
- Documentación de la biblioteca estándar para pdb
readline
– Biblioteca de edición interactiva de símbolos del sistema.cmd
– Crea programas interactivos.shlex
– Análisis de línea de comando de shell.- Python issue 26053 – Si la salida de
run
no coincide con los valores presentados aquí, consulta este error para obtener detalles sobre una regresión en la salida de pdb entre 2.7 y 3.5.