tamar-file-hub-client 0.0.1__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.
- file_hub_client/__init__.py +88 -0
- file_hub_client/client.py +414 -0
- file_hub_client/enums/__init__.py +12 -0
- file_hub_client/enums/export_format.py +16 -0
- file_hub_client/enums/role.py +7 -0
- file_hub_client/enums/upload_mode.py +11 -0
- file_hub_client/errors/__init__.py +30 -0
- file_hub_client/errors/exceptions.py +93 -0
- file_hub_client/py.typed +1 -0
- file_hub_client/rpc/__init__.py +10 -0
- file_hub_client/rpc/async_client.py +312 -0
- file_hub_client/rpc/gen/__init__.py +1 -0
- file_hub_client/rpc/gen/file_service_pb2.py +74 -0
- file_hub_client/rpc/gen/file_service_pb2_grpc.py +533 -0
- file_hub_client/rpc/gen/folder_service_pb2.py +53 -0
- file_hub_client/rpc/gen/folder_service_pb2_grpc.py +269 -0
- file_hub_client/rpc/generate_grpc.py +76 -0
- file_hub_client/rpc/protos/file_service.proto +147 -0
- file_hub_client/rpc/protos/folder_service.proto +65 -0
- file_hub_client/rpc/sync_client.py +313 -0
- file_hub_client/schemas/__init__.py +43 -0
- file_hub_client/schemas/context.py +160 -0
- file_hub_client/schemas/file.py +89 -0
- file_hub_client/schemas/folder.py +29 -0
- file_hub_client/services/__init__.py +17 -0
- file_hub_client/services/file/__init__.py +14 -0
- file_hub_client/services/file/async_blob_service.py +482 -0
- file_hub_client/services/file/async_file_service.py +257 -0
- file_hub_client/services/file/base_file_service.py +103 -0
- file_hub_client/services/file/sync_blob_service.py +478 -0
- file_hub_client/services/file/sync_file_service.py +255 -0
- file_hub_client/services/folder/__init__.py +10 -0
- file_hub_client/services/folder/async_folder_service.py +206 -0
- file_hub_client/services/folder/sync_folder_service.py +205 -0
- file_hub_client/utils/__init__.py +48 -0
- file_hub_client/utils/converter.py +108 -0
- file_hub_client/utils/download_helper.py +355 -0
- file_hub_client/utils/file_utils.py +105 -0
- file_hub_client/utils/retry.py +69 -0
- file_hub_client/utils/upload_helper.py +527 -0
- tamar_file_hub_client-0.0.1.dist-info/METADATA +874 -0
- tamar_file_hub_client-0.0.1.dist-info/RECORD +44 -0
- tamar_file_hub_client-0.0.1.dist-info/WHEEL +5 -0
- tamar_file_hub_client-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
"""
|
2
|
+
文件管理系统客户端SDK
|
3
|
+
|
4
|
+
一个基于gRPC的文件管理系统Python客户端SDK,支持:
|
5
|
+
- 文件和文件夹的增删改查
|
6
|
+
- 多种上传模式(普通上传、断点续传、流式上传、客户端直传)
|
7
|
+
- 传统文件类型和自定义文件类型
|
8
|
+
- 文件导出(将自定义文件类型导出为传统格式)
|
9
|
+
- 直接操作对象存储
|
10
|
+
- 同时支持同步和异步API
|
11
|
+
"""
|
12
|
+
|
13
|
+
from .client import (
|
14
|
+
AsyncTamarFileHubClient,
|
15
|
+
TamarFileHubClient,
|
16
|
+
tamar_client, # 默认的同步客户端单例
|
17
|
+
async_tamar_client, # 默认的异步客户端单例
|
18
|
+
get_client, # 获取同步客户端单例的函数
|
19
|
+
get_async_client, # 获取异步客户端单例的函数
|
20
|
+
)
|
21
|
+
|
22
|
+
from .enums import Role, UploadMode, ExportFormat
|
23
|
+
from .errors import (
|
24
|
+
FileHubError,
|
25
|
+
FileNotFoundError,
|
26
|
+
FolderNotFoundError,
|
27
|
+
UploadError,
|
28
|
+
DownloadError,
|
29
|
+
ExportError,
|
30
|
+
StorageError,
|
31
|
+
ValidationError,
|
32
|
+
ConnectionError,
|
33
|
+
TimeoutError,
|
34
|
+
PermissionError,
|
35
|
+
)
|
36
|
+
from .schemas import (
|
37
|
+
File,
|
38
|
+
UploadUrlResponse,
|
39
|
+
ShareLinkRequest,
|
40
|
+
FileVisitRequest,
|
41
|
+
FileListRequest,
|
42
|
+
FileListResponse,
|
43
|
+
FolderInfo,
|
44
|
+
FolderListResponse,
|
45
|
+
UserContext,
|
46
|
+
RequestContext,
|
47
|
+
FullContext,
|
48
|
+
)
|
49
|
+
|
50
|
+
__all__ = [
|
51
|
+
# 客户端
|
52
|
+
"AsyncTamarFileHubClient",
|
53
|
+
"TamarFileHubClient",
|
54
|
+
"tamar_client", # 默认同步客户端单例
|
55
|
+
"async_tamar_client", # 默认异步客户端单例
|
56
|
+
"get_client",
|
57
|
+
"get_async_client",
|
58
|
+
|
59
|
+
# 文件相关模型
|
60
|
+
"File",
|
61
|
+
"UploadUrlResponse",
|
62
|
+
"ShareLinkRequest",
|
63
|
+
"FileVisitRequest",
|
64
|
+
"FileListRequest",
|
65
|
+
"FileListResponse",
|
66
|
+
|
67
|
+
# 文件夹相关模型
|
68
|
+
"FolderInfo",
|
69
|
+
"FolderListResponse",
|
70
|
+
|
71
|
+
# 枚举
|
72
|
+
"Role",
|
73
|
+
"UploadMode",
|
74
|
+
"ExportFormat",
|
75
|
+
|
76
|
+
# 异常
|
77
|
+
"FileHubError",
|
78
|
+
"FileNotFoundError",
|
79
|
+
"FolderNotFoundError",
|
80
|
+
"UploadError",
|
81
|
+
"DownloadError",
|
82
|
+
"ExportError",
|
83
|
+
"StorageError",
|
84
|
+
"ValidationError",
|
85
|
+
"ConnectionError",
|
86
|
+
"TimeoutError",
|
87
|
+
"PermissionError",
|
88
|
+
]
|
@@ -0,0 +1,414 @@
|
|
1
|
+
"""
|
2
|
+
文件管理系统客户端
|
3
|
+
|
4
|
+
提供与文件管理系统交互的客户端接口
|
5
|
+
"""
|
6
|
+
import os
|
7
|
+
import threading
|
8
|
+
from asyncio import Lock
|
9
|
+
from typing import Optional, Dict
|
10
|
+
|
11
|
+
from .enums import Role
|
12
|
+
from .rpc.async_client import AsyncGrpcClient
|
13
|
+
from .rpc.sync_client import SyncGrpcClient
|
14
|
+
from .services import (
|
15
|
+
AsyncBlobService,
|
16
|
+
AsyncFileService,
|
17
|
+
AsyncFolderService,
|
18
|
+
SyncBlobService,
|
19
|
+
SyncFileService,
|
20
|
+
SyncFolderService,
|
21
|
+
)
|
22
|
+
from .schemas.context import UserContext, RequestContext
|
23
|
+
|
24
|
+
|
25
|
+
class AsyncTamarFileHubClient:
|
26
|
+
"""异步文件管理系统客户端"""
|
27
|
+
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
host: Optional[str] = None,
|
31
|
+
port: Optional[int] = None,
|
32
|
+
secure: Optional[bool] = None,
|
33
|
+
credentials: Optional[dict] = None,
|
34
|
+
auto_connect: bool = True,
|
35
|
+
retry_count: Optional[int] = None,
|
36
|
+
retry_delay: Optional[float] = None,
|
37
|
+
default_metadata: Optional[Dict[str, str]] = None,
|
38
|
+
user_context: Optional[UserContext] = None,
|
39
|
+
request_context: Optional[RequestContext] = None,
|
40
|
+
):
|
41
|
+
"""
|
42
|
+
初始化客户端
|
43
|
+
|
44
|
+
Args:
|
45
|
+
host: 服务器地址(默认从环境变量 FILE_HUB_HOST 读取,否则使用 localhost)
|
46
|
+
port: 服务器端口(默认从环境变量 FILE_HUB_PORT 读取,否则使用 50051)
|
47
|
+
secure: 是否使用TLS(默认从环境变量 FILE_HUB_SECURE 读取,否则使用 False)
|
48
|
+
credentials: 认证凭据
|
49
|
+
auto_connect: 是否自动连接(默认 True)
|
50
|
+
retry_count: 连接重试次数(默认从环境变量 FILE_HUB_RETRY_COUNT 读取,否则使用 3)
|
51
|
+
retry_delay: 重试延迟秒数(默认从环境变量 FILE_HUB_RETRY_DELAY 读取,否则使用 1.0)
|
52
|
+
default_metadata: 默认的元数据(如 org_id, user_id 等)
|
53
|
+
user_context: 用户上下文
|
54
|
+
request_context: 请求上下文
|
55
|
+
|
56
|
+
环境变量:
|
57
|
+
FILE_HUB_HOST: gRPC 服务器地址
|
58
|
+
FILE_HUB_PORT: gRPC 服务器端口
|
59
|
+
FILE_HUB_SECURE: 是否启用 TLS(true/false)
|
60
|
+
FILE_HUB_API_KEY: API 密钥(如果需要)
|
61
|
+
FILE_HUB_RETRY_COUNT: 连接重试次数
|
62
|
+
FILE_HUB_RETRY_DELAY: 重试延迟(秒)
|
63
|
+
"""
|
64
|
+
# 从环境变量或参数获取配置
|
65
|
+
self._host = host or os.getenv('FILE_HUB_HOST', 'localhost')
|
66
|
+
self._port = port or int(os.getenv('FILE_HUB_PORT', '50051'))
|
67
|
+
self._secure = secure if secure is not None else os.getenv('FILE_HUB_SECURE', 'false').lower() == 'true'
|
68
|
+
self._retry_count = retry_count or int(os.getenv('FILE_HUB_RETRY_COUNT', '3'))
|
69
|
+
self._retry_delay = retry_delay or float(os.getenv('FILE_HUB_RETRY_DELAY', '1.0'))
|
70
|
+
self._connect_lock = Lock()
|
71
|
+
|
72
|
+
# 处理认证凭据
|
73
|
+
if credentials is None and os.getenv('FILE_HUB_API_KEY'):
|
74
|
+
credentials = {'api_key': os.getenv('FILE_HUB_API_KEY')}
|
75
|
+
|
76
|
+
# 处理默认元数据
|
77
|
+
if default_metadata is None:
|
78
|
+
default_metadata = {}
|
79
|
+
|
80
|
+
self._client = AsyncGrpcClient(
|
81
|
+
self._host,
|
82
|
+
self._port,
|
83
|
+
self._secure,
|
84
|
+
credentials,
|
85
|
+
retry_count=self._retry_count,
|
86
|
+
retry_delay=self._retry_delay,
|
87
|
+
default_metadata=default_metadata,
|
88
|
+
user_context=user_context,
|
89
|
+
request_context=request_context,
|
90
|
+
)
|
91
|
+
self._blob_service = None
|
92
|
+
self._file_service = None
|
93
|
+
self._folder_service = None
|
94
|
+
self._auto_connect = auto_connect
|
95
|
+
self._connected = False
|
96
|
+
|
97
|
+
async def _ensure_connected(self):
|
98
|
+
"""确保客户端已连接"""
|
99
|
+
if not self._connected and self._auto_connect:
|
100
|
+
await self.connect()
|
101
|
+
|
102
|
+
@property
|
103
|
+
def blobs(self) -> AsyncBlobService:
|
104
|
+
"""获取文件服务"""
|
105
|
+
if self._blob_service is None:
|
106
|
+
self._blob_service = AsyncBlobService(self._client)
|
107
|
+
return self._blob_service
|
108
|
+
|
109
|
+
@property
|
110
|
+
def files(self) -> AsyncFileService:
|
111
|
+
"""获取文件服务"""
|
112
|
+
if self._file_service is None:
|
113
|
+
self._file_service = AsyncFileService(self._client)
|
114
|
+
return self._file_service
|
115
|
+
|
116
|
+
@property
|
117
|
+
def folders(self) -> AsyncFolderService:
|
118
|
+
"""获取文件夹服务"""
|
119
|
+
if self._folder_service is None:
|
120
|
+
self._folder_service = AsyncFolderService(self._client)
|
121
|
+
return self._folder_service
|
122
|
+
|
123
|
+
def set_user_context(self, org_id: str, user_id: str, role: Role = Role.ACCOUNT, actor_id: Optional[str] = None):
|
124
|
+
"""
|
125
|
+
设置用户上下文信息
|
126
|
+
|
127
|
+
Args:
|
128
|
+
org_id: 组织ID
|
129
|
+
user_id: 用户ID
|
130
|
+
role: 用户角色(默认为 ACCOUNT)
|
131
|
+
actor_id: 操作者ID(如果不同于 user_id)
|
132
|
+
"""
|
133
|
+
self._client.set_user_context(org_id, user_id, role, actor_id)
|
134
|
+
|
135
|
+
def get_user_context(self) -> Optional[UserContext]:
|
136
|
+
"""获取当前用户上下文"""
|
137
|
+
return self._client.get_user_context()
|
138
|
+
|
139
|
+
def clear_user_context(self):
|
140
|
+
"""清除用户上下文信息"""
|
141
|
+
self._client.clear_user_context()
|
142
|
+
|
143
|
+
def set_request_context(self, request_context: RequestContext):
|
144
|
+
"""设置请求上下文"""
|
145
|
+
self._client.set_request_context(request_context)
|
146
|
+
|
147
|
+
def get_request_context(self) -> RequestContext:
|
148
|
+
"""获取当前请求上下文"""
|
149
|
+
return self._client.get_request_context()
|
150
|
+
|
151
|
+
def update_request_context(self, **kwargs):
|
152
|
+
"""
|
153
|
+
更新请求上下文的部分字段
|
154
|
+
|
155
|
+
Args:
|
156
|
+
client_ip: 客户端IP地址
|
157
|
+
client_version: 客户端版本
|
158
|
+
client_type: 客户端类型
|
159
|
+
user_agent: User-Agent信息
|
160
|
+
extra: 额外的元数据字典
|
161
|
+
"""
|
162
|
+
self._client.update_request_context(**kwargs)
|
163
|
+
|
164
|
+
def update_metadata(self, **kwargs):
|
165
|
+
"""
|
166
|
+
更新默认元数据
|
167
|
+
|
168
|
+
Args:
|
169
|
+
**kwargs: 要更新的元数据键值对
|
170
|
+
"""
|
171
|
+
self._client.update_default_metadata(**kwargs)
|
172
|
+
|
173
|
+
async def connect(self):
|
174
|
+
"""连接到服务器"""
|
175
|
+
if not self._connected:
|
176
|
+
async with self._connect_lock:
|
177
|
+
if not self._connected:
|
178
|
+
await self._client.connect()
|
179
|
+
self._connected = True
|
180
|
+
|
181
|
+
async def close(self):
|
182
|
+
"""关闭连接"""
|
183
|
+
if self._connected:
|
184
|
+
await self._client.close()
|
185
|
+
self._connected = False
|
186
|
+
|
187
|
+
async def __aenter__(self) -> "AsyncTamarFileHubClient":
|
188
|
+
await self._ensure_connected()
|
189
|
+
return self
|
190
|
+
|
191
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
192
|
+
await self.close()
|
193
|
+
|
194
|
+
|
195
|
+
class TamarFileHubClient:
|
196
|
+
"""同步文件管理系统客户端"""
|
197
|
+
|
198
|
+
def __init__(
|
199
|
+
self,
|
200
|
+
host: Optional[str] = None,
|
201
|
+
port: Optional[int] = None,
|
202
|
+
secure: Optional[bool] = None,
|
203
|
+
credentials: Optional[dict] = None,
|
204
|
+
auto_connect: bool = True,
|
205
|
+
retry_count: Optional[int] = None,
|
206
|
+
retry_delay: Optional[float] = None,
|
207
|
+
default_metadata: Optional[Dict[str, str]] = None,
|
208
|
+
user_context: Optional[UserContext] = None,
|
209
|
+
request_context: Optional[RequestContext] = None,
|
210
|
+
):
|
211
|
+
"""
|
212
|
+
初始化客户端
|
213
|
+
|
214
|
+
Args:
|
215
|
+
host: 服务器地址(默认从环境变量 FILE_HUB_HOST 读取,否则使用 localhost)
|
216
|
+
port: 服务器端口(默认从环境变量 FILE_HUB_PORT 读取,否则使用 50051)
|
217
|
+
secure: 是否使用TLS(默认从环境变量 FILE_HUB_SECURE 读取,否则使用 False)
|
218
|
+
credentials: 认证凭据
|
219
|
+
auto_connect: 是否自动连接(默认 True)
|
220
|
+
retry_count: 连接重试次数(默认从环境变量 FILE_HUB_RETRY_COUNT 读取,否则使用 3)
|
221
|
+
retry_delay: 重试延迟秒数(默认从环境变量 FILE_HUB_RETRY_DELAY 读取,否则使用 1.0)
|
222
|
+
default_metadata: 默认的元数据(如 org_id, user_id 等)
|
223
|
+
user_context: 用户上下文
|
224
|
+
request_context: 请求上下文
|
225
|
+
|
226
|
+
环境变量:
|
227
|
+
FILE_HUB_HOST: gRPC 服务器地址
|
228
|
+
FILE_HUB_PORT: gRPC 服务器端口
|
229
|
+
FILE_HUB_SECURE: 是否启用 TLS(true/false)
|
230
|
+
FILE_HUB_API_KEY: API 密钥(如果需要)
|
231
|
+
FILE_HUB_RETRY_COUNT: 连接重试次数
|
232
|
+
FILE_HUB_RETRY_DELAY: 重试延迟(秒)
|
233
|
+
"""
|
234
|
+
# 从环境变量或参数获取配置
|
235
|
+
self._host = host or os.getenv('FILE_HUB_HOST', 'localhost')
|
236
|
+
self._port = port or int(os.getenv('FILE_HUB_PORT', '50051'))
|
237
|
+
self._secure = secure if secure is not None else os.getenv('FILE_HUB_SECURE', 'false').lower() == 'true'
|
238
|
+
self._retry_count = retry_count or int(os.getenv('FILE_HUB_RETRY_COUNT', '3'))
|
239
|
+
self._retry_delay = retry_delay or float(os.getenv('FILE_HUB_RETRY_DELAY', '1.0'))
|
240
|
+
self._connect_lock = threading.Lock()
|
241
|
+
|
242
|
+
# 处理认证凭据
|
243
|
+
if credentials is None and os.getenv('FILE_HUB_API_KEY'):
|
244
|
+
credentials = {'api_key': os.getenv('FILE_HUB_API_KEY')}
|
245
|
+
|
246
|
+
# 处理默认元数据
|
247
|
+
if default_metadata is None:
|
248
|
+
default_metadata = {}
|
249
|
+
|
250
|
+
self._client = SyncGrpcClient(
|
251
|
+
self._host,
|
252
|
+
self._port,
|
253
|
+
self._secure,
|
254
|
+
credentials,
|
255
|
+
retry_count=self._retry_count,
|
256
|
+
retry_delay=self._retry_delay,
|
257
|
+
default_metadata=default_metadata,
|
258
|
+
user_context=user_context,
|
259
|
+
request_context=request_context,
|
260
|
+
)
|
261
|
+
self._blob_service = None
|
262
|
+
self._file_service = None
|
263
|
+
self._folder_service = None
|
264
|
+
self._auto_connect = auto_connect
|
265
|
+
self._connected = False
|
266
|
+
|
267
|
+
def _ensure_connected(self):
|
268
|
+
"""确保客户端已连接"""
|
269
|
+
if not self._connected and self._auto_connect:
|
270
|
+
self.connect()
|
271
|
+
|
272
|
+
@property
|
273
|
+
def blobs(self) -> SyncBlobService:
|
274
|
+
"""获取文件服务"""
|
275
|
+
self._ensure_connected()
|
276
|
+
if self._blob_service is None:
|
277
|
+
self._blob_service = SyncBlobService(self._client)
|
278
|
+
return self._blob_service
|
279
|
+
|
280
|
+
@property
|
281
|
+
def files(self) -> SyncFileService:
|
282
|
+
"""获取文件服务"""
|
283
|
+
self._ensure_connected()
|
284
|
+
if self._file_service is None:
|
285
|
+
self._file_service = SyncFileService(self._client)
|
286
|
+
return self._file_service
|
287
|
+
|
288
|
+
@property
|
289
|
+
def folders(self) -> SyncFolderService:
|
290
|
+
"""获取文件夹服务"""
|
291
|
+
self._ensure_connected()
|
292
|
+
if self._folder_service is None:
|
293
|
+
self._folder_service = SyncFolderService(self._client)
|
294
|
+
return self._folder_service
|
295
|
+
|
296
|
+
def set_user_context(self, org_id: str, user_id: str, role: Role = Role.ACCOUNT, actor_id: Optional[str] = None):
|
297
|
+
"""
|
298
|
+
设置用户上下文信息
|
299
|
+
|
300
|
+
Args:
|
301
|
+
org_id: 组织ID
|
302
|
+
user_id: 用户ID
|
303
|
+
role: 用户角色(默认为 ACCOUNT)
|
304
|
+
actor_id: 操作者ID(如果不同于 user_id)
|
305
|
+
"""
|
306
|
+
self._client.set_user_context(org_id, user_id, role, actor_id)
|
307
|
+
|
308
|
+
def get_user_context(self) -> Optional[UserContext]:
|
309
|
+
"""获取当前用户上下文"""
|
310
|
+
return self._client.get_user_context()
|
311
|
+
|
312
|
+
def clear_user_context(self):
|
313
|
+
"""清除用户上下文信息"""
|
314
|
+
self._client.clear_user_context()
|
315
|
+
|
316
|
+
def set_request_context(self, request_context: RequestContext):
|
317
|
+
"""设置请求上下文"""
|
318
|
+
self._client.set_request_context(request_context)
|
319
|
+
|
320
|
+
def get_request_context(self) -> RequestContext:
|
321
|
+
"""获取当前请求上下文"""
|
322
|
+
return self._client.get_request_context()
|
323
|
+
|
324
|
+
def update_request_context(self, **kwargs):
|
325
|
+
"""
|
326
|
+
更新请求上下文的部分字段
|
327
|
+
|
328
|
+
Args:
|
329
|
+
client_ip: 客户端IP地址
|
330
|
+
client_version: 客户端版本
|
331
|
+
client_type: 客户端类型
|
332
|
+
user_agent: User-Agent信息
|
333
|
+
extra: 额外的元数据字典
|
334
|
+
"""
|
335
|
+
self._client.update_request_context(**kwargs)
|
336
|
+
|
337
|
+
def update_metadata(self, **kwargs):
|
338
|
+
"""
|
339
|
+
更新默认元数据
|
340
|
+
|
341
|
+
Args:
|
342
|
+
**kwargs: 要更新的元数据键值对
|
343
|
+
"""
|
344
|
+
self._client.update_default_metadata(**kwargs)
|
345
|
+
|
346
|
+
def connect(self):
|
347
|
+
"""连接到服务器"""
|
348
|
+
if not self._connected:
|
349
|
+
with self._connect_lock:
|
350
|
+
if not self._connected:
|
351
|
+
self._client.connect()
|
352
|
+
self._connected = True
|
353
|
+
|
354
|
+
def close(self):
|
355
|
+
"""关闭连接"""
|
356
|
+
if self._connected:
|
357
|
+
self._client.close()
|
358
|
+
self._connected = False
|
359
|
+
|
360
|
+
def __enter__(self) -> "TamarFileHubClient":
|
361
|
+
self.connect()
|
362
|
+
return self
|
363
|
+
|
364
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
365
|
+
self.close()
|
366
|
+
|
367
|
+
def __del__(self):
|
368
|
+
"""析构函数,确保连接被关闭"""
|
369
|
+
try:
|
370
|
+
self.close()
|
371
|
+
except:
|
372
|
+
pass
|
373
|
+
|
374
|
+
|
375
|
+
# 创建默认的单例客户端实例
|
376
|
+
_default_client = None
|
377
|
+
_default_async_client = None
|
378
|
+
|
379
|
+
|
380
|
+
def get_client(**kwargs) -> TamarFileHubClient:
|
381
|
+
"""
|
382
|
+
获取默认的同步客户端实例(单例)
|
383
|
+
|
384
|
+
Args:
|
385
|
+
**kwargs: 客户端初始化参数,仅在第一次调用时生效
|
386
|
+
|
387
|
+
Returns:
|
388
|
+
TamarFileHubClient: 同步客户端实例
|
389
|
+
"""
|
390
|
+
global _default_client
|
391
|
+
if _default_client is None:
|
392
|
+
_default_client = TamarFileHubClient(**kwargs)
|
393
|
+
return _default_client
|
394
|
+
|
395
|
+
|
396
|
+
def get_async_client(**kwargs) -> AsyncTamarFileHubClient:
|
397
|
+
"""
|
398
|
+
获取默认的异步客户端实例(单例)
|
399
|
+
|
400
|
+
Args:
|
401
|
+
**kwargs: 客户端初始化参数,仅在第一次调用时生效
|
402
|
+
|
403
|
+
Returns:
|
404
|
+
AsyncTamarFileHubClient: 异步客户端实例
|
405
|
+
"""
|
406
|
+
global _default_async_client
|
407
|
+
if _default_async_client is None:
|
408
|
+
_default_async_client = AsyncTamarFileHubClient(**kwargs)
|
409
|
+
return _default_async_client
|
410
|
+
|
411
|
+
|
412
|
+
# 默认客户端实例(自动从环境变量读取配置)
|
413
|
+
tamar_client = get_client()
|
414
|
+
async_tamar_client = get_async_client()
|
@@ -0,0 +1,30 @@
|
|
1
|
+
"""
|
2
|
+
错误和异常定义
|
3
|
+
"""
|
4
|
+
from .exceptions import (
|
5
|
+
FileHubError,
|
6
|
+
FileNotFoundError,
|
7
|
+
FolderNotFoundError,
|
8
|
+
PermissionError,
|
9
|
+
StorageError,
|
10
|
+
UploadError,
|
11
|
+
DownloadError,
|
12
|
+
ExportError,
|
13
|
+
ValidationError,
|
14
|
+
ConnectionError,
|
15
|
+
TimeoutError,
|
16
|
+
)
|
17
|
+
|
18
|
+
__all__ = [
|
19
|
+
"FileHubError",
|
20
|
+
"FileNotFoundError",
|
21
|
+
"FolderNotFoundError",
|
22
|
+
"PermissionError",
|
23
|
+
"StorageError",
|
24
|
+
"UploadError",
|
25
|
+
"DownloadError",
|
26
|
+
"ExportError",
|
27
|
+
"ValidationError",
|
28
|
+
"ConnectionError",
|
29
|
+
"TimeoutError",
|
30
|
+
]
|
@@ -0,0 +1,93 @@
|
|
1
|
+
"""
|
2
|
+
异常类定义
|
3
|
+
"""
|
4
|
+
from typing import Optional, Any
|
5
|
+
|
6
|
+
|
7
|
+
class FileHubError(Exception):
|
8
|
+
"""文件管理系统基础异常"""
|
9
|
+
|
10
|
+
def __init__(self, message: str, code: Optional[str] = None, details: Optional[Any] = None):
|
11
|
+
super().__init__(message)
|
12
|
+
self.message = message
|
13
|
+
self.code = code
|
14
|
+
self.details = details
|
15
|
+
|
16
|
+
|
17
|
+
class FileNotFoundError(FileHubError):
|
18
|
+
"""文件不存在异常"""
|
19
|
+
|
20
|
+
def __init__(self, file_id: str):
|
21
|
+
super().__init__(f"文件不存在: {file_id}", code="FILE_NOT_FOUND")
|
22
|
+
self.file_id = file_id
|
23
|
+
|
24
|
+
|
25
|
+
class FolderNotFoundError(FileHubError):
|
26
|
+
"""文件夹不存在异常"""
|
27
|
+
|
28
|
+
def __init__(self, folder_id: str):
|
29
|
+
super().__init__(f"文件夹不存在: {folder_id}", code="FOLDER_NOT_FOUND")
|
30
|
+
self.folder_id = folder_id
|
31
|
+
|
32
|
+
|
33
|
+
class PermissionError(FileHubError):
|
34
|
+
"""权限不足异常"""
|
35
|
+
|
36
|
+
def __init__(self, message: str):
|
37
|
+
super().__init__(message, code="PERMISSION_DENIED")
|
38
|
+
|
39
|
+
|
40
|
+
class StorageError(FileHubError):
|
41
|
+
"""存储操作异常"""
|
42
|
+
|
43
|
+
def __init__(self, message: str, operation: Optional[str] = None):
|
44
|
+
super().__init__(message, code="STORAGE_ERROR")
|
45
|
+
self.operation = operation
|
46
|
+
|
47
|
+
|
48
|
+
class UploadError(FileHubError):
|
49
|
+
"""上传异常"""
|
50
|
+
|
51
|
+
def __init__(self, message: str, upload_id: Optional[str] = None):
|
52
|
+
super().__init__(message, code="UPLOAD_ERROR")
|
53
|
+
self.upload_id = upload_id
|
54
|
+
|
55
|
+
|
56
|
+
class DownloadError(FileHubError):
|
57
|
+
"""下载异常"""
|
58
|
+
|
59
|
+
def __init__(self, message: str, file_id: Optional[str] = None):
|
60
|
+
super().__init__(message, code="DOWNLOAD_ERROR")
|
61
|
+
self.file_id = file_id
|
62
|
+
|
63
|
+
|
64
|
+
class ExportError(FileHubError):
|
65
|
+
"""导出异常"""
|
66
|
+
|
67
|
+
def __init__(self, message: str, file_id: Optional[str] = None, format: Optional[str] = None):
|
68
|
+
super().__init__(message, code="EXPORT_ERROR")
|
69
|
+
self.file_id = file_id
|
70
|
+
self.format = format
|
71
|
+
|
72
|
+
|
73
|
+
class ValidationError(FileHubError):
|
74
|
+
"""验证异常"""
|
75
|
+
|
76
|
+
def __init__(self, message: str, field: Optional[str] = None):
|
77
|
+
super().__init__(message, code="VALIDATION_ERROR")
|
78
|
+
self.field = field
|
79
|
+
|
80
|
+
|
81
|
+
class ConnectionError(FileHubError):
|
82
|
+
"""连接异常"""
|
83
|
+
|
84
|
+
def __init__(self, message: str):
|
85
|
+
super().__init__(message, code="CONNECTION_ERROR")
|
86
|
+
|
87
|
+
|
88
|
+
class TimeoutError(FileHubError):
|
89
|
+
"""超时异常"""
|
90
|
+
|
91
|
+
def __init__(self, message: str, timeout: Optional[float] = None):
|
92
|
+
super().__init__(message, code="TIMEOUT_ERROR")
|
93
|
+
self.timeout = timeout
|
file_hub_client/py.typed
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|