Skip to main content

In this article

10 dimensiones del análisis estático de Python

Escrito por
feature python linting

17 de julio de 2024

0 minutos de lectura

El análisis estático de Python, también conocido como «linting», es un aspecto fundamental del desarrollo de software. Consiste en inspeccionar el código Python sin ejecutarlo para identificar posibles errores, fallas de programación, problemas de estilo o patrones que no cumplen con los estándares de codificación predefinidos. También ayuda a detectar vulnerabilidades en las primeras etapas del desarrollo y reduce las posibilidades de implementar código inseguro en producción. Por ejemplo, puede detectar prácticas de codificación inseguras, como el uso de credenciales codificadas, errores comunes que pueden provocar ataques de inyección SQL o posibles problemas con la sanitización de datos.

Python ofrece varias bibliotecas y herramientas para el análisis estático, como Pylint, Pyflakes y Flake8. Por ejemplo, Pylint puede ayudar a aplicar estándares de codificación, detectar errores y examinar la complejidad del código. Este es un fragmento de código sencillo que muestra cómo usar Pylint:

# test.py

def add(x, y):
    return x + y

add(1, '2')

Al ejecutarlo con Pylint, obtendríamos:

$ pylint test.py
************* Module test
test.py:5:4: E1136: Value '2' is unsubscriptable (unsubscriptable-object)

--------------------------------------------------
Your code has been rated at -10.00/10

Pylint detectó un error cuando intentamos sumar un entero y una cadena, una operación no válida en Python.

1. Verificación de tipos en el análisis estático de Python

Python es un lenguaje versátil y dinámico, y su flexibilidad es una de las razones por las que es el favorito de muchos desarrolladores. Sin embargo, este dinamismo puede provocar errores difíciles de encontrar, especialmente los relacionados con los tipos. Aquí es donde entra en juego el análisis estático de Python.

Antes de profundizar en la verificación de tipos, veamos qué significa que Python tenga tipado dinámico. En Python, el tipo de una variable se verifica durante la ejecución, por lo que no es necesario especificarlo de forma explícita. Esto se muestra en el siguiente fragmento de código:

# x is an integer
x = 5
# x is now a string
x = "Hello, World!"

Aunque el tipado dinámico puede hacer que Python sea más fácil de usar y leer, también puede provocar errores de tipos que solo se descubren cuando se ejecuta el código.

La función de la verificación estática de tipos

La verificación estática de tipos permite detectar errores de tipos en las primeras etapas del desarrollo, es decir, durante la programación o la compilación, en lugar de detectarlos en tiempo de ejecución. Esto puede evitar que posibles errores lleguen al código de producción y, así, mejorar la seguridad y la confiabilidad del código. Java y C++ son ejemplos de lenguajes de programación con tipado estático.

Aunque Python es un lenguaje con tipado dinámico, también admite el tipado estático opcional mediante anotaciones de tipos. Esta función permite anotar los tipos esperados de variables, parámetros de funciones y valores de retorno. El entorno de ejecución de Python no aplica estos tipos, pero las herramientas de análisis estático pueden usarlos para detectar posibles errores de tipos.

def greet(name: str) -> str:
    return 'Hello, ' + name

En este ejemplo, se espera que name sea una cadena y que la función también devuelva una cadena.

Herramientas para la verificación estática de tipos en Python

Hay varias herramientas que pueden ayudar con la verificación estática de tipos en Python. Entre ellas, Mypy, Pyright y Pyre se destacan por su solidez y amplia aceptación en la comunidad de Python.

  • Mypy: Verificador estático de tipos opcional para Python que busca combinar las ventajas del tipado dinámico y el estático. Es versátil y se puede usar en un pipeline de CI/CD para detectar errores de tipos antes de que lleguen a producción.

  • Pyright: Desarrollado por Microsoft, Pyright es un verificador estático de tipos que se centra en el rendimiento. Está escrito en TypeScript y se ejecuta en Node.js.

  • Pyre: Pyre es un verificador de tipos de alto rendimiento para Python, desarrollado por Facebook. Está diseñado para ayudar a mejorar la calidad del código y acelerar el desarrollo, ya que señala errores de tipos de forma interactiva en la terminal o en tu editor favorito.

