Creada API de Debug

This commit is contained in:
Miguel 2025-06-05 18:18:48 +02:00
parent b2a4237504
commit 60401010a6
15 changed files with 3153 additions and 2 deletions

508
.doc/LLM_DEBUG_API_GUIDE.md Normal file
View File

@ -0,0 +1,508 @@
# Simple Debug API - Guía para LLM
## Propósito
Esta API permite debuggear la **Calculadora MAV CAS** sin modificar código fuente ni crear scripts adicionales. Como LLM, puedes usar esta herramienta para:
- **Diagnosticar problemas** en el motor de evaluación
- **Verificar comportamiento** de tipos personalizados (FourBytes, IntBase, IP4Mask, etc.)
- **Inspeccionar el estado interno** del motor (variables, contexto, tipos registrados)
- **Analizar el formato de salida** exacto como se muestra en la aplicación
- **Testing de regresión** para verificar que cambios no rompan funcionalidad existente
## Flujo de Trabajo Recomendado
### 1. Crear archivo JSON con queries
### 2. Ejecutar: `python simple_debug.py archivo.json`
### 3. Analizar resultados en el archivo `*_results.json`
---
## Tipos de Query
### Query tipo `input`
Evalúa expresiones como si el usuario las escribiera en la calculadora.
```json
{"index": 0, "type": "input", "content": "10.1.1.1 + 1"}
```
### Query tipo `exec`
Ejecuta código Python para inspeccionar el estado interno del motor.
```json
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}
```
---
## Plantilla Base
```json
{
"queries": [
{"index": 0, "type": "input", "content": "EXPRESION_A_EVALUAR"},
{"index": 1, "type": "exec", "content": "CODIGO_PYTHON_INSPECCION"}
]
}
```
---
## Casos de Uso Comunes
### Debugging de Tipos Personalizados
**Problema**: Verificar si FourBytes maneja correctamente operaciones IP.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "192.168.1.1 + 1"},
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"},
{"index": 2, "type": "exec", "content": "engine.last_result.original"},
{"index": 3, "type": "exec", "content": "engine.last_result._numeric_value"},
{"index": 4, "type": "input", "content": "10.0.0.0/8 + 256"},
{"index": 5, "type": "exec", "content": "engine.last_result.has_symbols"}
]
}
```
### Debugging de Tokenización
**Problema**: La expresión `192.168.1.x + 16#FF` no se tokeniza correctamente.
```json
{
"queries": [
{"index": 0, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 16#FF')"},
{"index": 1, "type": "input", "content": "192.168.1.x + 16#FF"},
{"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()"},
{"index": 3, "type": "exec", "content": "len(engine.parser.tokenizer.tokenization_rules)"}
]
}
```
### Debugging de Errores
**Problema**: IP4Mask rechaza máscaras que deberían ser válidas.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "mask = 255.240.0.0"},
{"index": 1, "type": "input", "content": "IP4Mask(mask)"},
{"index": 2, "type": "exec", "content": "engine.last_result"},
{"index": 3, "type": "input", "content": "IP4Mask(255.240.0.3)"},
{"index": 4, "type": "exec", "content": "engine.last_result"}
]
}
```
### Debugging de Estado del Motor
**Problema**: Las variables no se están guardando correctamente.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "x = 5"},
{"index": 1, "type": "input", "content": "y = x + 10"},
{"index": 2, "type": "exec", "content": "engine.symbol_table"},
{"index": 3, "type": "exec", "content": "len(engine.symbol_table)"},
{"index": 4, "type": "exec", "content": "list(engine.symbol_table.keys())"},
{"index": 5, "type": "input", "content": "solve(z**2 + x, z)"},
{"index": 6, "type": "exec", "content": "len(engine.equations)"}
]
}
```
---
## Interpretación de Resultados
### Estructura del Resultado
```json
{
"execution_info": {
"timestamp": "2025-06-05T18:01:28.644799Z",
"total_queries": 5,
"successful": 4,
"failed": 1
},
"results": [...]
}
```
### Resultado Individual (Query `input`)
```json
{
"index": 0,
"input": "10.1.1.1 + 1",
"output": "10.1.1.2", // Resultado básico
"result_type": "FourBytes", // Tipo del objeto resultado
"success": true,
"error": null,
"display_class": "[FourBytes]", // Nombre de clase para display
"output_raw": { // ⭐ INFORMACIÓN DE FORMATO
"parts": [
["custom_type", "10.1.1.2"], // [tag_color, contenido]
["class_hint", "[FourBytes]"]
],
"formatted_text": "[custom_type]10.1.1.2[class_hint][FourBytes]",
"tag_info": { // Información de colores
"custom_type": {"fg": "#f9a825"},
"class_hint": {"fg": "#888888"}
}
}
}
```
### Resultado Individual (Query `exec`)
```json
{
"index": 1,
"input": "type(engine.last_result).__name__",
"output": "FourBytes",
"result_type": "str",
"success": true,
"error": null,
"exec_result": "FourBytes" // Resultado crudo del exec
}
```
### Resultado con Error
```json
{
"index": 2,
"input": "IP4Mask(255.240.0.3)",
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "❌ Máscara inválida: 255.240.0.3...",
"output_raw": {
"parts": [["error", "Error: ❌ Máscara inválida..."]],
"formatted_text": "[error]Error: ❌ Máscara inválida...",
"tag_info": {"error": {"fg": "#ff6b6b", "font": "bold"}}
}
}
```
---
## Funciones de Inspección Útiles
### Estado del Motor
```python
"engine.symbol_table" # Variables actuales
"engine.last_result" # Último resultado
"engine.symbolic_mode" # ¿Modo simbólico activo?
"len(engine.equations)" # Cantidad de ecuaciones en el sistema
"engine.debug" # ¿Debug habilitado?
"list(engine.base_context.keys())[:10]" # Funciones disponibles (muestra 10)
```
### Información de Tipos
```python
"engine.get_available_types()" # Info completa de tipos registrados
"list(engine.registered_types_info['registered_classes'].keys())" # Lista de tipos
"engine.registered_types_info['class_count']" # Cantidad de tipos registrados
"type(engine.last_result).__name__" # Tipo del último resultado
"hasattr(engine.last_result, 'has_symbols')" # ¿El resultado tiene símbolos?
```
### Tokenización y Parsing
```python
"engine.parser.get_tokenization_info()" # Info completa de tokenización
"engine.parser.process_expression('test')" # Procesar expresión específica
"len(engine.parser.tokenizer.tokenization_rules)" # Cantidad de reglas
"engine._classify_line('x = 5')" # Clasificar tipo de línea
"engine._extract_variable_names('x + y')" # Extraer nombres de variables
```
### Análisis de Objetos Específicos
```python
# Para FourBytes
"engine.last_result.original" # String original
"engine.last_result._numeric_value" # Valor numérico interno
"engine.last_result.has_symbols" # ¿Tiene símbolos?
# Para IntBase
"engine.last_result.base" # Base numérica (10, 16, 8, 2)
"engine.last_result.value_str" # String del valor
"engine.last_result._symbols" # Símbolos detectados
# Para IP4Mask
"engine.last_result.get_prefix_int()" # Prefijo como entero
"engine.last_result.is_valid()" # ¿Es máscara válida?
```
---
## Patrones de Debugging
### 1. Debugging de Regresión
**Cuándo usar**: Verificar que cambios no rompan funcionalidad existente.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "10.1.1.1 + 1"},
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"},
{"index": 2, "type": "input", "content": "16#FF + 10"},
{"index": 3, "type": "exec", "content": "engine.last_result.base"},
{"index": 4, "type": "input", "content": "IP4Mask(255.255.0.0)"},
{"index": 5, "type": "exec", "content": "engine.last_result.get_prefix_int()"}
]
}
```
### 2. Debugging de Nuevas Funcionalidades
**Cuándo usar**: Verificar que nueva funcionalidad trabaja correctamente.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "NUEVA_FUNCIONALIDAD_AQUI"},
{"index": 1, "type": "exec", "content": "type(engine.last_result)"},
{"index": 2, "type": "exec", "content": "dir(engine.last_result)"},
{"index": 3, "type": "exec", "content": "str(engine.last_result)"},
{"index": 4, "type": "exec", "content": "engine.symbol_table"}
]
}
```
### 3. Debugging de Performance
**Cuándo usar**: Identificar operaciones lentas o problemáticas.
```json
{
"queries": [
{"index": 0, "type": "exec", "content": "import time; start = time.time()"},
{"index": 1, "type": "input", "content": "OPERACION_LENTA"},
{"index": 2, "type": "exec", "content": "time.time() - start"},
{"index": 3, "type": "exec", "content": "len(engine.symbol_table)"}
]
}
```
### 4. Debugging de Display/Formato
**Cuándo usar**: Verificar cómo se muestra exactamente un resultado.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "EXPRESION"},
{"index": 1, "type": "exec", "content": "str(engine.last_result)"},
{"index": 2, "type": "exec", "content": "repr(engine.last_result)"},
{"index": 3, "type": "exec", "content": "type(engine.last_result).__name__"}
]
}
```
---
## Análisis de output_raw
El campo `output_raw` contiene información exacta sobre cómo se muestra el resultado en la aplicación:
### Tags de Color Importantes
- **`error`** (`#ff6b6b`): Errores
- **`custom_type`** (`#f9a825`): Tipos personalizados (FourBytes, IntBase, etc.)
- **`symbolic`** (`#82aaff`): Expresiones simbólicas
- **`numeric`** (`#c3e88d`): Aproximaciones numéricas
- **`class_hint`** (`#888888`): Pistas de clase `[FourBytes]`
- **`ip`** (`#fff176`): Direcciones IP específicamente
- **`hex`** (`#f9a825`): Números hexadecimales
### Interpretación de Parts
```json
"parts": [
["custom_type", "192.168.1.2"], // Resultado principal en naranja
["class_hint", "[FourBytes]"] // Pista de clase en gris
]
```
Esto significa: "192.168.1.2" se muestra en color naranjo seguido de "[FourBytes]" en gris.
---
## Comandos de Ejecución
```bash
# Ejecución básica
python simple_debug.py mi_debug.json
# Con archivo de salida específico
python simple_debug.py mi_debug.json --output resultados.json
# Modo verboso (para ver progreso)
python simple_debug.py mi_debug.json --verbose
```
---
## Templates Existentes para Reutilizar
### Casos Básicos
**Archivo**: `debug_templates/basic_test.json`
- Operaciones con FourBytes e IntBase
- Variables y SymPy básico
### Testing de Errores
**Archivo**: `debug_templates/error_debug.json`
- Máscaras inválidas
- IPs fuera de rango
- Expresiones malformadas
### Información de Contexto
**Archivo**: `debug_templates/context_debug.json`
- Estado completo del motor
- Tipos registrados
- Sistema de ecuaciones
### Formato de Display
**Archivo**: `debug_templates/display_format_test.json`
- Demostración de `output_raw`
- Diferentes tipos de formato
### Tokenización
**Archivo**: `debug_templates/tokenization_test.json`
- Debug del sistema de parsing
- Reglas de tokenización
---
## Flujo de Resolución de Problemas
### 1. Identificar el Problema
- ¿Es un error de evaluación?
- ¿Es un problema de formato/display?
- ¿Es un problema de tokenización?
- ¿Es un problema de estado del motor?
### 2. Crear Query de Diagnóstico
- Usar query `input` para reproducir el problema
- Usar query `exec` para inspeccionar el estado
### 3. Analizar Resultados
- Verificar `success: true/false`
- Examinar `error` si hay fallo
- Analizar `output_raw` para problemas de display
- Usar `exec_result` para inspección detallada
### 4. Iterar
- Crear nuevas queries basadas en hallazgos
- Profundizar en áreas problemáticas
- Verificar soluciones con queries adicionales
---
## Ejemplo Completo de Debugging
**Problema**: "La operación `192.168.1.x + 1` no funciona correctamente"
### Paso 1: Crear archivo de debug
```json
{
"queries": [
{"index": 0, "type": "input", "content": "192.168.1.x + 1"},
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"},
{"index": 2, "type": "exec", "content": "engine.last_result.has_symbols"},
{"index": 3, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 1')"},
{"index": 4, "type": "input", "content": "ip = 192.168.1.x"},
{"index": 5, "type": "exec", "content": "type(engine.symbol_table['ip'])"},
{"index": 6, "type": "input", "content": "ip.substitute(x=5)"}
]
}
```
### Paso 2: Ejecutar
```bash
python simple_debug.py debug_problema.json
```
### Paso 3: Analizar resultados
- Verificar si `192.168.1.x + 1` se evalúa correctamente
- Comprobar el tipo resultante
- Verificar si tiene símbolos
- Examinar cómo se tokeniza la expresión
- Probar operaciones relacionadas
Este flujo te permite identificar exactamente dónde está el problema y verificar la solución.
---
## Consejos para LLMs
1. **Siempre usa índices secuenciales** en las queries para facilitar la lectura
2. **Combina queries `input` y `exec`** para obtener contexto completo
3. **Verifica tanto `output` como `output_raw`** para problemas de display
4. **Usa los templates existentes** como punto de partida
5. **Examina `success` y `error`** antes de analizar resultados
6. **Aprovecha `exec_result`** para inspección detallada del estado
7. **Crea queries incrementales** que construyan sobre resultados anteriores
Con esta API, puedes debuggear efectivamente la calculadora sin necesidad de modificar código fuente o crear scripts adicionales.
---
## Referencia Rápida
### Query Básica
```json
{"index": N, "type": "input|exec", "content": "CONTENIDO"}
```
### Comandos Esenciales
```bash
python simple_debug.py archivo.json # Ejecutar debug
python simple_debug.py archivo.json --verbose # Con detalles
python simple_debug.py archivo.json -o resultado.json # Salida específica
```
### Inspección Básica del Motor
```python
"engine.last_result" # Último resultado
"type(engine.last_result)" # Tipo del resultado
"engine.symbol_table" # Variables actuales
"engine.get_available_types()" # Tipos registrados
"engine.parser.get_tokenization_info()" # Info de parsing
```
### Análisis de Resultado
```python
"success": true/false # ¿Éxito?
"error": "mensaje" # Error si falla
"output": "resultado" # Resultado básico
"output_raw": {...} # Formato exacto de la app
"exec_result": valor # Resultado crudo (exec queries)
```
### Templates Disponibles
- `debug_templates/basic_test.json` - Pruebas básicas
- `debug_templates/error_debug.json` - Testing de errores
- `debug_templates/context_debug.json` - Estado del motor
- `debug_templates/tokenization_test.json` - Debug de parsing
- `debug_templates/display_format_test.json` - Formato de display
### Workflow Típico
1. **Identificar problema** → Crear query `input` para reproducir
2. **Inspeccionar estado** → Añadir queries `exec` para diagnosticar
3. **Analizar resultados** → Examinar `success`, `error`, `output_raw`
4. **Iterar** → Crear nuevas queries basadas en hallazgos

