deeporigin-sdk 5.0.0__tar.gz → 5.0.2__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.
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/PKG-INFO +1 -1
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/PKG-INFO +1 -1
- deeporigin_sdk-5.0.2/src/VERSION +1 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/files.py +82 -10
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/constants.py +7 -0
- deeporigin_sdk-5.0.0/src/VERSION +0 -1
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/README.md +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/SOURCES.txt +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/dependency_links.txt +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/entry_points.txt +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/requires.txt +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/top_level.txt +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/pyproject.toml +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/setup.cfg +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/__init__.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/auth.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/cli.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/config.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/exceptions.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/__init__.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/client.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/entities.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/results.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/tools.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/__init__.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/display.py +0 -0
- {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/env.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5.0.2
|
|
@@ -22,6 +22,48 @@ _FILES_BASE = "/files"
|
|
|
22
22
|
|
|
23
23
|
_MISSING_URL_FIELD = "Signed URL response missing 'url' field"
|
|
24
24
|
|
|
25
|
+
# Signed-URL PUTs upload full file bodies; default httpx read timeout (5s) is too
|
|
26
|
+
# short under concurrent upload_tree workers waiting on S3.
|
|
27
|
+
_SIGNED_URL_UPLOAD_TIMEOUT = httpx.Timeout(
|
|
28
|
+
connect=10.0,
|
|
29
|
+
read=120.0,
|
|
30
|
+
write=300.0,
|
|
31
|
+
pool=10.0,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Platform GET for presigned URLs can queue under many concurrent upload_tree workers.
|
|
35
|
+
_SIGNED_URL_API_TIMEOUT = httpx.Timeout(
|
|
36
|
+
connect=10.0,
|
|
37
|
+
read=30.0,
|
|
38
|
+
write=10.0,
|
|
39
|
+
pool=10.0,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Match httpx IteratorByteStream.CHUNK_SIZE for signed-URL PUT bodies.
|
|
43
|
+
_SIGNED_URL_UPLOAD_CHUNK_SIZE = 65_536
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _iter_local_file_chunks(
|
|
47
|
+
local_path: Path,
|
|
48
|
+
*,
|
|
49
|
+
chunk_size: int = _SIGNED_URL_UPLOAD_CHUNK_SIZE,
|
|
50
|
+
) -> Iterator[bytes]:
|
|
51
|
+
"""Yield fixed-size byte chunks from a local file for signed-URL PUTs.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
local_path: Local file to read.
|
|
55
|
+
chunk_size: Maximum bytes per yielded chunk.
|
|
56
|
+
|
|
57
|
+
Yields:
|
|
58
|
+
Successive byte chunks until EOF.
|
|
59
|
+
"""
|
|
60
|
+
with open(local_path, "rb") as file_obj:
|
|
61
|
+
while True:
|
|
62
|
+
chunk = file_obj.read(chunk_size)
|
|
63
|
+
if not chunk:
|
|
64
|
+
break
|
|
65
|
+
yield chunk
|
|
66
|
+
|
|
25
67
|
|
|
26
68
|
class FileStream:
|
|
27
69
|
"""Streaming wrapper around an HTTP download response.
|
|
@@ -285,6 +327,7 @@ class Files:
|
|
|
285
327
|
response = self._c.get_json(
|
|
286
328
|
f"/files/{self._c.org_key}/signedUrl/{remote_path}",
|
|
287
329
|
params=params,
|
|
330
|
+
timeout=_SIGNED_URL_API_TIMEOUT,
|
|
288
331
|
)
|
|
289
332
|
|
|
290
333
|
if "url" not in response:
|
|
@@ -297,17 +340,21 @@ class Files:
|
|
|
297
340
|
local_path: Path,
|
|
298
341
|
remote_path: str,
|
|
299
342
|
*,
|
|
343
|
+
file_size: int | None = None,
|
|
300
344
|
max_retries: int = 3,
|
|
301
345
|
retry_backoff_factor: float = 1.0,
|
|
302
346
|
) -> str:
|
|
303
347
|
"""Upload a single file to a signed URL with retries.
|
|
304
348
|
|
|
305
|
-
|
|
306
|
-
``local_path
|
|
349
|
+
On each attempt, requests a fresh upload signed URL for ``remote_path``,
|
|
350
|
+
streams the file at ``local_path`` in fixed-size chunks via HTTP PUT to
|
|
351
|
+
that URL, and retries on transient failures.
|
|
307
352
|
|
|
308
353
|
Args:
|
|
309
354
|
local_path: Local file to upload.
|
|
310
355
|
remote_path: Remote path to request the signed URL for.
|
|
356
|
+
file_size: Optional precomputed ``st_size`` to avoid a duplicate stat
|
|
357
|
+
when ``upload_tree`` has already measured the file for sorting.
|
|
311
358
|
max_retries: Maximum retry attempts on transient failures.
|
|
312
359
|
retry_backoff_factor: Multiplier for exponential back-off between
|
|
313
360
|
retries. Delay = retry_backoff_factor * 2^attempt.
|
|
@@ -318,18 +365,21 @@ class Files:
|
|
|
318
365
|
Raises:
|
|
319
366
|
httpx.HTTPStatusError: If the PUT fails after all retries.
|
|
320
367
|
"""
|
|
321
|
-
signed_url = self.signed_url(remote_path, upload=True)
|
|
322
|
-
|
|
323
|
-
file_content = local_path.read_bytes()
|
|
324
|
-
|
|
325
368
|
last_exc: Exception | None = None
|
|
326
369
|
for attempt in range(max_retries + 1):
|
|
327
370
|
try:
|
|
328
|
-
|
|
371
|
+
signed_url = self.signed_url(remote_path, upload=True)
|
|
372
|
+
headers = {"Content-Type": "application/octet-stream"}
|
|
373
|
+
if file_size is None:
|
|
374
|
+
file_size = self._local_file_size(local_path)
|
|
375
|
+
if file_size > 0:
|
|
376
|
+
headers["Content-Length"] = str(file_size)
|
|
377
|
+
|
|
378
|
+
with httpx.Client(timeout=_SIGNED_URL_UPLOAD_TIMEOUT) as upload_client:
|
|
329
379
|
resp = upload_client.put(
|
|
330
380
|
signed_url,
|
|
331
|
-
content=
|
|
332
|
-
headers=
|
|
381
|
+
content=_iter_local_file_chunks(local_path),
|
|
382
|
+
headers=headers,
|
|
333
383
|
)
|
|
334
384
|
resp.raise_for_status()
|
|
335
385
|
return remote_path
|
|
@@ -344,6 +394,26 @@ class Files:
|
|
|
344
394
|
|
|
345
395
|
raise last_exc # type: ignore[misc]
|
|
346
396
|
|
|
397
|
+
@staticmethod
|
|
398
|
+
def _local_file_size(local_path: Path) -> int:
|
|
399
|
+
"""Return ``st_size`` for a local file, or ``0`` when stat fails."""
|
|
400
|
+
try:
|
|
401
|
+
return local_path.stat().st_size
|
|
402
|
+
except OSError:
|
|
403
|
+
return 0
|
|
404
|
+
|
|
405
|
+
@staticmethod
|
|
406
|
+
def _sort_upload_pairs_by_size_desc(
|
|
407
|
+
upload_pairs: list[tuple[Path, str]],
|
|
408
|
+
) -> list[tuple[Path, str, int]]:
|
|
409
|
+
"""Return upload pairs with cached sizes, largest first."""
|
|
410
|
+
sized_pairs = [
|
|
411
|
+
(path, suffix, Files._local_file_size(path))
|
|
412
|
+
for path, suffix in upload_pairs
|
|
413
|
+
]
|
|
414
|
+
sized_pairs.sort(key=lambda pair: pair[2], reverse=True)
|
|
415
|
+
return sized_pairs
|
|
416
|
+
|
|
347
417
|
@staticmethod
|
|
348
418
|
def _resolve_upload_pairs(
|
|
349
419
|
local_path: str | Path | list[str | Path],
|
|
@@ -420,6 +490,7 @@ class Files:
|
|
|
420
490
|
remote_dir += "/"
|
|
421
491
|
|
|
422
492
|
upload_pairs = self._resolve_upload_pairs(local_path)
|
|
493
|
+
upload_pairs = self._sort_upload_pairs_by_size_desc(upload_pairs)
|
|
423
494
|
|
|
424
495
|
results: list[str] = []
|
|
425
496
|
errors: list[tuple[str, Exception]] = []
|
|
@@ -430,10 +501,11 @@ class Files:
|
|
|
430
501
|
self._put_to_signed_url,
|
|
431
502
|
fp,
|
|
432
503
|
f"{remote_dir}{suffix}",
|
|
504
|
+
file_size=file_size,
|
|
433
505
|
max_retries=max_retries,
|
|
434
506
|
retry_backoff_factor=retry_backoff_factor,
|
|
435
507
|
): fp
|
|
436
|
-
for fp, suffix in upload_pairs
|
|
508
|
+
for fp, suffix, file_size in upload_pairs
|
|
437
509
|
}
|
|
438
510
|
|
|
439
511
|
for future in tqdm(
|
|
@@ -90,3 +90,10 @@ MOLPROPS_PROPERTY_KEYS: frozenset[str] = frozenset(
|
|
|
90
90
|
|
|
91
91
|
MOLPROPS_DEFAULT_PROPERTIES: frozenset[str] = MOLPROPS_PROPERTY_KEYS
|
|
92
92
|
"""Default full ADMET bundle for :class:`~deeporigin.drug_discovery.molprops.Molprops`."""
|
|
93
|
+
|
|
94
|
+
JOB_WATCH_BLOCK_ENV = "JOB_WATCH_BLOCK"
|
|
95
|
+
"""Env var for blocking :meth:`~deeporigin.drug_discovery.notebook_watch_mixin.NotebookWatchMixin.watch`.
|
|
96
|
+
|
|
97
|
+
When set to a truthy value (``1``, ``true``, ``yes``, ``on``), ``watch()`` blocks the
|
|
98
|
+
notebook cell until the execution reaches a terminal state. Used by doc notebook CI
|
|
99
|
+
(``scripts/build_docs.sh``) and ``nbconvert --execute``."""
|
deeporigin_sdk-5.0.0/src/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
5.0.0
|
|
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
|
|
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
|
|
File without changes
|