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.

pdb_script.py
 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().

pdb_set_trace.py
 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().

pdb_post_mortem.py
 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.

pdb_function_arguments.py
 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.

pdb_pp.py
 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.

pdb_step.py
 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.

pdb_next.py
 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.

pdb_break.py
 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.

pdb_break_remote.py
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.

pdb_jump.py
 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.

pdb_no_jump.py
 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.

pdb_run.py
 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