339
basic_test_results.json Normal file
View File

@ -0,0 +1,339 @@
{
"execution_info": {
"timestamp": "2025-06-05T18:10:43.323812Z",
"total_queries": 8,
"successful": 8,
"failed": 0,
"input_file": "debug_templates\\basic_test.json"
},
"results": [
{
"index": 0,
"input": "10.1.1.1 + 1",
"output": "10.1.1.2",
"result_type": "FourBytes",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"custom_type",
"10.1.1.2"
],
[
"class_hint",
"[FourBytes]"
]
],
"formatted_text": "[custom_type]10.1.1.2[class_hint][FourBytes]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[FourBytes]"
},
{
"index": 1,
"input": "type(engine.last_result).__name__",
"output": "FourBytes",
"result_type": "str",
"success": true,
"error": null,
"exec_result": "FourBytes"
},
{
"index": 2,
"input": "16#FF",
"output": "16#FF",
"result_type": "IntBase",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"custom_type",
"16#FF"
],
[
"class_hint",
"[IntBase]"
]
],
"formatted_text": "[custom_type]16#FF[class_hint][IntBase]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[IntBase]"
},
{
"index": 3,
"input": "engine.last_result.base",
"output": "16",
"result_type": "int",
"success": true,
"error": null,
"exec_result": 16
},
{
"index": 4,
"input": "mask = 255.255.0.0",
"output": "255.255.0.0",
"result_type": "FourBytes",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"info",
"mask = 255.255.0.0"
]
],
"formatted_text": "[info]mask = 255.255.0.0",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[FourBytes]"
},
{
"index": 5,
"input": "engine.symbol_table",
"output": "{'mask': FourBytes('255.255.0.0')}",
"result_type": "dict",
"success": true,
"error": null,
"exec_result": "{'mask': FourBytes('255.255.0.0')}"
},
{
"index": 6,
"input": "solve(x**2 + 1, x)",
"output": "[-I, I]",
"result_type": "list",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"result",
"[-I, I]"
],
[
"class_hint",
"[List]"
]
],
"formatted_text": "[result][-I, I][class_hint][List]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[list]"
},
{
"index": 7,
"input": "len(engine.symbol_table)",
"output": "1",
"result_type": "int",
"success": true,
"error": null,
"exec_result": 1
}
]
}

