tamar-file-hub-client 0.0.1__py3-none-any.whl → 0.0.2__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.
Files changed (36) hide show
  1. file_hub_client/__init__.py +39 -0
  2. file_hub_client/client.py +43 -6
  3. file_hub_client/rpc/async_client.py +91 -11
  4. file_hub_client/rpc/gen/taple_service_pb2.py +225 -0
  5. file_hub_client/rpc/gen/taple_service_pb2_grpc.py +1626 -0
  6. file_hub_client/rpc/generate_grpc.py +2 -2
  7. file_hub_client/rpc/interceptors.py +550 -0
  8. file_hub_client/rpc/protos/taple_service.proto +874 -0
  9. file_hub_client/rpc/sync_client.py +91 -9
  10. file_hub_client/schemas/__init__.py +60 -0
  11. file_hub_client/schemas/taple.py +413 -0
  12. file_hub_client/services/__init__.py +5 -0
  13. file_hub_client/services/file/async_blob_service.py +558 -482
  14. file_hub_client/services/file/async_file_service.py +18 -9
  15. file_hub_client/services/file/base_file_service.py +19 -6
  16. file_hub_client/services/file/sync_blob_service.py +554 -478
  17. file_hub_client/services/file/sync_file_service.py +18 -9
  18. file_hub_client/services/folder/async_folder_service.py +20 -11
  19. file_hub_client/services/folder/sync_folder_service.py +20 -11
  20. file_hub_client/services/taple/__init__.py +10 -0
  21. file_hub_client/services/taple/async_taple_service.py +2281 -0
  22. file_hub_client/services/taple/base_taple_service.py +353 -0
  23. file_hub_client/services/taple/idempotent_taple_mixin.py +142 -0
  24. file_hub_client/services/taple/sync_taple_service.py +2256 -0
  25. file_hub_client/utils/__init__.py +43 -1
  26. file_hub_client/utils/file_utils.py +59 -11
  27. file_hub_client/utils/idempotency.py +196 -0
  28. file_hub_client/utils/logging.py +315 -0
  29. file_hub_client/utils/retry.py +241 -2
  30. file_hub_client/utils/smart_retry.py +403 -0
  31. tamar_file_hub_client-0.0.2.dist-info/METADATA +2050 -0
  32. tamar_file_hub_client-0.0.2.dist-info/RECORD +57 -0
  33. tamar_file_hub_client-0.0.1.dist-info/METADATA +0 -874
  34. tamar_file_hub_client-0.0.1.dist-info/RECORD +0 -44
  35. {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.2.dist-info}/WHEEL +0 -0
  36. {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.2.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, **metadata) -> File:
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, **metadata) -> File:
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, **metadata) -> None:
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
- file_path.suffix.lstrip('.').lower() if file_path.suffix else '',
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
- return None, file, len(file), "application/octet-stream", '', sha256
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
- file_type = Path(file_name).suffix.lstrip('.').lower() if file_name else ''
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
- return Path(file_name).name if file_name else None, content, file_size, mime_type, file_type, file_hash
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)}")