Desbloquea El Poder De Python AST Para Fórmulas Rápidas Y Seguras

by Admin 66 views
Desbloquea el Poder de Python AST para Fórmulas Rápidas y Seguras

¡Adiós a los Dolores de Cabeza con Fórmulas: La Revolución AST ha Llegado!

¡Qué onda, gente! Hoy vamos a hablar de una mejora súper heavy que estamos implementando en nuestro motor de fórmulas, y créanme, esto va a cambiar el juego por completo. Si alguna vez te has preguntado cómo hacer que tus evaluadores de expresiones en Python sean más rápidos, seguros y precisos, ¡estás en el lugar correcto! Estamos llevando nuestro sistema de evaluación de expresiones matemáticas al siguiente nivel, reemplazando la forma antigua y, seamos honestos, un poco frágil, por una bestia de la programación: el Árbol de Sintaxis Abstracta (AST) de Python. Este no es un simple ajuste, es una revolución que aborda problemas críticos de precedencia de operadores, aumenta la seguridad a niveles insospechados y, agárrense, ¡mejora el rendimiento de fórmulas entre un 300% y un 500% para las expresiones más complejas! Sí, leyeron bien: ¡hasta cinco veces más rápido! Nuestro objetivo es que nuestro motor de fórmulas no solo sea el más eficiente, sino también el más confiable y seguro del mercado. Imaginen procesar miles de cálculos de nómina o financieros con una velocidad asombrosa y la certeza de que cada número es preciso hasta el último decimal. La implementación de un evaluador de expresiones AST significa que estamos construyendo sobre los cimientos sólidos de cómo Python mismo entiende y ejecuta código, lo que nos da una ventaja brutal en robustez y escalabilidad. No más dolores de cabeza con fórmulas complicadas o preocupaciones de seguridad; con el AST, estamos blindando nuestro sistema contra errores comunes y amenazas externas, garantizando que cada cálculo se maneje con la máxima integridad. Esta actualización es un game-changer para cualquier sistema que dependa de evaluaciones dinámicas y complejas, asegurando que nuestros usuarios reciban resultados impecables en tiempo récord. Estamos hablando de una inversión en el futuro de nuestra plataforma, dotándola de una capacidad de procesamiento de expresiones que no solo cumple, sino que supera las expectativas más exigentes, preparándonos para cualquier desafío que venga.

El Antiguo Camino: ¿Por Qué Nuestro Evaluador Actual Estaba Quedándose Corto?

Bueno, antes de celebrar al nuevo campeón, hablemos un poco de por qué necesitábamos este cambio. Nuestro sistema anterior de evaluación de expresiones matemáticas se basaba en una tokenización manual. Imaginen que intentan entender una frase compleja separándola palabra por palabra, pero sin tener muy claras las reglas de gramática o puntuación. Así se sentía un poco nuestro antiguo tokenizador manual. Tenía sus méritos, claro, pero para las complejidades del mundo real, simplemente no daba la talla. Las limitaciones del tokenizador eran varias, y bastante serias:

  1. Precedencia incorrecta: Este era el problema principal y el más doloroso. Piensen en una expresión tan simple como 2 + 3 * 4. Lógicamente, todos sabemos que primero se hace la multiplicación, ¿verdad? Así que el resultado debería ser 2 + 12 = 14. Pues nuestro antiguo sistema a veces lo evaluaba como (2 + 3) * 4 = 20. ¡Una locura! Imaginen esto en cálculos de nómina o impuestos, donde un error así podría significar millones en discrepancias. ¡Un verdadero dolor de cabeza, tíos!

  2. Validación insuficiente: Las expresiones mal formadas, con errores de sintaxis o lógica, a menudo no se detectaban a tiempo. Esto significaba que los errores solo saltaban mucho después, cuando ya estaban causando problemas en los resultados. Era como dejar que un coche con el motor fallando saliera a la carretera y esperar que llegara a su destino sin problemas.

  3. Vulnerable a inyección: Al manejar el parsing de forma manual, siempre había un riesgo inherente. Podría ser explotado si un usuario malintencionado intentara inyectar código peligroso. En nuestro mundo, la seguridad de código no es negociable; tiene que ser blindada.

  4. Mantenibilidad pobre: Querer agregar un nuevo operador o una nueva función matemática significaba tocar múltiples partes del código, como un juego de dominó donde al mover una pieza, todo se caía. La mantenibilidad de expresiones era una pesadilla para el equipo de desarrollo, consumiendo tiempo valioso que podríamos usar en innovar.

  5. Rendimiento subóptimo: El proceso de evaluación era lineal, lo que significa que para expresiones muy largas o complejas, la velocidad caía en picado. No había optimizaciones que aprovecharan el poder computacional moderno. Era como usar una calculadora de los 80 para un cálculo que hoy se hace en un microsegundo.

