codeocean 0.12.0__tar.gz → 0.13.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.
- {codeocean-0.12.0 → codeocean-0.13.0}/CHANGELOG.md +4 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/PKG-INFO +1 -1
- {codeocean-0.12.0 → codeocean-0.13.0}/RELEASE.md +1 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/examples/run_pipeline.py +1 -1
- {codeocean-0.12.0 → codeocean-0.13.0}/pyproject.toml +1 -1
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/capsule.py +10 -30
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/client.py +3 -1
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/computation.py +3 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/data_asset.py +17 -0
- codeocean-0.13.0/src/codeocean/pipeline.py +71 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/.flake8 +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/.gitignore +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/LICENSE +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/README.md +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/examples/create_data_asset.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/examples/run_capsule.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/__init__.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/components.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/custom_metadata.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/enum.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/error.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/folder.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/tests/__init__.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/tests/test_client.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/tests/test_error.py +0 -0
- {codeocean-0.12.0 → codeocean-0.13.0}/tests/test_package.py +0 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
CHANGELOG
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
## 0.13.0 (2026-01-23)
|
|
5
|
+
- [#64](https://github.com/codeocean/codeocean-sdk-python/pull/64) feat: Code Ocean version 4.1 functionality
|
|
6
|
+
- **Minimum Code Ocean platform version updated to `4.1.0`.**
|
|
7
|
+
|
|
4
8
|
## 0.12.0 (2025-10-20)
|
|
5
9
|
- [#61](https://github.com/codeocean/codeocean-sdk-python/pull/61) feat: add search_pipelines method
|
|
6
10
|
|
|
@@ -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:
|
|
@@ -470,33 +470,34 @@ class Capsules:
|
|
|
470
470
|
"""Client for interacting with Code Ocean capsule APIs."""
|
|
471
471
|
|
|
472
472
|
client: BaseUrlSession
|
|
473
|
+
_route: str = "capsules"
|
|
473
474
|
|
|
474
475
|
def get_capsule(self, capsule_id: str) -> Capsule:
|
|
475
476
|
"""Retrieve metadata for a specific capsule by its ID."""
|
|
476
|
-
res = self.client.get(f"
|
|
477
|
+
res = self.client.get(f"{self._route}/{capsule_id}")
|
|
477
478
|
|
|
478
479
|
return Capsule.from_dict(res.json())
|
|
479
480
|
|
|
480
481
|
def delete_capsule(self, capsule_id: str):
|
|
481
482
|
"""Delete a capsule permanently."""
|
|
482
|
-
self.client.delete(f"
|
|
483
|
+
self.client.delete(f"{self._route}/{capsule_id}")
|
|
483
484
|
|
|
484
485
|
def get_capsule_app_panel(self, capsule_id: str, version: Optional[int] = None) -> AppPanel:
|
|
485
486
|
"""Retrieve app panel information for a specific capsule by its ID."""
|
|
486
|
-
res = self.client.get(f"
|
|
487
|
+
res = self.client.get(f"{self._route}/{capsule_id}/app_panel", params={"version": version} if version else None)
|
|
487
488
|
|
|
488
489
|
return AppPanel.from_dict(res.json())
|
|
489
490
|
|
|
490
491
|
def list_computations(self, capsule_id: str) -> list[Computation]:
|
|
491
492
|
"""Get all computations associated with a specific capsule."""
|
|
492
|
-
res = self.client.get(f"
|
|
493
|
+
res = self.client.get(f"{self._route}/{capsule_id}/computations")
|
|
493
494
|
|
|
494
495
|
return [Computation.from_dict(c) for c in res.json()]
|
|
495
496
|
|
|
496
497
|
def update_permissions(self, capsule_id: str, permissions: Permissions):
|
|
497
498
|
"""Update permissions for a capsule."""
|
|
498
499
|
self.client.post(
|
|
499
|
-
f"
|
|
500
|
+
f"{self._route}/{capsule_id}/permissions",
|
|
500
501
|
json=permissions.to_dict(),
|
|
501
502
|
)
|
|
502
503
|
|
|
@@ -507,7 +508,7 @@ class Capsules:
|
|
|
507
508
|
) -> list[DataAssetAttachResults]:
|
|
508
509
|
"""Attach one or more data assets to a capsule with optional mount paths."""
|
|
509
510
|
res = self.client.post(
|
|
510
|
-
f"
|
|
511
|
+
f"{self._route}/{capsule_id}/data_assets",
|
|
511
512
|
json=[j.to_dict() for j in attach_params],
|
|
512
513
|
)
|
|
513
514
|
|
|
@@ -516,21 +517,21 @@ class Capsules:
|
|
|
516
517
|
def detach_data_assets(self, capsule_id: str, data_assets: list[str]):
|
|
517
518
|
"""Detach one or more data assets from a capsule by their IDs."""
|
|
518
519
|
self.client.delete(
|
|
519
|
-
f"
|
|
520
|
+
f"{self._route}/{capsule_id}/data_assets/",
|
|
520
521
|
json=data_assets,
|
|
521
522
|
)
|
|
522
523
|
|
|
523
524
|
def archive_capsule(self, capsule_id: str, archive: bool):
|
|
524
525
|
"""Archive or unarchive a capsule to control its visibility and accessibility."""
|
|
525
526
|
self.client.patch(
|
|
526
|
-
f"
|
|
527
|
+
f"{self._route}/{capsule_id}/archive",
|
|
527
528
|
params={"archive": archive},
|
|
528
529
|
)
|
|
529
530
|
|
|
530
531
|
def search_capsules(self, search_params: CapsuleSearchParams) -> CapsuleSearchResults:
|
|
531
532
|
"""Search for capsules with filtering, sorting, and pagination
|
|
532
533
|
options."""
|
|
533
|
-
res = self.client.post("
|
|
534
|
+
res = self.client.post(f"{self._route}/search", json=search_params.to_dict())
|
|
534
535
|
|
|
535
536
|
return CapsuleSearchResults.from_dict(res.json())
|
|
536
537
|
|
|
@@ -547,24 +548,3 @@ class Capsules:
|
|
|
547
548
|
return
|
|
548
549
|
|
|
549
550
|
params["next_token"] = response.next_token
|
|
550
|
-
|
|
551
|
-
def search_pipelines(self, search_params: CapsuleSearchParams) -> CapsuleSearchResults:
|
|
552
|
-
"""Search for pipelines with filtering, sorting, and pagination
|
|
553
|
-
options."""
|
|
554
|
-
res = self.client.post("pipelines/search", json=search_params.to_dict())
|
|
555
|
-
|
|
556
|
-
return CapsuleSearchResults.from_dict(res.json())
|
|
557
|
-
|
|
558
|
-
def search_pipelines_iterator(self, search_params: CapsuleSearchParams) -> Iterator[Capsule]:
|
|
559
|
-
"""Iterate through all pipelines matching search criteria with automatic pagination."""
|
|
560
|
-
params = search_params.to_dict()
|
|
561
|
-
while True:
|
|
562
|
-
response = self.search_pipelines(search_params=CapsuleSearchParams(**params))
|
|
563
|
-
|
|
564
|
-
for result in response.results:
|
|
565
|
-
yield result
|
|
566
|
-
|
|
567
|
-
if not response.has_more:
|
|
568
|
-
return
|
|
569
|
-
|
|
570
|
-
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.
|
|
41
|
+
MIN_SERVER_VERSION = "4.1.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:
|
|
@@ -300,6 +300,12 @@ class AWSS3Source:
|
|
|
300
300
|
"description": "The S3 bucket from which the data asset will be created",
|
|
301
301
|
},
|
|
302
302
|
)
|
|
303
|
+
endpoint_name: Optional[str] = field(
|
|
304
|
+
default=None,
|
|
305
|
+
metadata={
|
|
306
|
+
"description": "The name of the custom S3 endpoint where the bucket is stored",
|
|
307
|
+
},
|
|
308
|
+
)
|
|
303
309
|
prefix: Optional[str] = field(
|
|
304
310
|
default=None,
|
|
305
311
|
metadata={
|
|
@@ -318,6 +324,13 @@ class AWSS3Source:
|
|
|
318
324
|
"description": "When true, Code Ocean will access the source bucket without credentials",
|
|
319
325
|
},
|
|
320
326
|
)
|
|
327
|
+
use_input_bucket: Optional[bool] = field(
|
|
328
|
+
default=None,
|
|
329
|
+
metadata={
|
|
330
|
+
"description": "When true, Code Ocean will try to create the dataset from an internal "
|
|
331
|
+
"input bucket. All properties are ignored except for prefix. Only allowed to Admin users.",
|
|
332
|
+
},
|
|
333
|
+
)
|
|
321
334
|
|
|
322
335
|
|
|
323
336
|
@dataclass_json
|
|
@@ -396,6 +409,10 @@ class AWSS3Target:
|
|
|
396
409
|
bucket: str = field(
|
|
397
410
|
metadata={"description": "The S3 bucket where the data asset will be stored"},
|
|
398
411
|
)
|
|
412
|
+
endpoint_name: Optional[str] = field(
|
|
413
|
+
default=None,
|
|
414
|
+
metadata={"description": "The name of the custom S3 endpoint where the bucket is stored"},
|
|
415
|
+
)
|
|
399
416
|
prefix: Optional[str] = field(
|
|
400
417
|
default=None,
|
|
401
418
|
metadata={
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Iterator
|
|
5
|
+
from requests_toolbelt.sessions import BaseUrlSession
|
|
6
|
+
|
|
7
|
+
from codeocean.capsule import (
|
|
8
|
+
Capsule,
|
|
9
|
+
Capsules,
|
|
10
|
+
CapsuleSearchParams,
|
|
11
|
+
CapsuleSearchResults,
|
|
12
|
+
AppPanel,
|
|
13
|
+
)
|
|
14
|
+
from codeocean.components import Permissions
|
|
15
|
+
from codeocean.computation import Computation
|
|
16
|
+
from codeocean.data_asset import DataAssetAttachParams, DataAssetAttachResults
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Pipelines:
|
|
21
|
+
"""Client for interacting with Code Ocean pipeline APIs."""
|
|
22
|
+
|
|
23
|
+
client: BaseUrlSession
|
|
24
|
+
_capsules: Capsules = field(init=False, repr=False)
|
|
25
|
+
|
|
26
|
+
def __post_init__(self):
|
|
27
|
+
self._capsules = Capsules(client=self.client, _route="pipelines")
|
|
28
|
+
|
|
29
|
+
def get_pipeline(self, pipeline_id: str) -> Capsule:
|
|
30
|
+
"""Retrieve metadata for a specific pipeline by its ID."""
|
|
31
|
+
return self._capsules.get_capsule(pipeline_id)
|
|
32
|
+
|
|
33
|
+
def delete_pipeline(self, pipeline_id: str):
|
|
34
|
+
"""Delete a pipeline permanently."""
|
|
35
|
+
return self._capsules.delete_capsule(pipeline_id)
|
|
36
|
+
|
|
37
|
+
def get_pipeline_app_panel(self, pipeline_id: str, version: int | None = None) -> AppPanel:
|
|
38
|
+
"""Retrieve app panel information for a specific pipeline by its ID."""
|
|
39
|
+
return self._capsules.get_capsule_app_panel(pipeline_id, version)
|
|
40
|
+
|
|
41
|
+
def list_computations(self, pipeline_id: str) -> list[Computation]:
|
|
42
|
+
"""Get all computations associated with a specific pipeline."""
|
|
43
|
+
return self._capsules.list_computations(pipeline_id)
|
|
44
|
+
|
|
45
|
+
def update_permissions(self, pipeline_id: str, permissions: Permissions):
|
|
46
|
+
"""Update permissions for a pipeline."""
|
|
47
|
+
return self._capsules.update_permissions(pipeline_id, permissions)
|
|
48
|
+
|
|
49
|
+
def attach_data_assets(
|
|
50
|
+
self,
|
|
51
|
+
pipeline_id: str,
|
|
52
|
+
attach_params: list[DataAssetAttachParams],
|
|
53
|
+
) -> list[DataAssetAttachResults]:
|
|
54
|
+
"""Attach one or more data assets to a pipeline with optional mount paths."""
|
|
55
|
+
return self._capsules.attach_data_assets(pipeline_id, attach_params)
|
|
56
|
+
|
|
57
|
+
def detach_data_assets(self, pipeline_id: str, data_assets: list[str]):
|
|
58
|
+
"""Detach one or more data assets from a pipeline by their IDs."""
|
|
59
|
+
return self._capsules.detach_data_assets(pipeline_id, data_assets)
|
|
60
|
+
|
|
61
|
+
def archive_pipeline(self, pipeline_id: str, archive: bool):
|
|
62
|
+
"""Archive or unarchive a pipeline to control its visibility and accessibility."""
|
|
63
|
+
return self._capsules.archive_capsule(pipeline_id, archive)
|
|
64
|
+
|
|
65
|
+
def search_pipelines(self, search_params: CapsuleSearchParams) -> CapsuleSearchResults:
|
|
66
|
+
"""Search for pipelines with filtering, sorting, and pagination options."""
|
|
67
|
+
return self._capsules.search_capsules(search_params)
|
|
68
|
+
|
|
69
|
+
def search_pipelines_iterator(self, search_params: CapsuleSearchParams) -> Iterator[Capsule]:
|
|
70
|
+
"""Iterate through all pipelines matching search criteria with automatic pagination."""
|
|
71
|
+
return self._capsules.search_capsules_iterator(search_params)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|