Ejemplo de código: cómo usar Mypy para verificar tipos

Instalar Mypy es tan sencillo como ejecutar pip install mypy. Una vez instalado, puedes usarlo para verificar si tus archivos Python tienen errores de tipos. Este es un ejemplo sencillo:

# file: hello.py
def greet(name: str) -> str:
    return 'Hello, ' + name

# This will cause a type error
greet(123)

Puedes verificar este archivo con Mypy de la siguiente manera:

$ mypy hello.py
hello.py:5: error: Argument 1 to "greet" has incompatible type "int"; expected "str"
Found 1 error in 1 file (checked 1 source file)

Como puedes ver, Mypy detectó el error de tipos antes de que se ejecutara el código.

2. Linters en el análisis estático de Python

Los linters son herramientas de análisis estático de código que se usan para señalar errores de programación, bugs, errores de estilo y construcciones sospechosas. En el ecosistema de Python, cumplen una función fundamental para mantener la calidad y la seguridad del código. Ayudan a aplicar estándares de codificación, detectar posibles bugs y vulnerabilidades de seguridad en las primeras etapas del desarrollo, y mejorar la legibilidad y facilidad de mantenimiento del código Python.

En el ecosistema de Python, algunos de los linters más utilizados son Pylint, Flake8 y Pyflakes.

  • Pylint: Herramienta de código abierto muy configurable que busca errores en el código Python, aplica estándares de codificación y detecta code smells. Pylint también permite escribir complementos personalizados para casos de uso específicos.

  • Flake8: Biblioteca de Python que combina PyFlakes, pycodestyle y el script McCabe de Ned Batchelder. Es un excelente conjunto de herramientas que verifica que tu proyecto cumpla con PEP 8, la guía de estilo de Python.

  • Pyflakes: Pyflakes verifica rápidamente si hay errores en los archivos fuente de Python, sin ejecutarlos. Es más rápido que Pylint y Flake8 porque no aplica guías de estilo, por lo que es una buena opción para una revisión básica y rápida de tu código Python.

El concepto de code smells y la aplicación de estándares de codificación

Los code smells son indicadores de problemas más profundos en el código. Son patrones que sugieren que se están incumpliendo principios fundamentales de diseño y que afectan negativamente la calidad del diseño. Algunos ejemplos comunes de code smells en Python son las clases grandes, los métodos largos, el código duplicado y el código muerto.

Linters como Pylint, Flake8 y Pyflakes ayudan a detectar estos code smells y a aplicar estándares de codificación en tu código Python para mejorar la calidad del código y mantenerla en un nivel aceptable. Lo hacen comparando tu código con reglas y pautas predefinidas, que suelen basarse en prácticas recomendadas adoptadas por la comunidad, como PEP 8 para Python.

Ejemplo de código que muestra un linter en acción

Este es un ejemplo de cómo usar Pylint para hacer un análisis estático de un archivo Python llamado example.py:

# example.py
def add(a, b):
    return a+b

Para ejecutar Pylint en este archivo, usarías el siguiente comando:

pylint example.py

Si hay algún problema en tu código, Pylint lo señalará y proporcionará un informe detallado. Por ejemplo, podría indicar que a la función add le falta una cadena de documentación, lo que incumple las pautas de PEP 8 para escribir código Python de calidad.

Reglas personalizadas en linters

Linters como Pylint te permiten definir reglas personalizadas para tus proyectos de Python. Esto es especialmente útil cuando tienes estándares o pautas de codificación específicos para tu proyecto u organización.

Para crear una regla personalizada en Pylint, debes crear una clase de Python que herede de una de las clases de verificación de Pylint y definir métodos que realicen la verificación del código. Una vez definidas las reglas personalizadas, puedes agregarlas al verificador de Pylint mediante el método register_checker.

Este es un ejemplo sencillo de una regla personalizada de Pylint que verifica si se usa la instrucción print :

from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker

class NoPrintStatementsChecker(BaseChecker):
    __implements__ = IAstroidChecker
    name = 'no-print-statements'
    msgs = {
        'W0001': (
            'Print statement used',
            'no-print-statements',
            'Print statements should not be used',
        ),
    }

    def visit_print(self, node):
        self.add_message('no-print-statements', node=node)