Para ponerlo en perspectiva, consideren este ejemplo crítico de cálculo de impuesto: salario * 0.15 - deducciones / 12 + bono_extra. Con nuestro tokenizador actual, la evaluación podía ser: (salario * 0.15 - deducciones) / 12 + bono_extra. ¡Totalmente incorrecto! El deducciones / 12 debería evaluarse primero. La versión con AST lo resuelve correctamente: salario * 0.15 - (deducciones / 12) + bono_extra. Esta diferencia no es un detalle menor; es la base para asegurar que nuestros cálculos son, y siempre serán, correctos. Entendiendo estos puntos débiles, nos quedó claro que necesitábamos una solución robusta, inteligente y, sobre todo, segura. Y esa solución, amigos, es el AST.

La Solución Definitiva: ¿Cómo el AST de Python Rescata la Situación?

¡Aquí viene el héroe de la película, chicos! El Abstract Syntax Tree (AST) de Python. Imaginen que cada fórmula que escribimos no es solo una cadena de texto, sino una estructura lógica que Python puede entender perfectamente. Un AST es como un diagrama de flujo o un árbol genealógico de nuestra expresión, donde cada rama y cada hoja representan una operación, un número o una variable, y su posición en el árbol define exactamente cómo se relacionan entre sí. Python ya usa los ASTs internamente para entender y ejecutar su propio código, ¡así que estamos aprovechando una herramienta nativa y súper poderosa! ¿Y qué beneficios nos trae esta maravilla?

Primero, la precedencia de operadores correcta es intrínseca al diseño del AST. Cuando el AST construye el árbol, ya sabe que la multiplicación y la división van antes que la suma y la resta. Así, el problema de 2 + 3 * 4 se resuelve automáticamente, dándonos el 14 que esperamos. ¡Punto para la precisión!

Segundo, la seguridad. Con un evaluador de expresiones AST, podemos inspeccionar este árbol antes de que cualquier cálculo se ejecute. Es como tener un detector de metales en la entrada, asegurándonos de que solo las operaciones seguras y permitidas pasen. Esto nos permite mejorar la seguridad en Python al máximo, bloqueando cualquier intento de inyección de código peligroso de raíz. Adiós a las vulnerabilidades de parsing manual; hola a un entorno sandbox a prueba de balas.

Tercero, el rendimiento. ¿Recuerdan ese asombroso aumento del 300% al 500%? Se debe a que el AST nos permite compilar las expresiones a bytecode de Python. Una vez que la expresión ha sido parseada y validada en AST, la podemos compilar y guardar en caché. La próxima vez que se use la misma expresión, ¡simplemente ejecutamos el bytecode ya compilado! Esto es increíblemente rápido, especialmente para expresiones complejas que se evalúan muchas veces. Es como pre-cocinar una comida para que solo tengas que calentarla y servirla cuando la necesites, en lugar de empezar de cero cada vez. Es un aumento de rendimiento en evaluación de fórmulas que se sentirá en cada cálculo.

