File size: 6,817 Bytes
8f10eaa
 
 
 
 
 
 
 
4c22a32
 
8f10eaa
 
 
 
 
 
 
 
 
 
 
 
 
4c22a32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f10eaa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483978b
 
 
 
 
8f10eaa
4c22a32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bd4e55e
 
 
 
 
 
 
 
 
 
4c22a32
 
 
8f10eaa
bd4e55e
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
from flask import Flask, request, jsonify
import subprocess
import os
import tempfile
import uuid
import time
import logging
from flask_cors import CORS
import gradio as gr
import requests

app = Flask(__name__)
CORS(app)  # Habilitar CORS para todas las rutas

# Configuraci贸n de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Directorio temporal para los archivos de c贸digo
TEMP_DIR = os.path.join(tempfile.gettempdir(), '42coderunner')
os.makedirs(TEMP_DIR, exist_ok=True)

# Tiempo m谩ximo de ejecuci贸n (en segundos)
MAX_EXECUTION_TIME = 10 # Aumentamos un poco para Gradio

# URL base de la API (para la interfaz Gradio)
# Detecta si estamos en Hugging Face Spaces
API_BASE_URL = os.environ.get("SPACE_HOST")
if API_BASE_URL:
    # En Hugging Face, usa la URL p煤blica del espacio
    # Aseg煤rate de que el protocolo sea https
    API_BASE_URL = f"https://{API_BASE_URL}"
else:
    # Localmente, usa localhost
    API_BASE_URL = "http://localhost:5000"

API_EXECUTE_URL = f"{API_BASE_URL}/api/execute"

# --- Funciones de la API Flask ---

@app.route('/api/execute', methods=['POST'])
def execute_code():
    try:
        # Obtener el c贸digo C del request
        data = request.get_json()
        if not data or 'code' not in data:
            return jsonify({'success': False, 'error': 'No se proporcion贸 c贸digo'}), 400
        
        code = data['code']
        
        # Crear un ID 煤nico para este trabajo
        job_id = str(uuid.uuid4())
        
        # Crear archivos temporales para el c贸digo y la salida
        code_file = os.path.join(TEMP_DIR, f"{job_id}.c")
        executable = os.path.join(TEMP_DIR, f"{job_id}.exe")
        
        # Guardar el c贸digo en un archivo temporal
        with open(code_file, 'w') as f:
            f.write(code)
        
        # Compilar el c贸digo
        logger.info(f"Compilando c贸digo para job {job_id}")
        compile_process = subprocess.run(
            ['gcc', code_file, '-o', executable],
            capture_output=True,
            text=True
        )
        
        # Verificar si la compilaci贸n fue exitosa
        if compile_process.returncode != 0:
            return jsonify({
                'success': False,
                'error': compile_process.stderr
            })
        
        # Ejecutar el c贸digo compilado
        logger.info(f"Ejecutando c贸digo para job {job_id}")
        try:
            start_time = time.time()
            run_process = subprocess.run(
                [executable],
                capture_output=True,
                text=True,
                timeout=MAX_EXECUTION_TIME
            )
            execution_time = time.time() - start_time
            
            # Preparar la respuesta
            result = {
                'success': run_process.returncode == 0,
                'output': run_process.stdout,
                'error': run_process.stderr,
                'execution_time': execution_time
            }
            
        except subprocess.TimeoutExpired:
            result = {
                'success': False,
                'error': f'La ejecuci贸n excedi贸 el tiempo l铆mite de {MAX_EXECUTION_TIME} segundos'
            }
        
        # Limpiar archivos temporales
        try:
            os.remove(code_file)
            if os.path.exists(executable):
                os.remove(executable)
        except Exception as e:
            logger.error(f"Error al limpiar archivos temporales: {e}")
        
        return jsonify(result)
    
    except Exception as e:
        logger.error(f"Error inesperado: {e}")
        return jsonify({'success': False, 'error': f'Error interno del servidor: {str(e)}'}), 500

@app.route('/api/health', methods=['GET'])
def health_check():
    """Basic health check endpoint."""
    logger.info("Health check requested.")
    # A simple health check is enough for Hugging Face
    # The previous checks (compiler, temp dir) might fail in the HF environment
    return jsonify({'status': 'ok', 'timestamp': time.time()})

# --- Interfaz Gradio ---

def run_c_code(c_code):
    """Funci贸n que se ejecuta cuando el usuario env铆a c贸digo C a trav茅s de Gradio."""
    logger.info(f"Solicitud Gradio recibida. URL API: {API_EXECUTE_URL}")
    try:
        response = requests.post(API_EXECUTE_URL, json={'code': c_code}, timeout=MAX_EXECUTION_TIME + 5) # Timeout un poco mayor para la request
        response.raise_for_status() # Lanza excepci贸n para errores HTTP
        data = response.json()

        if data.get('success'):
            output = f"--- Salida ({data.get('execution_time', 0):.4f}s) ---\n{data.get('output', '')}"
            if data.get('error'): # Mostrar stderr aunque la ejecuci贸n sea exitosa (warnings, etc.)
                output += f"\n\n--- Errores (stderr) ---\n{data['error']}"
            return output
        else:
            return f"--- Error ---\n{data.get('error', 'Error desconocido')}"

    except requests.exceptions.RequestException as e:
        logger.error(f"Error de red al llamar a la API: {e}")
        return f"Error de red al conectar con el backend: {e}"
    except Exception as e:
        logger.error(f"Error inesperado en la interfaz Gradio: {e}")
        return f"Error inesperado en la interfaz: {str(e)}"

# Ejemplo de c贸digo C para la interfaz
EXAMPLE_CODE = """#include <stdio.h>



int main() {

    printf("隆Hola desde 42CodeRunner!\n");

    return 0;

}

"""

# Crear la interfaz Gradio
iface = gr.Interface(
    fn=run_c_code,
    inputs=gr.Code(language="c", label="C贸digo C", value=EXAMPLE_CODE),
    outputs=gr.Textbox(label="Resultado de la Ejecuci贸n", lines=15),
    title="馃強u200d鈾傦笍 42CodeRunner",
    description="Escribe tu c贸digo C, haz clic en 'Submit' y mira la magia suceder. Ejecutado de forma segura en el backend.",
    allow_flagging='never'
)

# --- Integration and Execution ---

# Create a main Gradio Blocks app to host both the UI and the API
with gr.Blocks(title="馃強u200d鈾傦笍 42CodeRunner") as demo:
    # Render the defined interface within the Blocks
    iface.render()

# Mount the Flask app (containing the API) onto the Gradio Blocks app under /api
# This makes the Flask routes available at paths like /api/execute
demo = gr.mount_wsgi_app(demo, app, path="/api")

# --- Ejecuci贸n de la App ---

if __name__ == '__main__':
    # Use Gradio's default port or environment variable
    port = int(os.environ.get('PORT', 7860))
    # Launch the combined Gradio Blocks app
    # Allow connection from network (needed for Docker/Spaces)
    demo.launch(server_name='0.0.0.0', server_port=port)