scalebox-sdk 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scalebox/__init__.py +80 -0
- scalebox/api/__init__.py +128 -0
- scalebox/api/client/__init__.py +8 -0
- scalebox/api/client/api/__init__.py +1 -0
- scalebox/api/client/api/sandboxes/__init__.py +0 -0
- scalebox/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
- scalebox/api/client/api/sandboxes/get_sandboxes.py +176 -0
- scalebox/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
- scalebox/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
- scalebox/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
- scalebox/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +214 -0
- scalebox/api/client/api/sandboxes/get_v2_sandboxes.py +229 -0
- scalebox/api/client/api/sandboxes/post_sandboxes.py +174 -0
- scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +165 -0
- scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +182 -0
- scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +190 -0
- scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +194 -0
- scalebox/api/client/client.py +288 -0
- scalebox/api/client/errors.py +16 -0
- scalebox/api/client/models/__init__.py +81 -0
- scalebox/api/client/models/build_log_entry.py +79 -0
- scalebox/api/client/models/created_access_token.py +100 -0
- scalebox/api/client/models/created_team_api_key.py +166 -0
- scalebox/api/client/models/error.py +67 -0
- scalebox/api/client/models/identifier_masking_details.py +83 -0
- scalebox/api/client/models/listed_sandbox.py +138 -0
- scalebox/api/client/models/log_level.py +11 -0
- scalebox/api/client/models/new_access_token.py +59 -0
- scalebox/api/client/models/new_sandbox.py +125 -0
- scalebox/api/client/models/new_team_api_key.py +59 -0
- scalebox/api/client/models/node.py +154 -0
- scalebox/api/client/models/node_detail.py +152 -0
- scalebox/api/client/models/node_status.py +11 -0
- scalebox/api/client/models/node_status_change.py +61 -0
- scalebox/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
- scalebox/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
- scalebox/api/client/models/resumed_sandbox.py +68 -0
- scalebox/api/client/models/sandbox.py +125 -0
- scalebox/api/client/models/sandbox_detail.py +178 -0
- scalebox/api/client/models/sandbox_log.py +70 -0
- scalebox/api/client/models/sandbox_logs.py +73 -0
- scalebox/api/client/models/sandbox_metric.py +110 -0
- scalebox/api/client/models/sandbox_state.py +9 -0
- scalebox/api/client/models/sandboxes_with_metrics.py +59 -0
- scalebox/api/client/models/team.py +83 -0
- scalebox/api/client/models/team_api_key.py +158 -0
- scalebox/api/client/models/team_user.py +68 -0
- scalebox/api/client/models/template.py +179 -0
- scalebox/api/client/models/template_build.py +117 -0
- scalebox/api/client/models/template_build_file_upload.py +70 -0
- scalebox/api/client/models/template_build_request.py +115 -0
- scalebox/api/client/models/template_build_request_v2.py +88 -0
- scalebox/api/client/models/template_build_start_v2.py +114 -0
- scalebox/api/client/models/template_build_status.py +11 -0
- scalebox/api/client/models/template_step.py +91 -0
- scalebox/api/client/models/template_update_request.py +59 -0
- scalebox/api/client/models/update_team_api_key.py +59 -0
- scalebox/api/client/py.typed +1 -0
- scalebox/api/client/types.py +46 -0
- scalebox/api/metadata.py +19 -0
- scalebox/cli.py +125 -0
- scalebox/client/__init__.py +0 -0
- scalebox/client/aclient.py +57 -0
- scalebox/client/api.proto +460 -0
- scalebox/client/buf.gen.yaml +8 -0
- scalebox/client/client.py +102 -0
- scalebox/client/requirements.txt +5 -0
- scalebox/code_interpreter/__init__.py +12 -0
- scalebox/code_interpreter/charts.py +230 -0
- scalebox/code_interpreter/code_interpreter_async.py +369 -0
- scalebox/code_interpreter/code_interpreter_sync.py +317 -0
- scalebox/code_interpreter/constants.py +3 -0
- scalebox/code_interpreter/exceptions.py +13 -0
- scalebox/code_interpreter/models.py +485 -0
- scalebox/connection_config.py +92 -0
- scalebox/csx_connect/__init__.py +1 -0
- scalebox/csx_connect/client.py +485 -0
- scalebox/csx_desktop/__init__.py +0 -0
- scalebox/csx_desktop/main.py +651 -0
- scalebox/exceptions.py +83 -0
- scalebox/generated/__init__.py +0 -0
- scalebox/generated/api.py +61 -0
- scalebox/generated/api_pb2.py +203 -0
- scalebox/generated/api_pb2.pyi +956 -0
- scalebox/generated/api_pb2_connect.py +1456 -0
- scalebox/generated/rpc.py +50 -0
- scalebox/generated/versions.py +3 -0
- scalebox/requirements.txt +36 -0
- scalebox/sandbox/__init__.py +0 -0
- scalebox/sandbox/commands/__init__.py +0 -0
- scalebox/sandbox/commands/command_handle.py +69 -0
- scalebox/sandbox/commands/main.py +39 -0
- scalebox/sandbox/filesystem/__init__.py +0 -0
- scalebox/sandbox/filesystem/filesystem.py +95 -0
- scalebox/sandbox/filesystem/watch_handle.py +60 -0
- scalebox/sandbox/main.py +139 -0
- scalebox/sandbox/sandbox_api.py +91 -0
- scalebox/sandbox/signature.py +40 -0
- scalebox/sandbox/utils.py +34 -0
- scalebox/sandbox_async/__init__.py +1 -0
- scalebox/sandbox_async/commands/command.py +307 -0
- scalebox/sandbox_async/commands/command_handle.py +187 -0
- scalebox/sandbox_async/commands/pty.py +187 -0
- scalebox/sandbox_async/filesystem/filesystem.py +557 -0
- scalebox/sandbox_async/filesystem/watch_handle.py +61 -0
- scalebox/sandbox_async/main.py +646 -0
- scalebox/sandbox_async/sandbox_api.py +365 -0
- scalebox/sandbox_async/utils.py +7 -0
- scalebox/sandbox_sync/__init__.py +2 -0
- scalebox/sandbox_sync/commands/__init__.py +0 -0
- scalebox/sandbox_sync/commands/command.py +300 -0
- scalebox/sandbox_sync/commands/command_handle.py +150 -0
- scalebox/sandbox_sync/commands/pty.py +181 -0
- scalebox/sandbox_sync/filesystem/__init__.py +0 -0
- scalebox/sandbox_sync/filesystem/filesystem.py +543 -0
- scalebox/sandbox_sync/filesystem/watch_handle.py +66 -0
- scalebox/sandbox_sync/main.py +790 -0
- scalebox/sandbox_sync/sandbox_api.py +356 -0
- scalebox/test/CODE_INTERPRETER_TESTS_READY.md +323 -0
- scalebox/test/README.md +329 -0
- scalebox/test/__init__.py +0 -0
- scalebox/test/aclient.py +72 -0
- scalebox/test/code_interpreter_centext.py +21 -0
- scalebox/test/code_interpreter_centext_sync.py +21 -0
- scalebox/test/code_interpreter_test.py +34 -0
- scalebox/test/code_interpreter_test_sync.py +34 -0
- scalebox/test/run_all_validation_tests.py +334 -0
- scalebox/test/run_code_interpreter_tests.sh +67 -0
- scalebox/test/run_tests.sh +230 -0
- scalebox/test/test_basic.py +78 -0
- scalebox/test/test_code_interpreter_async_comprehensive.py +2653 -0
- scalebox/test/test_code_interpreter_e2basync_comprehensive.py +2655 -0
- scalebox/test/test_code_interpreter_e2bsync_comprehensive.py +3416 -0
- scalebox/test/test_code_interpreter_sync_comprehensive.py +3412 -0
- scalebox/test/test_e2b_first.py +11 -0
- scalebox/test/test_sandbox_async_comprehensive.py +738 -0
- scalebox/test/test_sandbox_stress_and_edge_cases.py +778 -0
- scalebox/test/test_sandbox_sync_comprehensive.py +770 -0
- scalebox/test/test_sandbox_usage_examples.py +987 -0
- scalebox/test/testacreate.py +24 -0
- scalebox/test/testagetinfo.py +18 -0
- scalebox/test/testcodeinterpreter_async.py +508 -0
- scalebox/test/testcodeinterpreter_sync.py +239 -0
- scalebox/test/testcomputeuse.py +243 -0
- scalebox/test/testnovnc.py +12 -0
- scalebox/test/testsandbox_async.py +118 -0
- scalebox/test/testsandbox_sync.py +38 -0
- scalebox/utils/__init__.py +0 -0
- scalebox/utils/httpcoreclient.py +297 -0
- scalebox/utils/httpxclient.py +403 -0
- scalebox/version.py +16 -0
- scalebox_sdk-0.1.0.dist-info/METADATA +292 -0
- scalebox_sdk-0.1.0.dist-info/RECORD +157 -0
- scalebox_sdk-0.1.0.dist-info/WHEEL +5 -0
- scalebox_sdk-0.1.0.dist-info/entry_points.txt +2 -0
- scalebox_sdk-0.1.0.dist-info/licenses/LICENSE +21 -0
- scalebox_sdk-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Comprehensive usage examples and best practices for both sandbox modules.
|
|
4
|
+
|
|
5
|
+
This file demonstrates:
|
|
6
|
+
- Real-world usage scenarios
|
|
7
|
+
- Best practices for different use cases
|
|
8
|
+
- Code patterns and idioms
|
|
9
|
+
- Integration examples
|
|
10
|
+
- Performance optimization techniques
|
|
11
|
+
- Error handling strategies
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import datetime
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
import tempfile
|
|
20
|
+
import threading
|
|
21
|
+
import time
|
|
22
|
+
from contextlib import asynccontextmanager, contextmanager
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Dict, List, Optional, Union
|
|
25
|
+
|
|
26
|
+
from scalebox.exceptions import SandboxException
|
|
27
|
+
from scalebox.sandbox.commands.command_handle import PtySize
|
|
28
|
+
from scalebox.sandbox.filesystem.filesystem import EntryInfo, WriteInfo
|
|
29
|
+
from scalebox.sandbox_async.main import AsyncSandbox
|
|
30
|
+
from scalebox.sandbox_sync.main import Sandbox
|
|
31
|
+
|
|
32
|
+
# 配置日志
|
|
33
|
+
logging.basicConfig(
|
|
34
|
+
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
|
35
|
+
)
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ======================== 工具类和辅助函数 ========================
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SandboxManager:
|
|
43
|
+
"""沙箱管理器 - 展示资源管理最佳实践"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, template: str = "base", max_concurrent: int = 5):
|
|
46
|
+
self.template = template
|
|
47
|
+
self.max_concurrent = max_concurrent
|
|
48
|
+
self._active_sandboxes: Dict[str, Sandbox] = {}
|
|
49
|
+
self._lock = (
|
|
50
|
+
asyncio.Lock() if asyncio.iscoroutinefunction(self.__class__) else None
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@contextmanager
|
|
54
|
+
def get_sandbox(self, sandbox_id: Optional[str] = None):
|
|
55
|
+
"""上下文管理器获取沙箱"""
|
|
56
|
+
sandbox = None
|
|
57
|
+
created_new = False
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
if sandbox_id and sandbox_id in self._active_sandboxes:
|
|
61
|
+
sandbox = self._active_sandboxes[sandbox_id]
|
|
62
|
+
else:
|
|
63
|
+
sandbox = Sandbox(template=self.template)
|
|
64
|
+
sandbox_id = sandbox.sandbox_id
|
|
65
|
+
self._active_sandboxes[sandbox_id] = sandbox
|
|
66
|
+
created_new = True
|
|
67
|
+
logger.info(f"创建新沙箱: {sandbox_id}")
|
|
68
|
+
|
|
69
|
+
yield sandbox
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.error(f"沙箱操作异常: {e}")
|
|
73
|
+
raise
|
|
74
|
+
finally:
|
|
75
|
+
if created_new and sandbox:
|
|
76
|
+
try:
|
|
77
|
+
sandbox.kill()
|
|
78
|
+
self._active_sandboxes.pop(sandbox_id, None)
|
|
79
|
+
logger.info(f"清理沙箱: {sandbox_id}")
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f"清理沙箱失败: {e}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class AsyncSandboxManager:
|
|
85
|
+
"""异步沙箱管理器"""
|
|
86
|
+
|
|
87
|
+
def __init__(self, template: str = "base", max_concurrent: int = 10):
|
|
88
|
+
self.template = template
|
|
89
|
+
self.max_concurrent = max_concurrent
|
|
90
|
+
self._active_sandboxes: Dict[str, AsyncSandbox] = {}
|
|
91
|
+
self._lock = asyncio.Lock()
|
|
92
|
+
self._semaphore = asyncio.Semaphore(max_concurrent)
|
|
93
|
+
|
|
94
|
+
@asynccontextmanager
|
|
95
|
+
async def get_sandbox(self, sandbox_id: Optional[str] = None):
|
|
96
|
+
"""异步上下文管理器获取沙箱"""
|
|
97
|
+
async with self._semaphore: # 限制并发数
|
|
98
|
+
sandbox = None
|
|
99
|
+
created_new = False
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
async with self._lock:
|
|
103
|
+
if sandbox_id and sandbox_id in self._active_sandboxes:
|
|
104
|
+
sandbox = self._active_sandboxes[sandbox_id]
|
|
105
|
+
else:
|
|
106
|
+
sandbox = await AsyncSandbox.create(template=self.template)
|
|
107
|
+
sandbox_id = sandbox.sandbox_id
|
|
108
|
+
self._active_sandboxes[sandbox_id] = sandbox
|
|
109
|
+
created_new = True
|
|
110
|
+
logger.info(f"创建新异步沙箱: {sandbox_id}")
|
|
111
|
+
|
|
112
|
+
yield sandbox
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.error(f"异步沙箱操作异常: {e}")
|
|
116
|
+
raise
|
|
117
|
+
finally:
|
|
118
|
+
if created_new and sandbox:
|
|
119
|
+
try:
|
|
120
|
+
await sandbox.kill()
|
|
121
|
+
async with self._lock:
|
|
122
|
+
self._active_sandboxes.pop(sandbox_id, None)
|
|
123
|
+
logger.info(f"清理异步沙箱: {sandbox_id}")
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.error(f"清理异步沙箱失败: {e}")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def retry_on_failure(max_retries: int = 3, delay: float = 1.0):
|
|
129
|
+
"""重试装饰器"""
|
|
130
|
+
|
|
131
|
+
def decorator(func):
|
|
132
|
+
def wrapper(*args, **kwargs):
|
|
133
|
+
last_exception = None
|
|
134
|
+
for attempt in range(max_retries):
|
|
135
|
+
try:
|
|
136
|
+
return func(*args, **kwargs)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
last_exception = e
|
|
139
|
+
if attempt < max_retries - 1:
|
|
140
|
+
logger.warning(
|
|
141
|
+
f"尝试 {attempt + 1}/{max_retries} 失败: {e}, {delay}秒后重试"
|
|
142
|
+
)
|
|
143
|
+
time.sleep(delay)
|
|
144
|
+
else:
|
|
145
|
+
logger.error(f"所有重试失败: {e}")
|
|
146
|
+
raise last_exception
|
|
147
|
+
|
|
148
|
+
return wrapper
|
|
149
|
+
|
|
150
|
+
return decorator
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def async_retry_on_failure(max_retries: int = 3, delay: float = 1.0):
|
|
154
|
+
"""异步重试装饰器"""
|
|
155
|
+
|
|
156
|
+
def decorator(func):
|
|
157
|
+
async def wrapper(*args, **kwargs):
|
|
158
|
+
last_exception = None
|
|
159
|
+
for attempt in range(max_retries):
|
|
160
|
+
try:
|
|
161
|
+
return await func(*args, **kwargs)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
last_exception = e
|
|
164
|
+
if attempt < max_retries - 1:
|
|
165
|
+
logger.warning(
|
|
166
|
+
f"异步尝试 {attempt + 1}/{max_retries} 失败: {e}, {delay}秒后重试"
|
|
167
|
+
)
|
|
168
|
+
await asyncio.sleep(delay)
|
|
169
|
+
else:
|
|
170
|
+
logger.error(f"所有异步重试失败: {e}")
|
|
171
|
+
raise last_exception
|
|
172
|
+
|
|
173
|
+
return wrapper
|
|
174
|
+
|
|
175
|
+
return decorator
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ======================== 实际使用场景示例 ========================
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class CodeExecutionService:
|
|
182
|
+
"""代码执行服务 - 展示实际应用场景"""
|
|
183
|
+
|
|
184
|
+
def __init__(self, use_async: bool = False):
|
|
185
|
+
self.use_async = use_async
|
|
186
|
+
if use_async:
|
|
187
|
+
self.sandbox_manager = AsyncSandboxManager()
|
|
188
|
+
else:
|
|
189
|
+
self.sandbox_manager = SandboxManager()
|
|
190
|
+
|
|
191
|
+
def execute_python_code(self, code: str, timeout: int = 30) -> Dict[str, Any]:
|
|
192
|
+
"""执行Python代码"""
|
|
193
|
+
with self.sandbox_manager.get_sandbox() as sandbox:
|
|
194
|
+
# 创建Python文件
|
|
195
|
+
code_file = "/tmp/user_code.py"
|
|
196
|
+
sandbox.files.write(code_file, code)
|
|
197
|
+
|
|
198
|
+
# 执行代码
|
|
199
|
+
result = sandbox.commands.run(
|
|
200
|
+
f"cd /tmp && timeout {timeout} python3 user_code.py",
|
|
201
|
+
timeout=timeout + 5,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
"exit_code": result.exit_code,
|
|
206
|
+
"stdout": result.stdout,
|
|
207
|
+
"stderr": result.stderr,
|
|
208
|
+
"execution_time": time.time(),
|
|
209
|
+
"timed_out": result.exit_code == 124, # timeout命令的退出码
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async def async_execute_python_code(
|
|
213
|
+
self, code: str, timeout: int = 30
|
|
214
|
+
) -> Dict[str, Any]:
|
|
215
|
+
"""异步执行Python代码"""
|
|
216
|
+
async with self.sandbox_manager.get_sandbox() as sandbox:
|
|
217
|
+
# 创建Python文件
|
|
218
|
+
code_file = "/tmp/async_user_code.py"
|
|
219
|
+
await sandbox.files.write(code_file, code)
|
|
220
|
+
|
|
221
|
+
# 异步执行代码
|
|
222
|
+
result = await sandbox.commands.run(
|
|
223
|
+
f"cd /tmp && timeout {timeout} python3 async_user_code.py",
|
|
224
|
+
timeout=timeout + 5,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
"exit_code": result.exit_code,
|
|
229
|
+
"stdout": result.stdout,
|
|
230
|
+
"stderr": result.stderr,
|
|
231
|
+
"execution_time": time.time(),
|
|
232
|
+
"timed_out": result.exit_code == 124,
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class FileProcessingService:
|
|
237
|
+
"""文件处理服务 - 展示文件操作最佳实践"""
|
|
238
|
+
|
|
239
|
+
@retry_on_failure(max_retries=3)
|
|
240
|
+
def process_files_batch(
|
|
241
|
+
self, files_data: List[Dict[str, str]]
|
|
242
|
+
) -> List[Dict[str, Any]]:
|
|
243
|
+
"""批量处理文件"""
|
|
244
|
+
with SandboxManager().get_sandbox() as sandbox:
|
|
245
|
+
results = []
|
|
246
|
+
|
|
247
|
+
# 批量写入文件
|
|
248
|
+
write_operations = [
|
|
249
|
+
{"path": f"/tmp/{file_info['name']}", "data": file_info["content"]}
|
|
250
|
+
for file_info in files_data
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
written_files = sandbox.files.write(write_operations)
|
|
254
|
+
logger.info(f"成功写入 {len(written_files)} 个文件")
|
|
255
|
+
|
|
256
|
+
# 处理每个文件
|
|
257
|
+
for file_info in files_data:
|
|
258
|
+
file_path = f"/tmp/{file_info['name']}"
|
|
259
|
+
|
|
260
|
+
# 获取文件信息
|
|
261
|
+
file_stats = sandbox.files.get_info(file_path)
|
|
262
|
+
|
|
263
|
+
# 根据文件类型进行处理
|
|
264
|
+
if file_info["name"].endswith(".txt"):
|
|
265
|
+
# 文本文件:统计行数和字符数
|
|
266
|
+
result = sandbox.commands.run(f"wc -l -c {file_path}")
|
|
267
|
+
lines_chars = result.stdout.strip().split()
|
|
268
|
+
|
|
269
|
+
results.append(
|
|
270
|
+
{
|
|
271
|
+
"name": file_info["name"],
|
|
272
|
+
"size": file_stats.size,
|
|
273
|
+
"lines": int(lines_chars[0]) if len(lines_chars) > 0 else 0,
|
|
274
|
+
"chars": int(lines_chars[1]) if len(lines_chars) > 1 else 0,
|
|
275
|
+
"type": "text",
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
elif file_info["name"].endswith(".json"):
|
|
280
|
+
# JSON文件:验证格式
|
|
281
|
+
content = sandbox.files.read(file_path)
|
|
282
|
+
try:
|
|
283
|
+
json.loads(content)
|
|
284
|
+
valid_json = True
|
|
285
|
+
error = None
|
|
286
|
+
except json.JSONDecodeError as e:
|
|
287
|
+
valid_json = False
|
|
288
|
+
error = str(e)
|
|
289
|
+
|
|
290
|
+
results.append(
|
|
291
|
+
{
|
|
292
|
+
"name": file_info["name"],
|
|
293
|
+
"size": file_stats.size,
|
|
294
|
+
"valid_json": valid_json,
|
|
295
|
+
"error": error,
|
|
296
|
+
"type": "json",
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
else:
|
|
301
|
+
# 其他文件:基本信息
|
|
302
|
+
results.append(
|
|
303
|
+
{
|
|
304
|
+
"name": file_info["name"],
|
|
305
|
+
"size": file_stats.size,
|
|
306
|
+
"type": "other",
|
|
307
|
+
}
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# 清理文件
|
|
311
|
+
sandbox.files.remove(file_path)
|
|
312
|
+
|
|
313
|
+
return results
|
|
314
|
+
|
|
315
|
+
@async_retry_on_failure(max_retries=3)
|
|
316
|
+
async def async_process_files_batch(
|
|
317
|
+
self, files_data: List[Dict[str, str]]
|
|
318
|
+
) -> List[Dict[str, Any]]:
|
|
319
|
+
"""异步批量处理文件"""
|
|
320
|
+
async with AsyncSandboxManager().get_sandbox() as sandbox:
|
|
321
|
+
# 批量异步写入文件
|
|
322
|
+
write_operations = [
|
|
323
|
+
{"path": f"/tmp/{file_info['name']}", "data": file_info["content"]}
|
|
324
|
+
for file_info in files_data
|
|
325
|
+
]
|
|
326
|
+
|
|
327
|
+
written_files = await sandbox.files.write(write_operations)
|
|
328
|
+
logger.info(f"异步成功写入 {len(written_files)} 个文件")
|
|
329
|
+
|
|
330
|
+
# 并发处理文件
|
|
331
|
+
async def process_single_file(file_info: Dict[str, str]) -> Dict[str, Any]:
|
|
332
|
+
file_path = f"/tmp/{file_info['name']}"
|
|
333
|
+
|
|
334
|
+
# 获取文件信息
|
|
335
|
+
file_stats = await sandbox.files.get_info(file_path)
|
|
336
|
+
|
|
337
|
+
# 根据文件类型进行异步处理
|
|
338
|
+
if file_info["name"].endswith(".txt"):
|
|
339
|
+
result = await sandbox.commands.run(f"wc -l -c {file_path}")
|
|
340
|
+
lines_chars = result.stdout.strip().split()
|
|
341
|
+
|
|
342
|
+
await sandbox.files.remove(file_path) # 异步清理
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
"name": file_info["name"],
|
|
346
|
+
"size": file_stats.size,
|
|
347
|
+
"lines": int(lines_chars[0]) if len(lines_chars) > 0 else 0,
|
|
348
|
+
"chars": int(lines_chars[1]) if len(lines_chars) > 1 else 0,
|
|
349
|
+
"type": "text",
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
elif file_info["name"].endswith(".json"):
|
|
353
|
+
content = await sandbox.files.read(file_path)
|
|
354
|
+
try:
|
|
355
|
+
json.loads(content)
|
|
356
|
+
valid_json = True
|
|
357
|
+
error = None
|
|
358
|
+
except json.JSONDecodeError as e:
|
|
359
|
+
valid_json = False
|
|
360
|
+
error = str(e)
|
|
361
|
+
|
|
362
|
+
await sandbox.files.remove(file_path)
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
"name": file_info["name"],
|
|
366
|
+
"size": file_stats.size,
|
|
367
|
+
"valid_json": valid_json,
|
|
368
|
+
"error": error,
|
|
369
|
+
"type": "json",
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
else:
|
|
373
|
+
await sandbox.files.remove(file_path)
|
|
374
|
+
return {
|
|
375
|
+
"name": file_info["name"],
|
|
376
|
+
"size": file_stats.size,
|
|
377
|
+
"type": "other",
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
# 并发执行所有文件处理任务
|
|
381
|
+
tasks = [process_single_file(file_info) for file_info in files_data]
|
|
382
|
+
results = await asyncio.gather(*tasks)
|
|
383
|
+
|
|
384
|
+
return results
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class InteractiveSession:
|
|
388
|
+
"""交互式会话 - 展示PTY使用最佳实践"""
|
|
389
|
+
|
|
390
|
+
def __init__(self, sandbox: Union[Sandbox, AsyncSandbox]):
|
|
391
|
+
self.sandbox = sandbox
|
|
392
|
+
self.is_async = isinstance(sandbox, AsyncSandbox)
|
|
393
|
+
self.session_log = []
|
|
394
|
+
|
|
395
|
+
def start_interactive_python(self):
|
|
396
|
+
"""启动交互式Python会话"""
|
|
397
|
+
if self.is_async:
|
|
398
|
+
raise ValueError("请使用 async_start_interactive_python 对于异步沙箱")
|
|
399
|
+
|
|
400
|
+
def output_handler(data):
|
|
401
|
+
self.session_log.append(("output", data, time.time()))
|
|
402
|
+
print(f"Python> {data.strip()}")
|
|
403
|
+
|
|
404
|
+
# 创建PTY
|
|
405
|
+
self.pty = self.sandbox.pty.create(
|
|
406
|
+
size=PtySize(rows=24, cols=80),
|
|
407
|
+
on_data=output_handler,
|
|
408
|
+
envs={"PYTHONPATH": "/tmp", "PYTHONUNBUFFERED": "1"},
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
# 启动Python解释器
|
|
412
|
+
self.sandbox.pty.send_stdin(self.pty.pid, b"python3 -i\n")
|
|
413
|
+
time.sleep(2) # 等待Python启动
|
|
414
|
+
|
|
415
|
+
return self.pty
|
|
416
|
+
|
|
417
|
+
async def async_start_interactive_python(self):
|
|
418
|
+
"""启动异步交互式Python会话"""
|
|
419
|
+
if not self.is_async:
|
|
420
|
+
raise ValueError("请使用 start_interactive_python 对于同步沙箱")
|
|
421
|
+
|
|
422
|
+
async def output_handler(data):
|
|
423
|
+
self.session_log.append(("output", data, time.time()))
|
|
424
|
+
print(f"AsyncPython> {data.strip()}")
|
|
425
|
+
|
|
426
|
+
# 创建PTY
|
|
427
|
+
self.pty = await self.sandbox.pty.create(
|
|
428
|
+
size=PtySize(rows=24, cols=80),
|
|
429
|
+
on_data=output_handler,
|
|
430
|
+
envs={"PYTHONPATH": "/tmp", "PYTHONUNBUFFERED": "1"},
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
# 启动Python解释器
|
|
434
|
+
await self.sandbox.pty.send_stdin(self.pty.pid, b"python3 -i\n")
|
|
435
|
+
await asyncio.sleep(2) # 等待Python启动
|
|
436
|
+
|
|
437
|
+
return self.pty
|
|
438
|
+
|
|
439
|
+
def execute_command(self, command: str):
|
|
440
|
+
"""在交互式会话中执行命令"""
|
|
441
|
+
if self.is_async:
|
|
442
|
+
raise ValueError("请使用 async_execute_command 对于异步沙箱")
|
|
443
|
+
|
|
444
|
+
self.session_log.append(("input", command, time.time()))
|
|
445
|
+
self.sandbox.pty.send_stdin(self.pty.pid, f"{command}\n".encode())
|
|
446
|
+
time.sleep(1) # 等待命令执行
|
|
447
|
+
|
|
448
|
+
async def async_execute_command(self, command: str):
|
|
449
|
+
"""在异步交互式会话中执行命令"""
|
|
450
|
+
if not self.is_async:
|
|
451
|
+
raise ValueError("请使用 execute_command 对于同步沙箱")
|
|
452
|
+
|
|
453
|
+
self.session_log.append(("input", command, time.time()))
|
|
454
|
+
await self.sandbox.pty.send_stdin(self.pty.pid, f"{command}\n".encode())
|
|
455
|
+
await asyncio.sleep(1) # 等待命令执行
|
|
456
|
+
|
|
457
|
+
def get_session_summary(self) -> Dict[str, Any]:
|
|
458
|
+
"""获取会话摘要"""
|
|
459
|
+
inputs = [entry for entry in self.session_log if entry[0] == "input"]
|
|
460
|
+
outputs = [entry for entry in self.session_log if entry[0] == "output"]
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
"total_commands": len(inputs),
|
|
464
|
+
"total_outputs": len(outputs),
|
|
465
|
+
"session_duration": (
|
|
466
|
+
self.session_log[-1][2] - self.session_log[0][2]
|
|
467
|
+
if self.session_log
|
|
468
|
+
else 0
|
|
469
|
+
),
|
|
470
|
+
"commands": [entry[1] for entry in inputs],
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
# ======================== 性能优化示例 ========================
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class OptimizedFileOperations:
|
|
478
|
+
"""优化的文件操作 - 展示性能优化技巧"""
|
|
479
|
+
|
|
480
|
+
@staticmethod
|
|
481
|
+
def batch_file_operations_sync(sandbox: Sandbox, operations: List[Dict[str, Any]]):
|
|
482
|
+
"""同步批量文件操作优化"""
|
|
483
|
+
# 按操作类型分组
|
|
484
|
+
writes = [op for op in operations if op["type"] == "write"]
|
|
485
|
+
reads = [op for op in operations if op["type"] == "read"]
|
|
486
|
+
|
|
487
|
+
results = {}
|
|
488
|
+
|
|
489
|
+
# 批量写入
|
|
490
|
+
if writes:
|
|
491
|
+
write_data = [{"path": op["path"], "data": op["data"]} for op in writes]
|
|
492
|
+
write_results = sandbox.files.write(write_data)
|
|
493
|
+
results["writes"] = write_results
|
|
494
|
+
|
|
495
|
+
# 批量读取(如果支持)
|
|
496
|
+
if reads:
|
|
497
|
+
read_results = []
|
|
498
|
+
for op in reads:
|
|
499
|
+
content = sandbox.files.read(op["path"])
|
|
500
|
+
read_results.append({"path": op["path"], "content": content})
|
|
501
|
+
results["reads"] = read_results
|
|
502
|
+
|
|
503
|
+
return results
|
|
504
|
+
|
|
505
|
+
@staticmethod
|
|
506
|
+
async def batch_file_operations_async(
|
|
507
|
+
sandbox: AsyncSandbox, operations: List[Dict[str, Any]]
|
|
508
|
+
):
|
|
509
|
+
"""异步批量文件操作优化"""
|
|
510
|
+
# 按操作类型分组
|
|
511
|
+
writes = [op for op in operations if op["type"] == "write"]
|
|
512
|
+
reads = [op for op in operations if op["type"] == "read"]
|
|
513
|
+
|
|
514
|
+
results = {}
|
|
515
|
+
|
|
516
|
+
# 批量写入
|
|
517
|
+
if writes:
|
|
518
|
+
write_data = [{"path": op["path"], "data": op["data"]} for op in writes]
|
|
519
|
+
write_results = await sandbox.files.write(write_data)
|
|
520
|
+
results["writes"] = write_results
|
|
521
|
+
|
|
522
|
+
# 并发读取
|
|
523
|
+
if reads:
|
|
524
|
+
|
|
525
|
+
async def read_file(path):
|
|
526
|
+
content = await sandbox.files.read(path)
|
|
527
|
+
return {"path": path, "content": content}
|
|
528
|
+
|
|
529
|
+
read_tasks = [read_file(op["path"]) for op in reads]
|
|
530
|
+
read_results = await asyncio.gather(*read_tasks)
|
|
531
|
+
results["reads"] = read_results
|
|
532
|
+
|
|
533
|
+
return results
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
class CommandExecutionPool:
|
|
537
|
+
"""命令执行池 - 展示命令执行优化"""
|
|
538
|
+
|
|
539
|
+
def __init__(self, sandbox: Sandbox, max_concurrent: int = 5):
|
|
540
|
+
self.sandbox = sandbox
|
|
541
|
+
self.max_concurrent = max_concurrent
|
|
542
|
+
self._semaphore = threading.Semaphore(max_concurrent)
|
|
543
|
+
self._lock = threading.Lock()
|
|
544
|
+
self.results = []
|
|
545
|
+
|
|
546
|
+
def execute_commands_concurrent(self, commands: List[str]) -> List[Dict[str, Any]]:
|
|
547
|
+
"""并发执行命令(使用线程池)"""
|
|
548
|
+
import concurrent.futures
|
|
549
|
+
|
|
550
|
+
def execute_single_command(cmd):
|
|
551
|
+
with self._semaphore:
|
|
552
|
+
try:
|
|
553
|
+
result = self.sandbox.commands.run(cmd)
|
|
554
|
+
return {
|
|
555
|
+
"command": cmd,
|
|
556
|
+
"exit_code": result.exit_code,
|
|
557
|
+
"stdout": result.stdout,
|
|
558
|
+
"stderr": result.stderr,
|
|
559
|
+
"success": result.exit_code == 0,
|
|
560
|
+
}
|
|
561
|
+
except Exception as e:
|
|
562
|
+
return {"command": cmd, "error": str(e), "success": False}
|
|
563
|
+
|
|
564
|
+
with concurrent.futures.ThreadPoolExecutor(
|
|
565
|
+
max_workers=self.max_concurrent
|
|
566
|
+
) as executor:
|
|
567
|
+
futures = [executor.submit(execute_single_command, cmd) for cmd in commands]
|
|
568
|
+
results = [
|
|
569
|
+
future.result() for future in concurrent.futures.as_completed(futures)
|
|
570
|
+
]
|
|
571
|
+
|
|
572
|
+
return results
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
class AsyncCommandExecutionPool:
|
|
576
|
+
"""异步命令执行池"""
|
|
577
|
+
|
|
578
|
+
def __init__(self, sandbox: AsyncSandbox, max_concurrent: int = 10):
|
|
579
|
+
self.sandbox = sandbox
|
|
580
|
+
self.semaphore = asyncio.Semaphore(max_concurrent)
|
|
581
|
+
|
|
582
|
+
async def execute_commands_concurrent(
|
|
583
|
+
self, commands: List[str]
|
|
584
|
+
) -> List[Dict[str, Any]]:
|
|
585
|
+
"""异步并发执行命令"""
|
|
586
|
+
|
|
587
|
+
async def execute_single_command(cmd):
|
|
588
|
+
async with self.semaphore:
|
|
589
|
+
try:
|
|
590
|
+
result = await self.sandbox.commands.run(cmd)
|
|
591
|
+
return {
|
|
592
|
+
"command": cmd,
|
|
593
|
+
"exit_code": result.exit_code,
|
|
594
|
+
"stdout": result.stdout,
|
|
595
|
+
"stderr": result.stderr,
|
|
596
|
+
"success": result.exit_code == 0,
|
|
597
|
+
}
|
|
598
|
+
except Exception as e:
|
|
599
|
+
return {"command": cmd, "error": str(e), "success": False}
|
|
600
|
+
|
|
601
|
+
tasks = [execute_single_command(cmd) for cmd in commands]
|
|
602
|
+
results = await asyncio.gather(*tasks)
|
|
603
|
+
|
|
604
|
+
return results
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
# ======================== 测试用例和示例 ========================
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def demo_code_execution_service():
|
|
611
|
+
"""演示代码执行服务"""
|
|
612
|
+
logger.info("=== 代码执行服务演示 ===")
|
|
613
|
+
|
|
614
|
+
service = CodeExecutionService()
|
|
615
|
+
|
|
616
|
+
# Python代码示例
|
|
617
|
+
python_codes = [
|
|
618
|
+
"print('Hello, World!')",
|
|
619
|
+
"""
|
|
620
|
+
import math
|
|
621
|
+
result = math.sqrt(16)
|
|
622
|
+
print(f'Square root of 16 is {result}')
|
|
623
|
+
""",
|
|
624
|
+
"""
|
|
625
|
+
# 测试错误处理
|
|
626
|
+
try:
|
|
627
|
+
x = 1 / 0
|
|
628
|
+
except ZeroDivisionError as e:
|
|
629
|
+
print(f'Caught error: {e}')
|
|
630
|
+
""",
|
|
631
|
+
"""
|
|
632
|
+
# 测试超时
|
|
633
|
+
import time
|
|
634
|
+
print('Starting long task...')
|
|
635
|
+
time.sleep(35) # 这会触发超时
|
|
636
|
+
print('Task completed')
|
|
637
|
+
""",
|
|
638
|
+
]
|
|
639
|
+
|
|
640
|
+
for i, code in enumerate(python_codes):
|
|
641
|
+
logger.info(f"执行Python代码 {i + 1}:")
|
|
642
|
+
try:
|
|
643
|
+
result = service.execute_python_code(code, timeout=30)
|
|
644
|
+
logger.info(f"退出码: {result['exit_code']}")
|
|
645
|
+
logger.info(f"输出: {result['stdout'][:200]}...") # 只显示前200字符
|
|
646
|
+
if result["stderr"]:
|
|
647
|
+
logger.info(f"错误: {result['stderr'][:200]}...")
|
|
648
|
+
if result["timed_out"]:
|
|
649
|
+
logger.info("任务因超时被终止")
|
|
650
|
+
except Exception as e:
|
|
651
|
+
logger.error(f"执行失败: {e}")
|
|
652
|
+
|
|
653
|
+
logger.info("-" * 50)
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
async def demo_async_code_execution_service():
|
|
657
|
+
"""演示异步代码执行服务"""
|
|
658
|
+
logger.info("=== 异步代码执行服务演示 ===")
|
|
659
|
+
|
|
660
|
+
service = CodeExecutionService(use_async=True)
|
|
661
|
+
|
|
662
|
+
# 并发执行多个Python代码
|
|
663
|
+
python_codes = [
|
|
664
|
+
"print(f'Task 1: {2 + 2}')",
|
|
665
|
+
"print(f'Task 2: {3 * 3}')",
|
|
666
|
+
"import time; time.sleep(2); print('Task 3: Long running task completed')",
|
|
667
|
+
"print(f'Task 4: {list(range(5))}')",
|
|
668
|
+
]
|
|
669
|
+
|
|
670
|
+
start_time = time.time()
|
|
671
|
+
|
|
672
|
+
# 并发执行
|
|
673
|
+
tasks = [service.async_execute_python_code(code) for code in python_codes]
|
|
674
|
+
results = await asyncio.gather(*tasks)
|
|
675
|
+
|
|
676
|
+
duration = time.time() - start_time
|
|
677
|
+
|
|
678
|
+
logger.info(f"并发执行 {len(python_codes)} 个任务,耗时: {duration:.3f}秒")
|
|
679
|
+
|
|
680
|
+
for i, result in enumerate(results):
|
|
681
|
+
logger.info(
|
|
682
|
+
f"任务 {i + 1} - 退出码: {result['exit_code']}, 输出: {result['stdout'].strip()}"
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def demo_file_processing_service():
|
|
687
|
+
"""演示文件处理服务"""
|
|
688
|
+
logger.info("=== 文件处理服务演示 ===")
|
|
689
|
+
|
|
690
|
+
service = FileProcessingService()
|
|
691
|
+
|
|
692
|
+
# 测试文件数据
|
|
693
|
+
test_files = [
|
|
694
|
+
{
|
|
695
|
+
"name": "sample.txt",
|
|
696
|
+
"content": "Hello World!\nThis is a test file.\nLine 3\nLine 4",
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
"name": "data.json",
|
|
700
|
+
"content": json.dumps(
|
|
701
|
+
{"key": "value", "numbers": [1, 2, 3], "nested": {"a": 1}}
|
|
702
|
+
),
|
|
703
|
+
},
|
|
704
|
+
{"name": "invalid.json", "content": '{"incomplete": json'},
|
|
705
|
+
{"name": "binary.bin", "content": "Binary content here"},
|
|
706
|
+
]
|
|
707
|
+
|
|
708
|
+
try:
|
|
709
|
+
results = service.process_files_batch(test_files)
|
|
710
|
+
|
|
711
|
+
logger.info(f"处理了 {len(results)} 个文件:")
|
|
712
|
+
for result in results:
|
|
713
|
+
logger.info(f" {result['name']}: {result}")
|
|
714
|
+
|
|
715
|
+
except Exception as e:
|
|
716
|
+
logger.error(f"文件处理失败: {e}")
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
async def demo_async_file_processing_service():
|
|
720
|
+
"""演示异步文件处理服务"""
|
|
721
|
+
logger.info("=== 异步文件处理服务演示 ===")
|
|
722
|
+
|
|
723
|
+
service = FileProcessingService()
|
|
724
|
+
|
|
725
|
+
# 创建更多测试文件进行并发处理
|
|
726
|
+
test_files = [
|
|
727
|
+
{"name": f"test_{i}.txt", "content": f"Content of file {i}\n" * (i + 1)}
|
|
728
|
+
for i in range(20)
|
|
729
|
+
]
|
|
730
|
+
|
|
731
|
+
# 添加一些JSON文件
|
|
732
|
+
for i in range(5):
|
|
733
|
+
test_files.append(
|
|
734
|
+
{
|
|
735
|
+
"name": f"data_{i}.json",
|
|
736
|
+
"content": json.dumps({"id": i, "data": list(range(i + 1))}),
|
|
737
|
+
}
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
start_time = time.time()
|
|
741
|
+
|
|
742
|
+
try:
|
|
743
|
+
results = await service.async_process_files_batch(test_files)
|
|
744
|
+
duration = time.time() - start_time
|
|
745
|
+
|
|
746
|
+
logger.info(f"异步处理了 {len(results)} 个文件,耗时: {duration:.3f}秒")
|
|
747
|
+
|
|
748
|
+
# 统计结果
|
|
749
|
+
text_files = [r for r in results if r["type"] == "text"]
|
|
750
|
+
json_files = [r for r in results if r["type"] == "json"]
|
|
751
|
+
|
|
752
|
+
logger.info(f"文本文件: {len(text_files)}, JSON文件: {len(json_files)}")
|
|
753
|
+
|
|
754
|
+
if text_files:
|
|
755
|
+
total_lines = sum(f["lines"] for f in text_files)
|
|
756
|
+
logger.info(f"总行数: {total_lines}")
|
|
757
|
+
|
|
758
|
+
if json_files:
|
|
759
|
+
valid_json = sum(1 for f in json_files if f["valid_json"])
|
|
760
|
+
logger.info(f"有效JSON文件: {valid_json}/{len(json_files)}")
|
|
761
|
+
|
|
762
|
+
except Exception as e:
|
|
763
|
+
logger.error(f"异步文件处理失败: {e}")
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def demo_interactive_session():
|
|
767
|
+
"""演示交互式会话"""
|
|
768
|
+
logger.info("=== 交互式会话演示 ===")
|
|
769
|
+
|
|
770
|
+
with SandboxManager().get_sandbox() as sandbox:
|
|
771
|
+
session = InteractiveSession(sandbox)
|
|
772
|
+
|
|
773
|
+
try:
|
|
774
|
+
# 启动交互式Python
|
|
775
|
+
pty = session.start_interactive_python()
|
|
776
|
+
|
|
777
|
+
# 执行一些Python命令
|
|
778
|
+
commands = [
|
|
779
|
+
"x = 10",
|
|
780
|
+
"y = 20",
|
|
781
|
+
"print(f'x + y = {x + y}')",
|
|
782
|
+
"import os",
|
|
783
|
+
"print(f'Current directory: {os.getcwd()}')",
|
|
784
|
+
"for i in range(3): print(f'Loop {i}')",
|
|
785
|
+
"exit()",
|
|
786
|
+
]
|
|
787
|
+
|
|
788
|
+
for cmd in commands:
|
|
789
|
+
logger.info(f"执行命令: {cmd}")
|
|
790
|
+
session.execute_command(cmd)
|
|
791
|
+
time.sleep(1) # 给命令一些执行时间
|
|
792
|
+
|
|
793
|
+
# 等待Python退出
|
|
794
|
+
time.sleep(3)
|
|
795
|
+
|
|
796
|
+
# 获取会话摘要
|
|
797
|
+
summary = session.get_session_summary()
|
|
798
|
+
logger.info(f"会话摘要: {summary}")
|
|
799
|
+
|
|
800
|
+
except Exception as e:
|
|
801
|
+
logger.error(f"交互式会话失败: {e}")
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
async def demo_async_interactive_session():
|
|
805
|
+
"""演示异步交互式会话"""
|
|
806
|
+
logger.info("=== 异步交互式会话演示 ===")
|
|
807
|
+
|
|
808
|
+
async with AsyncSandboxManager().get_sandbox() as sandbox:
|
|
809
|
+
session = InteractiveSession(sandbox)
|
|
810
|
+
|
|
811
|
+
try:
|
|
812
|
+
# 启动异步交互式Python
|
|
813
|
+
pty = await session.async_start_interactive_python()
|
|
814
|
+
|
|
815
|
+
# 异步执行Python命令
|
|
816
|
+
commands = [
|
|
817
|
+
"import asyncio",
|
|
818
|
+
"print('Async Python session started')",
|
|
819
|
+
"data = [1, 2, 3, 4, 5]",
|
|
820
|
+
"result = sum(data)",
|
|
821
|
+
"print(f'Sum: {result}')",
|
|
822
|
+
"exit()",
|
|
823
|
+
]
|
|
824
|
+
|
|
825
|
+
for cmd in commands:
|
|
826
|
+
logger.info(f"异步执行命令: {cmd}")
|
|
827
|
+
await session.async_execute_command(cmd)
|
|
828
|
+
await asyncio.sleep(1)
|
|
829
|
+
|
|
830
|
+
# 等待Python退出
|
|
831
|
+
await asyncio.sleep(3)
|
|
832
|
+
|
|
833
|
+
# 获取会话摘要
|
|
834
|
+
summary = session.get_session_summary()
|
|
835
|
+
logger.info(f"异步会话摘要: {summary}")
|
|
836
|
+
|
|
837
|
+
except Exception as e:
|
|
838
|
+
logger.error(f"异步交互式会话失败: {e}")
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def demo_performance_optimization():
|
|
842
|
+
"""演示性能优化技巧"""
|
|
843
|
+
logger.info("=== 性能优化演示 ===")
|
|
844
|
+
|
|
845
|
+
with SandboxManager().get_sandbox() as sandbox:
|
|
846
|
+
# 准备测试数据
|
|
847
|
+
operations = []
|
|
848
|
+
|
|
849
|
+
# 添加写操作
|
|
850
|
+
for i in range(50):
|
|
851
|
+
operations.append(
|
|
852
|
+
{
|
|
853
|
+
"type": "write",
|
|
854
|
+
"path": f"/tmp/perf_test_{i}.txt",
|
|
855
|
+
"data": f"Performance test file {i}\n" * 10,
|
|
856
|
+
}
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
# 添加读操作(先写入一些文件)
|
|
860
|
+
for i in range(10):
|
|
861
|
+
sandbox.files.write(f"/tmp/read_test_{i}.txt", f"Read test {i}")
|
|
862
|
+
operations.append({"type": "read", "path": f"/tmp/read_test_{i}.txt"})
|
|
863
|
+
|
|
864
|
+
# 测试优化的批量操作
|
|
865
|
+
start_time = time.time()
|
|
866
|
+
results = OptimizedFileOperations.batch_file_operations_sync(
|
|
867
|
+
sandbox, operations
|
|
868
|
+
)
|
|
869
|
+
duration = time.time() - start_time
|
|
870
|
+
|
|
871
|
+
logger.info(f"批量操作完成,耗时: {duration:.3f}秒")
|
|
872
|
+
logger.info(f"写入文件: {len(results.get('writes', []))}")
|
|
873
|
+
logger.info(f"读取文件: {len(results.get('reads', []))}")
|
|
874
|
+
|
|
875
|
+
# 测试并发命令执行
|
|
876
|
+
commands = [f"echo 'Command {i}'" for i in range(20)]
|
|
877
|
+
|
|
878
|
+
command_pool = CommandExecutionPool(sandbox, max_concurrent=5)
|
|
879
|
+
start_time = time.time()
|
|
880
|
+
command_results = command_pool.execute_commands_concurrent(commands)
|
|
881
|
+
duration = time.time() - start_time
|
|
882
|
+
|
|
883
|
+
logger.info(f"并发执行 {len(commands)} 个命令,耗时: {duration:.3f}秒")
|
|
884
|
+
successful_commands = sum(1 for r in command_results if r["success"])
|
|
885
|
+
logger.info(f"成功执行: {successful_commands}/{len(command_results)}")
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
async def demo_async_performance_optimization():
|
|
889
|
+
"""演示异步性能优化技巧"""
|
|
890
|
+
logger.info("=== 异步性能优化演示 ===")
|
|
891
|
+
|
|
892
|
+
async with AsyncSandboxManager().get_sandbox() as sandbox:
|
|
893
|
+
# 准备测试数据
|
|
894
|
+
operations = []
|
|
895
|
+
|
|
896
|
+
# 添加写操作
|
|
897
|
+
for i in range(100): # 异步可以处理更多
|
|
898
|
+
operations.append(
|
|
899
|
+
{
|
|
900
|
+
"type": "write",
|
|
901
|
+
"path": f"/tmp/async_perf_test_{i}.txt",
|
|
902
|
+
"data": f"Async performance test file {i}\n" * 10,
|
|
903
|
+
}
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
# 先写入一些文件用于读取测试
|
|
907
|
+
write_ops = [
|
|
908
|
+
{"path": f"/tmp/async_read_test_{i}.txt", "data": f"Async read test {i}"}
|
|
909
|
+
for i in range(20)
|
|
910
|
+
]
|
|
911
|
+
await sandbox.files.write(write_ops)
|
|
912
|
+
|
|
913
|
+
# 添加读操作
|
|
914
|
+
for i in range(20):
|
|
915
|
+
operations.append({"type": "read", "path": f"/tmp/async_read_test_{i}.txt"})
|
|
916
|
+
|
|
917
|
+
# 测试异步优化的批量操作
|
|
918
|
+
start_time = time.time()
|
|
919
|
+
results = await OptimizedFileOperations.batch_file_operations_async(
|
|
920
|
+
sandbox, operations
|
|
921
|
+
)
|
|
922
|
+
duration = time.time() - start_time
|
|
923
|
+
|
|
924
|
+
logger.info(f"异步批量操作完成,耗时: {duration:.3f}秒")
|
|
925
|
+
logger.info(f"写入文件: {len(results.get('writes', []))}")
|
|
926
|
+
logger.info(f"读取文件: {len(results.get('reads', []))}")
|
|
927
|
+
|
|
928
|
+
# 测试异步并发命令执行
|
|
929
|
+
commands = [f"echo 'Async Command {i}'" for i in range(50)]
|
|
930
|
+
|
|
931
|
+
command_pool = AsyncCommandExecutionPool(sandbox, max_concurrent=10)
|
|
932
|
+
start_time = time.time()
|
|
933
|
+
command_results = await command_pool.execute_commands_concurrent(commands)
|
|
934
|
+
duration = time.time() - start_time
|
|
935
|
+
|
|
936
|
+
logger.info(f"异步并发执行 {len(commands)} 个命令,耗时: {duration:.3f}秒")
|
|
937
|
+
successful_commands = sum(1 for r in command_results if r["success"])
|
|
938
|
+
logger.info(f"成功执行: {successful_commands}/{len(command_results)}")
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
# ======================== 主测试函数 ========================
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
def run_sync_examples():
|
|
945
|
+
"""运行同步示例"""
|
|
946
|
+
logger.info("开始运行同步沙箱使用示例...")
|
|
947
|
+
|
|
948
|
+
try:
|
|
949
|
+
demo_code_execution_service()
|
|
950
|
+
demo_file_processing_service()
|
|
951
|
+
demo_interactive_session()
|
|
952
|
+
demo_performance_optimization()
|
|
953
|
+
except Exception as e:
|
|
954
|
+
logger.error(f"同步示例执行失败: {e}")
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
async def run_async_examples():
|
|
958
|
+
"""运行异步示例"""
|
|
959
|
+
logger.info("开始运行异步沙箱使用示例...")
|
|
960
|
+
|
|
961
|
+
try:
|
|
962
|
+
await demo_async_code_execution_service()
|
|
963
|
+
await demo_async_file_processing_service()
|
|
964
|
+
await demo_async_interactive_session()
|
|
965
|
+
await demo_async_performance_optimization()
|
|
966
|
+
except Exception as e:
|
|
967
|
+
logger.error(f"异步示例执行失败: {e}")
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
def main():
|
|
971
|
+
"""主函数"""
|
|
972
|
+
logger.info("沙箱使用示例和最佳实践演示")
|
|
973
|
+
logger.info("=" * 60)
|
|
974
|
+
|
|
975
|
+
# 运行同步示例
|
|
976
|
+
run_sync_examples()
|
|
977
|
+
|
|
978
|
+
logger.info("\n" + "=" * 60)
|
|
979
|
+
|
|
980
|
+
# 运行异步示例
|
|
981
|
+
asyncio.run(run_async_examples())
|
|
982
|
+
|
|
983
|
+
logger.info("\n所有示例运行完成!")
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
if __name__ == "__main__":
|
|
987
|
+
main()
|