Cuarto, la compatibilidad con versiones anteriores. Era fundamental que, al implementar esta mejora, todas las fórmulas existentes siguieran funcionando perfectamente. Nuestro diseño asegura un 100% de compatibilidad, lo que significa que la transición será imperceptible para los usuarios. No hay riesgo de romper nada, solo de mejorar todo.

Finalmente, la extensibilidad del parser. Agregar nuevas funciones o operadores matemáticos ahora es mucho más sencillo. Como el sistema entiende la estructura de la expresión, integrar nuevas operaciones es más modular y menos propenso a errores, facilitando futuras mejoras y adaptaciones sin tener que reescribir todo el motor. Es como tener un kit de herramientas donde cada herramienta encaja perfectamente.

En resumen, con el AST, estamos hablando de una precisión del 100% en la evaluación, tiempos de respuesta menores a 1 milisegundo para la mayoría de las expresiones, cero vulnerabilidades de inyección de código, una cobertura de pruebas superior al 95% y una compatibilidad total con nuestros esquemas actuales. ¡Es un salto cuántico, amigos!

Diseñando el Futuro: La Arquitectura Detrás de Nuestro Nuevo Motor de Fórmulas

Ok, ya sabemos por qué el AST es la onda, pero ¿cómo vamos a implementarlo? Aquí les presento la arquitectura de nuestro evaluador de expresiones propuesta, que es bastante elegante y robusta. Piensen en nuestro FormulaEngine (la clase principal) como el cerebro central, y dentro de él, estamos creando un nuevo y poderoso subsistema llamado ASTExpressionEvaluator, que será el corazón inteligente que se encarga de todo el procesamiento de las fórmulas.

La FormulaEngine sigue siendo el punto de entrada principal, pero ahora, en lugar de hacer el trabajo pesado de parsing y evaluación por sí misma, delega esa responsabilidad al ASTExpressionEvaluator. Es como si el gerente (FormulaEngine) ahora tuviera un equipo especializado (ASTExpressionEvaluator) que hace el trabajo técnico más complejo y delicado.

Dentro de este nuevo ASTExpressionEvaluator, tenemos varios componentes clave que trabajan en conjunto:

  • Parser + Validator: Este módulo es el encargado de tomar la expresión de texto y transformarla en el famoso Árbol de Sintaxis Abstracta (AST). Además, valida que la expresión esté bien formada sintácticamente, atrapando errores desde el principio, antes de que puedan causar problemas más adelante. Es el primer filtro de calidad.
  • Security Scanner: ¡Este es nuestro bouncer de seguridad! Su trabajo es revisar el AST generado y asegurarse de que solo contenga operaciones seguras y permitidas. Aquí es donde entra en juego la clase SecurityValidator. Imaginen que tenemos una lista blanca de los tipos de nodos AST que están bien (como números, nombres de variables, operaciones básicas como suma, resta, multiplicación, división, etc.) y una lista blanca de funciones seguras (como min, max, abs, round). Si el scanner encuentra algo que no está en esas listas —como un intento de llamar a funciones del sistema o acceder a archivos—, ¡lo bloquea de inmediato! Esto es crucial para la seguridad de nuestro evaluador de expresiones Python.
  • Optimizer + Cache: Este es el componente que nos da la increíble mejora de rendimiento. Después de que una expresión es parseada y validada, se puede optimizar (por ejemplo, 2 + 2 se convierte directamente en 4) y luego compilar a bytecode de Python. Este bytecode compilado se guarda en una caché, de modo que la próxima vez que se evalúe la misma expresión, no es necesario pasar por todo el proceso de parsing y validación de nuevo; simplemente tomamos el bytecode ya listo y lo ejecutamos. Esto es lo que permite el aumento de rendimiento en la evaluación de fórmulas que mencionamos antes.

