python-sandbox-sdk 1.0.1__tar.gz → 1.0.2__tar.gz
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.
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/PKG-INFO +39 -3
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/README.md +38 -2
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/pyproject.toml +1 -1
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk/__init__.py +1 -1
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk/async_client.py +22 -27
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/PKG-INFO +39 -3
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk/client.py +0 -0
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk/dto.py +0 -0
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/SOURCES.txt +0 -0
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/dependency_links.txt +0 -0
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/requires.txt +0 -0
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/top_level.txt +0 -0
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/setup.cfg +0 -0
- {python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-sandbox-sdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Python SDK for Python Sandbox API - Run Python code, shell commands, and manage packages in Docker containers
|
|
5
5
|
Home-page: https://github.com/zjwan461/python-sandbox
|
|
6
6
|
Author-email: Jerry <826935261@qq.com>
|
|
@@ -45,7 +45,8 @@ Python Sandbox 官方 Python SDK — 在隔离的 Docker 容器中执行 Python
|
|
|
45
45
|
- 🔌 **上下文管理器**:自动管理 HTTP 连接与会话生命周期
|
|
46
46
|
- 🛡️ **异常体系**:独立的 `ApiRequestError` / `SandboxError` 类型
|
|
47
47
|
- 🧵 **线程安全**:`requests.Session` 复用,支持 `ThreadPoolExecutor` 并发
|
|
48
|
-
-
|
|
48
|
+
- ⚡ **异步支持**:提供 `AsyncSandboxClient`,基于 `aiohttp` 实现完整异步 API
|
|
49
|
+
- 🐍 **Python ≥ 3.8**:同步客户端仅依赖 `requests>=2.31.0`
|
|
49
50
|
|
|
50
51
|
## 📦 安装
|
|
51
52
|
|
|
@@ -57,6 +58,9 @@ pip install python-sandbox-sdk
|
|
|
57
58
|
git clone https://github.com/zjwan461/python-sandbox.git
|
|
58
59
|
cd python-sandbox/sdk/python-sandbox-sdk
|
|
59
60
|
pip install -e .
|
|
61
|
+
|
|
62
|
+
# 安装异步支持(可选)
|
|
63
|
+
pip install python-sandbox-sdk[async]
|
|
60
64
|
```
|
|
61
65
|
|
|
62
66
|
## 🚀 快速开始
|
|
@@ -112,7 +116,6 @@ content = client.read_file(session_id, "/tmp/config.json")
|
|
|
112
116
|
client.upload_file(session_id, "/tmp/image.png", b"\\x89PNG...")
|
|
113
117
|
data = client.download_file(session_id, "/tmp/image.png")
|
|
114
118
|
```
|
|
115
|
-
|
|
116
119
|
## 📚 使用样例(覆盖全部 API 与典型场景)
|
|
117
120
|
|
|
118
121
|
完整可运行样例见 [`usage/`](usage/) 目录:
|
|
@@ -130,6 +133,8 @@ data = client.download_file(session_id, "/tmp/image.png")
|
|
|
130
133
|
| [`09_long_running.py`](usage/09_long_running.py) | 长任务:进度回报 + 异常安全 | `exec_python`, `exec_shell` |
|
|
131
134
|
| [`10_concurrent_sessions.py`](usage/10_concurrent_sessions.py) | 并发:`ThreadPoolExecutor` + 上限约束 | 多会话并行 |
|
|
132
135
|
| [`11_error_handling.py`](usage/11_error_handling.py) | 错误全景:鉴权 / 会话 / 黑名单 / 网络 | 全 API 错误路径 |
|
|
136
|
+
| [`12_async_client.py`](usage/12_async_client.py) | 异步客户端:完整异步操作示例 | 全 API 异步版本 |
|
|
137
|
+
| [`11_error_handling.py`](usage/11_error_handling.py) | 错误全景:鉴权 / 会话 / 黑名单 / 网络 | 全 API 错误路径 |
|
|
133
138
|
|
|
134
139
|
运行样例:
|
|
135
140
|
|
|
@@ -160,6 +165,8 @@ for f in 0[1-9]_*.py 1[0-1]_*.py; do python "$f"; done
|
|
|
160
165
|
|
|
161
166
|
`CommandResult` 字段:`exit_code`, `stdout`, `stderr`, `success`(property), `combined_output`(property)。
|
|
162
167
|
|
|
168
|
+
> 💡 **异步版本**:`AsyncSandboxClient` 提供完全相同的 API,所有方法均为 `async`,支持 `await` 调用和 `async with` 上下文管理器。
|
|
169
|
+
|
|
163
170
|
## ⚙️ 环境变量
|
|
164
171
|
|
|
165
172
|
为避免硬编码,样例通过环境变量注入连接信息:
|
|
@@ -189,6 +196,8 @@ except OSError as e:
|
|
|
189
196
|
|
|
190
197
|
## 🧵 并发使用
|
|
191
198
|
|
|
199
|
+
### 同步并发(ThreadPoolExecutor)
|
|
200
|
+
|
|
192
201
|
```python
|
|
193
202
|
from concurrent.futures import ThreadPoolExecutor
|
|
194
203
|
# 每个任务使用独立的 client + session(线程安全)
|
|
@@ -198,6 +207,33 @@ with ThreadPoolExecutor(max_workers=5) as pool:
|
|
|
198
207
|
print(f.result())
|
|
199
208
|
```
|
|
200
209
|
|
|
210
|
+
### 异步并发(AsyncSandboxClient)
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import asyncio
|
|
214
|
+
from python_sandbox_sdk import AsyncSandboxClient
|
|
215
|
+
|
|
216
|
+
async def run_task(task_id):
|
|
217
|
+
async with AsyncSandboxClient("http://localhost:8080", "sandbox-secret-key") as client:
|
|
218
|
+
session_id = await client.create_session()
|
|
219
|
+
try:
|
|
220
|
+
result = await client.exec_python(
|
|
221
|
+
session_id, f"print('Task {task_id} done')"
|
|
222
|
+
)
|
|
223
|
+
return result.stdout
|
|
224
|
+
finally:
|
|
225
|
+
await client.delete_session(session_id)
|
|
226
|
+
|
|
227
|
+
async def main():
|
|
228
|
+
# 并发执行 5 个任务
|
|
229
|
+
tasks = [run_task(i) for i in range(5)]
|
|
230
|
+
results = await asyncio.gather(*tasks)
|
|
231
|
+
for r in results:
|
|
232
|
+
print(r)
|
|
233
|
+
|
|
234
|
+
asyncio.run(main())
|
|
235
|
+
```
|
|
236
|
+
|
|
201
237
|
> ⚠️ 并发数受后端 `sandbox.max-containers`(默认 10)约束。
|
|
202
238
|
|
|
203
239
|
## 📦 打包发布
|
|
@@ -14,7 +14,8 @@ Python Sandbox 官方 Python SDK — 在隔离的 Docker 容器中执行 Python
|
|
|
14
14
|
- 🔌 **上下文管理器**:自动管理 HTTP 连接与会话生命周期
|
|
15
15
|
- 🛡️ **异常体系**:独立的 `ApiRequestError` / `SandboxError` 类型
|
|
16
16
|
- 🧵 **线程安全**:`requests.Session` 复用,支持 `ThreadPoolExecutor` 并发
|
|
17
|
-
-
|
|
17
|
+
- ⚡ **异步支持**:提供 `AsyncSandboxClient`,基于 `aiohttp` 实现完整异步 API
|
|
18
|
+
- 🐍 **Python ≥ 3.8**:同步客户端仅依赖 `requests>=2.31.0`
|
|
18
19
|
|
|
19
20
|
## 📦 安装
|
|
20
21
|
|
|
@@ -26,6 +27,9 @@ pip install python-sandbox-sdk
|
|
|
26
27
|
git clone https://github.com/zjwan461/python-sandbox.git
|
|
27
28
|
cd python-sandbox/sdk/python-sandbox-sdk
|
|
28
29
|
pip install -e .
|
|
30
|
+
|
|
31
|
+
# 安装异步支持(可选)
|
|
32
|
+
pip install python-sandbox-sdk[async]
|
|
29
33
|
```
|
|
30
34
|
|
|
31
35
|
## 🚀 快速开始
|
|
@@ -81,7 +85,6 @@ content = client.read_file(session_id, "/tmp/config.json")
|
|
|
81
85
|
client.upload_file(session_id, "/tmp/image.png", b"\\x89PNG...")
|
|
82
86
|
data = client.download_file(session_id, "/tmp/image.png")
|
|
83
87
|
```
|
|
84
|
-
|
|
85
88
|
## 📚 使用样例(覆盖全部 API 与典型场景)
|
|
86
89
|
|
|
87
90
|
完整可运行样例见 [`usage/`](usage/) 目录:
|
|
@@ -99,6 +102,8 @@ data = client.download_file(session_id, "/tmp/image.png")
|
|
|
99
102
|
| [`09_long_running.py`](usage/09_long_running.py) | 长任务:进度回报 + 异常安全 | `exec_python`, `exec_shell` |
|
|
100
103
|
| [`10_concurrent_sessions.py`](usage/10_concurrent_sessions.py) | 并发:`ThreadPoolExecutor` + 上限约束 | 多会话并行 |
|
|
101
104
|
| [`11_error_handling.py`](usage/11_error_handling.py) | 错误全景:鉴权 / 会话 / 黑名单 / 网络 | 全 API 错误路径 |
|
|
105
|
+
| [`12_async_client.py`](usage/12_async_client.py) | 异步客户端:完整异步操作示例 | 全 API 异步版本 |
|
|
106
|
+
| [`11_error_handling.py`](usage/11_error_handling.py) | 错误全景:鉴权 / 会话 / 黑名单 / 网络 | 全 API 错误路径 |
|
|
102
107
|
|
|
103
108
|
运行样例:
|
|
104
109
|
|
|
@@ -129,6 +134,8 @@ for f in 0[1-9]_*.py 1[0-1]_*.py; do python "$f"; done
|
|
|
129
134
|
|
|
130
135
|
`CommandResult` 字段:`exit_code`, `stdout`, `stderr`, `success`(property), `combined_output`(property)。
|
|
131
136
|
|
|
137
|
+
> 💡 **异步版本**:`AsyncSandboxClient` 提供完全相同的 API,所有方法均为 `async`,支持 `await` 调用和 `async with` 上下文管理器。
|
|
138
|
+
|
|
132
139
|
## ⚙️ 环境变量
|
|
133
140
|
|
|
134
141
|
为避免硬编码,样例通过环境变量注入连接信息:
|
|
@@ -158,6 +165,8 @@ except OSError as e:
|
|
|
158
165
|
|
|
159
166
|
## 🧵 并发使用
|
|
160
167
|
|
|
168
|
+
### 同步并发(ThreadPoolExecutor)
|
|
169
|
+
|
|
161
170
|
```python
|
|
162
171
|
from concurrent.futures import ThreadPoolExecutor
|
|
163
172
|
# 每个任务使用独立的 client + session(线程安全)
|
|
@@ -167,6 +176,33 @@ with ThreadPoolExecutor(max_workers=5) as pool:
|
|
|
167
176
|
print(f.result())
|
|
168
177
|
```
|
|
169
178
|
|
|
179
|
+
### 异步并发(AsyncSandboxClient)
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
import asyncio
|
|
183
|
+
from python_sandbox_sdk import AsyncSandboxClient
|
|
184
|
+
|
|
185
|
+
async def run_task(task_id):
|
|
186
|
+
async with AsyncSandboxClient("http://localhost:8080", "sandbox-secret-key") as client:
|
|
187
|
+
session_id = await client.create_session()
|
|
188
|
+
try:
|
|
189
|
+
result = await client.exec_python(
|
|
190
|
+
session_id, f"print('Task {task_id} done')"
|
|
191
|
+
)
|
|
192
|
+
return result.stdout
|
|
193
|
+
finally:
|
|
194
|
+
await client.delete_session(session_id)
|
|
195
|
+
|
|
196
|
+
async def main():
|
|
197
|
+
# 并发执行 5 个任务
|
|
198
|
+
tasks = [run_task(i) for i in range(5)]
|
|
199
|
+
results = await asyncio.gather(*tasks)
|
|
200
|
+
for r in results:
|
|
201
|
+
print(r)
|
|
202
|
+
|
|
203
|
+
asyncio.run(main())
|
|
204
|
+
```
|
|
205
|
+
|
|
170
206
|
> ⚠️ 并发数受后端 `sandbox.max-containers`(默认 10)约束。
|
|
171
207
|
|
|
172
208
|
## 📦 打包发布
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-sandbox-sdk"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "Python SDK for Python Sandbox API - Run Python code, shell commands, and manage packages in Docker containers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
Hello!
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
import
|
|
19
|
-
import tempfile
|
|
18
|
+
import io
|
|
20
19
|
from typing import Optional
|
|
21
20
|
|
|
22
21
|
import aiohttp
|
|
@@ -266,31 +265,27 @@ class AsyncSandboxClient:
|
|
|
266
265
|
container_path: 容器内目标路径
|
|
267
266
|
data: 文件字节数据
|
|
268
267
|
"""
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
await self._raise_if_error(resp)
|
|
291
|
-
finally:
|
|
292
|
-
if os.path.exists(tmp_path):
|
|
293
|
-
os.unlink(tmp_path)
|
|
268
|
+
import os
|
|
269
|
+
|
|
270
|
+
url = f"{self.base_url}/api/sandbox/file/upload"
|
|
271
|
+
|
|
272
|
+
# 使用 BytesIO 避免临时文件和阻塞 I/O
|
|
273
|
+
file_obj = io.BytesIO(data)
|
|
274
|
+
form = aiohttp.FormData()
|
|
275
|
+
form.add_field(
|
|
276
|
+
"file",
|
|
277
|
+
file_obj,
|
|
278
|
+
filename=os.path.basename(container_path),
|
|
279
|
+
)
|
|
280
|
+
form.add_field("sessionId", session_id)
|
|
281
|
+
form.add_field("path", container_path)
|
|
282
|
+
|
|
283
|
+
# 上传使用独立的 session,避免继承主 session 的 Content-Type
|
|
284
|
+
upload_timeout = aiohttp.ClientTimeout(total=120)
|
|
285
|
+
async with aiohttp.ClientSession(timeout=upload_timeout) as upload_session:
|
|
286
|
+
upload_session.headers.update({"X-Api-Key": self.api_key})
|
|
287
|
+
async with upload_session.post(url, data=form) as resp:
|
|
288
|
+
await self._raise_if_error(resp)
|
|
294
289
|
|
|
295
290
|
async def download_file(self, session_id: str, container_path: str) -> bytes:
|
|
296
291
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-sandbox-sdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Python SDK for Python Sandbox API - Run Python code, shell commands, and manage packages in Docker containers
|
|
5
5
|
Home-page: https://github.com/zjwan461/python-sandbox
|
|
6
6
|
Author-email: Jerry <826935261@qq.com>
|
|
@@ -45,7 +45,8 @@ Python Sandbox 官方 Python SDK — 在隔离的 Docker 容器中执行 Python
|
|
|
45
45
|
- 🔌 **上下文管理器**:自动管理 HTTP 连接与会话生命周期
|
|
46
46
|
- 🛡️ **异常体系**:独立的 `ApiRequestError` / `SandboxError` 类型
|
|
47
47
|
- 🧵 **线程安全**:`requests.Session` 复用,支持 `ThreadPoolExecutor` 并发
|
|
48
|
-
-
|
|
48
|
+
- ⚡ **异步支持**:提供 `AsyncSandboxClient`,基于 `aiohttp` 实现完整异步 API
|
|
49
|
+
- 🐍 **Python ≥ 3.8**:同步客户端仅依赖 `requests>=2.31.0`
|
|
49
50
|
|
|
50
51
|
## 📦 安装
|
|
51
52
|
|
|
@@ -57,6 +58,9 @@ pip install python-sandbox-sdk
|
|
|
57
58
|
git clone https://github.com/zjwan461/python-sandbox.git
|
|
58
59
|
cd python-sandbox/sdk/python-sandbox-sdk
|
|
59
60
|
pip install -e .
|
|
61
|
+
|
|
62
|
+
# 安装异步支持(可选)
|
|
63
|
+
pip install python-sandbox-sdk[async]
|
|
60
64
|
```
|
|
61
65
|
|
|
62
66
|
## 🚀 快速开始
|
|
@@ -112,7 +116,6 @@ content = client.read_file(session_id, "/tmp/config.json")
|
|
|
112
116
|
client.upload_file(session_id, "/tmp/image.png", b"\\x89PNG...")
|
|
113
117
|
data = client.download_file(session_id, "/tmp/image.png")
|
|
114
118
|
```
|
|
115
|
-
|
|
116
119
|
## 📚 使用样例(覆盖全部 API 与典型场景)
|
|
117
120
|
|
|
118
121
|
完整可运行样例见 [`usage/`](usage/) 目录:
|
|
@@ -130,6 +133,8 @@ data = client.download_file(session_id, "/tmp/image.png")
|
|
|
130
133
|
| [`09_long_running.py`](usage/09_long_running.py) | 长任务:进度回报 + 异常安全 | `exec_python`, `exec_shell` |
|
|
131
134
|
| [`10_concurrent_sessions.py`](usage/10_concurrent_sessions.py) | 并发:`ThreadPoolExecutor` + 上限约束 | 多会话并行 |
|
|
132
135
|
| [`11_error_handling.py`](usage/11_error_handling.py) | 错误全景:鉴权 / 会话 / 黑名单 / 网络 | 全 API 错误路径 |
|
|
136
|
+
| [`12_async_client.py`](usage/12_async_client.py) | 异步客户端:完整异步操作示例 | 全 API 异步版本 |
|
|
137
|
+
| [`11_error_handling.py`](usage/11_error_handling.py) | 错误全景:鉴权 / 会话 / 黑名单 / 网络 | 全 API 错误路径 |
|
|
133
138
|
|
|
134
139
|
运行样例:
|
|
135
140
|
|
|
@@ -160,6 +165,8 @@ for f in 0[1-9]_*.py 1[0-1]_*.py; do python "$f"; done
|
|
|
160
165
|
|
|
161
166
|
`CommandResult` 字段:`exit_code`, `stdout`, `stderr`, `success`(property), `combined_output`(property)。
|
|
162
167
|
|
|
168
|
+
> 💡 **异步版本**:`AsyncSandboxClient` 提供完全相同的 API,所有方法均为 `async`,支持 `await` 调用和 `async with` 上下文管理器。
|
|
169
|
+
|
|
163
170
|
## ⚙️ 环境变量
|
|
164
171
|
|
|
165
172
|
为避免硬编码,样例通过环境变量注入连接信息:
|
|
@@ -189,6 +196,8 @@ except OSError as e:
|
|
|
189
196
|
|
|
190
197
|
## 🧵 并发使用
|
|
191
198
|
|
|
199
|
+
### 同步并发(ThreadPoolExecutor)
|
|
200
|
+
|
|
192
201
|
```python
|
|
193
202
|
from concurrent.futures import ThreadPoolExecutor
|
|
194
203
|
# 每个任务使用独立的 client + session(线程安全)
|
|
@@ -198,6 +207,33 @@ with ThreadPoolExecutor(max_workers=5) as pool:
|
|
|
198
207
|
print(f.result())
|
|
199
208
|
```
|
|
200
209
|
|
|
210
|
+
### 异步并发(AsyncSandboxClient)
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import asyncio
|
|
214
|
+
from python_sandbox_sdk import AsyncSandboxClient
|
|
215
|
+
|
|
216
|
+
async def run_task(task_id):
|
|
217
|
+
async with AsyncSandboxClient("http://localhost:8080", "sandbox-secret-key") as client:
|
|
218
|
+
session_id = await client.create_session()
|
|
219
|
+
try:
|
|
220
|
+
result = await client.exec_python(
|
|
221
|
+
session_id, f"print('Task {task_id} done')"
|
|
222
|
+
)
|
|
223
|
+
return result.stdout
|
|
224
|
+
finally:
|
|
225
|
+
await client.delete_session(session_id)
|
|
226
|
+
|
|
227
|
+
async def main():
|
|
228
|
+
# 并发执行 5 个任务
|
|
229
|
+
tasks = [run_task(i) for i in range(5)]
|
|
230
|
+
results = await asyncio.gather(*tasks)
|
|
231
|
+
for r in results:
|
|
232
|
+
print(r)
|
|
233
|
+
|
|
234
|
+
asyncio.run(main())
|
|
235
|
+
```
|
|
236
|
+
|
|
201
237
|
> ⚠️ 并发数受后端 `sandbox.max-containers`(默认 10)约束。
|
|
202
238
|
|
|
203
239
|
## 📦 打包发布
|
|
File without changes
|
|
File without changes
|
{python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/requires.txt
RENAMED
|
File without changes
|
{python_sandbox_sdk-1.0.1 → python_sandbox_sdk-1.0.2}/python_sandbox_sdk.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|