def register(linter):
    linter.register_checker(NoPrintStatementsChecker(linter))

En este ejemplo, la clase NoPrintStatementsChecker verifica si se usa la instrucción print y genera una advertencia si la encuentra. Luego, se usa la función register para registrar este verificador personalizado en Pylint.

3. Detección de vulnerabilidades de seguridad

Una de las dimensiones fundamentales del análisis estático de Python es la detección de vulnerabilidades de seguridad. Veamos por qué la seguridad es importante en el análisis estático de Python, cuáles son los riesgos de seguridad más comunes en el código Python y qué herramientas puedes usar para detectar vulnerabilidades, como Bandit y Snyk Code.

Al escribir código Python, la seguridad no debe ser algo que se considere al final, sino una parte integral del proceso de desarrollo. El análisis estático de Python cumple una función fundamental para garantizar la seguridad del código, ya que puede ayudar a identificar posibles vulnerabilidades de seguridad en una etapa temprana del ciclo de desarrollo, incluso antes de ejecutar el código. Entre las vulnerabilidades comunes en el código Python se encuentran los ataques de inyección, las referencias directas inseguras a objetos, las configuraciones de seguridad incorrectas y el cross-site scripting.

Por ejemplo, una vulnerabilidad de inyección SQL en código Python podría verse así:

def get_user_details(user_id):
    return database.execute(f'SELECT * FROM users WHERE id = {user_id}')

En el ejemplo anterior, user_id se usa directamente en una consulta SQL, lo que abre la posibilidad de una inyección SQL si user_id no se sanitiza correctamente y lo proporciona el usuario.

Herramientas para detectar vulnerabilidades de seguridad

Bandit

Bandit es una herramienta diseñada para encontrar problemas de seguridad comunes en el código Python. Procesa cada archivo, crea un AST (árbol de sintaxis abstracta) a partir del código Python y ejecuta los complementos adecuados en los nodos del AST.

Así puedes usar Bandit para analizar tu código Python en busca de vulnerabilidades de seguridad:

$ bandit -r path/to/your/python/code

¿Bandit detectaría una referencia directa insegura a un objeto en Python? ¿No estás seguro? Sigue leyendo para conocer Snyk.

Snyk Code

Snyk Code es otra herramienta que ofrece un motor de análisis rápido, simbólico e impulsado por IA. Con Snyk Code, puedes encontrar rápidamente vulnerabilidades de seguridad en tu código Python. El motor de análisis estático de Snyk Code está diseñado para comprender el contexto y el flujo de tu código, lo que le permite detectar vulnerabilidades de seguridad complejas que otras herramientas podrían pasar por alto.

Snyk Code también incluye un complemento para el IDE PyCharm, que permite a los desarrolladores detectar y corregir problemas de seguridad en tiempo real mientras programan.

Para comenzar a usar Snyk Code, solo tienes que descargar el complemento de Snyk para el IDE PyCharm y podrás usarlo gratis.

Incorporar la detección de vulnerabilidades de seguridad en el análisis estático de Python es fundamental para garantizar la seguridad de tu software. Herramientas como Bandit y Snyk Code ofrecen a los desarrolladores una forma sencilla y eficiente de detectar y corregir vulnerabilidades de seguridad en su código Python.

4. Análisis de la complejidad del código

La complejidad del código es una métrica que se usa para medir qué tan difícil es entenderlo o modificarlo. Una mayor complejidad suele traducirse en más dificultades para comprender el código, lo que afecta su facilidad de mantenimiento.

Mantener el código es una tarea habitual para los desarrolladores, pero cuando la base de código se vuelve compleja, entenderla y modificarla resulta cada vez más difícil. Esto puede provocar bugs y reducir la calidad general del software. Por eso, el análisis de la complejidad del código es fundamental para garantizar un código de calidad y fácil de mantener.

Medición de la complejidad ciclomática con Radon

Para medir la complejidad del código Python, se pueden usar varias herramientas, entre ellas Radon. Radon es una herramienta de Python que calcula distintas métricas a partir de la información extraída del árbol de sintaxis abstracta (AST). Una de estas métricas es la complejidad ciclomática.