Para materializar esto, implementaremos dos clases principales:

  1. ASTExpressionEvaluator (Nueva): Será la clase central que orqueste todo el proceso. Tendrá un método evaluate que toma una expresión como string y devuelve el resultado. Se encargará de validar, parsear, compilar y ejecutar la expresión de forma segura. Además, manejará las caches internas para AST y bytecode, haciendo que las evaluaciones repetidas sean rapidísimas. Tendrá acceso a las variables del sistema para poder usarlas en las fórmulas.
  2. SecurityValidator (Nueva): Esta clase es la encargada de la seguridad. Contiene las listas de ALLOWED_NODE_TYPES y ALLOWED_FUNCTIONS. Su método validate recorrerá el árbol AST recursivamente, asegurándose de que cada parte de la expresión sea segura y no contenga ninguna construcción maliciosa o no autorizada. Esto es fundamental para evitar vulnerabilidades de inyección y mantener nuestro sistema íntegro. Su rol es crítico para la confianza y la integridad de nuestros cálculos.

En resumen, estamos construyendo un sistema donde la optimización de fórmulas y la seguridad van de la mano, con una arquitectura clara y modular que nos permitirá escalar y mantener el sistema con mucha más facilidad. ¡Es un diseño pensado para el futuro!

Integración Suave como la Seda: El Nuevo Motor en Acción con lo Existente

Una de las cosas más importantes en cualquier actualización grande de software es cómo se integra con lo que ya existe. Nadie quiere un big bang que lo rompa todo, ¿verdad? Por eso, hemos diseñado la integración de nuestro motor de fórmulas para que sea lo más suave y controlada posible. No estamos simplemente arrancando el motor viejo y poniendo el nuevo; estamos mejorando el motor existente para que use la nueva tecnología de AST de forma inteligente.

Para lograr esto, vamos a introducir una nueva clase que hereda de nuestra FormulaEngine actual, a la que llamaremos EnhancedFormulaEngine. Piensen en ella como una versión turbo cargada de nuestro motor actual. Esta EnhancedFormulaEngine tendrá la capacidad de usar el nuevo ASTExpressionEvaluator, pero de una manera muy controlada y segura. Esto nos permite una transición gradual de sistemas, minimizando cualquier riesgo.

La clave de esta integración es un concepto que llamamos feature flag. Imaginen un interruptor: podemos encenderlo para usar el nuevo evaluador AST, o apagarlo para seguir usando el método de tokenización manual. Esto se implementará a través de un método como _should_use_ast(). ¿Por qué es esto tan genial? Permite que el equipo decida cuándo y dónde activar la nueva funcionalidad. Podríamos empezar activándolo solo para un pequeño subconjunto de cálculos o usuarios, monitorear de cerca su comportamiento, y una vez que estemos 100% seguros, expandirlo a todo el sistema. Si por alguna razón encontramos un problema inesperado (¡que esperamos no suceda!), podemos simplemente apagar el feature flag y volver al método antiguo sin que nadie se entere. ¡Es la red de seguridad definitiva!

Cuando el feature flag está activado, el método _evaluate_expression de la EnhancedFormulaEngine llamará a un nuevo método: _evaluate_with_ast(expression). Este es el método que se conectará directamente con nuestro flamante ASTExpressionEvaluator. Si el evaluador AST aún no ha sido inicializado, lo hará en ese momento, pasándole las variables necesarias para los cálculos. Esto asegura que el ASTExpressionEvaluator siempre tenga el contexto correcto para evaluar las expresiones. Pero si el feature flag está apagado, o si hay algún error inesperado durante la evaluación con AST (lo cual es muy improbable pero siempre posible durante una transición), el sistema automáticamente caerá de vuelta al método original de la clase FormulaEngine padre. Este fallback automático es otra capa de seguridad que garantiza que nuestros cálculos nunca se detengan, incluso durante la fase de transición. Es un mecanismo robusto que nos da la confianza para implementar esta mejora sin preocuparnos por interrupciones en el servicio. La compatibilidad con versiones anteriores es, por lo tanto, una prioridad máxima y está garantizada por este enfoque de feature flags y fallback inteligente. Estamos construyendo un camino hacia el futuro, pero con un ojo siempre puesto en la estabilidad del presente.

