minhvh commited on
Commit
49503d1
·
verified ·
1 Parent(s): 18d0a18

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -207
app.py CHANGED
@@ -1,34 +1,39 @@
1
  import gradio as gr
2
  import subprocess
3
  import tempfile
4
- import json
5
  import textwrap
6
  import os
7
- import ast
8
- import zipfile
9
  import logging
10
 
11
- # Setup logging
12
  logging.basicConfig(level=logging.DEBUG)
13
  logger = logging.getLogger(__name__)
14
 
15
- def execute_code_input(code, attached_files):
16
- """Chạy code nhập trực tiếp với file đính kèm - mỗi lần chạy trong session độc lập"""
17
 
18
- # Kiểm tra pháp
 
 
 
19
  try:
20
- ast.parse(code)
21
- except SyntaxError as e:
22
- return f"SyntaxError: {e.msg}\nLine {e.lineno}: {e.text or ''}", ""
23
-
24
- # Indent code
25
- indented_code = textwrap.indent(code, ' ')
26
-
27
- template = '''import sys
 
 
 
 
 
 
28
  import json
29
  from io import StringIO
30
  import contextlib
31
- import ast
32
 
33
  @contextlib.contextmanager
34
  def capture_output():
@@ -41,225 +46,121 @@ def capture_output():
41
  sys.stdout, sys.stderr = old_out, old_err
42
 
43
  def execute_code():
44
- {}
45
- return None
46
 
47
  with capture_output() as (stdout, stderr):
48
  try:
49
- import traceback
50
- # Thực thi code người dùng
51
  result_value = execute_code()
52
-
53
- # Nếu không có return thì thử lấy expression cuối cùng
54
- if result_value is None:
55
- try:
56
- tree = ast.parse("""{}""")
57
- if tree.body and isinstance(tree.body[-1], ast.Expr):
58
- last_expr = compile(ast.Expression(tree.body[-1].value), "<ast>", "eval")
59
- result_value = eval(last_expr, globals(), locals())
60
- except Exception:
61
- pass
62
-
63
  except Exception as e:
 
64
  print("Error:", traceback.format_exc(), file=sys.stderr)
65
  result_value = None
66
 
67
- result = {{
68
- 'stdout': stdout.getvalue(),
69
- 'stderr': stderr.getvalue(),
70
- 'return_value': result_value
71
- }}
72
- print(json.dumps(result))
73
- '''.format(indented_code, code)
74
-
75
- final_code = template
76
 
77
- with tempfile.TemporaryDirectory() as temp_dir:
78
- # Lưu file đính kèm vào thư mục tạm
79
- if attached_files is not None:
80
- for file in attached_files:
81
- filename = os.path.basename(file.name)
82
- dest_path = os.path.join(temp_dir, filename)
83
- with open(dest_path, 'wb') as f:
84
- with open(file.name, 'rb') as src:
85
- f.write(src.read())
86
 
87
- # Tạo file code.py
88
- with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, dir=temp_dir) as temp_file:
89
- temp_file.write(final_code)
90
- temp_file.flush()
91
 
92
- logger.debug(f"Generated code:\n{final_code}")
 
 
 
93
 
94
- try:
95
- result = subprocess.run(
96
- ['python3', temp_file.name],
97
- cwd=temp_dir,
98
- capture_output=True,
99
- text=True,
100
- timeout=180
101
- )
102
-
103
- try:
104
- output = json.loads(result.stdout)
105
- stdout = output.get('stdout', '')
106
- stderr = output.get('stderr', '')
107
- return_value = output.get('return_value', None)
108
-
109
- log_lines = []
110
- if stdout.strip():
111
- log_lines.append(stdout.rstrip())
112
- if stderr.strip():
113
- log_lines.append(stderr.rstrip())
114
- log_text = "\n".join(log_lines)
115
-
116
- return_display = str(return_value) if return_value is not None else ""
117
- return log_text, return_display
118
-
119
- except json.JSONDecodeError:
120
- return f"Error: Failed to parse result", ""
121
-
122
- except subprocess.TimeoutExpired:
123
- return f"Error: Execution timed out after 3 minutes", ""
124
- except Exception as e:
125
- return f"Error: {str(e)}", ""
126
- finally:
127
- try:
128
- os.unlink(temp_file.name)
129
- except:
130
- pass
131
-
132
-
133
- def execute_uploaded_files(files, main_file):
134
- """Chạy file upload"""
135
- if not files:
136
- return "Error: No files uploaded", ""
137
 
138
  with tempfile.TemporaryDirectory() as temp_dir:
139
- if len(files) == 1 and files[0].name.endswith('.zip'):
140
- try:
141
- with zipfile.ZipFile(files[0].name, 'r') as zip_ref:
142
- zip_ref.extractall(temp_dir)
143
- except Exception as e:
144
- return f"Error extracting zip: {str(e)}", ""
145
- else:
146
- for file in files:
147
- filename = os.path.basename(file.name)
148
- dest_path = os.path.join(temp_dir, filename)
149
- with open(dest_path, 'wb') as f:
150
- with open(file.name, 'rb') as src:
151
- f.write(src.read())
152
-
153
- main_path = os.path.join(temp_dir, main_file)
154
- if not os.path.exists(main_path):
155
- return f"Error: Main file '{main_file}' not found", ""
156
-
157
- try:
158
- result = subprocess.run(
159
- ['python3', main_path],
160
- cwd=temp_dir,
161
- capture_output=True,
162
- text=True,
163
- timeout=180
164
- )
165
-
166
- stdout = result.stdout if result.stdout else ""
167
- stderr = result.stderr if result.stderr else ""
168
-
169
- log_lines = []
170
- if stdout.strip():
171
- log_lines.append(stdout.rstrip())
172
- if stderr.strip():
173
- log_lines.append(stderr.rstrip())
174
- log_text = "\n".join(log_lines)
175
-
176
- return log_text, ""
177
 
178
- except subprocess.TimeoutExpired:
179
- return f"Error: Execution timed out after 3 minutes", ""
180
- except Exception as e:
181
- return f"Error: {str(e)}", ""
 
182
 
 
183
 
184
- # Examples
185
  code_examples = [
186
  "1 + 1",
187
- "print('Hello World!')",
188
- "x = 5\ny = 10\nx + y",
189
  "for i in range(3):\n print(i)"
190
  ]
191
 
192
- file_examples = [
193
- [["examples/hello.py"], "hello.py"],
194
- ]
195
-
196
- # Giao diện
197
  with gr.Blocks(theme=gr.themes.Default(primary_hue="red")) as demo:
198
- gr.Markdown("# 🔴 Python Code Executor")
199
- gr.Markdown("Run Python code - Choose your preferred method")
200
-
201
- with gr.Tabs():
202
- with gr.Tab("⌨️ Direct Code Input"):
203
- with gr.Row():
204
- with gr.Column():
205
- code_input = gr.Code(
206
- label="Python Code",
207
- language="python",
208
- interactive=True
209
- )
210
-
211
- attached_files = gr.File(
212
- label="📎 Attach Files (optional)",
213
- file_count="multiple"
214
- )
215
-
216
- run_code_btn = gr.Button("▶️ Run Code", variant="primary")
217
 
218
- gr.Markdown("### 📝 Examples:")
219
- examples_component = gr.Examples(
220
- examples=[[ex] for ex in code_examples], # ép mỗi ví dụ thành 1 list => hiển thị dọc
221
- inputs=[code_input],
222
- label=None,
223
- examples_per_page=10,
224
- )
225
 
226
- with gr.Column():
227
- log1 = gr.Textbox(label="Execution Log", lines=10)
228
- result1 = gr.Textbox(label="Return Value")
229
 
230
- run_code_btn.click(
231
- execute_code_input,
232
- [code_input, attached_files],
233
- [log1, result1]
 
 
234
  )
235
 
236
- with gr.Tab("📁 Upload Files"):
237
- with gr.Row():
238
- with gr.Column():
239
- files_upload = gr.File(
240
- label="Upload Python Files (.py) or ZIP",
241
- file_types=[".py", ".zip"],
242
- file_count="multiple"
243
- )
244
- main_file_input = gr.Textbox(
245
- label="Main File Name (with .py)",
246
- value="main.py",
247
- placeholder="e.g., main.py"
248
- )
249
- run_files_btn = gr.Button("▶️ Run Files", variant="primary")
250
- gr.Markdown("### 📝 Instructions:")
251
- gr.Markdown("- Upload multiple .py files and specify main file")
252
- gr.Markdown("- Or upload a single .zip file with all Python files")
253
 
254
- with gr.Column():
255
- log2 = gr.Textbox(label="Execution Log", lines=10)
256
- result2 = gr.Textbox(label="Return Value")
257
-
258
- run_files_btn.click(
259
- execute_uploaded_files,
260
- [files_upload, main_file_input],
261
- [log2, result2]
262
- )
263
 
264
  if __name__ == "__main__":
265
- demo.launch()
 
1
  import gradio as gr
2
  import subprocess
3
  import tempfile
4
+ import ast
5
  import textwrap
6
  import os
7
+ import json
 
8
  import logging
9
 
 
10
  logging.basicConfig(level=logging.DEBUG)
11
  logger = logging.getLogger(__name__)
12
 
13
+ # ----------------- Helper Functions -----------------
 
14
 
15
+ def wrap_last_expr_with_return(code: str) -> str:
16
+ """
17
+ Nếu dòng cuối là expression, tự động chuyển thành return.
18
+ """
19
  try:
20
+ tree = ast.parse(code)
21
+ if tree.body and isinstance(tree.body[-1], ast.Expr):
22
+ tree.body[-1] = ast.Return(value=tree.body[-1].value)
23
+ return ast.unparse(tree)
24
+ except Exception as e:
25
+ logger.warning(f"AST transform failed: {e}")
26
+ return code
27
+
28
+ def wrap_code_for_execution(user_code: str) -> str:
29
+ """
30
+ Tạo wrapper code để bắt stdout/stderr và đánh dấu return_value.
31
+ """
32
+ indented_code = textwrap.indent(user_code, " ")
33
+ template = f'''import sys
34
  import json
35
  from io import StringIO
36
  import contextlib
 
37
 
38
  @contextlib.contextmanager
39
  def capture_output():
 
46
  sys.stdout, sys.stderr = old_out, old_err
47
 
48
  def execute_code():
49
+ {indented_code}
 
50
 
51
  with capture_output() as (stdout, stderr):
52
  try:
 
 
53
  result_value = execute_code()
 
 
 
 
 
 
 
 
 
 
 
54
  except Exception as e:
55
+ import traceback
56
  print("Error:", traceback.format_exc(), file=sys.stderr)
57
  result_value = None
58
 
59
+ # In log stdout/stderr
60
+ if stdout.getvalue():
61
+ print(stdout.getvalue(), end="")
62
+ if stderr.getvalue():
63
+ print(stderr.getvalue(), end="", file=sys.stderr)
 
 
 
 
64
 
65
+ # Đánh dấu return_value
66
+ print("__RETURN_VALUE__:" + json.dumps(result_value))
67
+ '''
68
+ return template
 
 
 
 
 
69
 
70
+ # ----------------- Executor -----------------
 
 
 
71
 
72
+ def execute_code_input_stream(code, attached_files):
73
+ """Chạy code input + file đính kèm, stream log realtime"""
74
+ # AST transform dòng cuối
75
+ code = wrap_last_expr_with_return(code)
76
 
77
+ final_code = wrap_code_for_execution(code)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  with tempfile.TemporaryDirectory() as temp_dir:
80
+ # Lưu file đính kèm nếu có
81
+ if attached_files is not None:
82
+ for file in attached_files:
83
+ dest = os.path.join(temp_dir, os.path.basename(file.name))
84
+ with open(dest, "wb") as f:
85
+ f.write(file.read())
86
+
87
+ # Ghi file code
88
+ script_path = os.path.join(temp_dir, "main.py")
89
+ with open(script_path, "w") as f:
90
+ f.write(final_code)
91
+
92
+ logger.debug(f"Generated code:\n{final_code}")
93
+
94
+ # Chạy subprocess
95
+ process = subprocess.Popen(
96
+ ["python3", script_path],
97
+ cwd=temp_dir,
98
+ stdout=subprocess.PIPE,
99
+ stderr=subprocess.STDOUT,
100
+ text=True,
101
+ bufsize=1,
102
+ )
103
+
104
+ return_value = None
105
+ for line in process.stdout:
106
+ line = line.rstrip("\n")
107
+ if line.startswith("__RETURN_VALUE__:"):
108
+ try:
109
+ return_value = json.loads(line.replace("__RETURN_VALUE__:", "", 1))
110
+ except Exception:
111
+ return_value = None
112
+ else:
113
+ yield line
 
 
 
 
114
 
115
+ process.wait()
116
+ if return_value is not None:
117
+ yield f"[Return Value] {return_value}"
118
+ elif process.returncode != 0:
119
+ yield f"[Process exited with code {process.returncode}]"
120
 
121
+ # ----------------- Gradio UI -----------------
122
 
 
123
  code_examples = [
124
  "1 + 1",
125
+ "a = 5\na",
126
+ "print('Hello')\n3 * 3",
127
  "for i in range(3):\n print(i)"
128
  ]
129
 
 
 
 
 
 
130
  with gr.Blocks(theme=gr.themes.Default(primary_hue="red")) as demo:
131
+ gr.Markdown("# 🔴 Python Executor (Stream Realtime + Return Last Expression)")
132
+
133
+ with gr.Row():
134
+ with gr.Column():
135
+ code_input = gr.Code(
136
+ label="Python Code",
137
+ language="python",
138
+ interactive=True
139
+ )
 
 
 
 
 
 
 
 
 
 
140
 
141
+ attached_files = gr.File(
142
+ label="📎 Attach Files (optional)",
143
+ file_count="multiple"
144
+ )
 
 
 
145
 
146
+ run_code_btn = gr.Button("▶️ Run Code", variant="primary")
 
 
147
 
148
+ gr.Markdown("### 📝 Examples:")
149
+ examples_component = gr.Examples(
150
+ examples=[[ex] for ex in code_examples], # ép dọc
151
+ inputs=[code_input],
152
+ label=None,
153
+ examples_per_page=10,
154
  )
155
 
156
+ with gr.Column():
157
+ log1 = gr.Textbox(label="Execution Log", lines=15)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
+ run_code_btn.click(
160
+ execute_code_input_stream,
161
+ [code_input, attached_files],
162
+ log1,
163
+ )
 
 
 
 
164
 
165
  if __name__ == "__main__":
166
+ demo.launch()