Skip to main content

Mejora la calidad del código con linting en Python

Escrito por

Keshav Malik

feature python linting

12 de octubre de 2022

0 minutos de lectura

Python es un lenguaje en crecimiento. A medida que evoluciona y se expande, también aumenta la cantidad de herramientas y estrategias de desarrollo disponibles para trabajar con él. Un proceso que se ha vuelto cada vez más popular es el linting, es decir, la revisión del código para detectar posibles problemas. El linting marca los errores en nuestro código para que podamos corregir prácticas de programación inusuales que podrían causar problemas.

El linting se realiza mientras se escribe el código fuente y antes de compilarlo. En otras palabras, es una comprobación previa a la compilación, también llamada «análisis estático de código». Revisar el código con linting de forma periódica garantiza la coherencia en todo el código y la base de código. Esto reduce la posibilidad de que pequeños errores se conviertan en problemas complejos cuando se ejecute el código.

Muchos desarrolladores no usan linting porque no ven el valor que aporta, ya que no evita los errores de software. Pero esta perspectiva subestima el valor del linting para mejorar la calidad del código.

En este artículo práctico, veremos lo rápido y fácil que es hacer comprobaciones rápidas de linting en Python con Pylint, una de las herramientas de linting más populares. También veremos cómo el linting del código puede ayudarnos a cumplir con la guía de estilo de código PEP8.

Requisitos previos

Antes de empezar, asegúrate de tener lo siguiente:

  • Python y pip instalados en tu equipo

  • Conocimientos básicos de interfaces de línea de comandos (CLI)

  • Conocimientos de conceptos de Python, como funciones y clases

Ten en cuenta que, aunque los comandos que se muestran aquí son compatibles con sistemas basados en Linux y macOS, debes tener cuidado al trabajar con Windows.

Linting de código Python

Antes de ver cómo usar un linter en Python, preparemos el entorno creando un directorio y un entorno virtual.

Configuración del entorno

Primero, crea un directorio para el proyecto. Para este tutorial, lo llamaremos pylint-demo.

$ mkdir pylint-demo

$ cd pylint-demo

A continuación, crea un entorno virtual. Esto aislará las dependencias del proyecto y evitará conflictos con otros proyectos.

$ pip install pipenv

$ pipenv shell

El indicador debería verse así: (pylint-demo) $. Esto indica que el entorno virtual está activo.

Con el entorno virtual activo, instala el linter con el siguiente comando:

$ pipenv install pylint

Ahora podemos ejecutar el linter con el comando pylint. Para comprobar que Pylint se instaló correctamente, ejecuta el siguiente comando:

$ pylint –help

Primeros pasos con el linting

Escribamos un programa básico en Python y usemos Pylint para ver cómo funciona. Crea un archivo main.py y copia el siguiente código:

def is_number_even(num):
    return "Even" if num % 2 == 0 else "Odd"

num = 5
print(f"The number {num} is {is_number_even(num)}")

En el código anterior, agregamos una función para comprobar si el número es par o impar. Para usar Pylint y detectar errores en este código, ejecutamos el siguiente comando:

$ pylint <<file_name>>
$ pylint main.py

El resultado de Pylint es el siguiente:

************* Module main
main.py:12:0: C0304: Final newline missing (missing-final-newline)
main.py:1:0: C0114: Missing module docstring (missing-module-docstring)
main.py:8:0: C0116: Missing function or method docstring (missing-function-docstring)
main.py:8:19: W0621: Redefining name 'num' from outer scope (line 11) (redefined-outer-name)
main.py:11:0: C0103: Constant name "num" doesn't conform to UPPER_CASE naming style (invalid-name)

------------------------------------------------------------------
Your code has been rated at 0.00/10 (previous run: 0.00/10, +0.00)

Podemos ver varios problemas fáciles de entender en el código, cada uno identificado con un código, como C0304. Pylint asigna un código de letra a todos los errores para distinguir su gravedad y naturaleza. Hay cinco categorías de errores:

  • C: Convención (para cualquier incumplimiento de las convenciones del código)

  • R: Refactorización (para cualquier problema relacionado con code smells y refactorización)

  • W: Advertencia (para cualquier problema de programación que no sea un error)

  • E: Error (para cualquier problema de programación que sí sea un error)

  • F: Fatal (para cualquier problema grave que haya detenido la ejecución de Pylint)