Los Secretos Internos: Cómo Garantizamos Velocidad, Seguridad y Precisión Total

Ahora, vamos a adentrarnos en los detalles más jugosos de cómo funciona esto a nivel técnico, garantizando que cada expresión se procese con la máxima velocidad, seguridad y precisión. Es la magia detrás del telón, amigos.

Primero, hablemos del procesamiento de expresiones seguras en Python. El flujo detallado de cómo manejamos una expresión como "salario * 0.15 + bono - (deducciones / 12)" es el siguiente:

  1. Input: Recibimos la expresión como una cadena de texto.
  2. Parsear a AST: Usamos la función nativa de Python ast.parse(expr, mode='eval'). Esto toma nuestra cadena y la convierte en ese hermoso Árbol de Sintaxis Abstracta que ya conocemos. Python es un genio para esto.
  3. Validar seguridad: Aquí entra en acción nuestro SecurityValidator. Este componente examina cada nodo del AST, asegurándose de que la expresión no intente hacer nada malicioso o no permitido. Es como un control de seguridad estricto en el aeropuerto, solo dejando pasar lo autorizado.
  4. Optimizar: Antes de compilar, podemos aplicar pequeñas optimizaciones, como el constant folding. Por ejemplo, si una expresión tiene 2 + 3, el optimizador lo puede reemplazar directamente con 5 antes de la ejecución, ahorrando tiempo. También podría simplificar variable * 1 a variable.
  5. Compilar: Una vez que el AST está limpio y optimizado, lo compilamos a bytecode de Python usando compile(ast, '<string>', 'eval'). Este bytecode es el lenguaje de máquina de Python, mucho más rápido de ejecutar que el código fuente original.
  6. Ejecutar: Finalmente, usamos eval(code, safe_globals, {}) para ejecutar el bytecode. Y aquí viene la parte clave de la seguridad...
  7. Output: El resultado de la evaluación, que convertimos cuidadosamente a Decimal.

La seguridad en el paso de ejecución es fundamental. La función eval() de Python es increíblemente poderosa, pero también puede ser peligrosa si no se usa con cuidado, ya que puede ejecutar cualquier código de Python. Por eso, creamos un contexto de ejecución seguro. En nuestro método _create_safe_context(), hacemos lo siguiente:

  • '__builtins__': {}: Esto es clave. Bloqueamos todos los built-ins de Python que podrían ser explotados (import, open, exec, eval, etc.). La expresión no tendrá acceso a funciones o módulos peligrosos.
  • Funciones matemáticas seguras: Solo permitimos un conjunto muy limitado de funciones que sabemos que son seguras y necesarias, como min, max, abs y round. Estas funciones son reescritas para trabajar con Decimal de forma segura. Por ejemplo, min: lambda *args: Decimal(str(min(args))).
  • Variables: Las variables que proporcionamos (salario, bono, etc.) se incluyen en este contexto, pero también se gestionan para mantener la seguridad y la precisión. Es como darle al evaluador solo las herramientas que necesita y nada más.

Finalmente, hablemos del manejo de Decimal vs Float. Este es un punto crítico para cualquier sistema financiero. Los números float (de coma flotante) en Python (y en casi todos los lenguajes) tienen problemas de precisión inherentes debido a cómo se representan internamente. Por ejemplo, 0.1 + 0.2 no es exactamente 0.3 en float. Para cálculos financieros, esto es inaceptable. Necesitamos Decimal para una precisión exacta. Por eso, tenemos utilidades para convertir de Decimal a float y viceversa de forma segura. Cuando pasamos variables al contexto de eval, las convertimos a float (para que las operaciones internas sean más rápidas), pero siempre usando el string como intermediario (float(str(value))) para evitar la pérdida de precisión. Y el resultado final de eval siempre se convierte de nuevo a Decimal (Decimal(str(value))) usando el mismo truco del string. Esto garantiza que nunca perdamos la precisión que Decimal nos ofrece. Así, combinamos la velocidad de la ejecución con seguridad total y precisión absoluta. ¡Es un sistema a prueba de errores!

