aind-codeocean-dispatch 0.2.0__tar.gz → 0.2.1__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 (24) hide show
  1. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/CHANGELOG.md +6 -0
  2. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/PKG-INFO +2 -2
  3. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/pyproject.toml +2 -2
  4. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/src/aind_codeocean_dispatch/runner.py +12 -6
  5. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/tests/fakes.py +18 -7
  6. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/tests/test_runner.py +5 -2
  7. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/uv.lock +2 -2
  8. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.copier-answers.yml +0 -0
  9. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  10. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  11. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.github/ISSUE_TEMPLATE/user-story.md +0 -0
  12. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.github/dependabot.yml +0 -0
  13. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.github/workflows/bump-call.yml +0 -0
  14. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.github/workflows/ci-call.yml +0 -0
  15. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.github/workflows/publish-call.yml +0 -0
  16. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.gitignore +0 -0
  17. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/.python-version +0 -0
  18. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/CLAUDE.md +0 -0
  19. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/LICENSE +0 -0
  20. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/README.md +0 -0
  21. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/scripts/run_linters_and_checks.sh +0 -0
  22. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/src/aind_codeocean_dispatch/__init__.py +0 -0
  23. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/src/aind_codeocean_dispatch/py.typed +0 -0
  24. {aind_codeocean_dispatch-0.2.0 → aind_codeocean_dispatch-0.2.1}/tests/__init__.py +0 -0
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v0.2.1 (2026-10-15)
9
+
10
+ ### Fix
11
+
12
+ - support codeocean 0.13 and reuse only the caller's own captures
13
+
8
14
  ## v0.2.0 (2026-10-15)
9
15
 
10
16
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: aind-codeocean-dispatch
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Launch, monitor, and capture batches of Code Ocean capsule runs
5
5
  Project-URL: Repository, https://github.com/AllenNeuralDynamics/aind-codeocean-dispatch/
6
6
  Project-URL: Issues, https://github.com/AllenNeuralDynamics/aind-codeocean-dispatch/issues
@@ -12,7 +12,7 @@ License-File: LICENSE
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Requires-Python: >=3.11
14
14
  Requires-Dist: aind-code-ocean-pipeline-utils>=0.7.1
15
- Requires-Dist: codeocean>=0.17
15
+ Requires-Dist: codeocean>=0.13
16
16
  Description-Content-Type: text/markdown
17
17
 
18
18
  # AIND Code Ocean dispatch
@@ -13,11 +13,11 @@ authors = [
13
13
  ]
14
14
  classifiers = ["Programming Language :: Python :: 3"]
15
15
  readme = "README.md"
16
- version = "0.2.0"
16
+ version = "0.2.1"
17
17
 
18
18
  dependencies = [
19
19
  "aind-code-ocean-pipeline-utils>=0.7.1",
20
- "codeocean>=0.17",
20
+ "codeocean>=0.13",
21
21
  ]
22
22
 
23
23
  [project.urls]
@@ -33,9 +33,13 @@ from typing import TYPE_CHECKING, Any, Literal, TypeVar
33
33
  import requests
34
34
  from aind_code_ocean_pipeline_utils.cache import input_fingerprint
35
35
  from aind_code_ocean_pipeline_utils.process import GracefulExit, check_shutdown
36
+
37
+ # Import paths that exist across supported SDK versions; 0.17 moved the models to
38
+ # ``codeocean.models`` and kept these as re-exports.
39
+ from codeocean.components import Ownership
40
+ from codeocean.computation import Computation, ComputationEndStatus, ComputationState, RunParams
41
+ from codeocean.data_asset import DataAsset, DataAssetParams, DataAssetSearchParams, DataAssetState
36
42
  from codeocean.error import Error as CodeOceanError
37
- from codeocean.models.computation import Computation, ComputationEndStatus, ComputationState, RunParams
38
- from codeocean.models.data_asset import DataAsset, DataAssetParams, DataAssetSearchParams, DataAssetState
39
43
 
40
44
  if TYPE_CHECKING:
41
45
  from codeocean import CodeOcean
@@ -88,8 +92,8 @@ class Job:
88
92
  can depend on it. ``None`` stops at a successful computation.
89
93
  key : str, optional
90
94
  Resume key (see :func:`job_key`), added to the captured asset as
