23.9 C
Santiago

Python: Inmutabilidad y Contratos Arquitectónicos con Mypy

Published:

El concepto de Contratos Arquitectónicos es el eje central de este análisis.

El spaghetti code es una falta de respeto a la máquina y al equipo de desarrollo; no lo tolero. El verdadero desafío en proyectos a gran escala no es escribir código que funcione, sino escribir código que no pueda romperse por error humano o por el paso del tiempo. Implementar contratos arquitectónicos estrictos, como la inmutabilidad y la segregación de interfaces, es el antídoto. Esto es difícil, requiere coraje y una disciplina de hierro, pero al hacerlo con Mypy y las herramientas de tipado de Python, garantizamos que el código sucio muera en la pre-ejecución, no en producción.

Requisitos del Sistema y Librerías

Para levantar esta estructura de validación, la primera fase es asegurar el toolkit correcto. Necesitaremos una versión de Python moderna (3.10 o superior es ideal para aprovechar la sintaxis de typing más concisa) y las dependencias clave. Cualquier optimización comienza con la lista de requerimientos más limpia posible.

# Entorno de trabajo. Mínimos de Python 3.10+. python3 --version

Publicidad

Gestión de Dependencias del Contrato

El contrato arquitectónico se define y se valida con estas librerías. La librería mypy es el motor principal, pero dataclasses y typing (que vienen de serie) son la tinta de nuestro contrato.

# requirements.txt mypy==1.7.1 pylint==3.0.3

Configuración del Entorno de Aislamiento

Un entorno limpio y aislado es la base de todo buen script. Evitamos la contaminación del sistema y la versión del código sucio global. El uso de venv es un comando fundamental, no una sugerencia.

Publicidad

# Paso 1: Creación y activación del entorno. python3 -m venv .venv source .venv/bin/activate  # Paso 2: Instalación de las dependencias. pip install -r requirements.txt

Definición de Contratos: Inmutabilidad Estructural

La inmutabilidad debe ser una garantía en cualquier Objeto de Transferencia de Datos (DTO) de la arquitectura. El uso combinado de dataclasses con el argumento `frozen=True` y el keyword Final establece este contrato que Mypy no permitirá violar. Si algo debe ser un valor constante, debe ser final.

# src/contratos.py from dataclasses import dataclass from typing import Final, Protocol  @dataclass(frozen=True, slots=True) class ConfiguracionSistema:     """DTO de configuración inmutable."""     VERSION_CORE: Final[str] = '1.0.0'     puerto_servicio: Final[int]     endpoint_raiz: Final[str]      def __post_init__(self):         # Mypy no permitirá reasignar los atributos Final         pass

Publicidad

Implementación de Segregación de Interfaces (ISP)

La segregación de interfaces con typing.Protocol es la forma más limpia de aplicar el principio de Liskov en Python, eliminando la necesidad de herencia base pesada. Definimos exactamente lo que el cliente puede ver y usar.

# src/contratos.py (Continuación) class EjecutorTarea(Protocol):     """Contrato que define una tarea ejecutable de bajo nivel."""     def ejecutar(self) -> bool:         ...  class Supervisor(Protocol):     """Contrato que define un supervisor de alto nivel."""     def auditar(self, resultado: bool) -> None:         ...  class ServicioCompleto(EjecutorTarea, Supervisor):     """Clase que implementa los contratos, sin forzar métodos no usados."""     def ejecutar(self) -> bool:         print("Tarea ejecutada.")         return True          def auditar(self, resultado: bool) -> None:         print(f"Auditoría: {resultado}")

Configuración de Mypy para Strict-Mode

El analizador estático debe operar con la máxima severidad. El archivo mypy.ini es la cláusula strict-mode del contrato, donde se rechazan implícitos. `disallow_untyped_defs` y `warn_unused_ignores` son obligatorios para evitar la complacencia.

Publicidad

# mypy.ini [mypy] files = src/ ignore_missing_imports = True disallow_untyped_defs = True disallow_incomplete_defs = True check_untyped_defs = True no_implicit_optional = True warn_unused_ignores = True strict_equality = True

Demostración de Violación de Contrato

Para demostrar la validez de nuestro contrato, debemos intentar violar la inmutabilidad y la segregación. Un intento de modificar un atributo `Final` o pasar una clase no compatible con un `Protocol` debe ser bloqueado por Mypy, no por un runtime error.

# src/main.py from contratos import ConfiguracionSistema, EjecutorTarea import sys  def procesar_configuracion(config: ConfiguracionSistema) -> None:     # Intento de violación de inmutabilidad     print(f"Puerto actual: {config.puerto_servicio}")     # config.puerto_servicio = 8081  # Mypy debe fallar aquí  def despachar_tarea(tarea: EjecutorTarea) -> None:     print("Despachando...")     tarea.ejecutar()  if __name__ == '__main__':     conf = ConfiguracionSistema(puerto_servicio=8080, endpoint_raiz='/api/v1')     procesar_configuracion(conf)     despachar_tarea(ServicioCompleto()) # Asumiendo ServicioCompleto se importa/define.

Publicidad

Pruebas de Ejecución y Manejo de Errores

El momento de la verdad llega al ejecutar el verificador de tipos. Si Mypy no detecta el intento de reasignación en `procesar_configuracion`, el contrato ha fallado y el spaghetti code se está cocinando.

# Ejecución de Mypy desde la raíz del proyecto mypy --config-file mypy.ini src/  # Resultado esperado: # src/main.py:10: error: Cannot assign to final attribute "puerto_servicio" of class "ConfiguracionSistema" # Found 1 error in 1 file (checked 2 source files)

La salida es clara: Mypy detecta el intento de modificación de un atributo Final antes de que el runtime tenga la oportunidad de lanzar un error, salvando al código del desastre.

La validación empática aquí es crucial: sé que la configuración inicial de este nivel de estrictez es un desafío, pero es la única vía para construir sistemas robustos. El costo de una hora extra en mypy.ini ahorra semanas de debugging en producción. Diez líneas de código que fallan ahora significan cero computadoras lentas mañana. La disciplina del typing estático es el cimiento de la arquitectura limpia.

Publicidad

Magnus ‘PEP8’ Vane
División de Arquitectura de Software

En conclusión, dominar el tema de Contratos Arquitectónicos es vital para avanzar.

Related articles

spot_img

Recent articles

spot_img