malva-client 0.3.2__tar.gz → 0.3.3__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 (28) hide show
  1. {malva_client-0.3.2/malva_client.egg-info → malva_client-0.3.3}/PKG-INFO +1 -1
  2. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/__init__.py +1 -1
  3. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/client.py +36 -6
  4. {malva_client-0.3.2 → malva_client-0.3.3/malva_client.egg-info}/PKG-INFO +1 -1
  5. {malva_client-0.3.2 → malva_client-0.3.3}/pyproject.toml +1 -1
  6. {malva_client-0.3.2 → malva_client-0.3.3}/CHANGELOG.md +0 -0
  7. {malva_client-0.3.2 → malva_client-0.3.3}/LICENSE +0 -0
  8. {malva_client-0.3.2 → malva_client-0.3.3}/MANIFEST.in +0 -0
  9. {malva_client-0.3.2 → malva_client-0.3.3}/README.md +0 -0
  10. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/auth.py +0 -0
  11. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/cli.py +0 -0
  12. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/config.py +0 -0
  13. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/exceptions.py +0 -0
  14. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/models.py +0 -0
  15. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/storage.py +0 -0
  16. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client/tools.py +0 -0
  17. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client.egg-info/SOURCES.txt +0 -0
  18. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client.egg-info/dependency_links.txt +0 -0
  19. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client.egg-info/entry_points.txt +0 -0
  20. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client.egg-info/requires.txt +0 -0
  21. {malva_client-0.3.2 → malva_client-0.3.3}/malva_client.egg-info/top_level.txt +0 -0
  22. {malva_client-0.3.2 → malva_client-0.3.3}/requirements.txt +0 -0
  23. {malva_client-0.3.2 → malva_client-0.3.3}/setup.cfg +0 -0
  24. {malva_client-0.3.2 → malva_client-0.3.3}/tests/test_client.py +0 -0
  25. {malva_client-0.3.2 → malva_client-0.3.3}/tests/test_config.py +0 -0
  26. {malva_client-0.3.2 → malva_client-0.3.3}/tests/test_exceptions.py +0 -0
  27. {malva_client-0.3.2 → malva_client-0.3.3}/tests/test_models.py +0 -0
  28. {malva_client-0.3.2 → malva_client-0.3.3}/tests/test_storage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: malva-client
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Python client for Malva Index
5
5
  Author-email: Daniel León-Periñán <daniel.leonperinan@mdc-berlin.de>, Nikos Karaiskos <nikolaos.karaiskos@mdc-berlin.de>
6
6
  Maintainer-email: Daniel León-Periñán <daniel.leonperinan@mdc-berlin.de>, Nikos Karaiskos <nikolaos.karaiskos@mdc-berlin.de>
@@ -6,7 +6,7 @@ try:
6
6
  from importlib.metadata import version as _get_version
7
7
  __version__ = _get_version("malva_client")
8
8
  except Exception:
9
- __version__ = "0.3.2"
9
+ __version__ = "0.3.3"
10
10
  __author__ = "Malva Team"
11
11
  __email__ = "hello@malva.bio"
12
12
 
@@ -366,13 +366,35 @@ class MalvaClient:
366
366
  payload['unstranded_mode'] = True
367
367
  return payload
368
368
 
369
+ def _get_job_status_lightweight(self, job_id: str) -> Dict[str, Any]:
370
+ """Fetch job status without forcing the server to return result data."""
371
+ try:
372
+ return self._request('GET', f'/search/{job_id}/status')
373
+ except MalvaAPIError as exc:
374
+ if self._is_not_found_error(exc):
375
+ return self._request('GET', f'/search/{job_id}')
376
+ raise
377
+
378
+ @staticmethod
379
+ def _adaptive_poll_delay(elapsed: float, poll_interval: int) -> float:
380
+ max_delay = max(float(poll_interval), 0.0)
381
+ if max_delay <= 0:
382
+ return 0.0
383
+ if elapsed < 2:
384
+ return min(max_delay, 0.2)
385
+ if elapsed < 5:
386
+ return min(max_delay, 0.5)
387
+ if elapsed < 15:
388
+ return min(max_delay, 1.0)
389
+ return max_delay
390
+
369
391
  def _poll_until_complete(self, job_id: str, poll_interval: int,
370
392
  max_wait: int, context: str = "search") -> SearchResult:
371
393
  """Poll GET /search/<job_id> until completed, return lazy SearchResult."""
372
394
  start_time = time.time()
373
395
  while time.time() - start_time < max_wait:
374
396
  try:
375
- resp = self._request('GET', f'/search/{job_id}')
397
+ resp = self._get_job_status_lightweight(job_id)
376
398
  status = resp.get('status')
377
399
  logger.info(f"{context} status: {status}")
378
400
  if status == 'completed':
@@ -385,7 +407,9 @@ class MalvaClient:
385
407
  pass # job not yet visible, keep polling
386
408
  else:
387
409
  raise
388
- time.sleep(poll_interval)
410
+ delay = self._adaptive_poll_delay(time.time() - start_time, poll_interval)
411
+ if delay > 0:
412
+ time.sleep(delay)
389
413
  raise MalvaAPIError(f"{context} timed out after {max_wait} seconds")
390
414
 
391
415
  def search(self, query: str,
@@ -837,7 +861,10 @@ class MalvaClient:
837
861
  sample_ids: Optional[List[int]],
838
862
  include_barcodes: bool,
839
863
  include_sample_metadata: bool) -> CellExpressionMatrixResult:
840
- response = self._request_raw('GET', f'/search/{job_id}/global-cell-ids')
864
+ params = {}
865
+ if sample_ids:
866
+ params['sample_ids'] = ','.join(str(s) for s in sample_ids)
867
+ response = self._request_raw('GET', f'/search/{job_id}/global-cell-ids', params=params)
841
868
  decoded = msgpack.unpackb(response.content, raw=False, strict_map_key=False)
842
869
  cells = np.frombuffer(decoded.get('c', b''), dtype=np.int64)
843
870
  samples = np.frombuffer(decoded.get('s', b''), dtype=np.int64)
@@ -1600,14 +1627,14 @@ class MalvaClient:
1600
1627
  from malva_client.storage import save_job_status
1601
1628
 
1602
1629
  try:
1603
- response = self._request('GET', f'/search/{job_id}')
1630
+ response = self._get_job_status_lightweight(job_id)
1604
1631
 
1605
1632
  # Update local storage
1606
1633
  save_job_status(
1607
1634
  job_id=job_id,
1608
1635
  status=response.get('status', 'unknown'),
1609
1636
  server_url=self.base_url,
1610
- results_data=response.get('results') if response.get('status') == 'completed' else None,
1637
+ results_data=None,
1611
1638
  error_message=response.get('error') if response.get('status') == 'error' else None
1612
1639
  )
1613
1640
 
@@ -1718,6 +1745,7 @@ class MalvaClient:
1718
1745
  start_time = time.time()
1719
1746
 
1720
1747
  while time.time() - start_time < max_wait:
1748
+ elapsed = time.time() - start_time
1721
1749
  status_response = self.get_job_status(job_id)
1722
1750
  status = status_response.get('status')
1723
1751
 
@@ -1728,7 +1756,9 @@ class MalvaClient:
1728
1756
  raise SearchError(f"Job {job_id} failed: {error_msg}")
1729
1757
  elif status in ['pending', 'running']:
1730
1758
  logger.info(f"Job {job_id} status: {status}")
1731
- time.sleep(poll_interval)
1759
+ delay = self._adaptive_poll_delay(elapsed, poll_interval)
1760
+ if delay > 0:
1761
+ time.sleep(delay)
1732
1762
  else:
1733
1763
  raise SearchError(f"Job {job_id} has unexpected status: {status}")
1734
1764
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: malva-client
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Python client for Malva Index
5
5
  Author-email: Daniel León-Periñán <daniel.leonperinan@mdc-berlin.de>, Nikos Karaiskos <nikolaos.karaiskos@mdc-berlin.de>
6
6
  Maintainer-email: Daniel León-Periñán <daniel.leonperinan@mdc-berlin.de>, Nikos Karaiskos <nikolaos.karaiskos@mdc-berlin.de>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "malva-client"
7
- version = "0.3.2"
7
+ version = "0.3.3"
8
8
  description = "Python client for Malva Index"
9
9
  readme = "README.md"
10
10
  license = "BSD-3-Clause-Clear"
File without changes
File without changes
File without changes
File without changes
File without changes