135
context_debug_results.json Normal file
View File

@ -0,0 +1,135 @@
{
"execution_info": {
"timestamp": "2025-06-05T18:02:09.698502Z",
"total_queries": 11,
"successful": 11,
"failed": 0,
"input_file": "debug_templates\\context_debug.json"
},
"results": [
{
"index": 0,
"input": "x = 5",
"output": "5",
"result_type": "Integer",
"success": true,
"error": null,
"display_class": "[Integer]"
},
{
"index": 1,
"input": "y = x + 10",
"output": "15",
"result_type": "Integer",
"success": true,
"error": null,
"display_class": "[Integer]"
},
{
"index": 2,
"input": "engine.symbol_table",
"output": "{'x': 5, 'y': 15}",
"result_type": "dict",
"success": true,
"error": null,
"exec_result": "{'x': 5, 'y': 15, 'z': [-sqrt(-b), sqrt(-b)]}"
},
{
"index": 3,
"input": "len(engine.equations)",
"output": "2",
"result_type": "int",
"success": true,
"error": null,
"exec_result": 2
},
{
"index": 4,
"input": "list(engine.base_context.keys())[:10]",
"output": "['pi', 'e', 'I', 'oo', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan']",
"result_type": "list",
"success": true,
"error": null,
"exec_result": [
"pi",
"e",
"I",
"oo",
"sin",
"cos",
"tan",
"asin",
"acos",
"atan"
]
},
{
"index": 5,
"input": "engine.get_available_types()",
"output": "{'registered_classes': {'Bin': <class 'bin_type.Class_Bin'>, 'Chr': <class 'chr_type.Class_Chr'>, 'Dec': <class 'dec_type.Class_Dec'>, 'FourBytes': <class 'fourbytes_type.FourBytes'>, 'Hex': <class 'hex_type.Class_Hex'>, 'IntBase': <class 'intbase_type.IntBase'>, 'IP4': <class 'ip4_type.Class_IP4'>, 'IP4Mask': <class 'ip4_type.IP4Mask'>, 'LaTeX': <class 'latex_type.Class_LaTeX'>}, 'bracket_classes': ['bin', 'intbase', 'IP4', 'chr', 'IntBase', 'ip4', 'fourbytes', 'Chr', 'dec', 'latex', 'FourBytes', 'Hex', 'LaTeX', 'Dec', 'Bin', 'IP4Mask', 'hex', 'ip4mask'], 'total_context_entries': 64, 'helper_functions_count': 9}",
"result_type": "dict",
"success": true,
"error": null,
"exec_result": "{'registered_classes': {'Bin': <class 'bin_type.Class_Bin'>, 'Chr': <class 'chr_type.Class_Chr'>, 'Dec': <class 'dec_type.Class_Dec'>, 'FourBytes': <class 'fourbytes_type.FourBytes'>, 'Hex': <class 'hex_type.Class_Hex'>, 'IntBase': <class 'intbase_type.IntBase'>, 'IP4': <class 'ip4_type.Class_IP4'>, 'IP4Mask': <class 'ip4_type.IP4Mask'>, 'LaTeX': <class 'latex_type.Class_LaTeX'>}, 'bracket_classes': ['bin', 'intbase', 'IP4', 'chr', 'IntBase', 'ip4', 'fourbytes', 'Chr', 'dec', 'latex', 'FourBytes', 'Hex', 'LaTeX', 'Dec', 'Bin', 'IP4Mask', 'hex', 'ip4mask'], 'total_context_entries': 64, 'helper_functions_count': 9}"
},
{
"index": 6,
"input": "list(engine.registered_types_info['registered_classes'].keys())",
"output": "['Bin', 'Chr', 'Dec', 'FourBytes', 'Hex', 'IntBase', 'IP4', 'IP4Mask', 'LaTeX']",
"result_type": "list",
"success": true,
"error": null,
"exec_result": [
"Bin",
"Chr",
"Dec",
"FourBytes",
"Hex",
"IntBase",
"IP4",
"IP4Mask",
"LaTeX"
]
},
{
"index": 7,
"input": "engine.registered_types_info['class_count']",
"output": "9",
"result_type": "int",
"success": true,
"error": null,
"exec_result": 9
},
{
"index": 8,
"input": "z = solve(a**2 + b, a)",
"output": "[-sqrt(-b), sqrt(-b)]",
"result_type": "list",
"success": true,
"error": null,
"display_class": "[list]"
},
{
"index": 9,
"input": "engine._classify_line('z = solve(a**2 + b, a)')",
"output": "assignment",
"result_type": "str",
"success": true,
"error": null,
"exec_result": "assignment"
},
{
"index": 10,
"input": "engine._extract_variable_names('x + y + z')",
"output": "['x', 'y', 'z']",
"result_type": "list",
"success": true,
"error": null,
"exec_result": [
"x",
"y",
"z"
]
}
]
}

