ancerlop commited on
Commit
1e75d97
·
verified ·
1 Parent(s): 6a3fba0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -187
app.py CHANGED
@@ -1,187 +1,81 @@
1
- from flask import Flask, request, jsonify
2
- import subprocess
3
- import os
4
- import tempfile
5
- import uuid
6
- import time
7
- import logging
8
- from flask_cors import CORS
9
- import gradio as gr
10
- import requests
11
-
12
- app = Flask(__name__)
13
- CORS(app) # Habilitar CORS para todas las rutas
14
-
15
- # Configuración de logging
16
- logging.basicConfig(level=logging.INFO)
17
- logger = logging.getLogger(__name__)
18
-
19
- # Directorio temporal para los archivos de código
20
- TEMP_DIR = os.path.join(tempfile.gettempdir(), '42coderunner')
21
- os.makedirs(TEMP_DIR, exist_ok=True)
22
-
23
- # Tiempo máximo de ejecución (en segundos)
24
- MAX_EXECUTION_TIME = 10 # Aumentamos un poco para Gradio
25
-
26
- # URL base de la API (para la interfaz Gradio)
27
- # Detecta si estamos en Hugging Face Spaces
28
- API_BASE_URL = os.environ.get("SPACE_HOST")
29
- if API_BASE_URL:
30
- # En Hugging Face, usa la URL pública del espacio
31
- # Asegúrate de que el protocolo sea https
32
- API_BASE_URL = f"https://{API_BASE_URL}"
33
- else:
34
- # Localmente, usa localhost
35
- API_BASE_URL = "http://localhost:5000"
36
-
37
- API_EXECUTE_URL = f"{API_BASE_URL}/api/execute"
38
-
39
- # --- Funciones de la API Flask ---
40
-
41
- @app.route('/api/execute', methods=['POST'])
42
- def execute_code():
43
- try:
44
- # Obtener el código C del request
45
- data = request.get_json()
46
- if not data or 'code' not in data:
47
- return jsonify({'success': False, 'error': 'No se proporcionó código'}), 400
48
-
49
- code = data['code']
50
-
51
- # Crear un ID único para este trabajo
52
- job_id = str(uuid.uuid4())
53
-
54
- # Crear archivos temporales para el código y la salida
55
- code_file = os.path.join(TEMP_DIR, f"{job_id}.c")
56
- executable = os.path.join(TEMP_DIR, f"{job_id}.exe")
57
-
58
- # Guardar el código en un archivo temporal
59
- with open(code_file, 'w') as f:
60
- f.write(code)
61
-
62
- # Compilar el código
63
- logger.info(f"Compilando código para job {job_id}")
64
- compile_process = subprocess.run(
65
- ['gcc', code_file, '-o', executable],
66
- capture_output=True,
67
- text=True
68
- )
69
-
70
- # Verificar si la compilación fue exitosa
71
- if compile_process.returncode != 0:
72
- return jsonify({
73
- 'success': False,
74
- 'error': compile_process.stderr
75
- })
76
-
77
- # Ejecutar el código compilado
78
- logger.info(f"Ejecutando código para job {job_id}")
79
- try:
80
- start_time = time.time()
81
- run_process = subprocess.run(
82
- [executable],
83
- capture_output=True,
84
- text=True,
85
- timeout=MAX_EXECUTION_TIME
86
- )
87
- execution_time = time.time() - start_time
88
-
89
- # Preparar la respuesta
90
- result = {
91
- 'success': run_process.returncode == 0,
92
- 'output': run_process.stdout,
93
- 'error': run_process.stderr,
94
- 'execution_time': execution_time
95
- }
96
-
97
- except subprocess.TimeoutExpired:
98
- result = {
99
- 'success': False,
100
- 'error': f'La ejecución excedió el tiempo límite de {MAX_EXECUTION_TIME} segundos'
101
- }
102
-
103
- # Limpiar archivos temporales
104
- try:
105
- os.remove(code_file)
106
- if os.path.exists(executable):
107
- os.remove(executable)
108
- except Exception as e:
109
- logger.error(f"Error al limpiar archivos temporales: {e}")
110
-
111
- return jsonify(result)
112
-
113
- except Exception as e:
114
- logger.error(f"Error inesperado: {e}")
115
- return jsonify({'success': False, 'error': f'Error interno del servidor: {str(e)}'}), 500
116
-
117
- @app.route('/api/health', methods=['GET'])
118
- def health_check():
119
- """Basic health check endpoint."""
120
- logger.info("Health check requested.")
121
- # A simple health check is enough for Hugging Face
122
- # The previous checks (compiler, temp dir) might fail in the HF environment
123
- return jsonify({'status': 'ok', 'timestamp': time.time()})
124
-
125
- # --- Interfaz Gradio ---
126
-
127
- def run_c_code(c_code):
128
- """Función que se ejecuta cuando el usuario envía código C a través de Gradio."""
129
- logger.info(f"Solicitud Gradio recibida. URL API: {API_EXECUTE_URL}")
130
- try:
131
- response = requests.post(API_EXECUTE_URL, json={'code': c_code}, timeout=MAX_EXECUTION_TIME + 5) # Timeout un poco mayor para la request
132
- response.raise_for_status() # Lanza excepción para errores HTTP
133
- data = response.json()
134
-
135
- if data.get('success'):
136
- output = f"--- Salida ({data.get('execution_time', 0):.4f}s) ---\n{data.get('output', '')}"
137
- if data.get('error'): # Mostrar stderr aunque la ejecución sea exitosa (warnings, etc.)
138
- output += f"\n\n--- Errores (stderr) ---\n{data['error']}"
139
- return output
140
- else:
141
- return f"--- Error ---\n{data.get('error', 'Error desconocido')}"
142
-
143
- except requests.exceptions.RequestException as e:
144
- logger.error(f"Error de red al llamar a la API: {e}")
145
- return f"Error de red al conectar con el backend: {e}"
146
- except Exception as e:
147
- logger.error(f"Error inesperado en la interfaz Gradio: {e}")
148
- return f"Error inesperado en la interfaz: {str(e)}"
149
-
150
- # Ejemplo de código C para la interfaz
151
- EXAMPLE_CODE = """#include <stdio.h>
152
-
153
- int main() {
154
- printf("¡Hola desde 42CodeRunner!\n");
155
- return 0;
156
- }
157
- """
158
-
159
- # Crear la interfaz Gradio
160
- iface = gr.Interface(
161
- fn=run_c_code,
162
- inputs=gr.Code(language="c", label="Código C", value=EXAMPLE_CODE),
163
- outputs=gr.Textbox(label="Resultado de la Ejecución", lines=15),
164
- title="🏊u200d♂️ 42CodeRunner",
165
- description="Escribe tu código C, haz clic en 'Submit' y mira la magia suceder. Ejecutado de forma segura en el backend.",
166
- allow_flagging='never'
167
- )
168
-
169
- # --- Integration and Execution ---
170
-
171
- # Create a main Gradio Blocks app to host both the UI and the API
172
- with gr.Blocks(title="🏊u200d♂️ 42CodeRunner") as demo:
173
- # Render the defined interface within the Blocks
174
- iface.render()
175
-
176
- # Mount the Flask app (containing the API) onto the Gradio Blocks app under /api
177
- # This makes the Flask routes available at paths like /api/execute
178
- demo = gr.mount_wsgi_app(demo, app, path="/api")
179
-
180
- # --- Ejecución de la App ---
181
-
182
- if __name__ == '__main__':
183
- # Use Gradio's default port or environment variable
184
- port = int(os.environ.get('PORT', 7860))
185
- # Launch the combined Gradio Blocks app
186
- # Allow connection from network (needed for Docker/Spaces)
187
- demo.launch(server_name='0.0.0.0', server_port=port)
 