La complejidad ciclomática es una métrica de software que indica la complejidad de un programa. Mide cuantitativamente la cantidad de rutas linealmente independientes en el código fuente de un programa. Radon analiza tu código Python y le asigna una calificación de A a F: A significa que el código es sencillo y fácil de entender, mientras que F indica que es demasiado complejo.

Para usar Radon, primero debes instalarlo. Puedes hacerlo con pip:

pip install radon

Ejemplo de código que muestra el análisis de complejidad

Después de instalar Radon, puedes usarlo para analizar tu código Python. Consideremos el siguiente código:

def calculate_sum(n):
    if n < 0:
        return "Negative number"
    else:
        return sum(range(n+1))

Para medir la complejidad del código anterior, ejecutarías el siguiente comando:

radon cc your_file.py

Este comando devolverá la complejidad ciclomática del código. La complejidad del código anterior es 2, lo que significa que no es muy complejo.

Pero si analizamos una función más compleja, por ejemplo:

def complex_function(a, b, c):
    if a > b:
        if a > c:
            return a
        else:
            return c
    else:
        if b > c:
            return b
        else:
            return c

Al ejecutar Radon en este código, obtendríamos una complejidad de 5, lo que indica un mayor nivel de complejidad.

Usar herramientas de complejidad ciclomática ayuda a mantener la calidad del código y facilita su comprensión y modificación. Herramientas como Radon ofrecen una forma sencilla de medir la complejidad del código en Python, lo que permite mantenerlo mejor y reducir la probabilidad de errores.

5. Análisis de dependencias

En el mundo del software de código abierto, un pilar del desarrollo de software, el análisis de dependencias es un aspecto importante que los desarrolladores no pueden pasar por alto. Este procedimiento es fundamental al trabajar con bases de código grandes en lenguajes como Python. El análisis de dependencias consiste en identificar las dependencias entre los distintos elementos de un proyecto de software. Exploremos la importancia de entender y administrar las dependencias, las herramientas para analizarlas, como Snyk y Snyk Advisor, y un ejemplo práctico que muestra el análisis de dependencias en acción.

Importancia de entender y administrar las dependencias

En los proyectos de Python, las dependencias son los paquetes o bibliotecas externos que usa tu proyecto. Pueden ser desde frameworks web como Django o Flask hasta bibliotecas de ciencia de datos como NumPy o pandas.

Entender y administrar las dependencias es fundamental por varios motivos:

  • Ayuda a mantener la estabilidad y confiabilidad de tu software. Si una dependencia de la que depende tu proyecto tiene un error o deja de funcionar, tu software también podría dejar de funcionar.

  • Ayuda a mejorar la seguridad de tu proyecto. Las dependencias pueden tener vulnerabilidades de seguridad que podrían afectar tu proyecto si no se corrigen.

  • Ayuda a cumplir con los requisitos de licencias. Cada dependencia puede tener su propia licencia, y debes asegurarte de cumplir con todas.

Snyk Advisor para el análisis de dependencias

Snyk Advisor es una herramienta que ofrece información sobre las dependencias de tu proyecto de Python, incluidos datos sobre vulnerabilidades de seguridad, problemas de licencias y paquetes desactualizados. Te ayuda a entender los riesgos asociados con tus dependencias y ofrece recomendaciones prácticas para mitigarlos.

La puntuación del estado del paquete requests de Python de PyPI que se muestra en Snyk Advisor


Puedes usar Snyk CLI para analizar las dependencias de tu proyecto de Python y recibir alertas sobre paquetes de código abierto vulnerables instalados desde PyPI o código inseguro en tu programa de Python. Después de instalar Snyk CLI, puedes ejecutar el siguiente comando para analizar tu proyecto:

snyk test --all-projects

Este comando analizará las dependencias de tu proyecto y generará un informe sobre posibles vulnerabilidades de seguridad, problemas de licencias o paquetes desactualizados. Por ejemplo, podría alertarte sobre vulnerabilidades al escanear un proyecto de FastAPI en Python:

Testing FastAPI...
✗ High severity vulnerability found in requests
  Description: Out-of-bounds Read
  Info: https://snyk.io/vuln/SNYK-PYTHON-REQUESTS-174006
  Introduced through: requests@2.25.1
  From: requests@2.25.1 > urllib3@1.26.3 > requests
  Fix: https://snyk.io/vuln/SNYK-PYTHON-REQUESTS-174006