188
debug_templates/README.md Normal file
View File

@ -0,0 +1,188 @@
# Templates de Debug - Simple Debug API
Esta carpeta contiene templates predefinidos para debuggear diferentes aspectos de la Calculadora MAV CAS.
## Uso Básico
```bash
# Ejecutar un template
python simple_debug.py debug_templates/basic_test.json
# Con archivo de salida personalizado
python simple_debug.py debug_templates/basic_test.json --output mi_debug.json
# Con información detallada
python simple_debug.py debug_templates/basic_test.json --verbose
```
## Templates Disponibles
### `basic_test.json`
**Propósito**: Pruebas básicas de funcionalidad
- Operaciones con FourBytes (IPs)
- Operaciones con IntBase (números en diferentes bases)
- Variables y tabla de símbolos
- Funciones de SymPy
### `tokenization_test.json`
**Propósito**: Debug del sistema de tokenización
- Análisis de expresiones complejas
- Información de reglas de tokenización
- Procesamiento de expressions mixtas (IP + hex)
### `regression_test.json`
**Propósito**: Testing de regresión para casos específicos
- Verificación de tipos: FourBytes, IntBase, IP4Mask
- Operaciones simbólicas
- Substitución de variables
### `error_debug.json`
**Propósito**: Debug de manejo de errores
- Máscaras IP inválidas
- IPs fuera de rango
- Números hexadecimales malformados
- División por cero
- Variables indefinidas
### `context_debug.json`
**Propósito**: Debug del contexto y estado del motor
- Tabla de símbolos
- Tipos registrados
- Contexto base
- Sistema de ecuaciones
### `display_format_test.json`
**Propósito**: Demostración de formatos de display y output_raw
- Diferentes tipos de resultados (IP, hex, símbolos, matrices)
- Comentarios y errores
- Comparación entre `output` y `output_raw`
## Formato de Template
Cada template es un archivo JSON con esta estructura:
```json
{
"queries": [
{
"index": 0,
"type": "input",
"content": "10.1.1.1 + 1"
},
{
"index": 1,
"type": "exec",
"content": "type(engine.last_result).__name__"
}
]
}
```
### Tipos de Query
- **`input`**: Evalúa expresiones como si las escribieras en la calculadora
- **`exec`**: Ejecuta código Python para inspeccionar el estado interno del motor
## Casos de Uso Comunes
### Debug de un Bug Específico
1. Copia `basic_test.json` a `mi_bug_debug.json`
2. Modifica las queries para reproducir el bug
3. Añade queries `exec` para inspeccionar el estado
4. Ejecuta: `python simple_debug.py mi_bug_debug.json`
### Verificar una Nueva Funcionalidad
1. Crea un template con queries `input` que usen la nueva funcionalidad
2. Añade queries `exec` para verificar el estado interno
3. Compara resultados antes y después de los cambios
### Testing de Regresión
1. Usa `regression_test.json` como base
2. Añade casos específicos que deben mantenerse funcionando
3. Ejecuta regularmente para detectar regresiones
## Funciones Útiles para Queries `exec`
### Estado del Motor
```python
"engine.symbol_table" # Variables actuales
"engine.last_result" # Último resultado
"engine.symbolic_mode" # ¿Modo simbólico?
"len(engine.equations)" # Cantidad de ecuaciones
```
### Información de Tipos
```python
"type(engine.last_result).__name__" # Tipo del resultado
"engine.get_available_types()" # Tipos disponibles
"list(engine.registered_types_info['registered_classes'].keys())"
```
### Tokenización
```python
"engine.parser.get_tokenization_info()" # Info de tokenización
"engine.parser.process_expression('test')" # Procesar expresión
```
### Análisis de Objetos
```python
"engine.last_result.original" # String original (FourBytes)
"engine.last_result.base" # Base numérica (IntBase)
"engine.last_result.has_symbols" # ¿Tiene símbolos?
```
## Formato de Resultado
### Resultado Individual para Query `input`
```json
{
"index": 0,
"input": "10.1.1.1 + 1",
"output": "10.1.1.2",
"result_type": "FourBytes",
"success": true,
"error": null,
"display_class": "[FourBytes]",
"output_raw": {
"parts": [
["custom_type", "10.1.1.2"],
["class_hint", "[FourBytes]"]
],
"formatted_text": "[custom_type]10.1.1.2[class_hint][FourBytes]",
"tag_info": {
"custom_type": {"fg": "#f9a825"},
"class_hint": {"fg": "#888888"}
}
}
}
```
### Campos del Resultado
- **`output`**: Resultado básico (antes del post-procesamiento)
- **`output_raw`**: Resultado exacto como se muestra en la aplicación
- **`parts`**: Lista de tuplas `[tag, contenido]` con información de color/formato
- **`formatted_text`**: Texto formateado como se vería en la aplicación
- **`tag_info`**: Información de colores y estilos para cada tag
- **`result_type`**: Tipo del objeto resultado
- **`display_class`**: Nombre de clase para mostrar
### Tags de Color Disponibles
- **`error`**: `#ff6b6b` (rojo) - Errores
- **`result`**: `#abdbe3` (azul claro) - Resultados generales
- **`symbolic`**: `#82aaff` (azul) - Expresiones simbólicas
- **`numeric`**: `#c3e88d` (verde) - Aproximaciones numéricas
- **`custom_type`**: `#f9a825` (naranja) - Tipos personalizados
- **`ip`**: `#fff176` (amarillo) - Direcciones IP
- **`hex`**: `#f9a825` (naranja) - Números hexadecimales
- **`class_hint`**: `#888888` (gris) - Pistas de clase
## Tips
- Usa `index` secuencial para facilitar la lectura de resultados
- Combina queries `input` y `exec` para verificar comportamiento y estado
- El archivo de resultados contiene información detallada de éxito/error
- **Usa `output_raw` para entender exactamente cómo se mostraría el resultado en la aplicación**
- Puedes copiar y modificar templates existentes para casos específicos

View File

@ -0,0 +1,44 @@
{
"queries": [
{
"index": 0,
"type": "input",
"content": "10.1.1.1 + 1"
},
{
"index": 1,
"type": "exec",
"content": "type(engine.last_result).__name__"
},
{
"index": 2,
"type": "input",
"content": "16#FF"
},
{
"index": 3,
"type": "exec",
"content": "engine.last_result.base"
},
{
"index": 4,
"type": "input",
"content": "mask = 255.255.0.0"
},
{
"index": 5,
"type": "exec",
"content": "engine.symbol_table"
},
{
"index": 6,
"type": "input",
"content": "solve(x**2 + 1, x)"
},
{
"index": 7,
"type": "exec",
"content": "len(engine.symbol_table)"
}
]
}

View File

@ -0,0 +1,59 @@
{
"queries": [
{
"index": 0,
"type": "input",
"content": "x = 5"
},
{
"index": 1,
"type": "input",
"content": "y = x + 10"
},
{
"index": 2,
"type": "exec",
"content": "engine.symbol_table"
},
{
"index": 3,
"type": "exec",
"content": "len(engine.equations)"
},
{
"index": 4,
"type": "exec",
"content": "list(engine.base_context.keys())[:10]"
},
{
"index": 5,
"type": "exec",
"content": "engine.get_available_types()"
},
{
"index": 6,
"type": "exec",
"content": "list(engine.registered_types_info['registered_classes'].keys())"
},
{
"index": 7,
"type": "exec",
"content": "engine.registered_types_info['class_count']"
},
{
"index": 8,
"type": "input",
"content": "z = solve(a**2 + b, a)"
},
{
"index": 9,
"type": "exec",
"content": "engine._classify_line('z = solve(a**2 + b, a)')"
},
{
"index": 10,
"type": "exec",
"content": "engine._extract_variable_names('x + y + z')"
}
]
}

View File

@ -0,0 +1,64 @@
{
"queries": [
{
"index": 0,
"type": "input",
"content": "192.168.1.1 + 1"
},
{
"index": 1,
"type": "exec",
"content": "engine.last_result"
},
{
"index": 2,
"type": "input",
"content": "16#FF00"
},
{
"index": 3,
"type": "exec",
"content": "engine.last_result.base"
},
{
"index": 4,
"type": "input",
"content": "x = solve(a**2 - 4, a)"
},
{
"index": 5,
"type": "exec",
"content": "type(engine.symbol_table['x'])"
},
{
"index": 6,
"type": "input",
"content": "IP4Mask(255.255.255.0)"
},
{
"index": 7,
"type": "exec",
"content": "engine.last_result.get_prefix_int()"
},
{
"index": 8,
"type": "input",
"content": "# Este es un comentario"
},
{
"index": 9,
"type": "input",
"content": "invalid_expression"
},
{
"index": 10,
"type": "input",
"content": "matriz = Matrix([[1, 2], [3, 4]])"
},
{
"index": 11,
"type": "exec",
"content": "engine.symbol_table['matriz'].shape"
}
]
}

View File

@ -0,0 +1,59 @@
{
"queries": [
{
"index": 0,
"type": "input",
"content": "bad_mask = 255.240.0.3"
},
{
"index": 1,
"type": "input",
"content": "IP4Mask(bad_mask)"
},
{
"index": 2,
"type": "exec",
"content": "engine.last_result"
},
{
"index": 3,
"type": "input",
"content": "invalid_ip = 300.1.1.1"
},
{
"index": 4,
"type": "exec",
"content": "hasattr(engine, 'last_error')"
},
{
"index": 5,
"type": "input",
"content": "16#GG"
},
{
"index": 6,
"type": "exec",
"content": "engine.debug"
},
{
"index": 7,
"type": "input",
"content": "divide_by_zero = 10/0"
},
{
"index": 8,
"type": "exec",
"content": "engine.symbolic_mode"
},
{
"index": 9,
"type": "input",
"content": "undefined_var + 5"
},
{
"index": 10,
"type": "exec",
"content": "list(engine.symbol_table.keys())"
}
]
}

View File

@ -0,0 +1,74 @@
{
"queries": [
{
"index": 0,
"type": "input",
"content": "mask=255.240.0.0"
},
{
"index": 1,
"type": "exec",
"content": "type(engine.symbol_table['mask']).__name__"
},
{
"index": 2,
"type": "input",
"content": "10.1.1.1 + 1"
},
{
"index": 3,
"type": "exec",
"content": "type(engine.last_result).__name__"
},
{
"index": 4,
"type": "exec",
"content": "engine.last_result.original"
},
{
"index": 5,
"type": "input",
"content": "IP4Mask(255.255.0.0)"
},
{
"index": 6,
"type": "exec",
"content": "engine.last_result.get_prefix_int()"
},
{
"index": 7,
"type": "input",
"content": "16#FF + 10"
},
{
"index": 8,
"type": "exec",
"content": "engine.last_result.base"
},
{
"index": 9,
"type": "exec",
"content": "engine.last_result.value_str"
},
{
"index": 10,
"type": "input",
"content": "ip = 10.1.1.x"
},
{
"index": 11,
"type": "exec",
"content": "type(engine.symbol_table['ip'])"
},
{
"index": 12,
"type": "exec",
"content": "engine.symbol_table['ip'].has_symbols"
},
{
"index": 13,
"type": "input",
"content": "ip.substitute(x=5)"
}
]
}

View File

@ -0,0 +1,44 @@
{
"queries": [
{
"index": 0,
"type": "exec",
"content": "engine.parser.process_expression('192.168.1.1 + 16#FF')"
},
{
"index": 1,
"type": "input",
"content": "192.168.1.1 + 16#FF"
},
{
"index": 2,
"type": "exec",
"content": "engine.parser.get_tokenization_info()['rules'][:3]"
},
{
"index": 3,
"type": "input",
"content": "10.0.0.0/8 + 1"
},
{
"index": 4,
"type": "exec",
"content": "engine.parser.get_tokenization_info()"
},
{
"index": 5,
"type": "input",
"content": "x = 192.168.y.z"
},
{
"index": 6,
"type": "exec",
"content": "engine.parser.process_expression('x = 192.168.y.z')"
},
{
"index": 7,
"type": "exec",
"content": "len(engine.parser.tokenizer.tokenization_rules)"
}
]
}

View File

@ -0,0 +1,569 @@
{
"execution_info": {
"timestamp": "2025-06-05T18:11:38.337776Z",
"total_queries": 12,
"successful": 12,
"failed": 0,
"input_file": "debug_templates\\display_format_test.json"
},
"results": [
{
"index": 0,
"input": "192.168.1.1 + 1",
"output": "192.168.1.2",
"result_type": "FourBytes",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"custom_type",
"192.168.1.2"
],
[
"class_hint",
"[FourBytes]"
]
],
"formatted_text": "[custom_type]192.168.1.2[class_hint][FourBytes]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[FourBytes]"
},
{
"index": 1,
"input": "engine.last_result",
"output": "192.168.1.2",
"result_type": "FourBytes",
"success": true,
"error": null,
"exec_result": "192.168.1.2"
},
{
"index": 2,
"input": "16#FF00",
"output": "16#FF00",
"result_type": "IntBase",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"custom_type",
"16#FF00"
],
[
"class_hint",
"[IntBase]"
]
],
"formatted_text": "[custom_type]16#FF00[class_hint][IntBase]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[IntBase]"
},
{
"index": 3,
"input": "engine.last_result.base",
"output": "16",
"result_type": "int",
"success": true,
"error": null,
"exec_result": 16
},
{
"index": 4,
"input": "x = solve(a**2 - 4, a)",
"output": "[-2, 2]",
"result_type": "list",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"info",
"x = [-2, 2]"
]
],
"formatted_text": "[info]x = [-2, 2]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[list]"
},
{
"index": 5,
"input": "type(engine.symbol_table['x'])",
"output": "<class 'list'>",
"result_type": "type",
"success": true,
"error": null,
"exec_result": "<class 'list'>"
},
{
"index": 6,
"input": "IP4Mask(255.255.255.0)",
"output": "255.255.255.0",
"result_type": "IP4Mask",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"custom_type",
"255.255.255.0"
],
[
"class_hint",
"[IP4Mask]"
]
],
"formatted_text": "[custom_type]255.255.255.0[class_hint][IP4Mask]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[IP4Mask]"
},
{
"index": 7,
"input": "engine.last_result.get_prefix_int()",
"output": "24",
"result_type": "int",
"success": true,
"error": null,
"exec_result": 24
},
{
"index": 8,
"input": "# Este es un comentario",
"output": "None",
"result_type": "NoneType",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"comment",
"# Este es un comentario"
]
],
"formatted_text": "[comment]# Este es un comentario",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[NoneType]"
},
{
"index": 9,
"input": "invalid_expression",
"output": "invalid_expression",
"result_type": "Symbol",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"symbolic",
"invalid_expression"
],
[
"class_hint",
"[Sympy]"
]
],
"formatted_text": "[symbolic]invalid_expression[class_hint][Sympy]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[Symbol]"
},
{
"index": 10,
"input": "matriz = Matrix([[1, 2], [3, 4]])",
"output": "Matrix([[1, 2], [3, 4]])",
"result_type": "MutableDenseMatrix",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"info",
"matriz = Matrix([[1, 2], [3, 4]])"
],
[
"numeric",
"≈ Matrix([[1.00000000000000, 2.00000000000000], [3.00000000000000, 4.00000000000000]])"
]
],
"formatted_text": "[info]matriz = Matrix([[1, 2], [3, 4]]) [numeric]≈ Matrix([[1.00000000000000, 2.00000000000000], [3.00000000000000, 4.00000000000000]])",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[MutableDenseMatrix]"
},
{
"index": 11,
"input": "engine.symbol_table['matriz'].shape",
"output": "(2, 2)",
"result_type": "tuple",
"success": true,
"error": null,
"exec_result": [
2,
2
]
}
]
}

113
error_debug_results.json Normal file
View File

@ -0,0 +1,113 @@
{
"execution_info": {
"timestamp": "2025-06-05T18:01:38.085095Z",
"total_queries": 11,
"successful": 9,
"failed": 2,
"input_file": "debug_templates\\error_debug.json"
},
"results": [
{
"index": 0,
"input": "bad_mask = 255.240.0.3",
"output": "255.240.0.3",
"result_type": "FourBytes",
"success": true,
"error": null,
"display_class": "[FourBytes]"
},
{
"index": 1,
"input": "IP4Mask(bad_mask)",
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "\n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n",
"display_class": "[NoneType]"
},
{
"index": 2,
"input": "engine.last_result",
"output": "None",
"result_type": "NoneType",
"success": true,
"error": null,
"exec_result": null
},
{
"index": 3,
"input": "invalid_ip = 300.1.1.1",
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "Error en asignación: Elemento inválido: '300'",
"display_class": "[NoneType]"
},
{
"index": 4,
"input": "hasattr(engine, 'last_error')",
"output": "False",
"result_type": "bool",
"success": true,
"error": null,
"exec_result": false
},
{
"index": 5,
"input": "16#GG",
"output": "16",
"result_type": "Integer",
"success": true,
"error": null,
"display_class": "[Integer]"
},
{
"index": 6,
"input": "engine.debug",
"output": "False",
"result_type": "bool",
"success": true,
"error": null,
"exec_result": false
},
{
"index": 7,
"input": "divide_by_zero = 10/0",
"output": "zoo",
"result_type": "ComplexInfinity",
"success": true,
"error": null,
"display_class": "[ComplexInfinity]"
},
{
"index": 8,
"input": "engine.symbolic_mode",
"output": "True",
"result_type": "bool",
"success": true,
"error": null,
"exec_result": true
},
{
"index": 9,
"input": "undefined_var + 5",
"output": "undefined_var + 5",
"result_type": "Add",
"success": true,
"error": null,
"display_class": "[Add]"
},
{
"index": 10,
"input": "list(engine.symbol_table.keys())",
"output": "['bad_mask', 'divide_by_zero']",
"result_type": "list",
"success": true,
"error": null,
"exec_result": [
"bad_mask",
"divide_by_zero"
]
}
]
}

491
error_test_with_raw.json Normal file
View File

@ -0,0 +1,491 @@
{
"execution_info": {
"timestamp": "2025-06-05T18:11:18.061505Z",
"total_queries": 11,
"successful": 9,
"failed": 2,
"input_file": "debug_templates\\error_debug.json"
},
"results": [
{
"index": 0,
"input": "bad_mask = 255.240.0.3",
"output": "255.240.0.3",
"result_type": "FourBytes",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"info",
"bad_mask = 255.240.0.3"
]
],
"formatted_text": "[info]bad_mask = 255.240.0.3",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[FourBytes]"
},
{
"index": 1,
"input": "IP4Mask(bad_mask)",
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "\n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n",
"output_raw": {
"parts": [
[
"error",
"Error: \n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n"
]
],
"formatted_text": "[error]Error: \n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[NoneType]"
},
{
"index": 2,
"input": "engine.last_result",
"output": "None",
"result_type": "NoneType",
"success": true,
"error": null,
"exec_result": null
},
{
"index": 3,
"input": "invalid_ip = 300.1.1.1",
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "Error en asignación: Elemento inválido: '300'",
"output_raw": {
"parts": [
[
"error",
"Error: Error en asignación: Elemento inválido: '300'"
]
],
"formatted_text": "[error]Error: Error en asignación: Elemento inválido: '300'",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[NoneType]"
},
{
"index": 4,
"input": "hasattr(engine, 'last_error')",
"output": "False",
"result_type": "bool",
"success": true,
"error": null,
"exec_result": false
},
{
"index": 5,
"input": "16#GG",
"output": "16",
"result_type": "Integer",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"symbolic",
"16"
],
[
"class_hint",
"[Integer]"
]
],
"formatted_text": "[symbolic]16[class_hint][Integer]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[Integer]"
},
{
"index": 6,
"input": "engine.debug",
"output": "False",
"result_type": "bool",
"success": true,
"error": null,
"exec_result": false
},
{
"index": 7,
"input": "divide_by_zero = 10/0",
"output": "zoo",
"result_type": "ComplexInfinity",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"info",
"divide_by_zero = zoo"
]
],
"formatted_text": "[info]divide_by_zero = zoo",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[ComplexInfinity]"
},
{
"index": 8,
"input": "engine.symbolic_mode",
"output": "True",
"result_type": "bool",
"success": true,
"error": null,
"exec_result": true
},
{
"index": 9,
"input": "undefined_var + 5",
"output": "undefined_var + 5",
"result_type": "Add",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"symbolic",
"undefined_var + 5"
],
[
"class_hint",
"[Sympy]"
],
[
"numeric",
"≈ undefined_var + 5.0"
]
],
"formatted_text": "[symbolic]undefined_var + 5[class_hint][Sympy] [numeric]≈ undefined_var + 5.0",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[Add]"
},
{
"index": 10,
"input": "list(engine.symbol_table.keys())",
"output": "['bad_mask', 'divide_by_zero']",
"result_type": "list",
"success": true,
"error": null,
"exec_result": [
"bad_mask",
"divide_by_zero"
]
}
]
}