El Plan de Batalla: Nuestro Camino Hacia la Implementación Exitosa

Para que todo esto no sea solo una idea genial en el papel, tenemos un plan de implementación AST bien estructurado, dividido en fases manejables. Nuestro objetivo es ser eficientes, metódicos y, sobre todo, garantizar la máxima calidad en cada etapa. ¡Aquí les va nuestro roadmap!

Fase 1: Desarrollo del Núcleo (1 semana)

El objetivo de esta fase es construir el corazón de nuestro nuevo sistema. Nos enfocaremos en crear el ASTExpressionEvaluator básico y todas sus piezas esenciales.

  • Tareas clave:
    • Crear el módulo ast_evaluator.py, que contendrá la lógica principal del evaluador.
    • Implementar el SecurityValidator con todas sus reglas de nodos y funciones permitidas. Este es nuestro guardián de seguridad, y tiene que ser robusto desde el día uno.
    • Integrar el parsing básico usando ast.parse para transformar las expresiones en ASTs.
    • Establecer el sistema de caché de AST, que será crucial para el rendimiento. Queremos que las expresiones ya procesadas se carguen al instante.
    • Escribir pruebas unitarias básicas para cada componente, asegurando que cada pieza funcione como se espera antes de unirlas.
  • Criterios de Completitud: Al final de esta fase, esperamos que el 100% de nuestras pruebas unitarias pasen, que el evaluador pueda procesar expresiones simples correctamente, que detecte cualquier expresión mal formada y, lo más importante, que bloquee cualquier intento de código inseguro. ¡Blindado desde el inicio!

Fase 2: Integración y Compatibilidad (3 días)

Una vez que el núcleo esté listo, el siguiente paso es conectar nuestro nuevo motor con el sistema existente, asegurándonos de que todo funcione en armonía y sin romper nada. La integración y compatibilidad de software son nuestras prioridades aquí.

  • Tareas clave:
    • Crear la clase EnhancedFormulaEngine que hereda del motor actual y servirá como el puente hacia la nueva funcionalidad.
    • Implementar los feature flags para permitir una transición gradual del sistema. Esto significa que podremos activar o desactivar el uso del AST a voluntad.
    • Configurar el fallback automático al tokenizador original en caso de cualquier error, garantizando la continuidad del servicio.
    • Crear tests de integración que validen la comunicación entre el motor existente y el nuevo evaluador AST.
    • Realizar benchmarks comparativos para medir el rendimiento del nuevo sistema frente al antiguo.
  • Criterios de Completitud: Al terminar, el sistema deberá tener un 100% de compatibilidad con todos los tests existentes, los feature flags deberán funcionar a la perfección, el fallback automático deberá estar operativo en caso de errores, y los benchmarks deberán demostrar una mejora clara en el rendimiento. ¡Resultados tangibles!

Fase 3: Optimización y Cache (2 días)

Con la integración hecha, es hora de exprimir al máximo el rendimiento. Aquí nos enfocaremos en hacer que el motor sea blazing fast.

  • Tareas clave:
    • Implementar una caché LRU (Least Recently Used) para expresiones compiladas. Esto significa que las expresiones más usadas permanecerán en caché para un acceso ultra-rápido.
    • Agregar optimizaciones como el constant folding (simplificar 2+2 a 4) y otras simplificaciones del AST.
    • Optimizar la validación recursiva del AST para que sea lo más eficiente posible.
    • Agregar profiling y métricas de rendimiento para identificar cuellos de botella y confirmar las mejoras.
    • Optimizar la creación del contexto de ejecución seguro para que sea lo más ligera posible.
  • Criterios de Completitud: Esperamos una tasa de aciertos en la caché (cache hit rate) superior al 80% en nuestros tests. El tiempo de evaluación para el 95% de las expresiones deberá ser inferior a 1 milisegundo, y la huella de memoria (memory footprint) del sistema deberá ser estable. ¡Eficiencia al máximo!