Pylint también asigna a nuestro código una puntuación sobre 10, según la cantidad de errores presentes.

En nuestro ejemplo, todos los códigos de error menos uno corresponden a errores de convención; el único restante es una advertencia. Para corregir estos problemas, hagamos algunos cambios en el código y volvamos a ejecutar Pylint para ver qué puntuación obtenemos.

""" File contains various function to under Pylint """

def is_number_even(num):
    """Function to check if number is even or odd"""
    return "Even" if num % 2 == 0 else "Odd"

NUM = 5
print(f"The number {NUM} is {is_number_even(NUM)}")

Con este código, agregamos una cadena de documentación para el módulo y otra para la función, una nueva línea al final y cambiamos el nombre de la variable del código anterior. Al volver a ejecutar Pylint, obtenemos una puntuación de 10/10 sin ningún problema.

Ejecutar Pylint en un solo archivo

Ahora que conocemos mejor el funcionamiento de Pylint, veamos otro ejemplo. Ingresa el siguiente código:

""" File contains various function to under Pylint """

class animal:
  def __init__(self, name):
    self.name = name

obj1 = animal("Horse", 21)
print(obj1.name)

En este fragmento, tenemos una clase sencilla llamada animal y un objeto de esa clase llamado obj1. Ahora usemos Pylint con este código.

************* Module main
main.py:4:0: W0311: Bad indentation. Found 2 spaces, expected 4 (bad-indentation)
main.py:5:0: W0311: Bad indentation. Found 4 spaces, expected 8 (bad-indentation)
main.py:3:0: C0115: Missing class docstring (missing-class-docstring)
main.py:3:0: C0103: Class name "animal" doesn't conform to PascalCase naming style (invalid-name)
main.py:3:0: R0903: Too few public methods (0/2) (too-few-public-methods)
main.py:7:7: E1121: Too many positional arguments for constructor call (too-many-function-args)

Observa que esta vez no tenemos problemas de calidad del código, pero los reemplazaron errores más importantes. Ahora que se identificaron los problemas, intentemos corregirlos con el siguiente código:

""" File contains various function to under Pylint """

class Animal:
    "Animal Class"
    def __init__(self, name):
        self.name = name

obj1 = Animal("John")
print(obj1.name)

Luego, vuelve a ejecutar Pylint. Después de cambiar el nombre de la clase de animal a Animal, agregar una cadena de documentación a la clase, eliminar los argumentos innecesarios de las llamadas a funciones y agregar la indentación adecuada, casi eliminamos todos los errores del código. Sin embargo, todavía queda uno:

************* Module main
main.py:3:0: R0903: Too few public methods (0/2) (too-few-public-methods)

Veamos cómo corregir este último error. Pylint indica que no tenemos dos o más métodos públicos, pero es muy probable que nuestro código no tenga dos o más métodos públicos. Entonces, ¿cómo lo corregimos?

En un caso como este, podemos usar comentarios de Python para suprimir estos problemas. La sintaxis para suprimirlos es la siguiente:

# pylint: disable=<<issue_name>>

Así se verá el código:

""" File contains various function to under Pylint """

# pylint: disable=too-few-public-methods
class Animal:
    "Animal Class"
    def __init__(self, name):
        self.name = name

obj1 = Animal("John")
print(obj1.name)

Si ahora revisamos el resultado de Pylint, veremos que el problema desapareció.

Ejecutar Pylint en un directorio

Ya vimos cómo ejecutar Pylint en un solo archivo, pero al trabajar en un proyecto no tendremos un único archivo que revisar. Tendremos que ejecutar linting en el directorio.

Para usar Pylint en todo el directorio, ejecuta el siguiente comando:

$ pylint <<name_of_directory>>

Para ver cómo funciona el linting de un directorio, creemos dos archivos más y agreguemos algo de código.

$ mkdir src; cd src
$ touch helpers.py config.py __init__.py

Mueve el archivo main.py al directorio src y pega el siguiente código en los archivos correspondientes:

<<main.py>>
""" File contains various function to under Pylint """

from helpers import connect_db
from config import DB_USER, DB_PASS

is_connected = connect_db(DB_USER, DB_PASS)

