codeocean 0.1.2__tar.gz → 0.1.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.
@@ -0,0 +1,17 @@
1
+ CHANGELOG
2
+ =========
3
+
4
+ ## 0.1.3 (2024-07-09)
5
+
6
+ - [#8](https://github.com/codeocean/codeocean-sdk-python/pull/8) fix: add TCPKeepAlive to client requests
7
+ - [#6](https://github.com/codeocean/codeocean-sdk-python/pull/6) feat: Add ResultsInfo to DataAssetParams
8
+ - [#5](https://github.com/codeocean/codeocean-sdk-python/pull/5) fix: SourceBucket optional fields
9
+ - [#3](https://github.com/codeocean/codeocean-sdk-python/pull/3) feat: Add external field to SourceBucket
10
+
11
+ ## 0.1.2 (2024-03-20)
12
+
13
+ - Fix minimum required Pyhton version (3.11)
14
+
15
+ ## 0.1.0 (2024-03-18)
16
+
17
+ Initial version
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) [2024] [Code Ocean]
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) [2024] [Code Ocean]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: codeocean
3
- Version: 0.1.2
3
+ Version: 0.1.3
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codeocean"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  authors = [
9
9
  { name="Code Ocean", email="dev@codeocean.com" },
10
10
  ]
@@ -1,77 +1,80 @@
1
- from dataclasses import dataclass
2
- from dataclasses_json import dataclass_json
3
- from enum import StrEnum
4
- from typing import Optional
5
- from requests_toolbelt.sessions import BaseUrlSession
6
-
7
- from codeocean.computation import Computation
8
- from codeocean.data_asset import DataAssetAttachParams, DataAssetAttachResults
9
-
10
-
11
- class CapsuleStatus(StrEnum):
12
- NonPublished = "non-published"
13
- Submitted = "submitted"
14
- Publishing = "publishing"
15
- Published = "published"
16
- Verified = "verified"
17
-
18
-
19
- @dataclass_json
20
- @dataclass(frozen=True)
21
- class OriginalCapsuleInfo:
22
- id: Optional[str] = None
23
- major_version: Optional[int] = None
24
- minor_version: Optional[int] = None
25
- name: Optional[str] = None
26
- created: Optional[int] = None
27
- public: Optional[bool] = None
28
-
29
-
30
- @dataclass_json
31
- @dataclass(frozen=True)
32
- class Capsule:
33
- id: str
34
- created: int
35
- name: str
36
- status: CapsuleStatus
37
- owner: str
38
- slug: str
39
- article: Optional[dict] = None
40
- cloned_from_url: Optional[str] = None
41
- description: Optional[str] = None
42
- field: Optional[str] = None
43
- keywords: Optional[list[str]] = None
44
- original_capsule: Optional[OriginalCapsuleInfo] = None
45
- published_capsule: Optional[str] = None
46
- submission: Optional[dict] = None
47
- versions: Optional[list[dict]] = None
48
-
49
-
50
- @dataclass
51
- class Capsules:
52
-
53
- client: BaseUrlSession
54
-
55
- def get_capsule(self, capsule_id: str) -> Capsule:
56
- res = self.client.get(f"capsules/{capsule_id}")
57
-
58
- return Capsule.from_dict(res.json())
59
-
60
- def list_computations(self, capsule_id: str) -> list[Computation]:
61
- res = self.client.get(f"capsules/{capsule_id}/computations")
62
-
63
- return [Computation.from_dict(c) for c in res.json()]
64
-
65
- def attach_data_assets(self, capsule_id: str, attach_params: list[DataAssetAttachParams]) -> DataAssetAttachResults:
66
- res = self.client.post(
67
- f"capsules/{capsule_id}/data_assets",
68
- json=[j.to_dict() for j in attach_params],
69
- )
70
-
71
- return DataAssetAttachResults.from_dict(res.json())
72
-
73
- def detach_data_assets(self, capsule_id: str, data_assets: list[str]):
74
- self.client.delete(
75
- f"capsules/{capsule_id}/data_assets/",
76
- json=data_assets,
77
- )
1
+ from dataclasses import dataclass
2
+ from dataclasses_json import dataclass_json
3
+ from enum import StrEnum
4
+ from typing import Optional
5
+ from requests_toolbelt.sessions import BaseUrlSession
6
+
7
+ from codeocean.computation import Computation
8
+ from codeocean.data_asset import DataAssetAttachParams, DataAssetAttachResults
9
+
10
+
11
+ class CapsuleStatus(StrEnum):
12
+ NonPublished = "non-published"
13
+ Submitted = "submitted"
14
+ Publishing = "publishing"
15
+ Published = "published"
16
+ Verified = "verified"
17
+
18
+
19
+ @dataclass_json
20
+ @dataclass(frozen=True)
21
+ class OriginalCapsuleInfo:
22
+ id: Optional[str] = None
23
+ major_version: Optional[int] = None
24
+ minor_version: Optional[int] = None
25
+ name: Optional[str] = None
26
+ created: Optional[int] = None
27
+ public: Optional[bool] = None
28
+
29
+
30
+ @dataclass_json
31
+ @dataclass(frozen=True)
32
+ class Capsule:
33
+ id: str
34
+ created: int
35
+ name: str
36
+ status: CapsuleStatus
37
+ owner: str
38
+ slug: str
39
+ article: Optional[dict] = None
40
+ cloned_from_url: Optional[str] = None
41
+ description: Optional[str] = None
42
+ field: Optional[str] = None
43
+ keywords: Optional[list[str]] = None
44
+ original_capsule: Optional[OriginalCapsuleInfo] = None
45
+ published_capsule: Optional[str] = None
46
+ submission: Optional[dict] = None
47
+ versions: Optional[list[dict]] = None
48
+
49
+
50
+ @dataclass
51
+ class Capsules:
52
+
53
+ client: BaseUrlSession
54
+
55
+ def get_capsule(self, capsule_id: str) -> Capsule:
56
+ res = self.client.get(f"capsules/{capsule_id}")
57
+
58
+ return Capsule.from_dict(res.json())
59
+
60
+ def list_computations(self, capsule_id: str) -> list[Computation]:
61
+ res = self.client.get(f"capsules/{capsule_id}/computations")
62
+
63
+ return [Computation.from_dict(c) for c in res.json()]
64
+
65
+ def attach_data_assets(
66
+ self,
67
+ capsule_id: str,
68
+ attach_params: list[DataAssetAttachParams]) -> list[DataAssetAttachResults]:
69
+ res = self.client.post(
70
+ f"capsules/{capsule_id}/data_assets",
71
+ json=[j.to_dict() for j in attach_params],
72
+ )
73
+
74
+ return [DataAssetAttachResults.from_dict(c) for c in res.json()]
75
+
76
+ def detach_data_assets(self, capsule_id: str, data_assets: list[str]):
77
+ self.client.delete(
78
+ f"capsules/{capsule_id}/data_assets/",
79
+ json=data_assets,
80
+ )
@@ -1,25 +1,27 @@
1
- from requests_toolbelt.sessions import BaseUrlSession
2
- from dataclasses import dataclass
3
-
4
- from codeocean.capsule import Capsules
5
- from codeocean.computation import Computations
6
- from codeocean.data_asset import DataAssets
7
-
8
-
9
- @dataclass
10
- class CodeOcean:
11
-
12
- domain: str
13
- token: str
14
-
15
- def __post_init__(self):
16
- self.session = BaseUrlSession(base_url=f"{self.domain}/api/v1/")
17
- self.session.auth = (self.token, "")
18
- self.session.headers.update({"Content-Type": "application/json"})
19
- self.session.hooks["response"] = [
20
- lambda response, *args, **kwargs: response.raise_for_status()
21
- ]
22
-
23
- self.capsules = Capsules(client=self.session)
24
- self.computations = Computations(client=self.session)
25
- self.data_assets = DataAssets(client=self.session)
1
+ from requests_toolbelt.sessions import BaseUrlSession
2
+ from requests_toolbelt.adapters.socket_options import TCPKeepAliveAdapter
3
+ from dataclasses import dataclass
4
+
5
+ from codeocean.capsule import Capsules
6
+ from codeocean.computation import Computations
7
+ from codeocean.data_asset import DataAssets
8
+
9
+
10
+ @dataclass
11
+ class CodeOcean:
12
+
13
+ domain: str
14
+ token: str
15
+
16
+ def __post_init__(self):
17
+ self.session = BaseUrlSession(base_url=f"{self.domain}/api/v1/")
18
+ self.session.auth = (self.token, "")
19
+ self.session.mount(self.domain, TCPKeepAliveAdapter())
20
+ self.session.headers.update({"Content-Type": "application/json"})
21
+ self.session.hooks["response"] = [
22
+ lambda response, *args, **kwargs: response.raise_for_status()
23
+ ]
24
+
25
+ self.capsules = Capsules(client=self.session)
26
+ self.computations = Computations(client=self.session)
27
+ self.data_assets = DataAssets(client=self.session)
@@ -1,171 +1,171 @@
1
- from dataclasses import dataclass
2
- from dataclasses_json import dataclass_json
3
- from enum import StrEnum
4
- from requests_toolbelt.sessions import BaseUrlSession
5
- from typing import Optional
6
- from time import sleep
7
-
8
-
9
- class ComputationState(StrEnum):
10
- Initializing = "initializing"
11
- Running = "running"
12
- Finalizing = "finalizing"
13
- Completed = "completed"
14
- Failed = "failed"
15
-
16
-
17
- class ComputationEndStatus(StrEnum):
18
- Succeeded = "succeeded"
19
- Failed = "failed"
20
- Stopped = "stopped"
21
-
22
-
23
- @dataclass_json
24
- @dataclass(frozen=True)
25
- class Param:
26
- name: Optional[str] = None
27
- param_name: Optional[str] = None
28
- value: Optional[str] = None
29
-
30
-
31
- @dataclass_json
32
- @dataclass(frozen=True)
33
- class PipelineProcess:
34
- name: str
35
- capsule_id: str
36
- version: Optional[int] = None
37
- public: Optional[bool] = None
38
- parameters: Optional[list[Param]] = None
39
-
40
-
41
- @dataclass_json
42
- @dataclass(frozen=True)
43
- class InputDataAsset:
44
- id: str
45
- mount: Optional[str] = None
46
-
47
-
48
- @dataclass_json
49
- @dataclass(frozen=True)
50
- class Computation:
51
- id: str
52
- created: int
53
- name: str
54
- state: ComputationState
55
- run_time: int
56
- cloud_workstation: Optional[bool] = None
57
- data_assets: Optional[list[InputDataAsset]] = None
58
- end_status: Optional[ComputationEndStatus] = None
59
- has_results: Optional[bool] = None
60
- parameters: Optional[list[Param]] = None
61
- processes: Optional[list[PipelineProcess]] = None
62
-
63
-
64
- @dataclass_json
65
- @dataclass(frozen=True)
66
- class DataAssetsRunParam:
67
- id: str
68
- mount: str
69
-
70
-
71
- @dataclass_json
72
- @dataclass(frozen=True)
73
- class NamedRunParam:
74
- param_name: str
75
- value: str
76
-
77
-
78
- @dataclass_json
79
- @dataclass(frozen=True)
80
- class PipelineProcessParams:
81
- name: str
82
- parameters: Optional[list[str]] = None
83
- named_parameters: Optional[list[NamedRunParam]] = None
84
-
85
-
86
- @dataclass_json
87
- @dataclass(frozen=True)
88
- class RunParams:
89
- capsule_id: Optional[str] = None
90
- pipeline_id: Optional[str] = None
91
- version: Optional[int] = None
92
- resume_run_id: Optional[str] = None
93
- data_assets: Optional[list[DataAssetsRunParam]] = None
94
- parameters: Optional[list[str]] = None
95
- named_parameters: Optional[list[NamedRunParam]] = None
96
- processes: Optional[list[PipelineProcessParams]] = None
97
-
98
-
99
- @dataclass_json
100
- @dataclass(frozen=True)
101
- class FolderItem:
102
- name: str
103
- path: str
104
- type: str
105
- size: Optional[int] = None
106
-
107
-
108
- @dataclass_json
109
- @dataclass(frozen=True)
110
- class Folder:
111
- items: list[FolderItem]
112
-
113
-
114
- @dataclass_json
115
- @dataclass(frozen=True)
116
- class ListFolderParams:
117
- path: str
118
-
119
-
120
- @dataclass_json
121
- @dataclass(frozen=True)
122
- class DownloadFileURL:
123
- url: str
124
-
125
-
126
- @dataclass
127
- class Computations:
128
-
129
- client: BaseUrlSession
130
-
131
- def get_computation(self, computation_id: str) -> Computation:
132
- res = self.client.get(f"computations/{computation_id}")
133
-
134
- return Computation.from_dict(res.json())
135
-
136
- def run_capsule(self, run_params: RunParams) -> Computation:
137
- res = self.client.post("computations", json=run_params.to_dict())
138
-
139
- return Computation.from_dict(res.json())
140
-
141
- def wait_until_completed(self, computation: Computation) -> Computation:
142
- """
143
- Polls the given computation until it reaches the 'Completed' or 'Failed' state.
144
- """
145
- while True:
146
- comp = self.get_computation(computation.id)
147
-
148
- if comp.state in [ComputationState.Completed, ComputationState.Failed]:
149
- return comp
150
-
151
- sleep(5)
152
-
153
- def list_computation_results(self, computation_id: str, path: str = "") -> Folder:
154
- data = {
155
- "path": path,
156
- }
157
-
158
- res = self.client.post(f"computations/{computation_id}/results", json=data)
159
-
160
- return Folder.from_dict(res.json())
161
-
162
- def get_result_file_download_url(self, computation_id: str, path: str) -> DownloadFileURL:
163
- res = self.client.get(
164
- f"computations/{computation_id}/results/download_url",
165
- params={"path": path},
166
- )
167
-
168
- return DownloadFileURL.from_dict(res.json())
169
-
170
- def delete_computation(self, computation_id: str):
171
- self.client.delete(f"computations/{computation_id}")
1
+ from dataclasses import dataclass
2
+ from dataclasses_json import dataclass_json
3
+ from enum import StrEnum
4
+ from requests_toolbelt.sessions import BaseUrlSession
5
+ from typing import Optional
6
+ from time import sleep
7
+
8
+
9
+ class ComputationState(StrEnum):
10
+ Initializing = "initializing"
11
+ Running = "running"
12
+ Finalizing = "finalizing"
13
+ Completed = "completed"
14
+ Failed = "failed"
15
+
16
+
17
+ class ComputationEndStatus(StrEnum):
18
+ Succeeded = "succeeded"
19
+ Failed = "failed"
20
+ Stopped = "stopped"
21
+
22
+
23
+ @dataclass_json
24
+ @dataclass(frozen=True)
25
+ class Param:
26
+ name: Optional[str] = None
27
+ param_name: Optional[str] = None
28
+ value: Optional[str] = None
29
+
30
+
31
+ @dataclass_json
32
+ @dataclass(frozen=True)
33
+ class PipelineProcess:
34
+ name: str
35
+ capsule_id: str
36
+ version: Optional[int] = None
37
+ public: Optional[bool] = None
38
+ parameters: Optional[list[Param]] = None
39
+
40
+
41
+ @dataclass_json
42
+ @dataclass(frozen=True)
43
+ class InputDataAsset:
44
+ id: str
45
+ mount: Optional[str] = None
46
+
47
+
48
+ @dataclass_json
49
+ @dataclass(frozen=True)
50
+ class Computation:
51
+ id: str
52
+ created: int
53
+ name: str
54
+ state: ComputationState
55
+ run_time: int
56
+ cloud_workstation: Optional[bool] = None
57
+ data_assets: Optional[list[InputDataAsset]] = None
58
+ end_status: Optional[ComputationEndStatus] = None
59
+ has_results: Optional[bool] = None
60
+ parameters: Optional[list[Param]] = None
61
+ processes: Optional[list[PipelineProcess]] = None
62
+
63
+
64
+ @dataclass_json
65
+ @dataclass(frozen=True)
66
+ class DataAssetsRunParam:
67
+ id: str
68
+ mount: str
69
+
70
+
71
+ @dataclass_json
72
+ @dataclass(frozen=True)
73
+ class NamedRunParam:
74
+ param_name: str
75
+ value: str
76
+
77
+
78
+ @dataclass_json
79
+ @dataclass(frozen=True)
80
+ class PipelineProcessParams:
81
+ name: str
82
+ parameters: Optional[list[str]] = None
83
+ named_parameters: Optional[list[NamedRunParam]] = None
84
+
85
+
86
+ @dataclass_json
87
+ @dataclass(frozen=True)
88
+ class RunParams:
89
+ capsule_id: Optional[str] = None
90
+ pipeline_id: Optional[str] = None
91
+ version: Optional[int] = None
92
+ resume_run_id: Optional[str] = None
93
+ data_assets: Optional[list[DataAssetsRunParam]] = None
94
+ parameters: Optional[list[str]] = None
95
+ named_parameters: Optional[list[NamedRunParam]] = None
96
+ processes: Optional[list[PipelineProcessParams]] = None
97
+
98
+
99
+ @dataclass_json
100
+ @dataclass(frozen=True)
101
+ class FolderItem:
102
+ name: str
103
+ path: str
104
+ type: str
105
+ size: Optional[int] = None
106
+
107
+
108
+ @dataclass_json
109
+ @dataclass(frozen=True)
110
+ class Folder:
111
+ items: list[FolderItem]
112
+
113
+
114
+ @dataclass_json
115
+ @dataclass(frozen=True)
116
+ class ListFolderParams:
117
+ path: str
118
+
119
+
120
+ @dataclass_json
121
+ @dataclass(frozen=True)
122
+ class DownloadFileURL:
123
+ url: str
124
+
125
+
126
+ @dataclass
127
+ class Computations:
128
+
129
+ client: BaseUrlSession
130
+
131
+ def get_computation(self, computation_id: str) -> Computation:
132
+ res = self.client.get(f"computations/{computation_id}")
133
+
134
+ return Computation.from_dict(res.json())
135
+
136
+ def run_capsule(self, run_params: RunParams) -> Computation:
137
+ res = self.client.post("computations", json=run_params.to_dict())
138
+
139
+ return Computation.from_dict(res.json())
140
+
141
+ def wait_until_completed(self, computation: Computation) -> Computation:
142
+ """
143
+ Polls the given computation until it reaches the 'Completed' or 'Failed' state.
144
+ """
145
+ while True:
146
+ comp = self.get_computation(computation.id)
147
+
148
+ if comp.state in [ComputationState.Completed, ComputationState.Failed]:
149
+ return comp
150
+
151
+ sleep(5)
152
+
153
+ def list_computation_results(self, computation_id: str, path: str = "") -> Folder:
154
+ data = {
155
+ "path": path,
156
+ }
157
+
158
+ res = self.client.post(f"computations/{computation_id}/results", json=data)
159
+
160
+ return Folder.from_dict(res.json())
161
+
162
+ def get_result_file_download_url(self, computation_id: str, path: str) -> DownloadFileURL:
163
+ res = self.client.get(
164
+ f"computations/{computation_id}/results/download_url",
165
+ params={"path": path},
166
+ )
167
+
168
+ return DownloadFileURL.from_dict(res.json())
169
+
170
+ def delete_computation(self, computation_id: str):
171
+ self.client.delete(f"computations/{computation_id}")
@@ -1,252 +1,268 @@
1
- from dataclasses_json import dataclass_json
2
- from dataclasses import dataclass
3
- from enum import StrEnum
4
- from requests_toolbelt.sessions import BaseUrlSession
5
- from time import sleep
6
- from typing import Optional
7
-
8
- from codeocean.components import SortOrder, SearchFilter, Permissions
9
-
10
-
11
- class DataAssetType(StrEnum):
12
- Dataset = "dataset"
13
- Result = "result"
14
-
15
-
16
- class DataAssetState(StrEnum):
17
- Draft = "draft"
18
- Ready = "ready"
19
- Failed = "failed"
20
-
21
-
22
- @dataclass_json
23
- @dataclass(frozen=True)
24
- class Provenance:
25
- commit: str
26
- run_script: str
27
- data_assets: str
28
- docker_image: str
29
- capsule: str
30
-
31
-
32
- class DataAssetOrigin(StrEnum):
33
- Local = "local"
34
- AWS = "aws"
35
- GCP = "gcp"
36
-
37
-
38
- @dataclass_json
39
- @dataclass(frozen=True)
40
- class SourceBucket:
41
- origin: DataAssetOrigin
42
- bucket: str
43
- prefix: str
44
-
45
-
46
- @dataclass_json
47
- @dataclass(frozen=True)
48
- class AppParameter:
49
- name: str
50
- value: str
51
-
52
-
53
- @dataclass_json
54
- @dataclass(frozen=True)
55
- class DataAsset:
56
- id: str
57
- created: int
58
- name: str
59
- mount: str
60
- state: DataAssetState
61
- type: DataAssetType
62
- last_used: int
63
- app_parameters: Optional[list[AppParameter]] = None
64
- custom_metadata: Optional[dict] = None
65
- description: Optional[str] = None
66
- failure_reason: Optional[str] = None
67
- files: Optional[int] = None
68
- provenance: Optional[Provenance] = None
69
- size: Optional[int] = None
70
- source_bucket: Optional[SourceBucket] = None
71
- tags: Optional[list[str]] = None
72
-
73
-
74
- @dataclass_json
75
- @dataclass(frozen=True)
76
- class DataAssetUpdateParams:
77
- name: str
78
- description: str
79
- tags: list[str]
80
- mount: str
81
- custom_metadata: Optional[dict] = None
82
-
83
-
84
- @dataclass_json
85
- @dataclass(frozen=True)
86
- class AWSS3Source:
87
- bucket: str
88
- prefix: Optional[str] = None
89
- keep_on_external_storage: Optional[bool] = None
90
- public: Optional[bool] = None
91
-
92
-
93
- @dataclass_json
94
- @dataclass(frozen=True)
95
- class GCPCloudStorageSource:
96
- bucket: str
97
- client_id: Optional[str] = None
98
- client_secret: Optional[str] = None
99
- prefix: Optional[str] = None
100
-
101
-
102
- @dataclass_json
103
- @dataclass(frozen=True)
104
- class ComputationSource:
105
- id: str
106
- path: Optional[str] = None
107
-
108
-
109
- @dataclass_json
110
- @dataclass(frozen=True)
111
- class Source:
112
- aws: Optional[AWSS3Source] = None
113
- gcp: Optional[GCPCloudStorageSource] = None
114
- computation: Optional[ComputationSource] = None
115
-
116
-
117
- @dataclass_json
118
- @dataclass(frozen=True)
119
- class AWSS3Target:
120
- bucket: str
121
- prefix: Optional[str] = None
122
-
123
-
124
- @dataclass_json
125
- @dataclass(frozen=True)
126
- class Target:
127
- aws: Optional[AWSS3Target] = None
128
-
129
-
130
- @dataclass_json
131
- @dataclass(frozen=True)
132
- class DataAssetParams:
133
- name: str
134
- tags: list[str]
135
- mount: str
136
- description: Optional[str] = None
137
- source: Optional[Source] = None
138
- target: Optional[Target] = None
139
- custom_metadata: Optional[dict] = None
140
- data_asset_ids: Optional[list[str]] = None
141
-
142
-
143
- @dataclass_json
144
- @dataclass(frozen=True)
145
- class DataAssetAttachParams:
146
- id: str
147
- mount: Optional[str] = None
148
-
149
-
150
- @dataclass_json
151
- @dataclass(frozen=True)
152
- class DataAssetAttachResults:
153
- id: str
154
- mount: str
155
- mount_state: str
156
- job_id: str
157
- external: bool
158
- ready: bool
159
-
160
-
161
- class DataAssetSortBy(StrEnum):
162
- Created = "created"
163
- Type = "type"
164
- Name = "name"
165
- Size = "size"
166
-
167
-
168
- class DataAssetOwnership(StrEnum):
169
- Private = "private"
170
- Shared = "shared"
171
- Created = "created"
172
-
173
-
174
- class DataAssetSearchOrigin(StrEnum):
175
- Internal = "internal"
176
- External = "external"
177
-
178
-
179
- @dataclass_json
180
- @dataclass(frozen=True)
181
- class DataAssetSearchParams:
182
- limit: int
183
- offset: int
184
- archived: bool
185
- favorite: bool
186
- query: Optional[str] = None
187
- sort_field: Optional[DataAssetSortBy] = None
188
- sort_order: Optional[SortOrder] = None
189
- type: Optional[DataAssetType] = None
190
- ownership: Optional[DataAssetOwnership] = None
191
- origin: Optional[DataAssetSearchOrigin] = None
192
- filters: Optional[list[SearchFilter]] = None
193
-
194
-
195
- @dataclass_json
196
- @dataclass(frozen=True)
197
- class DataAssetSearchResults:
198
- has_more: bool
199
- results: list[DataAsset]
200
-
201
-
202
- @dataclass
203
- class DataAssets:
204
-
205
- client: BaseUrlSession
206
-
207
- def get_data_asset(self, data_asset_id: str) -> DataAsset:
208
- res = self.client.get(f"data_assets/{data_asset_id}")
209
-
210
- return DataAsset.from_dict(res.json())
211
-
212
- def update_metadata(self, data_asset_id: str, update_params: DataAssetUpdateParams) -> DataAsset:
213
- res = self.client.put(f"data_assets/{data_asset_id}", json=update_params.to_dict())
214
-
215
- return DataAsset.from_dict(res.json())
216
-
217
- def create_data_asset(self, data_asset_params: DataAssetParams) -> DataAsset:
218
- res = self.client.post("data_assets", json=data_asset_params.to_dict())
219
-
220
- return DataAsset.from_dict(res.json())
221
-
222
- def wait_until_ready(self, data_asset: DataAsset) -> DataAsset:
223
- """
224
- Polls the given data asset until it reaches the 'Ready' or 'Failed' state.
225
- """
226
- while True:
227
- da = self.get_data_asset(data_asset.id)
228
-
229
- if da.state in [DataAssetState.Ready, DataAssetState.Failed]:
230
- return da
231
-
232
- sleep(5)
233
-
234
- def delete_data_asset(self, data_asset_id: str):
235
- self.client.delete(f"data_assets/{data_asset_id}")
236
-
237
- def update_permissions(self, data_asset_id: str, permissions: Permissions):
238
- self.client.post(
239
- f"data_assets/{data_asset_id}/permissions",
240
- json=permissions.to_dict(),
241
- )
242
-
243
- def archive_data_asset(self, data_asset_id: str, archive: bool):
244
- self.client.patch(
245
- f"data_assets/{data_asset_id}/archive",
246
- params={"archive": archive},
247
- )
248
-
249
- def search_data_assets(self, search_params: DataAssetSearchParams) -> DataAssetSearchResults:
250
- res = self.client.post("data_assets/search", json=search_params.to_dict())
251
-
252
- return DataAssetSearchResults.from_dict(res.json())
1
+ from dataclasses_json import dataclass_json
2
+ from dataclasses import dataclass
3
+ from enum import StrEnum
4
+ from requests_toolbelt.sessions import BaseUrlSession
5
+ from time import sleep
6
+ from typing import Optional
7
+
8
+ from codeocean.components import SortOrder, SearchFilter, Permissions
9
+ from codeocean.computation import PipelineProcess, Param
10
+
11
+
12
+ class DataAssetType(StrEnum):
13
+ Dataset = "dataset"
14
+ Result = "result"
15
+
16
+
17
+ class DataAssetState(StrEnum):
18
+ Draft = "draft"
19
+ Ready = "ready"
20
+ Failed = "failed"
21
+
22
+
23
+ @dataclass_json
24
+ @dataclass(frozen=True)
25
+ class Provenance:
26
+ commit: str
27
+ run_script: str
28
+ data_assets: str
29
+ docker_image: str
30
+ capsule: str
31
+
32
+
33
+ class DataAssetOrigin(StrEnum):
34
+ Local = "local"
35
+ AWS = "aws"
36
+ GCP = "gcp"
37
+
38
+
39
+ @dataclass_json
40
+ @dataclass(frozen=True)
41
+ class SourceBucket:
42
+ origin: DataAssetOrigin
43
+ bucket: Optional[str] = None
44
+ prefix: Optional[str] = None
45
+ external: Optional[bool] = None
46
+
47
+
48
+ @dataclass_json
49
+ @dataclass(frozen=True)
50
+ class AppParameter:
51
+ name: str
52
+ value: str
53
+
54
+
55
+ @dataclass_json
56
+ @dataclass(frozen=True)
57
+ class ResultsInfo:
58
+ capsule_id: Optional[str] = None
59
+ pipeline_id: Optional[str] = None
60
+ version: Optional[int] = None
61
+ commit: Optional[str] = None
62
+ run_script: Optional[str] = None
63
+ data_assets: Optional[list[str]] = None
64
+ parameters: Optional[list[Param]] = None
65
+ processes: Optional[list[PipelineProcess]] = None
66
+
67
+
68
+ @dataclass_json
69
+ @dataclass(frozen=True)
70
+ class DataAsset:
71
+ id: str
72
+ created: int
73
+ name: str
74
+ mount: str
75
+ state: DataAssetState
76
+ type: DataAssetType
77
+ last_used: int
78
+ app_parameters: Optional[list[AppParameter]] = None
79
+ custom_metadata: Optional[dict] = None
80
+ description: Optional[str] = None
81
+ failure_reason: Optional[str] = None
82
+ files: Optional[int] = None
83
+ provenance: Optional[Provenance] = None
84
+ size: Optional[int] = None
85
+ source_bucket: Optional[SourceBucket] = None
86
+ tags: Optional[list[str]] = None
87
+
88
+
89
+ @dataclass_json
90
+ @dataclass(frozen=True)
91
+ class DataAssetUpdateParams:
92
+ name: str
93
+ description: str
94
+ tags: list[str]
95
+ mount: str
96
+ custom_metadata: Optional[dict] = None
97
+
98
+
99
+ @dataclass_json
100
+ @dataclass(frozen=True)
101
+ class AWSS3Source:
102
+ bucket: str
103
+ prefix: Optional[str] = None
104
+ keep_on_external_storage: Optional[bool] = None
105
+ public: Optional[bool] = None
106
+
107
+
108
+ @dataclass_json
109
+ @dataclass(frozen=True)
110
+ class GCPCloudStorageSource:
111
+ bucket: str
112
+ client_id: Optional[str] = None
113
+ client_secret: Optional[str] = None
114
+ prefix: Optional[str] = None
115
+
116
+
117
+ @dataclass_json
118
+ @dataclass(frozen=True)
119
+ class ComputationSource:
120
+ id: str
121
+ path: Optional[str] = None
122
+
123
+
124
+ @dataclass_json
125
+ @dataclass(frozen=True)
126
+ class Source:
127
+ aws: Optional[AWSS3Source] = None
128
+ gcp: Optional[GCPCloudStorageSource] = None
129
+ computation: Optional[ComputationSource] = None
130
+
131
+
132
+ @dataclass_json
133
+ @dataclass(frozen=True)
134
+ class AWSS3Target:
135
+ bucket: str
136
+ prefix: Optional[str] = None
137
+
138
+
139
+ @dataclass_json
140
+ @dataclass(frozen=True)
141
+ class Target:
142
+ aws: Optional[AWSS3Target] = None
143
+
144
+
145
+ @dataclass_json
146
+ @dataclass(frozen=True)
147
+ class DataAssetParams:
148
+ name: str
149
+ tags: list[str]
150
+ mount: str
151
+ description: Optional[str] = None
152
+ source: Optional[Source] = None
153
+ target: Optional[Target] = None
154
+ custom_metadata: Optional[dict] = None
155
+ data_asset_ids: Optional[list[str]] = None
156
+ results_info: Optional[ResultsInfo] = None
157
+
158
+
159
+ @dataclass_json
160
+ @dataclass(frozen=True)
161
+ class DataAssetAttachParams:
162
+ id: str
163
+ mount: Optional[str] = None
164
+
165
+
166
+ @dataclass_json
167
+ @dataclass(frozen=True)
168
+ class DataAssetAttachResults:
169
+ id: str
170
+ mount: str
171
+ mount_state: str
172
+ job_id: str
173
+ external: bool
174
+ ready: bool
175
+
176
+
177
+ class DataAssetSortBy(StrEnum):
178
+ Created = "created"
179
+ Type = "type"
180
+ Name = "name"
181
+ Size = "size"
182
+
183
+
184
+ class DataAssetOwnership(StrEnum):
185
+ Private = "private"
186
+ Shared = "shared"
187
+ Created = "created"
188
+
189
+
190
+ class DataAssetSearchOrigin(StrEnum):
191
+ Internal = "internal"
192
+ External = "external"
193
+
194
+
195
+ @dataclass_json
196
+ @dataclass(frozen=True)
197
+ class DataAssetSearchParams:
198
+ limit: int
199
+ offset: int
200
+ archived: bool
201
+ favorite: bool
202
+ query: Optional[str] = None
203
+ sort_field: Optional[DataAssetSortBy] = None
204
+ sort_order: Optional[SortOrder] = None
205
+ type: Optional[DataAssetType] = None
206
+ ownership: Optional[DataAssetOwnership] = None
207
+ origin: Optional[DataAssetSearchOrigin] = None
208
+ filters: Optional[list[SearchFilter]] = None
209
+
210
+
211
+ @dataclass_json
212
+ @dataclass(frozen=True)
213
+ class DataAssetSearchResults:
214
+ has_more: bool
215
+ results: list[DataAsset]
216
+
217
+
218
+ @dataclass
219
+ class DataAssets:
220
+
221
+ client: BaseUrlSession
222
+
223
+ def get_data_asset(self, data_asset_id: str) -> DataAsset:
224
+ res = self.client.get(f"data_assets/{data_asset_id}")
225
+
226
+ return DataAsset.from_dict(res.json())
227
+
228
+ def update_metadata(self, data_asset_id: str, update_params: DataAssetUpdateParams) -> DataAsset:
229
+ res = self.client.put(f"data_assets/{data_asset_id}", json=update_params.to_dict())
230
+
231
+ return DataAsset.from_dict(res.json())
232
+
233
+ def create_data_asset(self, data_asset_params: DataAssetParams) -> DataAsset:
234
+ res = self.client.post("data_assets", json=data_asset_params.to_dict())
235
+
236
+ return DataAsset.from_dict(res.json())
237
+
238
+ def wait_until_ready(self, data_asset: DataAsset) -> DataAsset:
239
+ """
240
+ Polls the given data asset until it reaches the 'Ready' or 'Failed' state.
241
+ """
242
+ while True:
243
+ da = self.get_data_asset(data_asset.id)
244
+
245
+ if da.state in [DataAssetState.Ready, DataAssetState.Failed]:
246
+ return da
247
+
248
+ sleep(5)
249
+
250
+ def delete_data_asset(self, data_asset_id: str):
251
+ self.client.delete(f"data_assets/{data_asset_id}")
252
+
253
+ def update_permissions(self, data_asset_id: str, permissions: Permissions):
254
+ self.client.post(
255
+ f"data_assets/{data_asset_id}/permissions",
256
+ json=permissions.to_dict(),
257
+ )
258
+
259
+ def archive_data_asset(self, data_asset_id: str, archive: bool):
260
+ self.client.patch(
261
+ f"data_assets/{data_asset_id}/archive",
262
+ params={"archive": archive},
263
+ )
264
+
265
+ def search_data_assets(self, search_params: DataAssetSearchParams) -> DataAssetSearchResults:
266
+ res = self.client.post("data_assets/search", json=search_params.to_dict())
267
+
268
+ return DataAssetSearchResults.from_dict(res.json())
@@ -1,10 +0,0 @@
1
- CHANGELOG
2
- =========
3
-
4
- ## 0.1.2 (2024-03-20)
5
-
6
- - Fix minimum required Pyhton version (3.11)
7
-
8
- ## 0.1.0 (2024-03-18)
9
-
10
- Initial version
File without changes
File without changes
File without changes