Skip to content
Juan Gonzalez-Gomez edited this page Jun 25, 2024 · 40 revisions

Log de desarrollo

Viernes, 21-Junio-2024

Voy a comenzar las utilidades icerv, que son las excusa para documentar, aprender y afianzar conocimientos diversos. Voy a intentar hacer todo desde el vscode, definiendo las tareas necesarias. Quiero evitar, en la medida de lo posible, utilizar la línea de comandos. Creo que esto es la clave para enseñar a las personas recién llegadas a la tecnología

Empiezo creando el repo desde cero, y creando el fichero get-rars.py:

print("Hola...")
print("Vaamos!")

Este es el estado en el vs-code (1.90.2)

Estas son las extensiones que tengo instaladas para trabajar con python: Python, Python Debugger, Python Environment Manager, Pylance, isort

Pinchando en el icono de python de la izquierda se pueden ver los diferentes entornos y versiones de python que están instaladas

Con estas extensiones instaladas, y sin hacer nada adicional se puede ejecutar el programa get-rars.py. Nos vamos a la parte de depuración y pinchamos en Run and Debug

Pinchamos ahora en la opción Python File

Se abre un terminal en la parte de la izquierda y se ejecuta el archivo de python llamando al intérprete de python que teníamos seleccionados (python 3.10 en este caso)

Si queremos simplemente ejecutar el archivo python rápidamente, siempre podemos pinchar en el botón de play que hay en la parte superior derecha de la ventana de edición

Al pinchar, se abre el terminal y se ejecuta el archivo con el python actual:

También podemos depurar de una manera rápida. Primero ponemos un breakpoint (1), luego pinchamos en la flecha situada a la derecha del botón de play anterior (2) y seleccionamos la opción Debug python file:

Al hacerlo se abre el terminal y se ejecuta el programa de python en modo depuración. Vemos cómo se pasa automáticamente al modo depuración. Nos aparecen los controles de depuración en la parte superior y el programa se para en el primer breakpoint

Ahora apretamos en la opción de step over (Tecla F10) para ejecutar la instrucción actual. En el terminal vemos que aparece el mensaje "Hola". También vemos cómo ahora se para en la siguiente instrucción, que queda a la espera para ejecutarse

Si ahora volvemos a apretar F10, se ejecuta la siguiente instrucción (que es la última). Aparece el siguiente mensaje: "vaamos!" y, como es la última instrucción, se sale del modo depuración

Nos fijamos que ahora el símbolo del play ha cambiado: tiene el icono de play y un bicho (bug). Esto nos permite volver a depurar simplemente con pincharlo de nuevo. La última operación seleccionada es la que queda visible

Sábado, 22-Junio-2024

Cambiando el entorno de python

Los ejemplos anteriores se han ejecutado con el entorno de python 3.10.2, que es el que estaba por defecto. Pero esto lo podemos cambiar. Podemos seleccionar cualquier de los que tengamos instalados en el sistema. Pinchamos en el icono de python de la parte izquierda, y nos aparecerá en el lateral izquierdo todos los entornos python disponibles

En la parte superior aparece el entorno actual (y también lo vemos en la esquina inferior derecha). Vamos a Ejecutar el script de pruebas pero usando python 3.8, por ejemplo. Para ello seleccionamos python 3.8.18 y pinchamos en el icono de la estrella

Al hacerlo nos cambia el entorno por defecto, y lo vemos reflejado en el laterior izquierdo (parte superior) y también en la esquina inferior derecha

Si ahora ejecutamos el script como ya sabemos hacer, el entorno que se usa es 3.8.18 y NO el anterior 3.10.2

Usando entornos virtuales

Lo interesante de todo esto es que podemos crear los entornos virtuales fácilmente, sin usar la línea de comandos. Para ello pinchamos en el icono de python (como antes), y le damos al icono '+' que está en la izquierda de Venv

