tamar-file-hub-client 0.0.1__py3-none-any.whl → 0.0.3__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 +39 -0
- file_hub_client/client.py +43 -6
- file_hub_client/rpc/async_client.py +91 -11
- file_hub_client/rpc/gen/taple_service_pb2.py +225 -0
- file_hub_client/rpc/gen/taple_service_pb2_grpc.py +1626 -0
- file_hub_client/rpc/generate_grpc.py +2 -2
- file_hub_client/rpc/interceptors.py +550 -0
- file_hub_client/rpc/protos/taple_service.proto +874 -0
- file_hub_client/rpc/sync_client.py +91 -9
- file_hub_client/schemas/__init__.py +60 -0
- file_hub_client/schemas/taple.py +413 -0
- file_hub_client/services/__init__.py +5 -0
- file_hub_client/services/file/async_blob_service.py +558 -482
- file_hub_client/services/file/async_file_service.py +18 -9
- file_hub_client/services/file/base_file_service.py +19 -6
- file_hub_client/services/file/sync_blob_service.py +556 -478
- file_hub_client/services/file/sync_file_service.py +18 -9
- file_hub_client/services/folder/async_folder_service.py +20 -11
- file_hub_client/services/folder/sync_folder_service.py +20 -11
- file_hub_client/services/taple/__init__.py +10 -0
- file_hub_client/services/taple/async_taple_service.py +2281 -0
- file_hub_client/services/taple/base_taple_service.py +353 -0
- file_hub_client/services/taple/idempotent_taple_mixin.py +142 -0
- file_hub_client/services/taple/sync_taple_service.py +2256 -0
- file_hub_client/utils/__init__.py +43 -1
- file_hub_client/utils/file_utils.py +59 -11
- file_hub_client/utils/idempotency.py +196 -0
- file_hub_client/utils/logging.py +315 -0
- file_hub_client/utils/retry.py +241 -2
- file_hub_client/utils/smart_retry.py +403 -0
- tamar_file_hub_client-0.0.3.dist-info/METADATA +2050 -0
- tamar_file_hub_client-0.0.3.dist-info/RECORD +57 -0
- tamar_file_hub_client-0.0.1.dist-info/METADATA +0 -874
- tamar_file_hub_client-0.0.1.dist-info/RECORD +0 -44
- {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.3.dist-info}/WHEEL +0 -0
- {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.3.dist-info}/top_level.txt +0 -0
@@ -36,6 +36,7 @@ class AsyncFileService(BaseFileService):
|
|
36
36
|
expire_seconds: int = 86400,
|
37
37
|
max_access: Optional[int] = None,
|
38
38
|
share_password: Optional[str] = None,
|
39
|
+
request_id: Optional[str] = None,
|
39
40
|
**metadata
|
40
41
|
) -> str:
|
41
42
|
"""
|
@@ -70,7 +71,7 @@ class AsyncFileService(BaseFileService):
|
|
70
71
|
request.share_password = share_password
|
71
72
|
|
72
73
|
# 构建元数据
|
73
|
-
grpc_metadata = self.client.build_metadata(**metadata)
|
74
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
74
75
|
|
75
76
|
response = await stub.GenerateShareLink(request, metadata=grpc_metadata)
|
76
77
|
|
@@ -82,6 +83,7 @@ class AsyncFileService(BaseFileService):
|
|
82
83
|
access_type: str = "view",
|
83
84
|
access_duration: int = 0,
|
84
85
|
metadata: Optional[Dict[str, Any]] = None,
|
86
|
+
request_id: Optional[str] = None,
|
85
87
|
**extra_metadata
|
86
88
|
) -> None:
|
87
89
|
"""
|
@@ -113,11 +115,12 @@ class AsyncFileService(BaseFileService):
|
|
113
115
|
)
|
114
116
|
|
115
117
|
# 构建元数据
|
116
|
-
grpc_metadata = self.client.build_metadata(**extra_metadata)
|
118
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **extra_metadata)
|
117
119
|
|
118
120
|
await stub.VisitFile(request, metadata=grpc_metadata)
|
119
121
|
|
120
|
-
async def get_file(self, file_id: str,
|
122
|
+
async def get_file(self, file_id: str, request_id: Optional[str] = None,
|
123
|
+
**metadata) -> File:
|
121
124
|
"""
|
122
125
|
获取文件信息
|
123
126
|
|
@@ -135,7 +138,7 @@ class AsyncFileService(BaseFileService):
|
|
135
138
|
request = file_service_pb2.GetFileRequest(file_id=file_id)
|
136
139
|
|
137
140
|
# 构建元数据
|
138
|
-
grpc_metadata = self.client.build_metadata(**metadata)
|
141
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
139
142
|
|
140
143
|
try:
|
141
144
|
response = await stub.GetFile(request, metadata=grpc_metadata)
|
@@ -145,13 +148,15 @@ class AsyncFileService(BaseFileService):
|
|
145
148
|
raise FileNotFoundError(file_id)
|
146
149
|
raise
|
147
150
|
|
148
|
-
async def rename_file(self, file_id: str, new_name: str,
|
151
|
+
async def rename_file(self, file_id: str, new_name: str, request_id: Optional[str] = None,
|
152
|
+
**metadata) -> File:
|
149
153
|
"""
|
150
154
|
重命名文件
|
151
155
|
|
152
156
|
Args:
|
153
157
|
file_id: 文件ID
|
154
158
|
new_name: 新文件名
|
159
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
155
160
|
**metadata: 额外的元数据(如 x-org-id, x-user-id 等)
|
156
161
|
|
157
162
|
Returns:
|
@@ -167,7 +172,7 @@ class AsyncFileService(BaseFileService):
|
|
167
172
|
)
|
168
173
|
|
169
174
|
# 构建元数据
|
170
|
-
grpc_metadata = self.client.build_metadata(**metadata)
|
175
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
171
176
|
|
172
177
|
try:
|
173
178
|
response = await stub.RenameFile(request, metadata=grpc_metadata)
|
@@ -177,12 +182,14 @@ class AsyncFileService(BaseFileService):
|
|
177
182
|
raise FileNotFoundError(file_id)
|
178
183
|
raise
|
179
184
|
|
180
|
-
async def delete_file(self, file_id: str,
|
185
|
+
async def delete_file(self, file_id: str, request_id: Optional[str] = None,
|
186
|
+
**metadata) -> None:
|
181
187
|
"""
|
182
188
|
删除文件
|
183
189
|
|
184
190
|
Args:
|
185
191
|
file_id: 文件ID
|
192
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
186
193
|
**metadata: 额外的元数据(如 x-org-id, x-user-id 等)
|
187
194
|
"""
|
188
195
|
from ...rpc.gen import file_service_pb2, file_service_pb2_grpc
|
@@ -192,7 +199,7 @@ class AsyncFileService(BaseFileService):
|
|
192
199
|
request = file_service_pb2.DeleteFileRequest(file_id=file_id)
|
193
200
|
|
194
201
|
# 构建元数据
|
195
|
-
grpc_metadata = self.client.build_metadata(**metadata)
|
202
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
196
203
|
|
197
204
|
try:
|
198
205
|
await stub.DeleteFile(request, metadata=grpc_metadata)
|
@@ -210,6 +217,7 @@ class AsyncFileService(BaseFileService):
|
|
210
217
|
created_by: Optional[str] = None,
|
211
218
|
page_size: int = 20,
|
212
219
|
page: int = 1,
|
220
|
+
request_id: Optional[str] = None,
|
213
221
|
**metadata
|
214
222
|
) -> FileListResponse:
|
215
223
|
"""
|
@@ -223,6 +231,7 @@ class AsyncFileService(BaseFileService):
|
|
223
231
|
created_by: 创建者过滤
|
224
232
|
page_size: 每页大小
|
225
233
|
page: 页码
|
234
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
226
235
|
**metadata: 额外的元数据(如 x-org-id, x-user-id 等)
|
227
236
|
|
228
237
|
Returns:
|
@@ -248,7 +257,7 @@ class AsyncFileService(BaseFileService):
|
|
248
257
|
request.created_by = created_by
|
249
258
|
|
250
259
|
# 构建元数据
|
251
|
-
grpc_metadata = self.client.build_metadata(**metadata)
|
260
|
+
grpc_metadata = self.client.build_metadata(request_id=request_id, **metadata)
|
252
261
|
|
253
262
|
response = await stub.ListFiles(request, metadata=grpc_metadata)
|
254
263
|
|
@@ -24,8 +24,10 @@ class BaseFileService:
|
|
24
24
|
"""
|
25
25
|
|
26
26
|
def get_file_type_and_mime(file_path: Path) -> Tuple[str, str]:
|
27
|
+
# 获取文件扩展名,如果没有扩展名则默认为 'dat'
|
28
|
+
file_ext = file_path.suffix.lstrip('.').lower() if file_path.suffix else 'dat'
|
27
29
|
return (
|
28
|
-
|
30
|
+
file_ext,
|
29
31
|
get_file_mime_type(file_path)
|
30
32
|
)
|
31
33
|
|
@@ -53,19 +55,30 @@ class BaseFileService:
|
|
53
55
|
# Case 2: 原始字节流
|
54
56
|
elif isinstance(file, bytes):
|
55
57
|
sha256 = hashlib.sha256(file).hexdigest()
|
56
|
-
|
58
|
+
# 为字节流生成默认文件名
|
59
|
+
file_name = f"upload_{sha256[:8]}.dat"
|
60
|
+
return file_name, file, len(file), "application/octet-stream", 'dat', sha256
|
57
61
|
|
58
62
|
# Case 3: 可读文件对象
|
59
63
|
elif hasattr(file, 'read'):
|
60
64
|
file_name = getattr(file, 'name', None)
|
61
|
-
|
62
|
-
mime_type = get_file_mime_type(Path(file_name)) if file_name else "application/octet-stream"
|
63
|
-
|
65
|
+
|
64
66
|
if hasattr(file, 'seek'):
|
65
67
|
file.seek(0)
|
66
68
|
content, file_hash = calculate_sha256_and_bytes(file)
|
67
69
|
file_size = len(content)
|
68
|
-
|
70
|
+
|
71
|
+
# 如果没有文件名,生成一个默认的
|
72
|
+
if not file_name:
|
73
|
+
file_name = f"upload_{file_hash[:8]}.dat"
|
74
|
+
file_type = 'dat'
|
75
|
+
mime_type = "application/octet-stream"
|
76
|
+
else:
|
77
|
+
file_type = Path(file_name).suffix.lstrip('.').lower()
|
78
|
+
mime_type = get_file_mime_type(Path(file_name))
|
79
|
+
file_name = Path(file_name).name
|
80
|
+
|
81
|
+
return file_name, content, file_size, mime_type, file_type, file_hash
|
69
82
|
|
70
83
|
else:
|
71
84
|
raise ValidationError(f"不支持的文件类型: {type(file)}")
|