pilot-platform-object-storage 2.0.0__tar.gz → 2.1.0__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.
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/PKG-INFO +1 -1
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/factories.py +1 -1
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/providers/gcp/object_client.py +50 -4
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/pyproject.toml +1 -1
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/README.md +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/__init__.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/clients/__init__.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/clients/azure_blob_client.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/clients/azure_container_client.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/clients/base_container_client.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/clients/base_file_client.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/config.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/managers/__init__.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/managers/azure_blob_manager.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/managers/base_manager.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/providers/__init__.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/providers/azure.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/providers/enum.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/providers/gcp/__init__.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/providers/gcp/bucket_client.py +0 -0
- {pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/providers/gcp/model.py +0 -0
|
@@ -17,7 +17,7 @@ def get_file_client(provider: Provider | str, sas_url: str, **kwargs: dict[str,
|
|
|
17
17
|
if provider == 'azure':
|
|
18
18
|
return AzureBlobClient(blob_sas_url=sas_url)
|
|
19
19
|
else:
|
|
20
|
-
raise ValueError(f
|
|
20
|
+
raise ValueError(f'Unknown provider: {provider}')
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def get_gcs_object_client(project_id: str, service_json: dict[str, Any]) -> GCSObjectClient:
|
|
@@ -71,7 +71,7 @@ class GCSObjectClient:
|
|
|
71
71
|
|
|
72
72
|
def _part_object_name(self, final_key: str, upload_id: str, chunk_number: int) -> str:
|
|
73
73
|
# Temporary object path for each chunk in google starts with 'multipart/'
|
|
74
|
-
return f
|
|
74
|
+
return f'multipart/{upload_id}/{final_key}/part-{chunk_number:06d}'
|
|
75
75
|
|
|
76
76
|
# Object related methods
|
|
77
77
|
|
|
@@ -167,6 +167,52 @@ class GCSObjectClient:
|
|
|
167
167
|
|
|
168
168
|
return session_uri
|
|
169
169
|
|
|
170
|
+
async def cancel_resumable_upload(self, session_uri: str) -> None:
|
|
171
|
+
"""
|
|
172
|
+
Summary:
|
|
173
|
+
Cancel a Google Cloud Storage resumable upload session. After cancellation,
|
|
174
|
+
the session URI becomes unusable and the upload cannot be resumed or finalized.
|
|
175
|
+
|
|
176
|
+
Behavior:
|
|
177
|
+
- Sends a DELETE to the session URI with Content-Length: 0 (required by GCS JSON API).
|
|
178
|
+
- A successful cancellation returns HTTP 499 (Client Closed Request).
|
|
179
|
+
- Some environments may return 204 (No Content) instead — also valid.
|
|
180
|
+
- If a session is already invalid, GCS may return 404 or 410; these are treated as success.
|
|
181
|
+
- Does NOT delete any uploaded draft chunks or create a final object, because the object
|
|
182
|
+
is only committed at the final POST request.
|
|
183
|
+
|
|
184
|
+
Parameters:
|
|
185
|
+
session_uri (str):
|
|
186
|
+
The resumable upload session URL returned from `get_resumable_session_uri`.
|
|
187
|
+
This is also the value returned by GCS in the `Location` header on upload init.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
None
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
RuntimeError:
|
|
194
|
+
If the DELETE request returns a status code outside the expected success cases.
|
|
195
|
+
|
|
196
|
+
References:
|
|
197
|
+
https://cloud.google.com/storage/docs/performing-resumable-uploads#cancel-upload
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
def _cancel() -> None:
|
|
201
|
+
authed = self._authed()
|
|
202
|
+
headers = {'Content-Length': '0'}
|
|
203
|
+
|
|
204
|
+
resp = authed.delete(session_uri, headers=headers)
|
|
205
|
+
|
|
206
|
+
# Valid successful outcomes:
|
|
207
|
+
# 499 - documented success status for cancelled GCS resumable uploads
|
|
208
|
+
# 204 - alternative valid success response
|
|
209
|
+
# 404/410 - session already invalid/expired/completed (safe to treat as success)
|
|
210
|
+
success_statuses = (499, 204, 404, 410)
|
|
211
|
+
if resp.status_code not in success_statuses:
|
|
212
|
+
raise RuntimeError(f'Failed to cancel resumable upload: {resp.status_code} {resp.text}')
|
|
213
|
+
|
|
214
|
+
await asyncio.to_thread(_cancel)
|
|
215
|
+
|
|
170
216
|
async def get_presigned_download_url(
|
|
171
217
|
self,
|
|
172
218
|
bucket: str,
|
|
@@ -209,7 +255,7 @@ class GCSObjectClient:
|
|
|
209
255
|
"""
|
|
210
256
|
|
|
211
257
|
def _list() -> dict[int, int]:
|
|
212
|
-
prefix = f
|
|
258
|
+
prefix = f'multipart/{upload_id}/{final_key}/'
|
|
213
259
|
out: dict[int, int] = {}
|
|
214
260
|
for b in self._client.list_blobs(bucket, prefix=prefix):
|
|
215
261
|
leaf = b.name.rsplit('/', 1)[-1] # e.g., part-000037
|
|
@@ -318,7 +364,7 @@ class GCSObjectClient:
|
|
|
318
364
|
bucket_ref = self._get_bucket(bucket)
|
|
319
365
|
soft_delete_versions = list(self._client.list_blobs(bucket_ref, prefix=object_name, soft_deleted=True))
|
|
320
366
|
if not soft_delete_versions:
|
|
321
|
-
raise VersionNotFound(f
|
|
367
|
+
raise VersionNotFound(f'No soft-deleted versions found for {object_name!r}')
|
|
322
368
|
soft_delete_versions.sort(key=lambda b: (getattr(b, 'soft_delete_time', None), b.generation), reverse=True)
|
|
323
369
|
latest = soft_delete_versions[0]
|
|
324
370
|
gen = int(latest.generation)
|
|
@@ -331,7 +377,7 @@ class GCSObjectClient:
|
|
|
331
377
|
)
|
|
332
378
|
resp = authed.post(url, headers={'Content-Type': 'application/json'})
|
|
333
379
|
if resp.status_code >= 400:
|
|
334
|
-
raise RuntimeError(f
|
|
380
|
+
raise RuntimeError(f'Restore failed: {resp.status_code} {resp.text}')
|
|
335
381
|
|
|
336
382
|
live_blobs = list(self._client.list_blobs(bucket, match_glob=object_name))
|
|
337
383
|
live_blobs.sort(key=lambda b: (b.time_created, b.generation), reverse=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pilot_platform_object_storage-2.0.0 → pilot_platform_object_storage-2.1.0}/object_storage/config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|