Se nos abre un desplegable en el que está señalado el entorno de python actual. Seleccionamos la versión de python que queremos incluir en nuestro entorno virtual. Por ejemplo la última: python 3.10.12

Nos aparece una notificación en la parte interior indicando que se está creando el entorno virtual de python. Nos aparecerá otra en paralelo que nos dice si queremos seleccionar este entorno para trabjar con él

De momento pinchamos en NO, para hacerlo manualmente

Vemos que nos aparece ahora el nuevo entorno virtual dentro de la carpeta Venv:

Y se comporta como otro entorno más. Así que lo podemos seleccionar pinchando en la estrella

Ese entorno virtual es nuestro python por defecto! Si miramos los archivos vemos que se ha creado la carpeta .venv, que es la que contiene nuestro entorno virtual

Ahora ejecutammos el script get-rars.py:

Vemos que en el terminal efectivamente estamos dentro del entorno virtual .venv

Lo interesante es que podemos ver qué paquetes hay instalados en el entorno virtual. Por defecto no hay ninguno, salvo pip y setuptools

Instalando paquetes

La instalación de paquetes es muy fácil!!! Seleccionamos el entorno donde queremos instalar los paquetes y pinchamos en la lupa:

Escribirmos el nombre del paquete a instalar, o bien cualquier otro nombre para hacer una búsqueda. Vamos a probar a instalar el paquete colorama

Pinchamos en colorama y se nos instala automáticamente. Ahora lo podemos ver dentro del entorno:

Vamos a comprobar que funciona. Modificamos el código para imprimir mensajes en color, siguiendo la API de colorama:

from colorama import Fore, Style

print("Hola...")
print("Vaamos!")
print(Fore.RED + "Texto en Rojo!!")
print(Fore.GREEN + "Texto en verde")
print(Style.RESET_ALL)
print("Normal")

Note

Podría ocurrir que no detecte el módulo colorama y que el editor lo muestre subrayado con error. En ese caso cerrar el proyecto y volver a abrirlo para que se refresque todo

Ahora ya lo podemos ejecutar con normalidad, y veremos los textos en color:

Eliminar paquetes

Los paquetes se desinstalan muy fácilmente. Sólo hay que seleccionar el paquete en cuestión y pinchar en el icono de la basura

Mostrar las dependencias

Al seleccionar un paquete tenemos la opción de volcar a un fichero todas las dependencias:

Se nos crea el fichero requirements.txt con TODAS las dependencias. Aunque en este ejemplo sólo hay una única dependencia (colorama)

Lint

Para el analisis de los programas en python hay dos utilidades muy extendidas: flake8 y pylint. Vamos a configurar primero pylint. Lo instalamos como ya sabemos. Nos aparecerá como dependencia del paquete, aunque también aparecerán todos el resto de paquetes de los que depende pylint

Primero vamos a utilizar pylint desde el terminal. Abrimos el terminal (o usamos el que ya había abierto de la ejecución) y ejecutamos el comando: pylint get-rars.py

Vemos cómo nos aparecen varios errores. Y delante del comando ejecutado aparece una cruz roja indicando que se ha ejecutado con errores. Esto es lo bueno del lint. Aunque es un programa extremadamente sencillo, hay cosas que se pueden mejorar

Antes de arreglar el código vamos a automatizar esta tarea de Lint. No queremos escribirla en el terminal (aunque por supuesto podríamos hacerlo)

Domingo, 23-Junio-2024

Automatizar Lint

Vamos a configurar una tarea para que se ejecute el comando pylint get-rars.py. Pinchamos en Terminar/Run Task. Se nos abre un desplegable. Seleccionamos la opción Configure a task

En el siguiente desplegable seleccionamos Create tasks.json from template

Y luego Others

Se nos crea el fichero tasks.json, dentro de la carpeta .vscode, con una tarea "hola mundo". Esta tarea se llama echo y simplemente ejecuta el comando echo Hello en un terminal.

Este es el fichero:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "echo",
            "type": "shell",
            "command": "echo Hello"
        }
    ]
}