Organization:      snyk
Package manager:   pip
Target file:       requirements.txt

Empieza a usar Snyk para la seguridad de Python para proteger desde tu primera línea de código hasta tu última dependencia de pip y tus aplicaciones de contenedores de Python implementadas.

Analiza tu código Python en tiempo real y recibe sugerencias de correcciones en línea con tecnología de IA directamente en tus IDE favoritos, incluidos Visual Studio Code y PyCharm.

Uso de pipenv graph para analizar dependencias

Veamos una aplicación sencilla de Flask en Python para mostrar el análisis de dependencias en acción. Este es el archivo app.py:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

Para este ejemplo, usaremos Pipenv. Primero, inicializa Pipenv e instala Flask:

pipenv install flask

Ahora, al ejecutar pipenv graph, verás:

flask==1.1.2
  - click [required: >=5.1, installed: 7.1.2]
  - itsdangerous [required: >=0.24, installed: 1.1.0]
  - Jinja2 [required: >=2.10.1, installed: 2.11.2]
    - MarkupSafe [required: >=0.23, installed: 1.1.1]
  - Werkzeug [required: >=0.15, installed: 1.0.1]

Este resultado muestra que Flask, una dependencia directa de nuestro proyecto, depende de click, itsdangerous, Jinja2 y Werkzeug. A su vez, Jinja2 depende de MarkupSafe. Así, analizamos correctamente las dependencias de nuestro proyecto.

Si recién empiezas a usar pipenv y quieres aprender más, te recomiendo leer domina los entornos virtuales de Python: guía completa de venv, Docker y cómo proteger tu código para familiarizarte con el concepto de los entornos virtuales en Python.

En conclusión, el análisis periódico de dependencias en los proyectos de Python es necesario para mantener la confiabilidad del código, reforzar la seguridad y garantizar el cumplimiento de las licencias. Herramientas como Snyk, Snyk Advisor y Pipenv facilitan esta tarea y ofrecen una visión clara del árbol de dependencias.

6. Herramientas de formato de código

Seguir un estilo y formato de código coherentes es otro aspecto importante para mantener su legibilidad y eficiencia. Las herramientas de formato de código cumplen una función clave en este sentido. Ayudan a los desarrolladores a dar formato automático a su base de código según un conjunto de reglas predefinidas. Esto no solo mejora la calidad general del código, sino que también reduce el tiempo y el esfuerzo necesarios para las revisiones manuales. Conozcamos formateadores populares, como Black y YAPF.

Veamos un ejemplo para entender la diferencia que puede hacer un formateador de código. A continuación, se muestra un fragmento de código de Python antes y después de aplicar el formateador Black.

Antes de dar formato:

def function(arg1,arg2,arg3=4,arg4=8,arg5=16):
    return arg1+arg2+arg3+arg4+arg5
print(function(1,2))

Después de dar formato con Black:

def function(arg1, arg2, arg3=4, arg4=8, arg5=16):
    return arg1 + arg2 + arg3 + arg4 + arg5

print(function(1, 2))

Formateador de código YAPF para Python

YAPF (Yet Another Python Formatter) es otro formateador de código para Python que va más allá de PEP8 para ofrecer un estilo más uniforme. A diferencia de PEP8, YAPF tiene en cuenta la apariencia del código y lo hace más legible.

A continuación, te mostramos cómo usar YAPF:

# install yapf
pip install yapf

# use yapf to format a python file
yapf -i your_python_file.py

7. Árboles de sintaxis abstracta (AST) en Python

¿Qué es un árbol de sintaxis abstracta (AST)? Es una representación en forma de árbol de la estructura sintáctica del código fuente escrito en un lenguaje de programación. Cada nodo del árbol representa una construcción del código fuente. La sintaxis es «abstracta» porque no representa todos los detalles de la sintaxis real.

En Python, cuando el intérprete ejecuta código, primero lo convierte a una forma intermedia conocida como bytecode. Este proceso de conversión implica la creación de un AST. En Python, un AST es una representación simplificada de la estructura del código. Omite detalles como el formato y los espacios en blanco, y se centra únicamente en los comandos y su orden para ofrecer una vista estructural del código.