1
+ import gradio as gr
2
+ import subprocess, tempfile, os, uuid, resource
3
+
4
+ # --- Función principal --------------------------------------------------------
5
+ def compile_and_run(code: str, stdin: str = "") -> str:
6
+ """
7
+ Compila el código C, lo ejecuta y devuelve la salida (stdout + stderr).
8
+ Se aplican límites de tiempo y memoria para evitar abusos.
9
+ """
10
+ # 1. Crear carpeta temporal única
11
+ tmpdir = tempfile.mkdtemp(prefix="c_exec_")
12
+ c_path = os.path.join(tmpdir, "main.c")
13
+ bin_path = os.path.join(tmpdir, "main")
14
+
15
+ # 2. Guardar código
16
+ with open(c_path, "w") as f:
17
+ f.write(code)
18
+
19
+ # 3. Compilar
20
+ compile_proc = subprocess.run(
21
+ ["gcc", c_path, "-O2", "-std=c11", "-pipe", "-o", bin_path],
22
+ capture_output=True, text=True, timeout=10
23
+ )
24
+ if compile_proc.returncode != 0:
25
+ return f"❌ Error de compilación:\n{compile_proc.stderr}"
26
+
27
+ # 4. Establecer límites de recursos antes de ejecutar
28
+ def limit_resources():
29
+ resource.setrlimit(resource.RLIMIT_CPU, (2, 2)) # 2 s CPU
30
+ resource.setrlimit(resource.RLIMIT_AS, (128*1024*1024, # 128 MB
31
+ 128*1024*1024))
32
+
33
+ # 5. Ejecutar binario generado
34
+ try:
35
+ run_proc = subprocess.run(
36
+ [bin_path],
37
+ input=stdin,
38
+ text=True,
39
+ capture_output=True,
40
+ timeout=2, # tiempo real (wall-clock)
41
+ preexec_fn=limit_resources
42
+ )
43
+ except subprocess.TimeoutExpired:
44
+ return "⏰ Tiempo de ejecución excedido (2 s)."
45
+
46
+ out = run_proc.stdout
47
+ err = run_proc.stderr
48
+ exit = run_proc.returncode
49
+
50
+ response = f"⏹ Código de salida: {exit}\n"
51
+ if out:
52
+ response += f"\n📤 STDOUT\n{out}"
53
+ if err:
54
+ response += f"\n⚠️ STDERR\n{err}"
55
+ return response.strip()
56
+
57
+
58
+ # --- UI en Gradio -------------------------------------------------------------
59
+ title = "Compilador C online (42 Edition)"
60
+ description = """
61
+ Ejecuta fragmentos de **lenguaje C** con límite de recursos.
62
+ También disponible como endpoint REST (`/run/predict`).
63
+ """
64
+
65
+ demo = gr.Interface(
66
+ fn = compile_and_run,
67
+ inputs = [
68
+ gr.Code(language="c", label="Código C"),
69
+ gr.Textbox(lines=3, placeholder="Entrada estándar (stdin)", label="Stdin (opcional)")
70
+ ],
71
+ outputs = gr.Textbox(label="Resultado"),
72
+ title = title,
73
+ description = description,
74
+ examples = [
75
+ [r"#include <stdio.h>\nint main(){printf(\"Hola 42!\\n\");}", ""],
76
+ [r"#include <stdio.h>\nint main(){int a,b;scanf(\"%d %d\",&a,&b);printf(\"%d\\n\",a+b);}", "3 4"]
77
+ ],
78
+ )
79
+
80
+ if __name__ == "__main__":
81
+ demo.launch()