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.
Files changed (27) hide show
  1. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/PKG-INFO +1 -1
  2. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/PKG-INFO +1 -1
  3. deeporigin_sdk-5.0.2/src/VERSION +1 -0
  4. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/files.py +82 -10
  5. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/constants.py +7 -0
  6. deeporigin_sdk-5.0.0/src/VERSION +0 -1
  7. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/README.md +0 -0
  8. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/SOURCES.txt +0 -0
  9. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/dependency_links.txt +0 -0
  10. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/entry_points.txt +0 -0
  11. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/requires.txt +0 -0
  12. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/deeporigin_sdk.egg-info/top_level.txt +0 -0
  13. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/pyproject.toml +0 -0
  14. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/setup.cfg +0 -0
  15. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/__init__.py +0 -0
  16. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/auth.py +0 -0
  17. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/cli.py +0 -0
  18. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/config.py +0 -0
  19. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/exceptions.py +0 -0
  20. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/__init__.py +0 -0
  21. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/client.py +0 -0
  22. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/entities.py +0 -0
  23. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/results.py +0 -0
  24. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/platform/tools.py +0 -0
  25. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/__init__.py +0 -0
  26. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/display.py +0 -0
  27. {deeporigin_sdk-5.0.0 → deeporigin_sdk-5.0.2}/src/utils/env.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deeporigin-sdk
3
- Version: 5.0.0
3
+ Version: 5.0.2
4
4
  Summary: Deep Origin Platform SDK
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deeporigin-sdk
3
- Version: 5.0.0
3
+ Version: 5.0.2
4
4
  Summary: Deep Origin Platform SDK
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -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
- Gets an upload signed URL for ``remote_path``, reads the file at
306
- ``local_path``, and PUTs its contents directly to the signed URL.
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
- with httpx.Client() as upload_client:
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=file_content,
332
- headers={"Content-Type": "application/octet-stream"},
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``."""
@@ -1 +0,0 @@
1
- 5.0.0
File without changes
File without changes