Depurar Python como un Pro

Seguimos con la serie de posts sobre Python. Sigo con el bootcamp de Data Science y Machine Learning de Core Code School y en los últimos días hemos estado viendo cómo hacer visualizaciones de datos tremendamente molonas con Streamlit.

Durante el proceso de creación de la aplicación web con Streamlit nos encontramos con un problema que impedía ver los datos. Mientras que itentábamos ver la causa salió a colación el tema de la depuración de código y algunos compañeros no sabían lo que era.

Así que he visto una oportunidad magnífica de explicar lo que es la depuración de código, por qué es útil y cómo hacerlo en las herramientas que usamos de manera más usual en Core.

Qué es depurar

Lo primero y más importante es explicar y entender qué es eso de depurar el código o depurar un bug. También se suele decir mucho hacer Debug del código, hacer Debugging o, en una españolización del término, también puedes escuchar alguna vez Debuggear.

Básicamente, depurar es recorrer exhaustivamente un código para identificar y corregir la causa de un problema.

Este proceso tiene una parte muy fuerte de observación y otra parte creativa. Normalmente cuando vas depurando no sabes dónde está el fallo y por eso tiene que ir probando y recorriendo todo. Mientras vas depurando, tienes que ir viendo cómo cambian tus variables hasta que veas cuándo se produce el comportamiento anormal o que quieres resolver.

Para hacer todo esto, tienes muchas herramientas que te ayudan con el proceso.

 Por qué necesito depurar

La realidad es que no siempre necesitas depurar, pero alguna vez lo necesitarás sin ninguna duda y, cuando eso pase, mejor tener las herramientas y los conocimientos.

Un método de depuración muy habitual es imprimir mensajes informativos para ver por dónde va pasando el código y qué valores van tomando las variables. Un ejemplo de esto es:

Ahí ves que hay un print para ver el parámetro que se le pasa a la función, es algo muy sencillo y pequeño pero ilustra lo que quiero decir.

Ejemplos de depuración

Ahora voy ponerte un ejemplo de lo que es depurar en tiempo real, lo que se ve y lo que se puede hacer.

En ese código tengo una lista de frases típicas. Lo que hago es un for que recorra la lista y le aplique un cifrado césar a cada una.

Como puedes ver, a la izquierda de la pantalla está lo que se llama el "inspector", en el que tienes acceso a cada variable y al valor que toma en cada momento.

Para que el depurador se pare en una línea concreta de tu código tienes que poner lo que se llama un punto de interrupción (en inglés, breakpoint). Esto se hace poniendo el ratón a la izquierda de los números de línea y clicando donde aparece un puntito rojo.

Ese punto está sombreado porque no está puesto, cuando clicas ahí se pone en un rojo más intenso y ya significa que está puesto:

Además, cuando la depuración llega a una función, puedes entrar dentro de ella y depurar esa función paso a paso, como he hecho en el vídeo.

Incluso en el bucle que hay en la función que hace el cifrado puedes ir letra a letra y ver cómo va calculando el resultado.

Con este método podrías detectar un bug en tu código y solucionarlo de manera más o menos sencilla.

Una vez que sabes qué es depurar, te voy a enseñar cómo hacerlo en las herramientas habituales que usamos en Core.

Depurar en VSCode

La primera de todas es, cómo no, Visual Studio Code. También es la más sencilla de las que vas a ver en el post.

Si te fijas en la barra izquierda de VSCode, puedes ver que hay un botón que es un signo de Play con un insecto encima (en inglés, un bug):

Púlsalo y te mostrará la vista de depuración. VSCode controla las configuraciones de depuración con un archivo que se llama launch.json y que se encuentra en la carpeta .vscode de tu proyecto.

Si es la primera vez que abres la vista de depuración y no tienes ninguna configuración, verás una vista como esta:

Lo que harás será pulsar donde dice "cree un archivo launch.json". Lo importante es que pulses eso mientras tienes un archivo .py abierto, porque así VSCode te sugerirá configuraciones para depurar en Python, como ves aquí:

Como ves, VSCode tiene configuraciones predefinidas para varias opciones de Python como depurar un archivo, un módulo o frameworks como Django, Flask o FastApi.

En este primer ejemplo vas a elegir Python File. Cuando lo selecciones, te creará un archivo launch.json dentro de la carpeta .vscode con el siguiente contenido:

{
  // Use IntelliSense para saber los atributos posibles.
  // Mantenga el puntero para ver las descripciones de los existentes atributos.
  // Para más información, visite: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Python: Archivo actual",
      "type": "python",
      "request": "launch",
      "program": "${file}",
      "console": "integratedTerminal"
    }
  ]
}

Una vez que ese archivo está guardado, puedes volver a la vista de depuración y verás que te aparece una vista distinta a la de antes con una opción con el mismo nombre que aparece en el atributo name de ese JSON.

Si te sigue apareciendo la misma vista que antes para crear un archivo launch.json, cierra y abre VSCode para que aplique los cambios.

Una vez tienes esto configurado y algún punto de ruptura puesto, ya puedes darle al play verde que aparece en la vista de depuración, a la izquierda del nombre de la configuración:

Eso deberá lanzarte la depuración y pararse en el primer punto de ruptura que se encuentre mientras ejecuta el código.

Una vez parado, te aparecerán los llamados controles de depuración que te voy a detallar ahora:

  1. Seguir hasta el siguiente punto de interrupción. Reanuda la ejecución hasta que encuentre otro punto de ruptura. Ten en cuenta que si el punto está dentro de un bucle y le das al botón 1, se parará en ese mismo punto, pero en la siguiente vuelta del bucle.
  2. Saltar a la siguiente instrucción, omitiendo procedimientos. Ejecuta la instrucción en la que está parado el depurador actualmente y pasa la siguiente. Ten en cuenta que ejecutar una instrucción implica que se pueden actualizar variables, mantén siempre un ojo en la parte de inspección de variables (barra izquierda).
  3. Saltar a la siguiente instrucción, sin omitir procedimientos. Esto hace exactamente lo mismo que el botón anterior, salvo que salta a la siguiente instrucción inmediatamente. Puede parecer lo mismo, pero hay una diferencia. Si estás en una línea que ejecuta una función, el botón anterior no va a entrar dentro de la función, sino que va a omitirla (omitir el procedimiento) y saltará a la siguiente. Este botón entrará dentro. Aquí puedes ver la diferencia:
  4. Sale de la función que estás depurando. Es útil si entras en una función creyendo que puede estar el problema ahí y cuando vas por la mitad ves que no está ahí, en vez de poner otro punto de ruptura o empezar de nuevo, simplemente sales de esa función y sigues adelante. Si estás en un código que no está dentro de ninguna función y sales, la depuración continuará hasta el siguiente punto de interrupción, como si hubieras dado al botón 1.
  5. Reinicia el proceso de depuración. Lo mismo que parar y volver a empezar.
  6. Para el proceso de depuración. Para la ejecución sin ejecutar nada más, no termina el proceso de manera normal, sino que corta la ejecución.

Consejo de Pro: Explora la pestaña "Consola de Depuración". Cuando estás parado en un punto de ruptura, puedes ejecutar código Python desde ahí y ver el resultado inmediatamente. Puedes anticiparte a errores o bugs.

Depurar en Jupyter

Ahora que sabes depurar en VSCode, vamos a probar con Jupyter. Lo cierto es que Jupyter Notebook no permite hacer depuración pero Jupyter Lab sí.

Para instalarlo puedes seguir las instrucciones oficiales. Lo puedes instalar con Conda sin problema.

Una vez instalado, hay que poner un par de paquetes de python y alguna extensión de Jupyter Lab.

Lo primero que vas a instalar es un kernel de Python compatible con la depuración, en este caso Xeus. Para instalarlo, ejecuta en una terminal:

conda install -c conda-forge xeus

Una vez está, puedes instalar la extensión de depuración, para esto ejecuta en una terminal:

jupyter labextension install @jupyterlab/debugger
jupyter lab build

Una vez instalada la extensión y terminado el build, ejecuta Jupyter lab con el comando:

jupyter lab

Una vez lanzado Jupyter, abre el notebook que quieras depurar. Ahora tienes que cambiar el kernel de python al que hemos instalado, XPython. Esto lo puedes cambiar en el selector de la esquina superior derecha.

Cuando cliques, debes ver una ventana para seleccionar el kernel que quieres, elige XPython:

Ahora verás que hay 2 cosas nuevas, relacionadas con la depuración:

Lo primero es un interruptor al lado de un icono de un insecto, y lo segundo es un botón con el mismo icono del insecto. Primero activa el interruptor, eso mostrará una barra para poner puntos de interrupción y además mostrará una barra a la izquierda con información similar a la que teníamos en VSCode.

Puedes poner puntos de ruptura de la misma manera que en VSCode, clicando a la izquierda del número de línea:

Una vez tienes el punto de ruptura, puedes ejecutar la celda de manera normal, y se parará en el punto:

A la izquierda tienes lo mismo que había en VSCode, el inspector de variables, los mismos botones para controlar la depuración y una lista de los puntos de ruptura.

Si pulsas el botón para ejecutar la instrucción entrando en funciones y la función está en otra celda, Jupyter se encargará de que salte a la celda correcta y siga la depuración normal.

Depurar Streamlit

Otra herramienta que usamos en Core es Streamlit. Como también puede haber errores al desarrollar una app de Streamlit, vas a aprender a depurar eso también.

Lo único que hace falta es una configuración nueva para nuestro launch.json.

Si lo abres, verás que la propiedad configurations es un array. Ahí puedes poner todas las configuraciones que quieras. Ya debes tener una, así que añade esta:

{
      "name": "Python: Streamlit",
      "type": "python",
      "request": "launch",
      "module": "streamlit.cli",
      "args": [
        "run",
        "${file}",
      ],
}

El nombre de esa configuración será Python: Streamlit, decimos que la depuración es python y que vamos a lanzar algo. En vez de lanzar un archivo como antes, ahora lo que vamos a lanzar es un módulo, en este caso stramlit.cli, que básicamente es el intérprete de comandos de Streamlit. Y, por último, vamos a pasarle al cli los argumentos run ${file}.

VSCode sustituirá ${file} por el archivo que tengas abierto al lanzar esa depuración. Si no quieres que sea así y siempre quieres que lance el mismo archivo, puedes sustituir eso por el archivo que necesites.

Esa configuración es equivalente a llamar a streamlit run ${file}.

Ahora solo tendrás que poner tus puntos de interrupción y lanzar la depuración.

Depurar Flask

Depurar Flask es igual de sencillo que Streamlit. Solo necesitas añadir una configuración de depuración al launch.json y listo.

En este caso, tienes que añadir:

{
    "name": "Python: Flask",
    "type": "python",
    "request": "launch",
    "module": "flask",
    "env": {
        "FLASK_APP": "app.py",
        "FLASK_ENV": "development",
        "FLASK_DEBUG": "0"
    },
    "args": [
        "run",
        "--no-debugger",
        "--no-reload"
    ],
    "jinja": true
},

Ahí lo que le dices es que la configuración se llama Python: Flask, que es de tipo python y que quieres lanzar un módulo, que es flask.

Con env se ponen una serie de variables de entorno que le indican a flask qué archivo tiene que ejecutar, en qué entorno está y desactiva el modo de depuración (porque ya lo gestiona VSCode).

Además, igual que antes, añade una serie de argumentos al comando flask para configurar la ejecución y añade un parámetro jinja que es el motor de plantillas que usa Flask, para permitir depurar eso también.

Si tú no vas a usar plantillas, puedes eliminar esa opción.

De nuevo, una vez tienes eso, solo necesitas activar tus puntos de ruptura y ejecutar la depuración.

Conclusiones

Ahora sabes lo que es depurar, por qué es importante y cómo hacerlo en algunas herramientas habituales en el desarrollo y en Python, ya no hay quien te pare.

Recuerda dejar en los comentarios cualquier duda o sugerencia que tengas y mirar los cursos de Core.

Nos vemos en el próximo post!

17