codeocean 0.1.6__tar.gz → 0.2.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.
@@ -1,6 +1,14 @@
1
1
  CHANGELOG
2
2
  =========
3
3
 
4
+ ## 0.2.0 (2024-10-21)
5
+
6
+ - [#15](https://github.com/codeocean/codeocean-sdk-python/pull/15) feat: Add option for custom retries
7
+
8
+ ## 0.1.7 (2024-10-17)
9
+
10
+ - [#14](https://github.com/codeocean/codeocean-sdk-python/pull/14) feat: Add an optional timeout parameter to polling while loops
11
+
4
12
  ## 0.1.6 (2024-10-15)
5
13
 
6
14
  - [#13](https://github.com/codeocean/codeocean-sdk-python/pull/13) feat: makes polling interval configurable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: codeocean
3
- Version: 0.1.6
3
+ Version: 0.2.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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codeocean"
7
- version = "0.1.6"
7
+ version = "0.2.0"
8
8
  authors = [
9
9
  { name="Code Ocean", email="dev@codeocean.com" },
10
10
  ]
@@ -1,6 +1,8 @@
1
- from requests_toolbelt.sessions import BaseUrlSession
2
- from requests_toolbelt.adapters.socket_options import TCPKeepAliveAdapter
3
1
  from dataclasses import dataclass
2
+ from requests_toolbelt.adapters.socket_options import TCPKeepAliveAdapter
3
+ from requests_toolbelt.sessions import BaseUrlSession
4
+ from typing import Optional
5
+ from urllib3.util import Retry
4
6
 
5
7
  from codeocean.capsule import Capsules
6
8
  from codeocean.computation import Computations
@@ -12,15 +14,16 @@ class CodeOcean:
12
14
 
13
15
  domain: str
14
16
  token: str
17
+ retries: Optional[Retry] = None
15
18
 
16
19
  def __post_init__(self):
17
20
  self.session = BaseUrlSession(base_url=f"{self.domain}/api/v1/")
18
21
  self.session.auth = (self.token, "")
19
- self.session.mount(self.domain, TCPKeepAliveAdapter())
20
22
  self.session.headers.update({"Content-Type": "application/json"})
21
23
  self.session.hooks["response"] = [
22
24
  lambda response, *args, **kwargs: response.raise_for_status()
23
25
  ]
26
+ self.session.mount(self.domain, TCPKeepAliveAdapter(max_retries=self.retries))
24
27
 
25
28
  self.capsules = Capsules(client=self.session)
26
29
  self.computations = Computations(client=self.session)
@@ -4,7 +4,7 @@ from dataclasses import dataclass
4
4
  from dataclasses_json import dataclass_json
5
5
  from requests_toolbelt.sessions import BaseUrlSession
6
6
  from typing import Optional
7
- from time import sleep
7
+ from time import sleep, time
8
8
 
9
9
  from codeocean.enum import StrEnum
10
10
 
@@ -141,20 +141,39 @@ class Computations:
141
141
 
142
142
  return Computation.from_dict(res.json())
143
143
 
144
- def wait_until_completed(self, computation: Computation, polling_interval: int = 5) -> Computation:
144
+ def wait_until_completed(
145
+ self,
146
+ computation: Computation,
147
+ polling_interval: float = 5,
148
+ timeout: Optional[float] = None,
149
+ ) -> Computation:
145
150
  """
146
151
  Polls the given computation until it reaches the 'Completed' or 'Failed' state.
152
+
153
+ - `polling_interval` and `timeout` are in seconds
147
154
  """
148
155
  if polling_interval < 5:
149
156
  raise ValueError(
150
157
  f"Polling interval {polling_interval} should be greater than or equal to 5"
151
158
  )
159
+ if timeout is not None and timeout < polling_interval:
160
+ raise ValueError(
161
+ f"Timeout {timeout} should be greater than or equal to polling interval {polling_interval}"
162
+ )
163
+ if timeout is not None and timeout < 0:
164
+ raise ValueError(
165
+ f"Timeout {timeout} should be greater than or equal to 0 (seconds), or None"
166
+ )
167
+ t0 = time()
152
168
  while True:
153
169
  comp = self.get_computation(computation.id)
154
170
 
155
171
  if comp.state in [ComputationState.Completed, ComputationState.Failed]:
156
172
  return comp
157
173
 
174
+ if timeout is not None and (time() - t0) > timeout:
175
+ raise TimeoutError(f"Computation {computation.id} did not complete within {timeout} seconds")
176
+
158
177
  sleep(polling_interval)
159
178
 
160
179
  def list_computation_results(self, computation_id: str, path: str = "") -> Folder:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from dataclasses_json import dataclass_json
4
4
  from dataclasses import dataclass
5
5
  from requests_toolbelt.sessions import BaseUrlSession
6
- from time import sleep
6
+ from time import sleep, time
7
7
  from typing import Optional
8
8
 
9
9
  from codeocean.components import SortOrder, SearchFilter, Permissions
@@ -237,20 +237,39 @@ class DataAssets:
237
237
 
238
238
  return DataAsset.from_dict(res.json())
239
239
 
240
- def wait_until_ready(self, data_asset: DataAsset, polling_interval: int = 5) -> DataAsset:
240
+ def wait_until_ready(
241
+ self,
242
+ data_asset: DataAsset,
243
+ polling_interval: float = 5,
244
+ timeout: float | None = None,
245
+ ) -> DataAsset:
241
246
  """
242
247
  Polls the given data asset until it reaches the 'Ready' or 'Failed' state.
248
+
249
+ - `polling_interval` and `timeout` are in seconds
243
250
  """
244
251
  if polling_interval < 5:
245
252
  raise ValueError(
246
253
  f"Polling interval {polling_interval} should be greater than or equal to 5"
247
254
  )
255
+ if timeout is not None and timeout < polling_interval:
256
+ raise ValueError(
257
+ f"Timeout {timeout} should be greater than or equal to polling interval {polling_interval}"
258
+ )
259
+ if timeout is not None and timeout < 0:
260
+ raise ValueError(
261
+ f"Timeout {timeout} should be greater than or equal to 0 (seconds), or None"
262
+ )
263
+ t0 = time()
248
264
  while True:
249
265
  da = self.get_data_asset(data_asset.id)
250
266
 
251
267
  if da.state in [DataAssetState.Ready, DataAssetState.Failed]:
252
268
  return da
253
269
 
270
+ if timeout is not None and (time() - t0) > timeout:
271
+ raise TimeoutError(f"Data asset {data_asset.id} was not ready within {timeout} seconds")
272
+
254
273
  sleep(polling_interval)
255
274
 
256
275
  def delete_data_asset(self, data_asset_id: str):
File without changes
File without changes
File without changes
File without changes
File without changes