91
- :func:`key_tag`. With ``reuse=True``, a Ready asset carrying it is returned
92
- instead of launching. ``None`` never reuses.
95
+ :func:`key_tag`. With ``reuse=True``, a Ready asset carrying it that the
96
+ caller created is returned instead of launching. ``None`` never reuses.
93
97
  """
94
98
 
95
99
  label: str
@@ -243,7 +247,9 @@ def run_jobs(
243
247
  capture_retries : int, optional
244
248
  New capture attempts per job after a capture ends in the Failed state.
245
249
  reuse : bool, optional
246
- Return a Ready asset tagged with a job's key instead of launching it.
250
+ Return a Ready asset tagged with a job's key instead of launching it. Only
251
+ assets the caller created count: another user running identical inputs
252
+ produces the same key, and their asset is not the caller's to consume.
247
253
  poll_interval_s : float, optional
248
254
  Seconds between status checks of each running computation.
249
255
  timeout_s : float, optional
@@ -479,7 +485,7 @@ class _Dispatcher:
479
485
  if slot.next_at > _now():
480
486
  continue
481
487
  tag = key_tag(slot.job.key)
482
- params = DataAssetSearchParams(query=f"tag:{tag}", limit=100)
488
+ params = DataAssetSearchParams(query=f"tag:{tag}", ownership=Ownership.Created, limit=100)
483
489
  try:
484
490
  results = self.client.data_assets.search_data_assets(params)
485
491
  except (CodeOceanError, requests.RequestException) as exc:
@@ -8,14 +8,14 @@ creation order.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import dataclasses
11
12
  import itertools
12
13
  from dataclasses import dataclass, field
13
14
  from typing import Any
14
15
 
15
16
  import requests
16
- from codeocean.error import Error as CodeOceanError
17
- from codeocean.models.computation import Computation, ComputationEndStatus, ComputationState, RunParams
18
- from codeocean.models.data_asset import (
17
+ from codeocean.computation import Computation, ComputationEndStatus, ComputationState, RunParams
18
+ from codeocean.data_asset import (
19
19
  DataAsset,
20
20
  DataAssetParams,
21
21
  DataAssetSearchParams,
@@ -23,6 +23,16 @@ from codeocean.models.data_asset import (
23
23
  DataAssetState,
24
24
  DataAssetType,
25
25
  )
26
+ from codeocean.error import Error as CodeOceanError
27
+
28
+
29
+ def build(cls: Any, **values: Any) -> Any:
30
+ """Construct an SDK model, dropping fields this SDK version does not declare.
31
+
32
+ Required fields differ across supported versions (0.17 added ``owner``).
33
+ """
34
+ declared = {f.name for f in dataclasses.fields(cls)}
35
+ return cls(**{name: value for name, value in values.items() if name in declared})
26
36
 
27
37
 
28
38
  class FakeClock:
@@ -69,7 +79,8 @@ CRASHED = snapshot(ComputationState.Completed, end_status=ComputationEndStatus.F
69
79
 
70
80
 
71
81
  def asset(asset_id: str, state: DataAssetState, *, tags: list[str] | None = None, created: int = 0) -> DataAsset:
72
- return DataAsset(
82
+ return build(
83
+ DataAsset,
73
84
  id=asset_id,
74
85
  created=created,
75
86
  name=asset_id,
@@ -115,7 +126,7 @@ class FakeComputations:
115
126
 
116
127
  @staticmethod
117
128
  def _computation(computation_id: str, step: dict[str, Any]) -> Computation:
118
- return Computation(id=computation_id, created=0, name=computation_id, owner="owner", run_time=0, **step)
129
+ return build(Computation, id=computation_id, created=0, name=computation_id, owner="owner", run_time=0, **step)
119
130
 
120
131
 
121
132
  @dataclass
@@ -125,7 +136,7 @@ class FakeDataAssets:
125
136
  existing: list[DataAsset] = field(default_factory=list)
126
137
  search_errors: list[Exception] = field(default_factory=list)
127
138
  created: list[DataAssetParams] = field(default_factory=list)
128
- searches: list[str] = field(default_factory=list)
139
+ searches: list[DataAssetSearchParams] = field(default_factory=list)
129
140
  _scripts: dict[str, list[Any]] = field(default_factory=dict)
130
141
 
131
142
  def create_data_asset(self, data_asset_params: DataAssetParams) -> DataAsset:
@@ -145,7 +156,7 @@ class FakeDataAssets:
145
156
  return asset(data_asset_id, step)
146
157
 
147
158
  def search_data_assets(self, search_params: DataAssetSearchParams) -> DataAssetSearchResults:
148
- self.searches.append(search_params.query)
159
+ self.searches.append(search_params)
149
160
  if self.search_errors:
150
161
  raise self.search_errors.pop(0)
151
162
  return DataAssetSearchResults(has_more=False, results=list(self.existing))
@@ -5,8 +5,9 @@ from typing import Any
5
5
  import pytest
6
6
  import requests
7
7
  from aind_code_ocean_pipeline_utils.process import GracefulExit
8
- from codeocean.models.computation import Computation, DataAssetsRunParam, NamedRunParam, RunParams
9
- from codeocean.models.data_asset import DataAssetParams, DataAssetState
8
+ from codeocean.components import Ownership
9
+ from codeocean.computation import Computation, DataAssetsRunParam, NamedRunParam, RunParams
10
+ from codeocean.data_asset import DataAssetParams, DataAssetState
10
11
 
11
12
  from aind_codeocean_dispatch import (
12
13
  DispatchFailed,
@@ -255,6 +256,8 @@ def test_reuse_returns_newest_ready_asset_with_exact_tag(clock: FakeClock) -> No
255
256
  assert outcome.status == "reused"
256
257
  assert outcome.asset_id == "new"
257
258
  assert client.computations.launches == []
259
+ (search,) = client.data_assets.searches
260
+ assert search.ownership == Ownership.Created
258
261
 
259
262
 
260
263
  def test_reuse_launches_when_nothing_matches(clock: FakeClock) -> None:
@@ -17,7 +17,7 @@ wheels = [
17
17
 
18
18
  [[package]]
19
19
  name = "aind-codeocean-dispatch"
20
- version = "0.2.0"
20
+ version = "0.2.1"
21
21
  source = { editable = "." }
22
22
  dependencies = [
23
23
  { name = "aind-code-ocean-pipeline-utils" },
@@ -50,7 +50,7 @@ types = [
50
50
  [package.metadata]
51
51
  requires-dist = [
52
52
  { name = "aind-code-ocean-pipeline-utils", specifier = ">=0.7.1" },
53
- { name = "codeocean", specifier = ">=0.17" },
53
+ { name = "codeocean", specifier = ">=0.13" },
54
54
  ]
55
55
 
56
56
  [package.metadata.requires-dev]