Para comprobar su funcionamiento lo vamos a ejecutar. Pinchamos en Terminal/Run task, y en el desplegable que aparece veremos la nueva tarea creada: echo

La ejecutamos. En el nuevo desplegable pinchamos en la primera opción: continue without scanning the task output

Se nos abre el terminal y se ejecuta el comando. Ahí podemos ver el mensaje Hello

Editamos el fichero task.json para modifar la tarea echo por Lint, y que se ejecute el comando pylint get-rars.py (o también el comando equivalente python -m pylint get-rars.py

Esta es el nuevo tasks.json:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Lint",
            "detail": "pylint",
            "type": "shell",
            "command": "${command:python.interpreterPath}",
            "args": ["-m", "pylint", "get-rars.py"],
        }
    ]
}

El comando que se ejecuta se le pasa a través de una variable del sistema de vscode: ${command:python.interpreterPath} que tiene el path del intérprete de python que tenemos seleccionado. Los argumentos se pasan como un array de cadenas. Cada elemento es uno de los parámetros que pasamos, que en la linea de comandos van separados por espacios. En este caso estamos ejecutando python -m pylint get-rars.py

Para ejecutarlo lo hacemos igual que con la tarea echo. Pinchamos en Terminar/Run Task. Ahí nos aparece la nueva tarea: Lint

Al pinchar nos vuelve a aparecer un desplegable, donde indicamos la opción continue without scanning the task output. Pinchamos en ella...y se nos ejecuta el lint

Configurando el Lint

Ya tenemos el comando pylint get-rars.py automatizado, pero ahora vamos a integrarlo mejor con el vscode. Configuramos la tarea de lint para que se parsee el mensaje de salida y obtener la línea donde se produce el error, para que se nos indique directamente en el código. Este es el nuevo tasks.json:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Lint",
            "detail": "pylint",
            "type": "shell",
            "command": "${command:python.interpreterPath}",
            "args": ["-m", "pylint", "get-rars.py"],
            "problemMatcher": {
		        "owner": "python",
		        "fileLocation": [
			        "relative",
			        "${workspaceFolder}"
		        ],
		        "pattern": {
			        "regexp": "^(.*):(\\d+):(\\d+):\\s+(W|E|C).+:\\s+(.*)$",
			        "file": 1,
			        "line": 2,
			        "column": 3,
			        "severity": 4,
			        "message": 5
		        }
	        }
        }
    ]
}

Se ha añadido la propiedad problemMatcher que es la que parsea y extrae la información para que el vscode muestre la línea del error, así como el propio error

Ahora, además, como hemos configurado esa propiedad, al ejecutar la tarea Lint ya no nos preguntará nada más... directamente la ejecutará, parseará el resultado y nos lo mostrará gráficamente

Por último cambiamos la presentación. Queremos que se limpie el terminal antes de ejecutar el comando, y que NO salga el mensaje final. Todo esto lo configuramos en la propiedad presentation

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Lint",
            "detail": "pylint",
            "type": "shell",
            "command": "${command:python.interpreterPath}",
            "args": ["-m", "pylint", "get-rars.py"],
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "shared",
                "showReuseMessage": false,
                "clear": true, 
            },
            "problemMatcher": {
		        "owner": "python",
		        "fileLocation": [
			        "relative",
			        "${workspaceFolder}"
		        ],
		        "pattern": {
			        "regexp": "^(.*):(\\d+):(\\d+):\\s+(W|E|C).+:\\s+(.*)$",
			        "file": 1,
			        "line": 2,
			        "column": 3,
			        "severity": 4,
			        "message": 5
		        }
	        }
        }
    ]
}

Con esto ¡ya lo tenemos listo!

Eliminado los errores del Lint

Ahora que ya lo tenemos integrado, vamos a solucionar los problemas del lint. El Lint nos dice que hay que añadir documentación al inicio, así como modificar el nombre del fichero. Lo solucionamos y volvemos a lanzar el lint:

Ahora ya no aparece ningún error!!

Configurando Lint para cualquier fichero

La tarea de lint que tenemos hasta ahora sólo actúa en el fichero get_rars.py. Para que actúe en el fichero que tenemos abierto en el editor modificamos el fichero task.json para utilizar la variable del sistema ${file}. Así es como queda el task.json definitivo:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Lint",
            "detail": "pylint",
            "type": "shell",
            "command": "${command:python.interpreterPath}",
            "args": ["-m", "pylint", "${file}"],
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "shared",
                "showReuseMessage": false,
                "clear": true, 
            },
            "problemMatcher": {
		        "owner": "python",
		        "fileLocation": [
			        "relative",
			        "${workspaceFolder}"
		        ],
		        "pattern": {
			        "regexp": "^(.*):(\\d+):(\\d+):\\s+(W|E|C).+:\\s+(.*)$",
			        "file": 1,
			        "line": 2,
			        "column": 3,
			        "severity": 4,
			        "message": 5
		        }
	        }
        }
    ]
}

Formateando el codigo (Black)

Una utilidad muy interesante en python es el paquete black, que formatea automáticamente el código para cumplir con los códigos de estilo. Para utilizarlo instalamos el paquete black como ya sabemos, y ejecutamos en la consola python -m black get_rars.py

En el archivo task.json añadimos una nueva configuración:

{
            "label": "Format",
            "detail": "black",
            "type": "shell",
            "command": "${command:python.interpreterPath}",
            "args": ["-m", "black", "${file}"],
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "shared",
                "showReuseMessage": false,
                "clear": true, 
            },
            "problemMatcher": []
        }

Ahora al ejecutar la opción Terminal/Run Tasks nos aparecen las dos tareas configuradas

Ejecutamos el formateador:

Nos indica que no se ha modificado nada porque el código ya está bien formateado

Depurar con launch.json

Ya sabemos depurar los programas, de manera fácil. Pero vamos a configurarlo manualmente porque tenemos más opciones para, y también porque nos servirá para depurar otros programas que no están en python

Pinchamos en la parte de depuración y luego en create a launch.json file

En el desplegable que aparece pinchamos en Python Debugger

Y ahora en Python File

Se nos crea el fichero .vscode/launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python Debugger: Current File",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        }
    ]
}

Ahora ya podemos depurar. Ponemos un Breakpoint y ejecutamos la tarea: "Python Debugger: Current File"

Y la depuración comienza!!!

Lo interesante es que ahora sólo hay que abrir el fichero python a depurar, poner un breakpoint y darle al F5. La depuración comienza muy rápido! También, al tenerlo en el launch.json, se pueden costumizar muchas opciones

Test unitarios

Para los tests unitarios en python existen dos frames principales: unittest y pytest. El primero es el estándar de python, y está integrado su biblioteca estándar. El segundo se instala a parte. Este segundo es el usado en apio/icm...

Yo de momento voy a usar unittest. Primero tengo que aprender a utilizarlo... voy a empezar a leer aquí:

https://docs.python.org/3/library/unittest.html

Comienzo primero con un test hola mundo para entender bien cómo funciona el modulo unittest. Este es el código: (test.py)

"""Simple test for the get_rars script"""

import unittest


class Test1(unittest.TestCase):
    """Initial tests"""

    # --- All the test methods defined should start with
    # --- the name "test"

    def test_msg1(self):
        """Testing..."""

        a = True
        self.assertTrue(a)


if __name__ == "__main__":
    unittest.main()

Lo bueno es que lo podemos ejecutar directamente. Este es el resultado:

Se ejecutan los test y como se pasan, el resultado muestra OK. Lo interesante de esto es que podemos poner un Breakpoint y seguir el flujo de los tests!!

Para que sea más verboso, hay que ejecutar el test con el flag -v. Lo he añadido en el fichero launch.json como la configuración Test:

{
    "name": "Test",
    "type": "debugpy",
    "request": "launch",
    "program": "${file}",
    "args": ["-v"],
    "console": "integratedTerminal"
}

Y lo ejecutamos (sin poner Breakpoints):

Ahora vemos qué método de qué clase se está ejecutando

He modificado el test para incluir las funciones setUp() y tearDown() que se ejecutan al comienzo y al final de cada uno de los test dentro de la clase

"""Simple test for the get_rars script"""

import unittest


class Test1(unittest.TestCase):
    """Initial tests"""

    # -- Se ejecuta antes de cualquier test
    def setUp(self):
        """Configurar el test"""
        print("-> Setup!!")

    # -- Se ejecuta al finalizar cada test
    def tearDown(self):
        """Limpiar el test"""
        print("->Fin!")

    # --- All the test methods defined should start with
    # --- the name "test"

    def test_msg1(self):
        """Testing1"""

        a = True
        self.assertTrue(a)

    def test_msg2(self):
        """Testing2"""

        a = 5
        b = 5
        self.assertEqual(a, b)


if __name__ == "__main__":
    unittest.main()

Al ejecutarlo esto es lo que se muestra:

Este es el primer test unitario para get_rars.py. Simplemente se comprueba que la salida de la función main() sea la correcta. En el test se redirije la salida estándar y se lee en una variable, para compararla con la que debe ser

"""Testing the get_rars modules"""

import unittest
from unittest.mock import patch
from io import StringIO


# -- Module to test
import get_rars as gr


class TestInitial(unittest.TestCase):
    """Initial tests"""

    # -- Redirect the standard output
    @patch("sys.stdout", new_callable=StringIO)
    def test_main_output(self, stdout):
        """Testing the output of the main function"""

        # -- Function to test
        gr.main()

        # -- Read its stdoutput
        output = stdout.getvalue()

        # -- Check the output
        self.assertEqual(output, "Hola...\n")


if __name__ == "__main__":
    unittest.main()

Cosas pendientes que todavía no se como hacer:

  • Tener el test en un directorio separado... tengo lio con los paths...
  • Utilizar el vscode para mostrar el resultado de los tests... no consigo que funcione

Martes, 25-Junio-2024

Test unitarios

Voy a leer esta documentación: https://code.visualstudio.com/docs/python/testing

El problema está en la detección de los tests. En la ventana output, si seleccionamos python vemos lo que está ocurriendo:

(En esta ventana me he llevao output a la parte derecha)

Note

Para volver a lanzar la detección de los test hay que abrir la paleta (Ctrl+shift+P) y ejecutar Test: Refresh Tests

Estos son los mensaje de error de interés:

2024-06-25 20:25:19.965 [info] > ./.venv/bin/python -m pytest -p vscode_pytest --collect-only
2024-06-25 20:25:19.965 [info] cwd: .
2024-06-25 20:25:19.984 [error] /home/obijuan/Develop/icerv/.venv/bin/python: No module named pytest
[...]

OK... aunque no esté usando el framework pytest... parece que la extensión lo usa para detectar los tests... Lo que no se es como añadir los tests manualmente... que también se podría hacer....

OK... no vo ninguna opción para hacerlo manualmente... hay que pasar siempre un patrón para detectar los tests... Así que voy a instalar el pytest y a repetir todo...

Ahora síiiii!!! Ya me detecta el test. Ha detectado un test. Si ahora nos vamos a la zona de test vemos lo siguiente:

En este caso sólo tenemos un único test: test_main_output, pero normalmente tendremos varios. Vemos que si tenemos abierto el fichero de los test en el editor nos aparece un botón de play verde en el test. Lo podemos ejecutar desde ahí. O tmbién le podemos dar al icono en la parte superior para ejecutar todos los test

Al ejecutar el test se nos marcan con un check verde, en diferentes lugares, que el test se ha ejecutado correctamente. También vemos el resultado en la consola. En este caso todo ha funcionado bien: el test se ha pasado

Clone this wiki locally