Depurar aplicaciones de Python con el módulo PDB

    Introducción

    En este tutorial, aprenderemos a usar Python PDB módulo para depurar aplicaciones Python. La depuración se refiere al proceso de eliminar errores de software y hardware de una aplicación de software. PDB significa «Python Debugger» y es un depurador de código fuente interactivo integrado con una amplia gama de características, como pausar un programa, ver valores de variables en instancias específicas, cambiar esos valores, etc.

    En este artículo, cubriremos las funcionalidades más utilizadas del módulo PDB.

    Antecedentes

    La depuración es una de las actividades más desagradables en el desarrollo de software y, al mismo tiempo, es una de las tareas más importantes en el ciclo de vida del desarrollo de software. En algún momento, cada programador tiene que depurar su código, a menos que esté desarrollando una aplicación de software muy básica.

    Hay muchas formas diferentes de depurar una aplicación de software. Un método muy utilizado es usar las declaraciones «imprimir» en diferentes instancias de su código para ver qué está sucediendo durante la ejecución. Sin embargo, este método tiene muchos problemas, como la adición de código adicional que se utiliza para imprimir los valores de las variables, etc. Si bien este enfoque puede funcionar para un programa pequeño, el seguimiento de estos cambios de código en una aplicación grande con muchas líneas de código , repartidos en diferentes archivos, puede convertirse en un gran problema. El depurador nos resuelve ese problema. Nos ayuda a encontrar las fuentes de error en una aplicación usando comandos externos, por lo tanto, no hay cambios en el código.

    Nota: Como se mencionó anteriormente, PDB es un módulo Python integrado, por lo que no es necesario instalarlo desde una fuente externa.

    Comandos de teclado

    Para comprender los principales comandos o herramientas que tenemos a nuestra disposición en PDB, consideremos un programa básico de Python y luego intentemos depurarlo usando comandos PDB. De esta forma, veremos con un ejemplo qué hace exactamente cada comando.

    # Filename: calc.py
    
    operators = ['+', '-', '*', "https://Pharos.sh.com/"]
    numbers = [10, 20]
    
    def calculator():
        print("Operators available: ")
        for op in operators:
            print(op)
    
        print("Numbers to be used: ")
        for num in numbers:
            print(num)
    
    def main():
        calculator()
    
    main()
    

    Aquí está el resultado del script anterior:

    Operators available:
    +
    -
    *
    /
    Numbers to be used:
    10
    20
    

    No he agregado ningún comentario en el código anterior, ya que es amigable para principiantes y no involucra conceptos complejos o sintaxis en absoluto. No es importante intentar entender la «tarea» que logra este código, ya que su propósito era incluir ciertas cosas para que todos los comandos de PDB pudieran probarse en él. Muy bien entonces, ¡comencemos!

    El uso de PDB requiere el uso de la interfaz de línea de comandos (CLI), por lo que debe ejecutar su aplicación desde el terminal o el símbolo del sistema.

    Ejecute el siguiente comando en su CLI:

    Te puede interesar:Interpolación de cadenas de Python con el operador de porcentaje (%)
    $ python -m pdb calc.py
    

    En el comando anterior, el nombre de mi archivo es «calc.py», por lo que deberá insertar su propio nombre de archivo aquí.

    Nota: Los -m es una bandera y notifica al ejecutable de Python que un módulo necesita ser importado; esta bandera es seguida por el nombre del módulo, que en nuestro caso es pdb.

    La salida del comando se ve así:

    > /Users/junaid/Desktop/calc.py(3)<module>()
    -> operators = [ '+', '-', '*', "https://Pharos.sh.com/" ]
    (Pdb)
    

    La salida siempre tendrá la misma estructura. Comenzará con la ruta del directorio a nuestro archivo de código fuente. Luego, entre corchetes, indicará el número de línea de ese archivo al que apunta PDB, que en nuestro caso es «(3)». La siguiente línea, que comienza con el símbolo «->», indica la línea a la que se apunta actualmente.

    Para cerrar el indicador de PDB, simplemente ingrese quit o exit en el indicador de PDB.

    Algunas otras cosas a tener en cuenta, si su programa acepta parámetros como entradas, también puede pasarlos a través de la línea de comandos. Por ejemplo, si nuestro programa anterior hubiera requerido tres entradas del usuario, entonces este es el aspecto que tendría nuestro comando:

    $ python -m pdb calc.py var1 var2 var3
    

    Continuando, si antes había cerrado el indicador de PDB a través del quit o exit comando, luego vuelva a ejecutar el archivo de código a través de PDB. Después de eso, ejecute el siguiente comando en el indicador de PDB:

    (Pdb) list
    

    La salida se ve así:

      1     # Filename: calc.py
      2
      3  -> operators = ['+', '-', '*', "https://Pharos.sh.com/"]
      4     numbers = [10, 20]
      5
      6     def calculator():
      7         print("Operators available: ")
      8         for op in operators:
      9             print(op)
     10
     11         print("Numbers to be used: ")
    (Pdb)
    

    Esto le mostrará las primeras 11 líneas de su programa, con el «->» apuntando hacia la línea actual que está siendo ejecutada por el depurador. A continuación, intente este comando en el indicador de PDB:

    (Pdb) list 4,6
    

    Este comando debe mostrar solo las líneas seleccionadas, que en este caso son las líneas 4 a 6. Aquí está el resultado:

    Te puede interesar:Python para PNL: Creación de modelos de clasificación de varios tipos de datos con Keras
      4     numbers = [10, 20]
      5
      6     def calculator():
    (Pdb)
    

    Depurar con puntos de interrupción

    La siguiente cosa importante que aprenderemos es el punto de ruptura. Los puntos de interrupción se usan generalmente para programas más grandes, pero para comprenderlos mejor veremos cómo funcionan en nuestro ejemplo básico. Los puntos de ruptura son ubicaciones específicas que declaramos en nuestro código. Nuestro código se ejecuta hasta esa ubicación y luego se detiene. PDB asigna números automáticamente a estos puntos.

    Tenemos las siguientes opciones diferentes para crear puntos de ruptura:

    • Por número de línea
    • Por declaración de función
    • Por una condición

    Para declarar un punto de interrupción por número de línea, ejecute el siguiente comando en el indicador de PDB:

    (Pdb) break calc.py:8
    

    Este comando inserta un punto de interrupción en la octava línea de código, que pausará el programa una vez que llegue a ese punto. La salida de este comando se muestra como:

    Breakpoint 1 at /Users/junaid/Desktop/calc.py: 8
    (Pdb)
    

    Para declarar puntos de interrupción en una función, ejecute el siguiente comando en el indicador de PDB:

    (Pdb) break calc.calculator
    

    Para insertar un punto de interrupción de esta manera, debe declararlo usando el nombre del archivo y luego el nombre de la función. Esto da como resultado lo siguiente:

    Breakpoint 2 at /Users/junaid/Desktop/calc.py:6
    

    Como puede ver, a este punto de interrupción se le ha asignado el número 2 automáticamente, y también se muestra el número de línea, es decir, 6 en el que se declara la función.

    Los puntos de interrupción también se pueden declarar mediante una condición. En ese caso, el programa se ejecutará hasta que la condición sea falsa y se detendrá cuando esa condición se vuelva verdadera. Ejecute el siguiente comando en el indicador de PDB:

    (Pdb) break calc.py:8, op == "*"
    

    Esto rastreará el valor de la op variable durante la ejecución y solo se rompe cuando su valor es «*» en la línea 8.

    Para ver todos los puntos de ruptura que hemos declarado en forma de lista, ejecute el siguiente comando en el indicador de PDB:

    Te puede interesar:Minimax con poda alfa-beta en Python
    (Pdb) break
    

    La salida se ve así:

    Num Type         Disp Enb   Where
    1   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py: 8
    2   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py: 6
        breakpoint already hit 1 time
    3   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py: 8
        stop only if op == "*"
    (Pdb)
    

    Por último, veamos cómo podemos deshabilitar, habilitar y borrar un punto de interrupción específico en cualquier instancia. Ejecute el siguiente comando en el indicador de PDB:

    (Pdb) disable 2
    

    Esto desactivará el punto de interrupción 2, pero no lo eliminará de nuestra instancia del depurador.

    En la salida verá el número del punto de interrupción desactivado.

    Disabled breakpoint 2 at /Users/junaid/Desktop/calc.py:6
    (Pdb)
    

    Imprimamos la lista de todos los puntos de interrupción nuevamente para ver el valor «Enb» para el punto de interrupción 2:

    (Pdb) break
    

    Salida:

    Num Type         Disp Enb   Where
    1   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:8
    2   breakpoint   keep no    at /Users/junaid/Desktop/calc.py:4 # you can see here that the "ENB" column for #2 shows "no"
        breakpoint already hit 1 time
    3   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:8
        stop only if op == "*"
    (Pdb)
    

    Para volver a habilitar el punto de interrupción 2, ejecute el siguiente comando:

    (Pdb) enable 2
    

    Y nuevamente, aquí está el resultado:

    Enabled breakpoint 2 at /Users/junaid/Desktop/calc.py:6
    

    Ahora, si imprime la lista de todos los puntos de interrupción nuevamente, el valor de la columna «Enb» para el punto de interrupción 2 debería mostrar un «sí» nuevamente.

    Ahora borremos el punto de interrupción 1, que lo eliminará todo junto.

    Te puede interesar:Agrupación jerárquica con Python y Scikit-Learn
    (Pdb) clear 1
    

    El resultado es el siguiente:

    Deleted breakpoint 1 at /Users/junaid/Desktop/calc.py:8
    (Pdb)
    

    Si volvemos a imprimir la lista de puntos de interrupción, ahora solo debería mostrar dos filas de puntos de interrupción. Veamos la salida del comando «romper»:

    Num Type         Disp Enb   Where
    2   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:4
        breakpoint already hit 1 time
    3   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:8
        stop only if op == "*"
    

    Exactamente lo que esperábamos.

    Antes de continuar con esta sección, quiero mostrarle todo lo que se muestra cuando ejecutamos el código hasta el punto de interrupción especificado. Para hacer eso, borremos todos los puntos de interrupción anteriores y declaremos otro punto de interrupción a través del indicador de PDB:

    1. Borrar todos los puntos de interrupción

    (Pdb) clear
    

    Después de eso, escriba «y» y presione «Enter». Debería ver aparecer una salida como esta:

    Deleted breakpoint 2 at /Users/junaid/Desktop/calc.py:6
    Deleted breakpoint 3 at /Users/junaid/Desktop/calc.py:8
    

    2. Declare un nuevo punto de interrupción

    Lo que deseamos lograr es que el código se ejecute hasta el punto en que el valor de num variable es mayor que 10. Básicamente, el programa debería hacer una pausa antes de que se imprima el número «20».

    (Pdb) break calc.py:13, num > 10
    

    3. Ejecute el código hasta este punto de interrupción

    Para ejecutar el código, use el comando «continuar», que ejecutará el código hasta que llegue a un punto de interrupción o finalice:

    Te puede interesar:Generación de texto con Python y TensorFlow/Keras
    (Pdb) continue
    

    Debería ver el siguiente resultado:

    Operators available:
    +
    -
    *
    /
    Numbers to be used:
    10
    > /Users/junaid/Desktop/calc.py(13)calculator()
    -> print(num)
    

    Esto es exactamente lo que esperábamos, el programa se ejecuta hasta ese punto y luego se detiene, ahora depende de nosotros si deseamos cambiar algo, inspeccionar las variables o si queremos ejecutar el script hasta su finalización. Para indicarle que se ejecute hasta su finalización, vuelva a ejecutar el comando «continuar». La salida debe ser la siguiente:

    20
    The program finished and will be restarted
    > /Users/junaid/Desktop/calc.py(3)<module>()
    -> operators = [ '+', '-', '*', "https://Pharos.sh.com/" ]
    

    En la salida anterior, se puede ver que el programa continúa exactamente desde donde lo dejó, ejecuta la parte restante y luego se reinicia para permitirnos depurarlo más si lo deseamos. Pasemos a la siguiente sección ahora.

    Nota IMPORTANTE: Antes de seguir adelante, borre todos los puntos de interrupción ejecutando el comando «borrar», seguido de escribir «y» en el indicador de PDB.

    Funciones de paso y siguiente

    Por último, pero no menos importante, estudiemos el next y step funciones; estos se usarán con mucha frecuencia cuando comience a depurar sus aplicaciones, así que aprendamos qué hacen y cómo se pueden implementar.

    los step y next las funciones se utilizan para iterar a lo largo de nuestro código línea por línea; hay una pequeña diferencia entre los dos. Mientras itera, si el step función encuentra una llamada de función, se moverá a la primera línea de la definición de esa función y nos mostrará exactamente lo que está sucediendo dentro de la función; mientras que, si el next función encuentra una llamada de función, ejecutará todas las líneas de esa función de una sola vez y se detendrá en la siguiente llamada de función.

    ¿Confuso? Veamos eso en un ejemplo.

    Vuelva a ejecutar el programa a través del indicador de PDB con el siguiente comando:

    $ python -m pdb calc.py
    

    Ahora escribe continue en el indicador de PDB y continúe haciéndolo hasta que el programa llegue al final. A continuación, mostraré una sección de toda la secuencia de entrada y salida, que es suficiente para explicar el punto. La secuencia completa es bastante larga y solo lo confundiría más, por lo que se omitirá.

    > /Users/junaid/Desktop/calc.py(1)<module>()
    -> operators = [ '+', '-', '*', "https://Pharos.sh.com/" ]
    (Pdb) step
    > /Users/junaid/Desktop/calc.py(2)<module>()
    -> numbers = [ 10, 20 ]
    .
    .
    .
    .
    > /Users/junaid/Desktop/calc.py(6)calculator()
    -> print("Operators available: " )
    (Pdb) step
    Operators available:
    > /Users/junaid/Desktop/calc.py(8)calculator()
    -> for op in operators:
    (Pdb) step
    > /Users/junaid/Desktop/calc.py(10)calculator()
    -> print(op)
    (Pdb) step
    +
    > /Users/junaid/Desktop/calc.py(8)calculator()
    -> for op in operators:
    (Pdb) step
    > /Users/junaid/Desktop/calc.py(10)calculator()
    -> print(op)
    
    .
    .
    .
    .
    

    Ahora, vuelva a ejecutar todo el programa, pero esta vez, use el comando «siguiente» en lugar de «paso». También he mostrado el seguimiento de entrada y salida para eso.

    Te puede interesar:Tareas asíncronas en Django con Redis y Celery
    > /Users/junaid/Desktop/calc.py(3)<module>()
    -> operators = ['+', '-', '*', "https://Pharos.sh.com/"]
    (Pdb) next
    > /Users/junaid/Desktop/calc.py(4)<module>()
    -> numbers = [10, 20]
    (Pdb) next
    > /Users/junaid/Desktop/calc.py(6)<module>()
    -> def calculator():
    (Pdb) next
    > /Users/junaid/Desktop/calc.py(15)<module>()
    -> def main():
    (Pdb) next
    > /Users/junaid/Desktop/calc.py(18)<module>()
    -> main()
    (Pdb) next
    Operators available:
    +
    -
    *
    /
    Numbers to be used:
    10
    20
    --Return--
    

    Muy bien, ahora que tenemos el seguimiento de salida para ambas funciones, veamos en qué se diferencian. Para el step función, puede ver que cuando el calculator se llama a la función, se mueve dentro de esa función y la recorre en «pasos», mostrándonos exactamente lo que está sucediendo en cada paso.

    Sin embargo, si ve el seguimiento de salida del next función, cuando se llama a la función «principal», no nos muestra lo que sucede dentro de esa función (es decir, una llamada posterior a la función de calculadora), y luego imprime directamente el resultado final en un solo paso / paso.

    Estos comandos son útiles si está iterando a través de un programa y desea recorrer ciertas funciones, pero no otras, en cuyo caso puede utilizar cada comando para sus propósitos.

    Conclusión

    En este tutorial, aprendimos sobre una técnica sofisticada para depurar aplicaciones de Python utilizando un módulo incorporado llamado PDB. Nos sumergimos en los diferentes comandos de resolución de problemas que nos proporciona PDB, incluido el next y step declaraciones, puntos de interrupción, etc. También los aplicamos a un programa básico para verlos en acción.

     

    Rate this post

    Etiquetas: