minhvh commited on
Commit
b98e300
·
verified ·
1 Parent(s): 9a2d75a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -163
app.py CHANGED
@@ -1,179 +1,97 @@
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
- """Nếu dòng cuối là expression, tự động chuyển thành return."""
 
17
  try:
18
- tree = ast.parse(code)
19
- if tree.body and isinstance(tree.body[-1], ast.Expr):
20
- tree.body[-1] = ast.Return(value=tree.body[-1].value)
21
- return ast.unparse(tree)
22
  except Exception as e:
23
- logger.warning(f"AST transform failed: {e}")
24
- return code
25
-
26
- def wrap_code_for_execution(user_code: str) -> str:
27
- """Tạo wrapper code để bắt stdout/stderr và đánh dấu return_value."""
28
- indented_code = textwrap.indent(user_code, " ")
29
- template = f'''import sys
30
- import json
31
- from io import StringIO
32
- import contextlib
33
-
34
- @contextlib.contextmanager
35
- def capture_output():
36
- stdout, stderr = StringIO(), StringIO()
37
- old_out, old_err = sys.stdout, sys.stderr
38
- try:
39
- sys.stdout, sys.stderr = stdout, stderr
40
- yield stdout, stderr
41
- finally:
42
- sys.stdout, sys.stderr = old_out, old_err
43
-
44
- def execute_code():
45
- {indented_code}
46
-
47
- with capture_output() as (stdout, stderr):
48
  try:
49
- result_value = execute_code()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  except Exception as e:
51
- import traceback
52
- print("Error:", traceback.format_exc(), file=sys.stderr)
53
- result_value = None
54
-
55
- # In log stdout/stderr sau khi chạy xong
56
- log_text = stdout.getvalue() + stderr.getvalue()
57
- print(log_text, end="")
58
-
59
- # Đánh dấu return_value riêng
60
- print("__RETURN_VALUE__:" + json.dumps(result_value))
61
- '''
62
- return template
63
-
64
- # ----------------- Executor -----------------
65
-
66
- def execute_code_input(code, attached_files, state_process: gr.State):
67
- """Chạy code input + file đính kèm, trả log và return riêng, không stream"""
68
- code = wrap_last_expr_with_return(code)
69
- final_code = wrap_code_for_execution(code)
70
-
71
- with tempfile.TemporaryDirectory() as temp_dir:
72
- # Lưu file đính kèm
73
- if attached_files is not None:
74
- for file in attached_files:
75
- dest = os.path.join(temp_dir, os.path.basename(file.name))
76
- with open(dest, "wb") as f:
77
- f.write(file.read())
78
-
79
- # Ghi file code
80
- script_path = os.path.join(temp_dir, "main.py")
81
- with open(script_path, "w") as f:
82
- f.write(final_code)
83
-
84
- logger.debug(f"Generated code:\n{final_code}")
85
-
86
- # Chạy subprocess unbuffered (-u)
87
- process = subprocess.Popen(
88
- ["python3", "-u", script_path],
89
- cwd=temp_dir,
90
- stdout=subprocess.PIPE,
91
- stderr=subprocess.STDOUT,
92
- text=True,
93
- )
94
-
95
- state_process.value = process # Lưu process vào state để cancel
96
-
97
- # Chờ chạy xong
98
- stdout, _ = process.communicate()
99
- state_process.value = None
100
-
101
- log_text = []
102
- return_value = None
103
- for line in stdout.splitlines():
104
- if line.startswith("__RETURN_VALUE__:"):
105
- try:
106
- return_value = json.loads(line.replace("__RETURN_VALUE__:", "", 1))
107
- except Exception:
108
- return_value = None
109
- else:
110
- log_text.append(line)
111
-
112
- return "\n".join(log_text), str(return_value) if return_value is not None else ""
113
-
114
- def cancel_code(state_process: gr.State):
115
- process = state_process.value
116
- if process and process.poll() is None:
117
- process.terminate()
118
- state_process.value = None
119
- return "Execution Cancelled"
120
- return "No running process"
121
-
122
- # ----------------- Gradio UI -----------------
123
-
124
- code_examples = [
125
- "1 + 1",
126
- "a = 5\na",
127
- "print('Hello')\n3 * 3",
128
- "for i in range(3):\n print(i)",
129
- "from time import sleep\nfor i in [1,2,3]:\n sleep(1)\n print(i)"
130
- ]
131
-
132
- with gr.Blocks(theme=gr.themes.Default(primary_hue="red")) as demo:
133
- gr.Markdown("# 🔴 Python Executor (Log + Return Value Separate + Cancel)")
134
-
135
- state_process = gr.State(None) # Lưu subprocess để cancel
136
 
137
  with gr.Row():
138
  with gr.Column():
139
- code_input = gr.Code(
140
- label="Python Code",
141
- language="python",
142
- interactive=True
143
- )
144
-
145
- attached_files = gr.File(
146
- label="📎 Attach Files (optional)",
147
- file_types=[".txt", ".csv", ".json", ".py", ".md", ".xml", ".yaml", ".yml"],
148
- file_count="multiple"
149
- )
150
-
151
- run_code_btn = gr.Button("▶️ Run Code")
152
- cancel_btn = gr.Button("⏹ Cancel")
153
-
154
- gr.Markdown("### 📝 Examples:")
155
- examples_component = gr.Examples(
156
- examples=[[ex] for ex in code_examples], # ép dọc
157
- inputs=[code_input],
158
- label=None,
159
- examples_per_page=10,
160
- )
161
 
162
  with gr.Column():
163
- log_box = gr.Textbox(label="Execution Log", lines=15)
164
- return_box = gr.Textbox(label="Return Value", lines=1)
165
-
166
- run_code_btn.click(
167
- execute_code_input,
168
- [code_input, attached_files, state_process],
169
- [log_box, return_box],
170
- )
171
-
172
- cancel_btn.click(
173
- cancel_code,
174
- [state_process],
175
- [log_box]
176
  )
177
 
178
  if __name__ == "__main__":
179
- demo.launch()
 
1
  import gradio as gr
2
+ import sys
3
+ import io
4
+ import contextlib
5
  import ast
 
6
  import os
7
+ import shutil
8
+ import tempfile
 
 
 
 
 
9
 
10
+ def run(code):
11
+ if not code.strip():
12
+ return "No code provided", ""
13
  try:
14
+ ast.parse(code)
 
 
 
15
  except Exception as e:
16
+ return str(e), ""
17
+
18
+ temp_dir = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  try:
20
+ temp_dir = tempfile.mkdtemp()
21
+ sys.path.insert(0, temp_dir)
22
+
23
+ @contextlib.contextmanager
24
+ def capture_output():
25
+ stdout, stderr = io.StringIO(), io.StringIO()
26
+ old_out, old_err = sys.stdout, sys.stderr
27
+ try:
28
+ sys.stdout, sys.stderr = stdout, stderr
29
+ yield stdout, stderr
30
+ finally:
31
+ sys.stdout, sys.stderr = old_out, old_err
32
+
33
+ with capture_output() as (stdout, stderr):
34
+ try:
35
+ globals_dict = {}
36
+ locals_dict = {}
37
+
38
+ code_lines = code.strip().split('\n')
39
+
40
+ if len(code_lines) > 1:
41
+ exec('\n'.join(code_lines[:-1]), globals_dict, locals_dict)
42
+
43
+ last_line = code_lines[-1].strip()
44
+ result_value = None
45
+
46
+ if last_line:
47
+ try:
48
+ compile(last_line, '<string>', 'eval')
49
+ result_value = eval(last_line, globals_dict, locals_dict)
50
+ except SyntaxError:
51
+ exec(last_line, globals_dict, locals_dict)
52
+
53
+ except Exception as e:
54
+ print(str(e), file=sys.stderr)
55
+ result_value = None
56
+
57
+ combined_output = stdout.getvalue() + stderr.getvalue()
58
+ return_value = str(result_value) if result_value is not None else ""
59
+
60
+ return combined_output, return_value
61
+
62
  except Exception as e:
63
+ return str(e), ""
64
+ finally:
65
+ if temp_dir is not None:
66
+ try:
67
+ sys.path.remove(temp_dir)
68
+ except ValueError:
69
+ pass
70
+ try:
71
+ shutil.rmtree(temp_dir)
72
+ except:
73
+ pass
74
+
75
+ with gr.Blocks(title="Python Code Executor") as demo:
76
+ gr.Markdown("# 🔧 Python Code Executor")
77
+ gr.Markdown("Enter your Python code below or upload files, then click **Run** to execute it.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  with gr.Row():
80
  with gr.Column():
81
+ code_input = gr.Code(label="Python Code", language="python", lines=15)
82
+ output_display = gr.Textbox(label="Log", interactive=False)
83
+ return_value_display = gr.Textbox(label="Return", interactive=False)
84
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  with gr.Column():
87
+ file_upload = gr.File(label="Upload Files (txt, csv, images, etc.)", file_types=None, file_count="multiple")
88
+ run_button = gr.Button(" Run Code", variant="primary")
89
+
90
+ run_button.click(
91
+ fn=execute_python_code,
92
+ inputs=[code_input, file_upload],
93
+ outputs=[output_display, return_value_display]
 
 
 
 
 
 
94
  )
95
 
96
  if __name__ == "__main__":
97
+ demo.launch()