codeocean 0.12.0__tar.gz → 0.14.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.
Files changed (31) hide show
  1. {codeocean-0.12.0 → codeocean-0.14.0}/CHANGELOG.md +8 -0
  2. {codeocean-0.12.0 → codeocean-0.14.0}/PKG-INFO +1 -1
  3. {codeocean-0.12.0 → codeocean-0.14.0}/RELEASE.md +1 -0
  4. {codeocean-0.12.0 → codeocean-0.14.0}/examples/run_pipeline.py +3 -11
  5. {codeocean-0.12.0 → codeocean-0.14.0}/pyproject.toml +1 -1
  6. codeocean-0.14.0/src/codeocean/capsule.py +114 -0
  7. {codeocean-0.12.0 → codeocean-0.14.0}/src/codeocean/client.py +3 -1
  8. codeocean-0.14.0/src/codeocean/computation.py +164 -0
  9. codeocean-0.14.0/src/codeocean/data_asset.py +237 -0
  10. codeocean-0.14.0/src/codeocean/models/__init__.py +1 -0
  11. {codeocean-0.12.0/src/codeocean → codeocean-0.14.0/src/codeocean/models}/capsule.py +2 -110
  12. codeocean-0.14.0/src/codeocean/models/components.py +148 -0
  13. {codeocean-0.12.0/src/codeocean → codeocean-0.14.0/src/codeocean/models}/computation.py +0 -125
  14. {codeocean-0.12.0/src/codeocean → codeocean-0.14.0/src/codeocean/models}/data_asset.py +53 -205
  15. codeocean-0.14.0/src/codeocean/pipeline.py +71 -0
  16. {codeocean-0.12.0 → codeocean-0.14.0}/.flake8 +0 -0
  17. {codeocean-0.12.0 → codeocean-0.14.0}/.gitignore +0 -0
  18. {codeocean-0.12.0 → codeocean-0.14.0}/LICENSE +0 -0
  19. {codeocean-0.12.0 → codeocean-0.14.0}/README.md +0 -0
  20. {codeocean-0.12.0 → codeocean-0.14.0}/examples/create_data_asset.py +0 -0
  21. {codeocean-0.12.0 → codeocean-0.14.0}/examples/run_capsule.py +0 -0
  22. {codeocean-0.12.0 → codeocean-0.14.0}/src/codeocean/__init__.py +0 -0
  23. {codeocean-0.12.0 → codeocean-0.14.0}/src/codeocean/components.py +0 -0
  24. {codeocean-0.12.0 → codeocean-0.14.0}/src/codeocean/custom_metadata.py +0 -0
  25. {codeocean-0.12.0 → codeocean-0.14.0}/src/codeocean/enum.py +0 -0
  26. {codeocean-0.12.0 → codeocean-0.14.0}/src/codeocean/error.py +0 -0
  27. {codeocean-0.12.0/src/codeocean → codeocean-0.14.0/src/codeocean/models}/folder.py +0 -0
  28. {codeocean-0.12.0 → codeocean-0.14.0}/tests/__init__.py +0 -0
  29. {codeocean-0.12.0 → codeocean-0.14.0}/tests/test_client.py +0 -0
  30. {codeocean-0.12.0 → codeocean-0.14.0}/tests/test_error.py +0 -0
  31. {codeocean-0.12.0 → codeocean-0.14.0}/tests/test_package.py +0 -0
@@ -1,6 +1,14 @@
1
1
  CHANGELOG
2
2
  =========
3
3
 