View File

@ -1,5 +1,5 @@
mask=255.240.0.x
mask=255.240.0.0
mask.ToHex()
ip=10.1.1.x
10.1.1.1 + 1
@ -7,4 +7,6 @@ ip
IP4(10.1.1.4)
IP4Mask(mask)
IP4Mask(255.255.0.0)
IP4Mask(255.255.0.0)
bad_mask = 255.240.0.3

462
simple_debug.py Normal file
View File

@ -0,0 +1,462 @@
#!/usr/bin/env python3
"""
Simple Debug API - Calculadora MAV CAS
API CLI simple que usa el motor de evaluación existente para generar debug traces
sin modificar el código base. Permite debuggear "como si usaras la aplicación"
mediante archivos JSON.
Uso:
python simple_debug.py debug_input.json
python simple_debug.py debug_input.json --output debug_results.json
python simple_debug.py debug_input.json --verbose
"""
import json
import sys
import argparse
from datetime import datetime
from pathlib import Path
# Importar el motor de evaluación existente
from main_evaluation import HybridEvaluationEngine
import sympy
def _process_evaluation_result_for_debug(result, engine):
"""
Procesa el resultado de evaluación tal como lo haría la aplicación
Retorna información de formato, colores y contenido exacto
"""
output_parts = []
if result.is_error:
# Intentar obtener ayuda como lo hace la app
ayuda = _obtener_ayuda_simulada(result.original_line, engine)
if ayuda:
ayuda_linea = ayuda.replace("\n", " ").replace("\r", " ")
if len(ayuda_linea) > 120:
ayuda_linea = ayuda_linea[:117] + "..."
output_parts.append(("helper", ayuda_linea))
else:
output_parts.append(("error", f"Error: {result.error}"))
elif result.result_type == "comment":
output_parts.append(("comment", result.original_line))
elif result.result_type == "equation_added":
output_parts.append(("equation", result.symbolic_result))
elif result.result_type == "assignment":
output_parts.append(("info", result.symbolic_result))
# Mostrar evaluación numérica para asignaciones si existe
if result.numeric_result is not None and result.numeric_result != result.result:
output_parts.append(("numeric", f"{result.numeric_result}"))
else:
# Resultado normal
if result.result is not None:
# Determinar tag basado en tipo (simulando lógica dinámica)
tag = _get_result_tag_dynamic_debug(result.result, engine)
# Verificar si es resultado interactivo (simulado)
if _is_interactive_result(result.result):
interactive_tag, display_text = _create_interactive_tag_debug(result.result)
if interactive_tag:
output_parts.append((interactive_tag, display_text))
else:
output_parts.append((tag, str(result.result)))
else:
output_parts.append((tag, str(result.result)))
# Añadir pista de clase para el resultado principal
class_display_name = _get_class_display_name_dynamic_debug(result.result, engine)
if class_display_name:
output_parts.append(("class_hint", f"[{class_display_name}]"))
# Mostrar evaluación numérica si existe
if result.numeric_result is not None and result.numeric_result != result.result:
output_parts.append(("numeric", f"{result.numeric_result}"))
# Mostrar información adicional
if result.info:
output_parts.append(("info", f"({result.info})"))
# Convertir partes a string formateado como la app
formatted_output = _format_output_parts_debug(output_parts)
return {
'parts': output_parts,
'formatted_text': formatted_output,
'tag_info': _get_tag_color_info()
}
def _obtener_ayuda_simulada(input_str, engine):
"""Simula la obtención de ayuda como lo hace la app"""
try:
# Intentar usar los helpers del engine si están disponibles
if hasattr(engine, 'HELPERS'):
for helper in engine.HELPERS:
try:
ayuda = helper(input_str)
if ayuda:
return ayuda
except:
continue
except:
pass
return None
def _get_result_tag_dynamic_debug(result, engine):
"""Simula _get_result_tag_dynamic de la app"""
try:
registered_classes = engine.get_available_types().get('registered_classes', {})
# Verificar si es una instancia de alguna clase registrada
for name, cls in registered_classes.items():
if isinstance(result, cls):
name_lower = name.lower()
if name_lower == "hex":
return "hex"
elif name_lower == "bin":
return "bin"
elif name_lower in ["ip4", "ip"]:
return "ip"
elif name_lower == "chr":
return "chr_type"
elif name_lower == "date":
return "date"
else:
return "custom_type"
except Exception:
pass
# Fallback a tags existentes para tipos no registrados
if isinstance(result, sympy.Basic):
return "symbolic"
else:
return "result"
def _get_class_display_name_dynamic_debug(obj, engine):
"""Simula _get_class_display_name_dynamic de la app"""
try:
# Verificar si es una clase registrada dinámicamente
registered_classes = engine.get_available_types().get('registered_classes', {})
for name, cls in registered_classes.items():
if isinstance(obj, cls):
return name
except Exception:
pass
# Fallback a lógica existente para tipos nativos
if isinstance(obj, sympy.logic.boolalg.BooleanAtom):
return "Boolean"
elif isinstance(obj, sympy.Basic):
if hasattr(obj, 'is_number') and obj.is_number:
if hasattr(obj, 'is_Integer') and obj.is_Integer:
return "Integer"
elif hasattr(obj, 'is_Rational') and obj.is_Rational and not obj.is_Integer:
return "Rational"
elif hasattr(obj, 'is_Float') and obj.is_Float:
return "Float"
else:
return "SympyNumber"
else:
return "Sympy"
elif isinstance(obj, bool):
return "Boolean"
elif isinstance(obj, (int, float, str, list, dict, tuple, type(None))):
class_display_name = type(obj).__name__.capitalize()
if class_display_name == "Nonetype":
class_display_name = "None"
return class_display_name
return ""
def _is_interactive_result(result):
"""Simula la lógica is_interactive de EvaluationResult"""
try:
# Intentar importar PlotResult si existe
from tl_popup import PlotResult
if isinstance(result, PlotResult):
return True
except ImportError:
pass
return isinstance(result, sympy.Matrix) or \
(isinstance(result, list) and len(result) > 3)
def _create_interactive_tag_debug(result):
"""Simula la creación de tags interactivos"""
try:
from tl_popup import PlotResult
if isinstance(result, PlotResult):
return f"plot_{id(result)}", f"📊 Ver {result.plot_type.title()}"
except ImportError:
pass
if isinstance(result, sympy.Matrix):
rows, cols = result.shape
return f"matrix_{id(result)}", f"📋 Ver Matriz {rows}×{cols}"
elif isinstance(result, list) and len(result) > 5:
return f"list_{id(result)}", f"📋 Ver Lista ({len(result)} elementos)"
elif isinstance(result, dict) and len(result) > 3:
return f"dict_{id(result)}", f"🔍 Ver Diccionario ({len(result)} entradas)"
return None, None
def _format_output_parts_debug(output_parts):
"""Simula cómo _display_output formatea las partes para mostrar"""
formatted_lines = []
for part_idx, (tag, content) in enumerate(output_parts):
if not content:
continue
prefix = ""
if part_idx > 0:
prev_tag, prev_content = output_parts[part_idx-1] if part_idx > 0 else (None, None)
if tag not in ["class_hint", "numeric", "info"] and prev_content:
prefix = " ; "
elif tag in ["numeric", "info"] and prev_content:
prefix = " "
formatted_lines.append(f"{prefix}[{tag}]{content}")
return "".join(formatted_lines)
def _get_tag_color_info():
"""Información sobre los colores de los tags como en setup_output_tags"""
return {
"error": {"fg": "#ff6b6b", "font": "bold"},
"result": {"fg": "#abdbe3"},
"symbolic": {"fg": "#82aaff"},
"numeric": {"fg": "#c3e88d"},
"equation": {"fg": "#c792ea"},
"info": {"fg": "#ffcb6b"},
"comment": {"fg": "#546e7a"},
"class_hint": {"fg": "#888888"},
"type_hint": {"fg": "#6a6a6a"},
"custom_type": {"fg": "#f9a825"},
"hex": {"fg": "#f9a825"},
"bin": {"fg": "#4fc3f7"},
"ip": {"fg": "#fff176"},
"date": {"fg": "#ff8a80"},
"chr_type": {"fg": "#80cbc4"},
"helper": {"fg": "#ffd700", "font": "italic"}
}
def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
"""
Ejecuta las queries de debug desde un archivo JSON y guarda los resultados.
Args:
input_file: Archivo JSON con las queries
output_file: Archivo de salida (opcional)
verbose: Modo verboso
"""
# Cargar queries del archivo de entrada
try:
with open(input_file, 'r', encoding='utf-8') as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: No se encontró el archivo '{input_file}'")
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: JSON inválido en '{input_file}': {e}")
sys.exit(1)
if 'queries' not in data:
print("Error: El archivo JSON debe contener una clave 'queries'")
sys.exit(1)
# Crear motor de evaluación
if verbose:
print("Iniciando motor de evaluación...")
engine = HybridEvaluationEngine()
results = []
successful = 0
failed = 0
# Ejecutar cada query
for query in data['queries']:
if verbose:
print(f"Ejecutando query {query.get('index', '?')}: {query.get('content', '')[:50]}...")
try:
if query['type'] == 'input':
# Query de tipo input: evaluar expresión como si fuera entrada del usuario
result = engine.evaluate_line(query['content'])
output = {
'index': query['index'],
'input': query['content'],
'output': str(result.result) if hasattr(result, 'result') else str(result),
'result_type': type(result.result).__name__ if hasattr(result, 'result') else type(result).__name__,
'success': not (hasattr(result, 'is_error') and result.is_error),
'error': result.error if hasattr(result, 'is_error') and result.is_error else None
}
# Generar output_raw: resultado exacto como se muestra en la aplicación
try:
output_raw_data = _process_evaluation_result_for_debug(result, engine)
output['output_raw'] = output_raw_data
except Exception as e:
output['output_raw'] = f"Error generando output_raw: {e}"
# Añadir información adicional si está disponible
if hasattr(result, 'result') and hasattr(result.result, '__class__'):
output['display_class'] = f"[{result.result.__class__.__name__}]"
if not (hasattr(result, 'is_error') and result.is_error):
successful += 1
else:
failed += 1
elif query['type'] == 'exec':
# Query de tipo exec: ejecutar código Python para inspeccionar el estado
exec_result = eval(query['content'], {'engine': engine})
output = {
'index': query['index'],
'input': query['content'],
'output': str(exec_result),
'result_type': type(exec_result).__name__,
'success': True,
'error': None,
'exec_result': exec_result
}
successful += 1
else:
raise ValueError(f"Tipo de query desconocido: {query['type']}")
except Exception as e:
# Manejar errores en la ejecución
output = {
'index': query['index'],
'input': query['content'],
'output': None,
'result_type': None,
'success': False,
'error': str(e)
}
failed += 1
if verbose:
print(f" Error: {str(e)}")
results.append(output)
# Preparar salida final
final_output = {
'execution_info': {
'timestamp': datetime.now().isoformat() + 'Z',
'total_queries': len(data['queries']),
'successful': successful,
'failed': failed,
'input_file': input_file
},
'results': results
}
# Determinar archivo de salida
if output_file is None:
base_name = Path(input_file).stem
output_file = f"{base_name}_results.json"
# Guardar resultados
try:
# Usar un encoder personalizado para objetos no serializables
def json_serializer(obj):
"""Serializar objetos no estándar a string"""
try:
# Intentar conversión normal primero
json.dumps(obj)
return obj
except TypeError:
# Si no es serializable, convertir a string
return str(obj)
# Aplicar serialización personalizada a exec_result
for result in final_output['results']:
if 'exec_result' in result:
result['exec_result'] = json_serializer(result['exec_result'])
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(final_output, f, indent=2, ensure_ascii=False, default=str)
if verbose:
print(f"\nResultados guardados en: {output_file}")
print(f"Queries exitosas: {successful}")
print(f"Queries con error: {failed}")
except Exception as e:
print(f"Error al guardar resultados: {e}")
sys.exit(1)
return final_output
def main():
"""Función principal del CLI"""
parser = argparse.ArgumentParser(
description='Simple Debug API para Calculadora MAV CAS',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Ejemplos de uso:
python simple_debug.py debug_input.json
python simple_debug.py debug_input.json --output custom_results.json
python simple_debug.py debug_input.json --verbose
"""
)
parser.add_argument('input_file',
help='Archivo JSON con las queries de debug')
parser.add_argument('--output', '-o',
help='Archivo de salida (por defecto: <input>_results.json)')
parser.add_argument('--verbose', '-v',
action='store_true',
help='Modo verboso')
args = parser.parse_args()
# Verificar que el archivo de entrada existe
if not Path(args.input_file).exists():
print(f"Error: El archivo '{args.input_file}' no existe")
sys.exit(1)
# Ejecutar debug
try:
results = run_debug(args.input_file, args.output, args.verbose)
# Mostrar resumen si no es modo verboso
if not args.verbose:
info = results['execution_info']
print(f"Debug completado: {info['successful']}/{info['total_queries']} exitosas")
if args.output:
print(f"Resultados en: {args.output}")
else:
base_name = Path(args.input_file).stem
print(f"Resultados en: {base_name}_results.json")
except KeyboardInterrupt:
print("\nInterrumpido por el usuario")
sys.exit(1)
except Exception as e:
print(f"Error inesperado: {e}")
sys.exit(1)
if __name__ == '__main__':
main()