File size: 3,934 Bytes
b3c128e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
MimiReady Python Executor Backend
Exécute du code Python avec TensorFlow/Keras sur Hugging Face Spaces
"""

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional
import sys
import io
import base64
import traceback
from contextlib import redirect_stdout, redirect_stderr

# Configuration matplotlib avant import
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

app = FastAPI(title="MimiReady Python Executor")

# CORS pour permettre les requêtes depuis le frontend
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

class CodeRequest(BaseModel):
    code: str
    timeout: int = 30

class CodeResponse(BaseModel):
    stdout: str
    stderr: str
    plots: List[str]
    success: bool
    error: Optional[str] = None

# Variables globales pour capturer les plots
_plot_images: List[str] = []

def capture_plots():
    """Capture tous les plots matplotlib en base64"""
    global _plot_images
    _plot_images = []
    
    original_show = plt.show
    
    def patched_show(*args, **kwargs):
        global _plot_images
        buf = io.BytesIO()
        plt.savefig(buf, format='png', dpi=100, bbox_inches='tight', facecolor='white')
        buf.seek(0)
        img_str = base64.b64encode(buf.read()).decode('utf-8')
        _plot_images.append(img_str)
        plt.clf()
        plt.close('all')
    
    plt.show = patched_show
    return original_show

def restore_show(original_show):
    """Restaure plt.show original"""
    plt.show = original_show

@app.get("/")
def read_root():
    return {
        "service": "MimiReady Python Executor",
        "status": "running",
        "capabilities": ["numpy", "pandas", "matplotlib", "scikit-learn", "tensorflow", "keras"]
    }

@app.get("/health")
def health_check():
    return {"status": "healthy"}

@app.post("/execute", response_model=CodeResponse)
async def execute_code(request: CodeRequest):
    """Exécute du code Python et retourne les résultats"""
    global _plot_images
    
    stdout_capture = io.StringIO()
    stderr_capture = io.StringIO()
    
    # Préparer l'environnement d'exécution
    exec_globals = {
        "__builtins__": __builtins__,
        "__name__": "__main__",
    }
    
    # Pre-import des bibliothèques courantes
    try:
        import numpy as np
        exec_globals["np"] = np
        exec_globals["numpy"] = np
    except ImportError:
        pass
    
    try:
        import pandas as pd
        exec_globals["pd"] = pd
        exec_globals["pandas"] = pd
    except ImportError:
        pass
    
    try:
        import sklearn
        exec_globals["sklearn"] = sklearn
    except ImportError:
        pass
    
    # TensorFlow/Keras
    try:
        import tensorflow as tf
        from tensorflow import keras
        exec_globals["tf"] = tf
        exec_globals["tensorflow"] = tf
        exec_globals["keras"] = keras
    except ImportError as e:
        print(f"TensorFlow import warning: {e}")
    
    # Matplotlib
    exec_globals["plt"] = plt
    exec_globals["matplotlib"] = matplotlib
    
    success = True
    error_msg = None
    
    original_show = capture_plots()
    
    try:
        with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
            exec(request.code, exec_globals)
    except Exception as e:
        success = False
        error_msg = f"{type(e).__name__}: {str(e)}\n{traceback.format_exc()}"
    finally:
        restore_show(original_show)
    
    return CodeResponse(
        stdout=stdout_capture.getvalue(),
        stderr=stderr_capture.getvalue(),
        plots=_plot_images.copy(),
        success=success,
        error=error_msg
    )

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=7860)