4
+ ## 0.14.0 (2026-01-29)
5
+ - [#66](https://github.com/codeocean/codeocean-sdk-python/pull/66) feat: Code Ocean version 4.2 functionality
6
+ - **Minimum Code Ocean platform version updated to `4.2.0`.**
7
+
8
+ ## 0.13.0 (2026-01-23)
9
+ - [#64](https://github.com/codeocean/codeocean-sdk-python/pull/64) feat: Code Ocean version 4.1 functionality
10
+ - **Minimum Code Ocean platform version updated to `4.1.0`.**
11
+
4
12
  ## 0.12.0 (2025-10-20)
5
13
  - [#61](https://github.com/codeocean/codeocean-sdk-python/pull/61) feat: add search_pipelines method
6
14
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeocean
3
- Version: 0.12.0
3
+ Version: 0.14.0
4
4
  Summary: Code Ocean Python SDK
5
5
  Project-URL: Homepage, https://github.com/codeocean/codeocean-sdk-python
6
6
  Project-URL: Issues, https://github.com/codeocean/codeocean-sdk-python/issues
@@ -3,6 +3,7 @@
3
3
  1. Open a PR with the following changes:
4
4
  1. Bump the version in [pyproject.toml](pyproject.toml).
5
5
  1. Update the [CHANGELOG.md](CHANGELOG.md).
6
+ 1. If the change requires a newer Code Ocean server version, update `MIN_SERVER_VERSION` in [client.py](src/codeocean/client.py).
6
7
  1. Commit the updates with the message `Bump version to X.Y.Z`.
7
8
  1. Merge the PR.
8
9
  1. Locally, sync your clone with GitHub:
@@ -1,16 +1,8 @@
1
1
  import os
2
2
 
3
3
  from codeocean import CodeOcean
4
- from codeocean.computation import RunParams
5
- from codeocean.data_asset import (
6
- DataAssetParams,
7
- DataAssetsRunParam,
8
- PipelineProcessParams,
9
- Source,
10
- ComputationSource,
11
- Target,
12
- AWSS3Target,
13
- )
4
+ from codeocean.computation import RunParams, DataAssetsRunParam, PipelineProcessParams
5
+ from codeocean.data_asset import DataAssetParams, Source, ComputationSource, Target, AWSS3Target
14
6
 
15
7
 
16
8
  # Create the client using your domain and API token.
@@ -38,7 +30,7 @@ run_params = RunParams(
38
30
  ],
39
31
  )
40
32
 
41
- computation = client.computations.run_capsule(run_params)
33
+ computation = client.computations.run_pipeline(run_params)
42
34
 
43
35
  # Wait for pipeline to finish.
44
36
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codeocean"
7
- version = "0.12.0"
7
+ version = "0.14.0"
8
8
  authors = [
9
9
  { name="Code Ocean", email="dev@codeocean.com" },
10
10
  ]
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from requests_toolbelt.sessions import BaseUrlSession
5
+ from typing import Optional, Iterator
6
+
7
+ from codeocean.models.capsule import (
8
+ Capsule,
9
+ CapsuleSearchParams,
10
+ CapsuleSearchResults,
11
+ AppPanel,
12
+ )
13
+ # Re-exports for backward compatibility
14
+ from codeocean.models.capsule import ( # noqa: F401
15
+ CapsuleStatus,
16
+ CapsuleSortBy,
17
+ OriginalCapsuleInfo,
18
+ AppPanelDataAssetKind,
19
+ AppPanelParameterType,
20
+ AppPanelCategories,
21
+ AppPanelParameters,
22
+ AppPanelGeneral,
23
+ AppPanelDataAsset,
24
+ AppPanelResult,
25
+ AppPanelProcess,
26
+ )
27
+ from codeocean.models.components import Permissions
28
+ from codeocean.models.computation import Computation
29
+ from codeocean.models.data_asset import DataAssetAttachParams, DataAssetAttachResults
30
+
31
+
32
+ @dataclass
33
+ class Capsules:
34
+ """Client for interacting with Code Ocean capsule APIs."""
35
+
36
+ client: BaseUrlSession
37
+ _route: str = "capsules"
38
+
39
+ def get_capsule(self, capsule_id: str) -> Capsule:
40
+ """Retrieve metadata for a specific capsule by its ID."""
41
+ res = self.client.get(f"{self._route}/{capsule_id}")
42
+
43
+ return Capsule.from_dict(res.json())
44
+
45
+ def delete_capsule(self, capsule_id: str):
46
+ """Delete a capsule permanently."""
47
+ self.client.delete(f"{self._route}/{capsule_id}")
48
+
49
+ def get_capsule_app_panel(self, capsule_id: str, version: Optional[int] = None) -> AppPanel:
50
+ """Retrieve app panel information for a specific capsule by its ID."""
51
+ res = self.client.get(f"{self._route}/{capsule_id}/app_panel", params={"version": version} if version else None)
52
+
53
+ return AppPanel.from_dict(res.json())
54
+
55
+ def list_computations(self, capsule_id: str) -> list[Computation]:
56
+ """Get all computations associated with a specific capsule."""
57
+ res = self.client.get(f"{self._route}/{capsule_id}/computations")
58
+
59
+ return [Computation.from_dict(c) for c in res.json()]
60
+
61
+ def update_permissions(self, capsule_id: str, permissions: Permissions):
62
+ """Update permissions for a capsule."""
63
+ self.client.post(
64
+ f"{self._route}/{capsule_id}/permissions",
65
+ json=permissions.to_dict(),
66
+ )
67
+
68
+ def attach_data_assets(
69
+ self,
70
+ capsule_id: str,
71
+ attach_params: list[DataAssetAttachParams],
72
+ ) -> list[DataAssetAttachResults]:
73
+ """Attach one or more data assets to a capsule with optional mount paths."""
74
+ res = self.client.post(
75
+ f"{self._route}/{capsule_id}/data_assets",
76
+ json=[j.to_dict() for j in attach_params],
77
+ )
78
+
79
+ return [DataAssetAttachResults.from_dict(c) for c in res.json()]
80
+
81
+ def detach_data_assets(self, capsule_id: str, data_assets: list[str]):
82
+ """Detach one or more data assets from a capsule by their IDs."""
83
+ self.client.delete(
84
+ f"{self._route}/{capsule_id}/data_assets/",
85
+ json=data_assets,
86
+ )
87
+
88
+ def archive_capsule(self, capsule_id: str, archive: bool):
89
+ """Archive or unarchive a capsule to control its visibility and accessibility."""
90
+ self.client.patch(
91
+ f"{self._route}/{capsule_id}/archive",
92
+ params={"archive": archive},
93
+ )
94
+
95
+ def search_capsules(self, search_params: CapsuleSearchParams) -> CapsuleSearchResults:
96
+ """Search for capsules with filtering, sorting, and pagination
97
+ options."""
98
+ res = self.client.post(f"{self._route}/search", json=search_params.to_dict())
99
+
100
+ return CapsuleSearchResults.from_dict(res.json())
101
+
102
+ def search_capsules_iterator(self, search_params: CapsuleSearchParams) -> Iterator[Capsule]:
103
+ """Iterate through all capsules matching search criteria with automatic pagination."""
104
+ params = search_params.to_dict()
105
+ while True:
106
+ response = self.search_capsules(search_params=CapsuleSearchParams(**params))
107
+
108
+ for result in response.results:
109
+ yield result
110
+
111
+ if not response.has_more:
112
+ return
113
+
114
+ params["next_token"] = response.next_token
@@ -12,6 +12,7 @@ from codeocean.computation import Computations
12
12
  from codeocean.custom_metadata import CustomMetadataSchema
13
13
  from codeocean.data_asset import DataAssets
14
14
  from codeocean.error import Error
15
+ from codeocean.pipeline import Pipelines
15
16
 
16
17
 
17
18
  @dataclass
@@ -37,7 +38,7 @@ class CodeOcean:
37
38
  agent_id: Optional[str] = None
38
39
 
39
40
  # Minimum server version required by this SDK
40
- MIN_SERVER_VERSION = "4.0.0"
41
+ MIN_SERVER_VERSION = "4.2.0"
41
42
 
42
43
  def __post_init__(self):
43
44
  self.session = BaseUrlSession(base_url=f"{self.domain}/api/v1/")
@@ -55,6 +56,7 @@ class CodeOcean:
55
56
  self.computations = Computations(client=self.session)
56
57
  self.custom_metadata = CustomMetadataSchema(client=self.session)
57
58
  self.data_assets = DataAssets(client=self.session)
59
+ self.pipelines = Pipelines(client=self.session)
58
60
 
59
61
  def _error_handler(self, response, *args, **kwargs):
60
62
  try:
@@ -0,0 +1,164 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from requests_toolbelt.sessions import BaseUrlSession
5
+ from typing import Optional
6
+ from time import sleep, time
7
+ from warnings import warn
8
+
9
+ from codeocean.models.computation import Computation, ComputationState, RunParams
10
+ # Re-exports for backward compatibility
11
+ from codeocean.models.computation import ( # noqa: F401
12
+ ComputationEndStatus,
13
+ Param,
14
+ PipelineProcess,
15
+ InputDataAsset,
16
+ DataAssetsRunParam,
17
+ NamedRunParam,
18
+ PipelineProcessParams,
19
+ )
20
+ from codeocean.models.data_asset import DataAssetAttachParams, DataAssetAttachResults
21
+ from codeocean.models.folder import FileURLs, Folder, DownloadFileURL
22
+
23
+
24
+ @dataclass
25
+ class Computations:
26
+ """Client for interacting with Code Ocean computation APIs."""
27
+
28
+ client: BaseUrlSession
29
+
30
+ def get_computation(self, computation_id: str) -> Computation:
31
+ """Retrieve metadata and status information for a specific computation by its ID."""
32
+ res = self.client.get(f"computations/{computation_id}")
33
+
34
+ return Computation.from_dict(res.json())
35
+
36
+ def run_capsule(self, run_params: RunParams) -> Computation:
37
+ """
38
+ Execute a capsule or pipeline with specified parameters and data assets.
39
+
40
+ For capsule execution: Set run_params.capsule_id and optionally provide data_assets,
41
+ parameters, or named_parameters.
42
+
43
+ For pipeline execution: Set run_params.pipeline_id and optionally provide data_assets,
44
+ processes (with process-specific parameters), and nextflow_profile configuration.
45
+
46
+ """
47
+ res = self.client.post("computations", json=run_params.to_dict())
48
+
49
+ return Computation.from_dict(res.json())
50
+
51
+ # Alias for run_capsule
52
+ run_pipeline = run_capsule
53
+
54
+ def wait_until_completed(
55
+ self,
56
+ computation: Computation,
57
+ polling_interval: float = 5,
58
+ timeout: Optional[float] = None,
59
+ ) -> Computation:
60
+ """
61
+ Poll a computation until it reaches 'Completed' or 'Failed' state with configurable timing.
62
+
63
+ Args:
64
+ computation: The computation object to monitor
65
+ polling_interval: Time between status checks in seconds (minimum 5 seconds)
66
+ timeout: Maximum time to wait in seconds, or None for no timeout
67
+
68
+ Returns:
69
+ Updated computation object once completed or failed
70
+
71
+ Raises:
72
+ ValueError: If polling_interval < 5 or timeout constraints are violated
73
+ TimeoutError: If computation doesn't complete within the timeout period
74
+ """
75
+ if polling_interval < 5:
76
+ raise ValueError(
77
+ f"Polling interval {polling_interval} should be greater than or equal to 5"
78
+ )
79
+ if timeout is not None and timeout < polling_interval:
80
+ raise ValueError(
81
+ f"Timeout {timeout} should be greater than or equal to polling interval {polling_interval}"
82
+ )
83
+ if timeout is not None and timeout < 0:
84
+ raise ValueError(
85
+ f"Timeout {timeout} should be greater than or equal to 0 (seconds), or None"
86
+ )
87
+ t0 = time()
88
+ while True:
89
+ comp = self.get_computation(computation.id)
90
+
91
+ if comp.state in [ComputationState.Completed, ComputationState.Failed]:
92
+ return comp
93
+
94
+ if timeout is not None and (time() - t0) > timeout:
95
+ raise TimeoutError(
96
+ f"Computation {computation.id} did not complete within {timeout} seconds"
97
+ )
98
+
99
+ sleep(polling_interval)
100
+
101
+ def attach_data_assets(
102
+ self,
103
+ computation_id: str,
104
+ attach_params: list[DataAssetAttachParams],
105
+ ) -> list[DataAssetAttachResults]:
106
+ """Attach one or more data assets to a cloud workstation session computation."""
107
+ res = self.client.post(
108
+ f"computations/{computation_id}/data_assets",
109
+ json=[j.to_dict() for j in attach_params],
110
+ )
111
+ return [DataAssetAttachResults.from_dict(c) for c in res.json()]
112
+
113
+ def detach_data_assets(self, computation_id: str, data_assets: list[str]):
114
+ """Detach one or more data assets from a cloud workstation session computation by their IDs."""
115
+ self.client.delete(
116
+ f"computations/{computation_id}/data_assets/",
117
+ json=data_assets,
118
+ )
119
+
120
+ def list_computation_results(self, computation_id: str, path: str = "") -> Folder:
121
+ """List result files and folders generated by a computation
122
+ at the specified path. Empty path retrieves the /results root folder."""
123
+ data = {
124
+ "path": path,
125
+ }
126
+
127
+ res = self.client.post(f"computations/{computation_id}/results", json=data)
128
+
129
+ return Folder.from_dict(res.json())
130
+
131
+ def get_result_file_download_url(self, computation_id: str, path: str) -> DownloadFileURL:
132
+ """[DEPRECATED] Generate a download URL for a specific result file from a computation.
133
+
134
+ Deprecated: Use get_result_file_urls instead.
135
+ """
136
+ warn(
137
+ "get_result_file_download_url is deprecated and will be removed in a future release. "
138
+ "Use get_result_file_urls instead.",
139
+ DeprecationWarning,
140
+ stacklevel=2,
141
+ )
142
+ res = self.client.get(
143
+ f"computations/{computation_id}/results/download_url",
144
+ params={"path": path},
145
+ )
146
+
147
+ return DownloadFileURL.from_dict(res.json())
148
+
149
+ def get_result_file_urls(self, computation_id: str, path: str) -> FileURLs:
150
+ """Generate view and download URLs for a specific result file from a computation."""
151
+ res = self.client.get(
152
+ f"computations/{computation_id}/results/urls",
153
+ params={"path": path},
154
+ )
155
+
156
+ return FileURLs.from_dict(res.json())
157
+
158
+ def delete_computation(self, computation_id: str):
159
+ """Delete a computation and stop it if currently running."""
160
+ self.client.delete(f"computations/{computation_id}")
161
+
162
+ def rename_computation(self, computation_id: str, name: str):
163
+ """Rename an existing computation with a new display name."""
164
+ self.client.patch(f"computations/{computation_id}", params={"name": name})
@@ -0,0 +1,237 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from requests_toolbelt.sessions import BaseUrlSession
5
+ from time import sleep, time
6
+ from typing import Iterator
7
+ from warnings import warn
8
+
9
+ from codeocean.models.components import Permissions
10
+ from codeocean.models.data_asset import (
11
+ DataAsset,
12
+ DataAssetState,
13
+ DataAssetUpdateParams,
14
+ DataAssetParams,
15
+ DataAssetSearchParams,
16
+ DataAssetSearchResults,
17
+ TransferDataParams,
18
+ )
19
+ # Re-exports for backward compatibility
20
+ from codeocean.models.data_asset import ( # noqa: F401
21
+ DataAssetType,
22
+ DataAssetOrigin,
23
+ Provenance,
24
+ SourceBucket,
25
+ AppParameter,
26
+ ResultsInfo,
27
+ Source,
28
+ AWSS3Source,
29
+ GCPCloudStorageSource,
30
+ ComputationSource,
31
+ CloudWorkstationSource,
32
+ Target,
33
+ AWSS3Target,
34
+ DataAssetAttachParams,
35
+ DataAssetAttachResults,
36
+ DataAssetSortBy,
37
+ DataAssetSearchOrigin,
38
+ ContainedDataAsset,
39
+ )
40
+ from codeocean.models.folder import FileURLs, Folder, DownloadFileURL
41
+
42
+
43
+ @dataclass
44
+ class DataAssets:
45
+ """Client for interacting with Code Ocean data asset APIs."""
46
+
47
+ client: BaseUrlSession
48
+
49
+ def get_data_asset(self, data_asset_id: str) -> DataAsset:
50
+ """Retrieve metadata for a specific data asset by its ID."""
51
+ res = self.client.get(f"data_assets/{data_asset_id}")
52
+
53
+ return DataAsset.from_dict(res.json())
54
+
55
+ def update_metadata(self, data_asset_id: str, update_params: DataAssetUpdateParams) -> DataAsset:
56
+ """
57
+ Update metadata for a data asset including name, description, tags, mount,
58
+ and custom metadata.
59
+
60
+ Supports updating various metadata types:
61
+ - Basic metadata: name (display name), description (free text description)
62
+ - Organization: tags (keywords for searching), mount (default mount folder path)
63
+ - Custom metadata: admin-defined custom fields with user-set values according to
64
+ deployment configuration
65
+ (string, number, or date fields in unix epoch format)
66
+ """
67
+ res = self.client.put(
68
+ f"data_assets/{data_asset_id}",
69
+ json=update_params.to_dict(),
70
+ )
71
+
72
+ return DataAsset.from_dict(res.json())
73
+
74
+ def create_data_asset(self, data_asset_params: DataAssetParams) -> DataAsset:
75
+ """
76
+ Create a new data asset from various sources including S3 buckets,
77
+ computation results, or combined assets.
78
+
79
+ Data assets are versioned, immutable collections of files that serve as inputs
80
+ or outputs for computational workflows in Code Ocean. Internal data assets
81
+ store files within Code Ocean's infrastructure, while external data assets
82
+ reference files in external storage (S3/GCP) without copying.
83
+
84
+ Supports creating data assets from AWS S3, GCP Cloud Storage, computation
85
+ results, or combining existing data assets. Returns confirmation of creation
86
+ request validity, not success, as creation takes time. Use wait_until_ready()
87
+ to monitor creation progress.
88
+
89
+ """
90
+ res = self.client.post("data_assets", json=data_asset_params.to_dict())
91
+
92
+ return DataAsset.from_dict(res.json())
93
+
94
+ def wait_until_ready(
95
+ self,
96
+ data_asset: DataAsset,
97
+ polling_interval: float = 5,
98
+ timeout: float | None = None,
99
+ ) -> DataAsset:
100
+ """
101
+ Poll a data asset until it reaches 'Ready' or 'Failed' state with configurable
102
+ timing.
103
+
104
+ Args:
105
+ data_asset: The data asset object to monitor
106
+ polling_interval: Time between status checks in seconds
107
+ (minimum 5 seconds)
108
+ timeout: Maximum time to wait in seconds, or None for no timeout
109
+
110
+ Returns:
111
+ Updated data asset object once ready or failed
112
+
113
+ Raises:
114
+ ValueError: If polling_interval < 5 or timeout constraints are violated
115
+ TimeoutError: If data asset doesn't become ready within timeout period
116
+ """
117
+ if polling_interval < 5:
118
+ raise ValueError(
119
+ f"Polling interval {polling_interval} should be greater than or equal to 5"
120
+ )
121
+ if timeout is not None and timeout < polling_interval:
122
+ raise ValueError(
123
+ f"Timeout {timeout} should be greater than or equal to polling interval {polling_interval}"
124
+ )
125
+ if timeout is not None and timeout < 0:
126
+ raise ValueError(
127
+ f"Timeout {timeout} should be greater than or equal to 0 (seconds), or None"
128
+ )
129
+ t0 = time()
130
+ while True:
131
+ da = self.get_data_asset(data_asset.id)
132
+
133
+ if da.state in [DataAssetState.Ready, DataAssetState.Failed]:
134
+ return da
135
+
136
+ if timeout is not None and (time() - t0) > timeout:
137
+ raise TimeoutError(
138
+ f"Data asset {data_asset.id} was not ready within {timeout} seconds"
139
+ )
140
+
141
+ sleep(polling_interval)
142
+
143
+ def delete_data_asset(self, data_asset_id: str):
144
+ """Delete a data asset permanently."""
145
+ self.client.delete(f"data_assets/{data_asset_id}")
146
+
147
+ def update_permissions(self, data_asset_id: str, permissions: Permissions):
148
+ """Update permissions for a data asset to control user and group access."""
149
+ self.client.post(
150
+ f"data_assets/{data_asset_id}/permissions",
151
+ json=permissions.to_dict(),
152
+ )
153
+
154
+ def archive_data_asset(self, data_asset_id: str, archive: bool):
155
+ """Archive or unarchive a data asset to control its visibility and accessibility."""
156
+ self.client.patch(
157
+ f"data_assets/{data_asset_id}/archive",
158
+ params={"archive": archive},
159
+ )
160
+
161
+ def search_data_assets(self, search_params: DataAssetSearchParams) -> DataAssetSearchResults:
162
+ """Search for data assets with filtering, sorting, and pagination options."""
163
+ res = self.client.post("data_assets/search", json=search_params.to_dict())
164
+
165
+ return DataAssetSearchResults.from_dict(res.json())
166
+
167
+ def search_data_assets_iterator(self, search_params: DataAssetSearchParams) -> Iterator[DataAsset]:
168
+ """
169
+ Iterate through all data assets matching search criteria with
170
+ automatic pagination.
171
+ """
172
+ params = search_params.to_dict()
173
+ while True:
174
+ response = self.search_data_assets(
175
+ search_params=DataAssetSearchParams(**params),
176
+ )
177
+
178
+ for result in response.results:
179
+ yield result
180
+
181
+ if not response.has_more:
182
+ return
183
+
184
+ params["next_token"] = response.next_token
185
+
186
+ def list_data_asset_files(self, data_asset_id: str, path: str = "") -> Folder:
187
+ """
188
+ List files and folders within an internal data asset at the specified path.
189
+ Empty path retrieves root level contents.
190
+ """
191
+ data = {
192
+ "path": path,
193
+ }
194
+
195
+ res = self.client.post(f"data_assets/{data_asset_id}/files", json=data)
196
+
197
+ return Folder.from_dict(res.json())
198
+
199
+ def get_data_asset_file_download_url(self, data_asset_id: str, path: str) -> DownloadFileURL:
200
+ """(Deprecated) Generate a download URL for a specific file from an internal data asset.
201
+
202
+ Deprecated: Use get_data_asset_file_urls instead.
203
+ """
204
+ warn(
205
+ "get_data_asset_file_download_url is deprecated and will be removed in a future release. "
206
+ "Use get_data_asset_file_urls instead.",
207
+ DeprecationWarning,
208
+ stacklevel=2,
209
+ )
210
+ res = self.client.get(
211
+ f"data_assets/{data_asset_id}/files/download_url",
212
+ params={"path": path},
213
+ )
214
+
215
+ return DownloadFileURL.from_dict(res.json())
216
+
217
+ def get_data_asset_file_urls(self, data_asset_id: str, path: str) -> FileURLs:
218
+ """Generate view and download URLs for a specific file from an internal data asset."""
219
+ res = self.client.get(
220
+ f"data_assets/{data_asset_id}/files/urls",
221
+ params={"path": path},
222
+ )
223
+
224
+ return FileURLs.from_dict(res.json())
225
+
226
+ def transfer_data_asset(self, data_asset_id: str, transfer_params: TransferDataParams):
227
+ """
228
+ Transfer a data asset's files to a different S3 storage location (Admin only).
229
+
230
+ Can convert internal data assets to external or change storage location of
231
+ external data assets. Maintains provenance for result data assets. Use
232
+ force=True when transferring data assets used by release pipelines.
233
+ """
234
+ self.client.post(
235
+ f"data_assets/{data_asset_id}/transfer",
236
+ json=transfer_params.to_dict(),
237
+ )
@@ -0,0 +1 @@
1
+ """Models for Code Ocean SDK."""