El módulo ast de Python y sus capacidades

La biblioteca estándar de Python incluye el módulo ast, que permite crear, manipular e inspeccionar AST. Este módulo ofrece un conjunto de clases, funciones y métodos auxiliares para trabajar con AST de Python mediante programación.

La función ast.parse() es un punto de entrada común para crear AST. Analiza el código fuente de Python y lo convierte en un nodo AST, que luego se puede manipular o inspeccionar.

El módulo ast también ofrece la clase NodeVisitor para recorrer el AST y llamar a una función visitante por cada nodo encontrado. Puedes usar esta clase tal cual o crear una subclase para sobrescribir métodos y agregar funcionalidades para tipos de nodo específicos.

Inspección y manipulación de AST: un ejemplo práctico

Veamos un ejemplo sencillo de cómo usar el módulo ast para inspeccionar y manipular un AST en Python.

import ast

# Parse some code into an AST
root = ast.parse("print('Hello, World!')")

# Print all the nodes in the AST
for node in ast.walk(root):
    print(type(node))

# Modify the AST to print a different message
for node in ast.walk(root):
    if isinstance(node, ast.Str):  # Change the string literal in the Print node
        node.s = 'Hello, AST!'

# Convert the modified AST back into code
code = compile(root, filename="<ast>", mode="exec")

# Execute the modified code
exec(code)

En este ejemplo, primero analizamos una instrucción sencilla de impresión de Python y la convertimos en un AST con ast.parse(). Luego, inspeccionamos el AST recorriendo todos los nodos e imprimiendo sus tipos. Después, modificamos el AST cambiando el literal de cadena en el nodo Print. Por último, convertimos el AST modificado nuevamente en código con compile() y lo ejecutamos con exec(). El resultado de este código será «Hello, AST!», no «Hello, World!».

Este ejemplo muestra las capacidades del módulo ast para inspeccionar y modificar código en Python. Los posibles usos de los AST en la seguridad del código y el desarrollo de software son muchos: desde la refactorización automática del código hasta el análisis estático avanzado para detectar vulnerabilidades de seguridad.

8. Análisis del flujo de control y de datos

El análisis del flujo de control y de datos es fundamental. Esta técnica facilita la detección eficaz de errores, mejora la legibilidad del código y aumenta la calidad general de las aplicaciones de Python.

El análisis del flujo de control evalúa el orden de ejecución de las instrucciones de un programa, mientras que el análisis del flujo de datos se centra en rastrear y analizar cómo cambian los valores de los datos a lo largo del programa. Estas son algunas razones por las que ambos análisis son importantes:

  1. Detección de posibles errores: el análisis del flujo de control y de datos puede ayudar a identificar posibles errores y vulnerabilidades en el código, como bucles infinitos, código inalcanzable o variables sin usar.

  2. Optimización del código: al entender cómo se mueven los datos por el código y cómo fluye el control, puedes identificar áreas poco eficientes y hacer las mejoras necesarias.

  3. Mayor legibilidad del código: el análisis del flujo de control y de datos puede hacer que el código sea más fácil de entender al destacar las dependencias y las interacciones entre sus distintas partes.

  4. Garantía de seguridad: entender el flujo de datos y de control de tu aplicación es fundamental para garantizar su seguridad. Puede ayudar a identificar posibles vulnerabilidades, como filtraciones de datos o ataques de inyección.

Ejemplo de código que muestra el análisis del flujo de control y de datos

Para ilustrar el concepto de análisis del flujo de control y de datos en Python, veamos una función sencilla:

def calculate_total(price, quantity):
    if quantity <= 0:
        return "Invalid quantity"
    else:
        total = price * quantity
        return total

En esta función, el flujo de control está determinado por la instrucción if-else. Si quantity es menor o igual que 0, la función devuelve un mensaje de error. De lo contrario, calcula y devuelve el precio total.

Por otro lado, el flujo de datos se puede rastrear desde los parámetros de entrada (price y quantity) hasta el resultado final (total). Los valores de estas variables cambian a medida que pasan por la función.

Una herramienta de análisis estático analizaría el flujo de control y de datos de esta función para detectar posibles problemas. Por ejemplo, podría señalar un problema si se usaran price o quantity antes de definirse, o si hubiera una ruta posible por la función en la que total no estuviera definido antes de devolverse.