Fase 4: Pruebas Exhaustivas (2 días)

Esta es la fase crítica donde nos aseguramos de que todo esté perfecto. La pruebas exhaustivas de software son nuestra garantía de calidad y confianza.

  • Tareas clave:
    • Realizar pruebas de regresión con esquemas de fórmulas reales para asegurar que no se haya introducido ningún error en las funcionalidades existentes.
    • Ejecutar pruebas de seguridad completas (siguiendo directrices como OWASP) para intentar romper el sistema y garantizar que sea impenetrable.
    • Llevar a cabo pruebas de carga con más de 10,000 expresiones para verificar la estabilidad y el rendimiento bajo estrés.
    • Realizar pruebas de precisión rigurosas, comparando los resultados del Decimal con los float (para confirmar la ausencia de errores) y validando la exactitud de cada cálculo.
    • Probar edge cases (casos límite) que puedan desafiar la lógica del evaluador.
  • Criterios de Completitud: Al final, deberíamos tener cero regresiones en los tests existentes, cero vulnerabilidades de seguridad detectadas, una precisión del 100% en comparación con nuestro evaluador original, y una documentación completa de todo el proceso y la funcionalidad. ¡Cero errores y máxima confianza!

Mirando Bajo el Capó: La Estructura del Código y Ejemplos Clave

¡Muy bien, curiosos de la programación! Ya hablamos del plan, ahora veamos un poco la carnita, el código. No se preocupen, lo haremos de forma sencilla para que entiendan cómo se ve todo esto en la práctica y cómo garantiza esa estructura de código del evaluador AST la robustez que tanto buscamos.

Primero, la estructura de archivos será limpia y organizada, siguiendo las mejores prácticas para un proyecto Python:

coati_payroll/
├── formula_engine/
│   ├── __init__.py
│   ├── engine.py              # Nuestra clase principal existente
│   ├── ast_evaluator.py       # ¡El nuevo evaluador AST!
│   ├── security_validator.py  # El guardián de seguridad
│   ├── decimal_utils.py       # Utilidades para manejar Decimal
│   └── exceptions.py          # Excepciones personalizadas para errores
├── tests/
│   ├── test_ast_evaluator.py  # Tests para el nuevo evaluador
│   ├── test_security.py       # Tests específicos de seguridad
│   └── test_integration.py    # Tests que unen todo
└── benchmarks/
    └── expression_benchmark.py # Para medir el rendimiento

Ahora, veamos algunos ejemplos de código Python AST clave y cómo funcionan:

ASTExpressionEvaluator (en ast_evaluator.py)

Esta es la clase principal de la nueva implementación. Imaginen que es el director de orquesta. Cuando llamamos a su método evaluate(expression), hace varias cosas:

  1. Obtiene el bytecode: Primero, busca la expresión en una caché de bytecode. Si ya la compiló antes, ¡la usa directamente! Si no, la parsea a AST, la valida con el SecurityValidator, y luego la compila a bytecode de Python. Este es un ejemplo claro de optimización.
  2. Crea un contexto seguro: Configura un diccionario safe_globals que es un sandbox muy estricto. Esto significa que la expresión solo puede acceder a las variables y funciones que nosotros explícitamente permitimos (min, max, abs, round y las variables de nuestras fórmulas). ¡Lo más importante: __builtins__': {} bloquea cualquier función peligrosa de Python!
  3. Ejecuta el bytecode: Usa eval(bytecode, safe_globals, {}) para ejecutar la expresión de forma segura dentro de ese sandbox. Si la expresión es `