Comparación con Ruff¶
Resumen¶
Tanto Ruff como complexipy ayudan a mejorar la calidad del código Python, pero miden diferentes aspectos de la complejidad y sirven a propósitos complementarios. PLR0912 de Ruff mide la complejidad estructural, la densidad de pruebas y de ramas, mientras que complexipy mide qué tan difícil es el código para que los seres humanos lo entiendan y mantengan.
PLR0912 de Ruff: Demasiadas Ramas¶
La regla PLR0912 (too-many-branches) de Ruff se basa en la complejidad ciclomática y cuenta el número de puntos de decisión en una función.
Cómo Ruff Cuenta las Ramas¶
Ruff cuenta cada uno de estos como una rama:
- Sentencias if, elif, else
- Bucles for y while
- Operadores booleanos and, or
- Cláusulas except
- Casos de coincidencia de patrones
El umbral predeterminado es típicamente de 12 ramas por función.
Ejemplo: Análisis de Ruff¶
def example(a, b, c, d):
if a: # Rama 1
return 1
elif b: # Rama 2
return 2
elif c: # Rama 3
return 3
elif d: # Rama 4
return 4
else: # Rama 5
return 0
# Ruff: 5 ramas
complexipy: Complejidad Cognitiva¶
complexipy implementa la complejidad cognitiva, que pondera las ramas según su nivel de anidamiento para reflejar la comprensión humana.
Cómo Puntúa complexipy¶
complexipy usa la fórmula: complexity = base_score + nesting_level
def example(a, b, c, d):
if a: # +1 (1 + 0 anidamiento)
return 1
elif b: # +1 (elif cuenta como +1)
return 2
elif c: # +1
return 3
elif d: # +1
return 4
else: # +0 (else no añade puntuación base)
return 0
# complexipy: 4 puntos
Diferencias Clave¶
1. El Anidamiento es Crítico en complexipy¶
Ruff trata todas las ramas por igual:
# Ruff: 3 ramas
def flat_logic(a, b, c):
if a: # Rama 1
return 1
if b: # Rama 2
return 2
if c: # Rama 3
return 3
# Ruff: 3 ramas (igual que arriba)
def nested_logic(a, b, c):
if a: # Rama 1
if b: # Rama 2
if c: # Rama 3
return 1
complexipy tiene en cuenta el anidamiento:
# complexipy: 3 puntos
def flat_logic(a, b, c):
if a: # +1
return 1
if b: # +1
return 2
if c: # +1
return 3
# complexipy: 6 puntos (¡mucho peor!)
def nested_logic(a, b, c):
if a: # +1 (1 + 0)
if b: # +2 (1 + 1)
if c: # +3 (1 + 2)
return 1
2. Cláusulas else¶
Ruff: Cuenta else como una rama (+1)
complexipy: else no añade complejidad base, solo penalización por anidamiento
# Ruff: 2 ramas
# complexipy: 1 punto
def check(value):
if value > 0: # Ruff: +1, complexipy: +1
return "positive"
else: # Ruff: +1, complexipy: +0
return "non-positive"
3. Operadores Booleanos¶
Ambas cuentan los operadores booleanos, pero con filosofías diferentes:
Ruff: Cuenta cada and/or como una rama separada
complexipy: Cuenta operadores y penaliza los tipos de operador mezclados
# Ruff: 3 ramas (if + and + or)
# complexipy: 3 puntos (1 por if, +2 por operadores booleanos)
def check(a, b, c):
if a and b or c:
return True
4. Sentencias Match (Python 3.10+)¶
Ruff: Cuenta cada case como una rama
complexipy: El match en sí no añade complejidad, solo cuenta el contenido anidado
# Ruff: 3 ramas (match + 2 casos)
# complexipy: 2 puntos (solo los if anidados)
match value: # Ruff: +1, complexipy: +0
case 1: # Ruff: +1, complexipy: +0
if x: # Ruff: +1, complexipy: +1
pass
case 2: # Ya contado por Ruff
if y: # complexipy: +1
pass
Ejemplo de Comparación Práctica¶
Aquí hay un escenario del mundo real que muestra cómo difieren las dos herramientas:
def process_payment(order):
if order is None: # Ruff: 1, complexipy: 1
return False
if not order.is_valid(): # Ruff: 2, complexipy: 2
return False
if order.payment_method == "credit_card": # Ruff: 3, complexipy: 3
if order.amount > 1000: # Ruff: 4, complexipy: 5 (1+1 anidamiento)
if not verify_fraud_check(order): # Ruff: 5, complexipy: 8 (1+2 anidamiento)
return False
return process_credit_card(order)
elif order.payment_method == "paypal": # Ruff: 6, complexipy: 4
return process_paypal(order)
else: # Ruff: 7, complexipy: 4
return False
# Puntuaciones Finales:
# Ruff: 7 ramas
# complexipy: 8 puntos
En este ejemplo: - Ruff marca la función por tener 7 ramas (por encima del umbral típico) - complexipy le da 8 puntos, con la mayor complejidad proveniente de la verificación de fraude anidada - complexipy identifica mejor que la verificación de fraude profundamente anidada es la parte problemática
¿Cuál Deberías Usar?¶
Usa Ruff (PLR0912) Cuando:¶
- Necesitas medir la complejidad estructural y la densidad de ramas
- Estás diseñando estrategias de cobertura de pruebas
- Quieres limitar el número absoluto de puntos de decisión
- Ya estás usando Ruff para otros tipos de linting
Usa complexipy Cuando:¶
- Quieres identificar código que sea difícil de entender para los seres humanos
- Te enfocas en la legibilidad y mantenibilidad del código
- El anidamiento y la complejidad de flujo son preocupaciones en tu base de código
- Estás realizando revisiones de código enfocadas en la comprensión
¡Usa Ambas! (Recomendado)¶
Ruff y complexipy son complementarios:
# En tu pipeline de pre-commit o CI
ruff check . --select=PLR0912 # Detectar funciones con demasiadas ramas
complexipy . --max-complexity-allowed 15 # Detectar código profundamente anidado
Ruff detecta funciones anchas (muchas ramas), mientras que complexipy detecta funciones profundas (anidamiento intenso). Juntos, proporcionan una cobertura de complejidad integral.
Ejemplos de Configuración¶
Configuración de Ruff¶
Configuración de complexipy¶
Usar Ambas en CI¶
# .github/workflows/quality.yml
- name: Ruff Linting
run: ruff check . --select=PLR0912
- name: Cognitive Complexity Check
run: complexipy . --max-complexity-allowed 15
Estrategia de Migración¶
Si ya estás usando Ruff y quieres agregar complexipy:
- Línea Base Primero: Ejecuta
complexipy . --snapshot-createpara capturar el estado actual - Establecer Umbrales: Comienza con un umbral más alto (p. ej., 20) y redúcelo con el tiempo
- Corregir Código Nuevo: Solo falla el CI en nuevas violaciones
- Mejora Gradual: Refactoriza el código heredado de manera oportunista
Resumen¶
| Característica | Ruff PLR0912 | complexipy |
|---|---|---|
| Basado en | Complejidad Ciclomática | Complejidad Cognitiva |
| Cuenta anidamiento | ❌ No | ✅ Sí |
| Penalización por else | ✅ Sí | ❌ No (solo anidamiento) |
| Operadores booleanos | ✅ Sí | ✅ Sí |
| Sentencias match | ✅ Sí | Parcial (solo contenido) |
| Ideal para | Complejidad estructural, pruebas, densidad de ramas | Qué tan difícil es entender el código |
| Umbral | ~12 ramas | ~15 puntos |
| Rendimiento | Rápido (Rust) | Muy rápido (Rust) |
En Conclusión: PLR0912 de Ruff mide la complejidad estructural y la densidad de ramas (útil para pruebas y análisis), mientras que complexipy mide qué tan difícil es el código para que los seres humanos lo entiendan y mantengan, penalizando el anidamiento y las interrupciones de flujo. Ambas son valiosas, y usarlas juntas proporciona la mejor cobertura para la calidad del código.