Mediante el análisis del flujo de control y de datos, puedes desarrollar aplicaciones de Python eficientes, comprensibles y seguras. Al integrar este proceso en el ciclo de vida del desarrollo de software, puedes mantener la calidad del código y evitar que posibles vulnerabilidades de seguridad lleguen a tu aplicación.

9. Integración con entornos de desarrollo de Python

Las herramientas de análisis estático de Python no están pensadas para usarse de forma independiente. Son más eficaces cuando se integran sin problemas en el entorno de desarrollo, donde pueden mejorar los flujos de trabajo de desarrollo, las canalizaciones de integración continua (CI) y los editores de código.

A continuación, se describen las funciones de las herramientas de análisis estático de Python en estas áreas y se ofrece un ejemplo práctico de integración:

  • Función en los flujos de trabajo de desarrollo: Las herramientas de análisis estático cumplen una función clave en los flujos de trabajo de desarrollo. Los desarrolladores de Python pueden usarlas para identificar y corregir posibles problemas durante la fase de desarrollo, incluso antes de ejecutar el código. Esta detección temprana puede ahorrar mucho tiempo de depuración y mejorar la calidad del código.

  • Función en las canalizaciones de CI: En las canalizaciones de integración continua, se pueden usar herramientas de análisis estático para analizar automáticamente el código de cada commit o pull request. Así se garantiza que los cambios nuevos cumplan con los estándares de codificación y no introduzcan posibles vulnerabilidades. Estas herramientas se pueden integrar en canalizaciones de CI como Jenkins, GitLab CI y GitHub Actions, entre otras.

  • Función en los editores de código: La mayoría de los editores de código modernos, como Visual Studio Code, PyCharm, Atom y Sublime Text, admiten la integración de herramientas de análisis estático. Esto permite que los desarrolladores reciban comentarios en tiempo real sobre la estructura, el estilo y las posibles vulnerabilidades del código mientras lo escriben.

Ejemplo práctico: integración de PyLint en Visual Studio Code

PyLint es una herramienta popular de análisis estático para Python. Así puedes integrarla en Visual Studio Code, un editor de código muy usado.

Primero, asegúrate de que PyLint esté instalado en tu entorno de Python. Si no lo está, puedes instalarlo con pip:

pip install pylint

Luego, abre Visual Studio Code e instala la extensión Python de Microsoft desde la vista de extensiones.

Una vez instalada la extensión, abre la configuración (File > Preferences > Settings). En la sección User Settings, busca Python Linting. Asegúrate de que la casilla Enabled esté marcada y de que Pylint esté seleccionado en el menú desplegable Linter.

Ahora, cada vez que abras un archivo de Python en Visual Studio Code, PyLint analizará el código automáticamente y destacará los problemas que encuentre.

Este es un ejemplo de código de Python y de cómo PyLint destacaría un problema:

def add_numbers(a, b):
    return a + c

En el código anterior, PyLint subrayaría c y mostraría el mensaje de advertencia Undefined variable 'c', lo que te ayudaría a detectar el error antes de ejecutar el código.

10. Análisis del rendimiento

El análisis del rendimiento es una dimensión esencial del análisis estático de Python. Consiste en examinar el código para identificar cuellos de botella, secciones ineficientes o partes que consumen muchos recursos de CPU o memoria. Al realizar un análisis exhaustivo del rendimiento, los desarrolladores pueden optimizar el código y mejorar la velocidad y la eficiencia generales de la aplicación.

El análisis del rendimiento permite medir la complejidad del código en términos de tiempo y espacio. La complejidad temporal se relaciona con el tiempo total que se necesita para ejecutar un programa, mientras que la complejidad espacial se refiere a la cantidad de memoria que usa. Un perfil de rendimiento proporciona un informe detallado del número de llamadas a funciones, el tiempo dedicado a cada función y otra información que puede ayudar a optimizar el rendimiento de la aplicación.

Ejemplo de código Python que muestra el análisis del rendimiento

Python ofrece varias herramientas para analizar el rendimiento, pero una de las más comunes es el módulo integrado cProfile, que proporciona un generador de perfiles robusto y flexible. Aquí tienes un ejemplo sencillo de cómo usarlo:

import cProfile
import re

def re_test():
    re.compile("foo|bar")

cProfile.run('re_test()')

Al ejecutar este código, cProfile generará un informe que muestra cuánto tiempo se dedicó a cada función. El resultado podría verse así:

   200 function calls (195 primitive calls) in 0.002 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 :0(setprofile)
       43    0.000    0.000    0.000    0.000 :0(time)
        5    0.000    0.000    0.000    0.000 <ipython-input-26-6e8e8b2a9e4f>:1(re_test)
...

Este resultado muestra el número de llamadas a cada función (ncalls), el tiempo total dedicado a la función, sin contar las llamadas a subfunciones (tottime), el tiempo acumulado dedicado a la función, incluidas las subfunciones (cumtime), y otra información útil.

El análisis del rendimiento relacionado con las fugas y el uso de memoria también es fundamental para las aplicaciones en producción. Drew Wright escribió un excelente artículo sobre cómo diagnosticar y corregir fugas de memoria en Python, basado en su experiencia y las dificultades que enfrentó con problemas de fugas de memoria mientras formaba parte del equipo de ingeniería de Snyk.

El análisis del rendimiento tiene un impacto directo en tu código. Te ayuda a identificar las áreas que representan cuellos de botella, para que puedas enfocarte en optimizarlas. El análisis también puede ayudar a encontrar errores que quizá no sean visibles durante las pruebas habituales. Además, puede influir en la forma en que escribes código. Comprender bien la complejidad temporal y espacial puede orientarte para escribir código más eficiente y, en última instancia, mejorar el rendimiento de la aplicación.

Conclusión

El análisis estático es una herramienta poderosa que puede mejorar considerablemente la calidad de tu código y hacerlo más seguro. Al aprovechar las diez dimensiones del análisis estático de Python que analizamos en este artículo, puedes mejorar tu proceso de desarrollo de software, evitar que se introduzcan errores potenciales en tu base de código y asegurarte de que tus aplicaciones sean menos vulnerables a las amenazas de seguridad.

Las dimensiones del análisis estático de Python trabajan en conjunto para detectar problemas potenciales antes de que se conviertan en problemas reales en el entorno de producción. Si detectas los errores a tiempo, ahorras los costos asociados con corregirlos más adelante. El análisis estático de Python también es esencial para garantizar la seguridad del código. Puede detectar posibles vulnerabilidades de seguridad que podrían pasar inadvertidas en las revisiones manuales de código. Al incorporar el análisis estático de Python en tu proceso de desarrollo de software, puedes hacer que tus aplicaciones sean más seguras y protegerlas de posibles ataques.

Motivar a los desarrolladores a escribir código más seguro y de mayor calidad

Espero que este artículo te haya ayudado a comprender mejor las diez dimensiones del análisis estático de Python y cómo pueden mejorar la calidad y la seguridad del código. Recuerda que escribir código seguro y de alta calidad no consiste solo en prevenir errores o vulnerabilidades: se trata de crear software confiable, sólido y capaz de afrontar los desafíos del mundo real.

¿Sabes cómo prevenir la inyección de comandos en Python? ¿Cómo puedes protegerte contra la inyección de código en Python?

Seguir las prácticas recomendadas de seguridad de Python es esencial para garantizar la seguridad de tus aplicaciones Python. Empieza hoy con una cuenta gratuita de Snyk e instala la extensión de Snyk para IDE para encontrar y corregir vulnerabilidades de seguridad en tu código Python de forma rápida y sencilla.

Leer más

Blog

Los modelos de frontera encontraron las vulnerabilidades. Solo el atacante encontró las cadenas.

El análisis estático encontró las fallas, pero solo las pruebas de ataque en vivo demostraron cómo podían encadenarse para provocar brechas. Una comparación de Evo COS, Claude Security y Claude Code Security.

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.

Blog

Por qué los agentes de programación con IA siguen generando fallas de control de acceso

Los agentes de programación con IA pueden generar lógica de autorización que compila y supera la revisión, pero expone los datos de un inquilino a otro. Descubre por qué es difícil detectar el control de acceso roto y cómo prevenirlo.