if is_connected:
    print("Connected to DB")
else:
    print("Failed to connect to DB")

<<helpers.py>>
def connect_db(user, password):
    """Dummy function to connect to DB"""
    if user is None or password is None:
        return False
    return True

<<config.py>>
DB_USER = "root"
DB_PASS = "toor"

Tenemos tres archivos en el directorio src: main.py, helpers.py y config.py. En main.py, tenemos una función de ejemplo que indica si estamos conectados a la base de datos. helpers.py contiene una función auxiliar de ejemplo para conectarse a la base de datos, y el archivo config.py contiene el nombre de usuario y la contraseña de la base de datos.

Ahora ejecutemos Pylint en todo el directorio con el siguiente comando desde el directorio raíz:

$ pylint src

El resultado del comando será el siguiente:

************* Module src.config
src/config.py:2:0: C0304: Final newline missing (missing-final-newline)
src/config.py:1:0: C0114: Missing module docstring (missing-module-docstring)
************* Module src.main
src/main.py:11:0: C0304: Final newline missing (missing-final-newline)
src/main.py:3:0: E0401: Unable to import 'helpers' (import-error)
src/main.py:4:0: E0401: Unable to import 'config' (import-error)
************* Module src.helpers
src/helpers.py:6:0: C0304: Final newline missing (missing-final-newline)
src/helpers.py:1:0: C0114: Missing module docstring (missing-module-docstring)

Como podemos ver, Pylint muestra los resultados de distintos archivos separados por *** y el nombre del módulo. Para corregir los problemas, debemos hacer los siguientes cambios:

  • Agrega una nueva línea al final de cada archivo.

  • Agrega una cadena de documentación a cada archivo y función.

  • Modifica la instrucción import de helpers import connect_db a .helpers import connect_db.

Una vez que corrijamos estos problemas, veremos otro: debemos escribir en mayúscula la variable is_connected. Podemos cambiar el nombre de la variable o suprimir la advertencia para resolver este error.

Cómo suprimir advertencias

Es muy probable que necesites personalizar o suprimir varias advertencias al ejecutar linting en tu código Python. No tendría sentido agregar un comentario cada vez. En lugar de gestionar cada caso de supresión de advertencias por separado, puedes crear un archivo .rc para personalizar el comportamiento de Pylint y suprimir advertencias para todo el proyecto directamente desde el archivo .rc.

Puedes crear uno con el siguiente comando:

$ pylint --generate-rcfile > pylint.rc

Mejor código y más seguro con linting

El linting en Python revisa el código fuente mientras se escribe y marca los errores a medida que aparecen, antes de ejecutar el código. También puedes integrar Pylint en los editores para ver el linting en tiempo real.

Aunque el linting no corrige los errores de software automáticamente, usarlo de forma constante ayuda a mantener la calidad del código. Por eso, aunque algunos desarrolladores lo consideren una pérdida de tiempo, es muy eficaz para detectar problemas pequeños antes de que se conviertan en otros más grandes.

A lo largo de este artículo, vimos cómo el linting y la aplicación de las recomendaciones de Pylint mejoraron nuestro código de ejemplo. Además, este proceso nos ayuda a cumplir con la guía de estilo PEP8. Ahora que puedes implementar linting en tus proyectos, explora las numerosas herramientas disponibles y determina cuál complementa y mejora mejor tu forma de desarrollar en Python.

Comienza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.

Leer más

feature insights context
Blog

Los ataques autónomos ya están aquí. La defensa debe estar a su altura.

Los atacantes autónomos están reduciendo el tiempo disponible para defenderse. Descubre cómo el descubrimiento, la corrección, la validación y la prevención continuos pueden ayudar a los equipos de seguridad a seguirles el ritmo.

illustration hero ai
Blog

El huracán de la IA ha llegado

La IA está acelerando por igual la creación de software y los ciberataques. Los líderes deben proteger los agentes y el código desde el inicio, aplicar controles en tiempo de ejecución y validar las defensas de forma independiente.

feature insights context
Blog

¿La prevención es, en esencia, un problema ya resuelto?

La prevención en el código generado por agentes está resuelta desde el punto de vista arquitectónico, pero elegir controles que protejan la seguridad sin ralentizar el desarrollo sigue siendo el desafío.