sycommon-python-lib 0.2.4a6__py3-none-any.whl → 0.2.4a7__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.
- sycommon/agent/sandbox/http_sandbox_backend.py +27 -3
- sycommon/agent/sandbox/minio_sync.py +26 -13
- sycommon/agent/sandbox/sandbox_recovery.py +3 -2
- sycommon/synacos/feign.py +5 -2
- sycommon/synacos/feign_client.py +7 -4
- {sycommon_python_lib-0.2.4a6.dist-info → sycommon_python_lib-0.2.4a7.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.2.4a6.dist-info → sycommon_python_lib-0.2.4a7.dist-info}/RECORD +10 -10
- {sycommon_python_lib-0.2.4a6.dist-info → sycommon_python_lib-0.2.4a7.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.2.4a6.dist-info → sycommon_python_lib-0.2.4a7.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.4a6.dist-info → sycommon_python_lib-0.2.4a7.dist-info}/top_level.txt +0 -0
|
@@ -37,6 +37,11 @@ except ImportError:
|
|
|
37
37
|
_SSL_CONTEXT = ssl.create_default_context()
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
def _read_file_safe(path: str) -> bytes:
|
|
41
|
+
with open(path, "rb") as f:
|
|
42
|
+
return f.read()
|
|
43
|
+
|
|
44
|
+
|
|
40
45
|
class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
|
|
41
46
|
"""
|
|
42
47
|
通过 HTTP API 连接远程沙箱容器
|
|
@@ -210,6 +215,24 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
|
|
|
210
215
|
nacos_group=group,
|
|
211
216
|
)
|
|
212
217
|
|
|
218
|
+
@classmethod
|
|
219
|
+
async def afrom_nacos(
|
|
220
|
+
cls,
|
|
221
|
+
service_name: str,
|
|
222
|
+
user_id: str,
|
|
223
|
+
group: str = "DEFAULT_GROUP",
|
|
224
|
+
version: str = None,
|
|
225
|
+
timeout: int = 180,
|
|
226
|
+
sync_dirs: List[tuple[str, str]] = None,
|
|
227
|
+
auto_sync: bool = False,
|
|
228
|
+
load_balance: bool = True,
|
|
229
|
+
) -> "HTTPSandboxBackend":
|
|
230
|
+
"""异步从 Nacos 服务发现创建后端实例"""
|
|
231
|
+
return await asyncio.to_thread(
|
|
232
|
+
cls.from_nacos,
|
|
233
|
+
service_name, user_id, group, version, timeout, sync_dirs, auto_sync, load_balance,
|
|
234
|
+
)
|
|
235
|
+
|
|
213
236
|
# ============== 内部方法 - 同步版本 ==============
|
|
214
237
|
|
|
215
238
|
def _refresh_from_nacos_and_switch_sync(self) -> bool:
|
|
@@ -565,7 +588,7 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
|
|
|
565
588
|
else:
|
|
566
589
|
# 单文件
|
|
567
590
|
SYLogger.info(f"[Sandbox] 上传单文件: {local_path}")
|
|
568
|
-
content = await asyncio.to_thread(lambda:
|
|
591
|
+
content = await asyncio.to_thread(lambda p: _read_file_safe(p), local_dir)
|
|
569
592
|
upload_results = await self.aupload_files([(remote_path, content)], timeout=timeout)
|
|
570
593
|
if upload_results[0].error:
|
|
571
594
|
results[local_path] = {"success": 0, "failed": 1, "errors": [{"path": remote_path, "error": upload_results[0].error}]}
|
|
@@ -669,7 +692,8 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
|
|
|
669
692
|
def _read_batch():
|
|
670
693
|
items = []
|
|
671
694
|
for sandbox_path, local_file in batch_files:
|
|
672
|
-
|
|
695
|
+
with open(local_file, "rb") as f:
|
|
696
|
+
content = f.read()
|
|
673
697
|
items.append((sandbox_path, content))
|
|
674
698
|
return items
|
|
675
699
|
batch_items = await asyncio.to_thread(_read_batch)
|
|
@@ -1037,7 +1061,7 @@ class HTTPSandboxBackend(FileOperationsMixin, SandboxBackendProtocol):
|
|
|
1037
1061
|
SYLogger.info(f"[Sandbox] 异步目录上传完成: {local_path}, 成功={result['success']}, 失败={result['failed']}")
|
|
1038
1062
|
else:
|
|
1039
1063
|
SYLogger.info(f"[Sandbox] 异步上传单文件: {local_path}")
|
|
1040
|
-
content = await asyncio.to_thread(lambda:
|
|
1064
|
+
content = await asyncio.to_thread(lambda p: _read_file_safe(p), local_dir)
|
|
1041
1065
|
upload_results = await self.aupload_files([(remote_path, content)], timeout=timeout)
|
|
1042
1066
|
if upload_results[0].error:
|
|
1043
1067
|
results[local_path] = {"success": 0, "failed": 1, "errors": [{"path": remote_path, "error": upload_results[0].error}]}
|
|
@@ -162,7 +162,7 @@ class MinioSyncService(metaclass=SingletonMeta):
|
|
|
162
162
|
return None
|
|
163
163
|
|
|
164
164
|
def get_presigned_url(self, object_key: str, expires_days: int = 7) -> Optional[str]:
|
|
165
|
-
"""生成预签名下载 URL"""
|
|
165
|
+
"""生成预签名下载 URL(同步)"""
|
|
166
166
|
if not self._client:
|
|
167
167
|
return None
|
|
168
168
|
try:
|
|
@@ -177,8 +177,12 @@ class MinioSyncService(metaclass=SingletonMeta):
|
|
|
177
177
|
f"[MinIO] Presigned URL failed: {object_key}, error={e}")
|
|
178
178
|
return None
|
|
179
179
|
|
|
180
|
+
async def aget_presigned_url(self, object_key: str, expires_days: int = 7) -> Optional[str]:
|
|
181
|
+
"""异步生成预签名下载 URL"""
|
|
182
|
+
return await asyncio.to_thread(self.get_presigned_url, object_key, expires_days)
|
|
183
|
+
|
|
180
184
|
def remove_object(self, object_key: str) -> bool:
|
|
181
|
-
"""从 MinIO
|
|
185
|
+
"""从 MinIO 删除文件(同步)"""
|
|
182
186
|
if not self._client:
|
|
183
187
|
return False
|
|
184
188
|
try:
|
|
@@ -189,12 +193,12 @@ class MinioSyncService(metaclass=SingletonMeta):
|
|
|
189
193
|
SYLogger.warning(f"[MinIO] Delete failed: {object_key}, error={e}")
|
|
190
194
|
return False
|
|
191
195
|
|
|
192
|
-
def
|
|
193
|
-
"""
|
|
196
|
+
async def aremove_object(self, object_key: str) -> bool:
|
|
197
|
+
"""异步从 MinIO 删除文件"""
|
|
198
|
+
return await asyncio.to_thread(self.remove_object, object_key)
|
|
194
199
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
"""
|
|
200
|
+
def remove_prefix(self, prefix: str) -> int:
|
|
201
|
+
"""删除 MinIO 中指定前缀下的所有对象(同步,用于目录删除)"""
|
|
198
202
|
if not self._client:
|
|
199
203
|
return 0
|
|
200
204
|
try:
|
|
@@ -210,6 +214,10 @@ class MinioSyncService(metaclass=SingletonMeta):
|
|
|
210
214
|
SYLogger.warning(f"[MinIO] Delete prefix failed: {prefix}, error={e}")
|
|
211
215
|
return 0
|
|
212
216
|
|
|
217
|
+
async def aremove_prefix(self, prefix: str) -> int:
|
|
218
|
+
"""异步删除 MinIO 中指定前缀下的所有对象"""
|
|
219
|
+
return await asyncio.to_thread(self.remove_prefix, prefix)
|
|
220
|
+
|
|
213
221
|
# ============== 查找最近副本 ==============
|
|
214
222
|
|
|
215
223
|
def find_latest_object_key(self, user_id: str, file_path: str) -> Optional[str]:
|
|
@@ -252,7 +260,7 @@ class MinioSyncService(metaclass=SingletonMeta):
|
|
|
252
260
|
SYLogger.warning(f"[MinIO] find_latest failed: {e}")
|
|
253
261
|
return None
|
|
254
262
|
|
|
255
|
-
async def
|
|
263
|
+
async def afind_latest_object_key(self, user_id: str, file_path: str) -> Optional[str]:
|
|
256
264
|
"""异步查找某个文件最近一天的 object key"""
|
|
257
265
|
return await asyncio.to_thread(self.find_latest_object_key, user_id, file_path)
|
|
258
266
|
|
|
@@ -473,12 +481,17 @@ class MinioSyncService(metaclass=SingletonMeta):
|
|
|
473
481
|
files_to_upload = []
|
|
474
482
|
for rel_path in batch:
|
|
475
483
|
try:
|
|
476
|
-
|
|
477
|
-
self._client.get_object,
|
|
484
|
+
def _download_object(bucket, key):
|
|
485
|
+
resp = self._client.get_object(bucket, key)
|
|
486
|
+
try:
|
|
487
|
+
return resp.read()
|
|
488
|
+
finally:
|
|
489
|
+
resp.close()
|
|
490
|
+
resp.release_conn()
|
|
491
|
+
|
|
492
|
+
content = await asyncio.to_thread(
|
|
493
|
+
_download_object, self._bucket, f"{prefix}{rel_path}"
|
|
478
494
|
)
|
|
479
|
-
content = response.read()
|
|
480
|
-
response.close()
|
|
481
|
-
response.release_conn()
|
|
482
495
|
files_to_upload.append((f"/{rel_path}", content))
|
|
483
496
|
except Exception as e:
|
|
484
497
|
SYLogger.warning(f"[MinIO] 下载文件失败: {rel_path}, error={e}")
|
|
@@ -123,7 +123,7 @@ class SandboxRecoveryManager:
|
|
|
123
123
|
bool: 切换成功返回 True,否则返回 False
|
|
124
124
|
"""
|
|
125
125
|
# 优先使用底层已有的切换逻辑(会尝试迁移工作空间文件)
|
|
126
|
-
switched = await self.backend.
|
|
126
|
+
switched = await asyncio.to_thread(self.backend._refresh_from_nacos_and_switch_sync)
|
|
127
127
|
|
|
128
128
|
if switched:
|
|
129
129
|
self._consecutive_failures = 0
|
|
@@ -149,7 +149,8 @@ class SandboxRecoveryManager:
|
|
|
149
149
|
SYLogger.error("[Sandbox] NacosService 未初始化")
|
|
150
150
|
return False
|
|
151
151
|
|
|
152
|
-
instances =
|
|
152
|
+
instances = await asyncio.to_thread(
|
|
153
|
+
nacos_manager.get_service_instances,
|
|
153
154
|
self.backend._nacos_service_name,
|
|
154
155
|
group=self.backend._nacos_group
|
|
155
156
|
)
|
sycommon/synacos/feign.py
CHANGED
|
@@ -203,8 +203,11 @@ async def _feign_internal(service_name, api_path, method='GET', params=None, hea
|
|
|
203
203
|
raise TypeError(f"files 参数必须是字典或列表,实际为 {type(files)}")
|
|
204
204
|
if file_path:
|
|
205
205
|
filename = os.path.basename(file_path)
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
def _read_file():
|
|
207
|
+
with open(file_path, 'rb') as f:
|
|
208
|
+
return f.read()
|
|
209
|
+
content = await asyncio.to_thread(_read_file)
|
|
210
|
+
data.add_field('file', content, filename=filename)
|
|
208
211
|
# 移除Content-Type,让aiohttp自动处理
|
|
209
212
|
headers.pop('Content-Type', None)
|
|
210
213
|
async with session.request(
|
sycommon/synacos/feign_client.py
CHANGED
|
@@ -303,10 +303,13 @@ def feign_client(
|
|
|
303
303
|
for path in file_paths:
|
|
304
304
|
if not os.path.exists(path):
|
|
305
305
|
raise FileNotFoundError(f"文件不存在: {path}")
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
306
|
+
def _read_file(p: str) -> bytes:
|
|
307
|
+
with open(p, "rb") as f:
|
|
308
|
+
return f.read()
|
|
309
|
+
content = await asyncio.to_thread(_read_file, path)
|
|
310
|
+
form_data.add_field(
|
|
311
|
+
meta.field_name, content, filename=os.path.basename(path)
|
|
312
|
+
)
|
|
310
313
|
# 处理表单字段(支持 Pydantic 模型)
|
|
311
314
|
form_params = {
|
|
312
315
|
n: m for n, m in param_meta.items() if isinstance(m, Form)}
|
|
@@ -137,10 +137,10 @@ sycommon/agent/mcp/models.py,sha256=RBAIbGETNXkqD3wQZT7eKS4ozkgE9DQEneF1WKZf1C0,
|
|
|
137
137
|
sycommon/agent/mcp/tool_loader.py,sha256=SEny14f7Bm9I17pT-9PJWMbhi9Ki77wvCR0KRNEJmyM,6428
|
|
138
138
|
sycommon/agent/sandbox/__init__.py,sha256=jR7LlkD4J4Y6QYyRXQClkwmqDBCCPmycV_hQV9p9YHw,4621
|
|
139
139
|
sycommon/agent/sandbox/file_ops.py,sha256=6ymRMM0WchM7G_YmF1ckrLjf5s_JCh1wrAp2g_-sg8k,23162
|
|
140
|
-
sycommon/agent/sandbox/http_sandbox_backend.py,sha256=
|
|
141
|
-
sycommon/agent/sandbox/minio_sync.py,sha256=
|
|
140
|
+
sycommon/agent/sandbox/http_sandbox_backend.py,sha256=kwuPEmrOMyxfrRu20AEGqWD9t38L-DrtKSFp6CWt44o,56877
|
|
141
|
+
sycommon/agent/sandbox/minio_sync.py,sha256=d1kuWllvyAvAMsFZCP0OdHEQtXN9BEIgHbupC31BjSk,20000
|
|
142
142
|
sycommon/agent/sandbox/sandbox_pool.py,sha256=eMn8sLakCWf90l6ni2-333QM8oBdX1CflV-WzneFp_k,9133
|
|
143
|
-
sycommon/agent/sandbox/sandbox_recovery.py,sha256=
|
|
143
|
+
sycommon/agent/sandbox/sandbox_recovery.py,sha256=X-eDODx1tmGMh_iTngV6e1ppfDBHpTdkPreJusN5MHY,7358
|
|
144
144
|
sycommon/agent/sandbox/session.py,sha256=TjzC3yFC-VaJ75UwCyL26QX4PRTGNNfQae1FKFuOsYI,2365
|
|
145
145
|
sycommon/auth/__init__.py,sha256=W814cfHlLXFymmxeTi3pIreFb4nhKnQ7NY1H38x1Gic,974
|
|
146
146
|
sycommon/auth/ldap_service.py,sha256=fOcpVov5LWJkBk62qbTaltks1c4la7JsbD104KfdBOI,10102
|
|
@@ -239,8 +239,8 @@ sycommon/sse/sse.py,sha256=OQ3ElV8WCi-AD3-e0nbiUF28Syf6GRpGztneWTn77EM,10356
|
|
|
239
239
|
sycommon/synacos/__init__.py,sha256=Re9YKVjL62AZURejgSQ3-OvIiMXY-KeAAjIcRJ8PsO0,329
|
|
240
240
|
sycommon/synacos/example.py,sha256=FOnBkvodR8WF_jf-RovM3ngVmvZQX6wKwMLscUTGn2M,8707
|
|
241
241
|
sycommon/synacos/example2.py,sha256=yYuQscfHUIl1HLZ8kSRBuZpHUcNWZMi5H3Mb-LjYnvk,8136
|
|
242
|
-
sycommon/synacos/feign.py,sha256=
|
|
243
|
-
sycommon/synacos/feign_client.py,sha256=
|
|
242
|
+
sycommon/synacos/feign.py,sha256=RU6p2gRP3LZoHYBBEUUY9z5KKzGiUmwj4qdo7aF9UNA,12610
|
|
243
|
+
sycommon/synacos/feign_client.py,sha256=i6O20JWl1g2fjD-et7olFaO-0z2yEbKT8-pKluAHCgs,19917
|
|
244
244
|
sycommon/synacos/nacos_client_base.py,sha256=iP5kLkBD2VOrx6X8v6_RnC9NWiBWmL6-Bgf493QOtxc,6899
|
|
245
245
|
sycommon/synacos/nacos_config_manager.py,sha256=Swqsd9X2xO5-x2VfKUrq8HjzRJn8JBPDqyXazWlF-T4,6859
|
|
246
246
|
sycommon/synacos/nacos_heartbeat_manager.py,sha256=LfimUKpG4KaqsVQl150sg3MLK8psanuUwQ07tjL3uBE,10963
|
|
@@ -265,8 +265,8 @@ sycommon/tools/syemail.py,sha256=BDFhgf7WDOQeTcjxJEQdu0dQhnHFPO_p3eI0-Ni3LhQ,561
|
|
|
265
265
|
sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
266
266
|
sycommon/xxljob/__init__.py,sha256=7eoBlQxv-B39IfRSCY2bkqdGYs1QRe1umAWd88VMEEM,86
|
|
267
267
|
sycommon/xxljob/xxljob_service.py,sha256=JIEJaGXhqrTLcyxlyynSrsHg9bBnDNzX-D4qIWLRPUE,6815
|
|
268
|
-
sycommon_python_lib-0.2.
|
|
269
|
-
sycommon_python_lib-0.2.
|
|
270
|
-
sycommon_python_lib-0.2.
|
|
271
|
-
sycommon_python_lib-0.2.
|
|
272
|
-
sycommon_python_lib-0.2.
|
|
268
|
+
sycommon_python_lib-0.2.4a7.dist-info/METADATA,sha256=ReaCw3hM57OWF1Gso6vD-CXssScGQMrJl3f7u78z0uM,7879
|
|
269
|
+
sycommon_python_lib-0.2.4a7.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
270
|
+
sycommon_python_lib-0.2.4a7.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
|
|
271
|
+
sycommon_python_lib-0.2.4a7.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
|
|
272
|
+
sycommon_python_lib-0.2.4a7.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.2.4a6.dist-info → sycommon_python_lib-0.2.4a7.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{sycommon_python_lib-0.2.4a6.dist-info → sycommon_python_lib-0.2.4a7.dist-info}/top_level.txt
RENAMED
|
File without changes
|