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.
Files changed (26) hide show
  1. {codeocean-0.12.0 → codeocean-0.13.0}/CHANGELOG.md +4 -0
  2. {codeocean-0.12.0 → codeocean-0.13.0}/PKG-INFO +1 -1
  3. {codeocean-0.12.0 → codeocean-0.13.0}/RELEASE.md +1 -0
  4. {codeocean-0.12.0 → codeocean-0.13.0}/examples/run_pipeline.py +1 -1
  5. {codeocean-0.12.0 → codeocean-0.13.0}/pyproject.toml +1 -1
  6. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/capsule.py +10 -30
  7. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/client.py +3 -1
  8. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/computation.py +3 -0
  9. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/data_asset.py +17 -0
  10. codeocean-0.13.0/src/codeocean/pipeline.py +71 -0
  11. {codeocean-0.12.0 → codeocean-0.13.0}/.flake8 +0 -0
  12. {codeocean-0.12.0 → codeocean-0.13.0}/.gitignore +0 -0
  13. {codeocean-0.12.0 → codeocean-0.13.0}/LICENSE +0 -0
  14. {codeocean-0.12.0 → codeocean-0.13.0}/README.md +0 -0
  15. {codeocean-0.12.0 → codeocean-0.13.0}/examples/create_data_asset.py +0 -0
  16. {codeocean-0.12.0 → codeocean-0.13.0}/examples/run_capsule.py +0 -0
  17. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/__init__.py +0 -0
  18. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/components.py +0 -0
  19. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/custom_metadata.py +0 -0
  20. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/enum.py +0 -0
  21. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/error.py +0 -0
  22. {codeocean-0.12.0 → codeocean-0.13.0}/src/codeocean/folder.py +0 -0
  23. {codeocean-0.12.0 → codeocean-0.13.0}/tests/__init__.py +0 -0
  24. {codeocean-0.12.0 → codeocean-0.13.0}/tests/test_client.py +0 -0
  25. {codeocean-0.12.0 → codeocean-0.13.0}/tests/test_error.py +0 -0
  26. {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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeocean
3
- Version: 0.12.0
3
+ Version: 0.13.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:
@@ -38,7 +38,7 @@ run_params = RunParams(
38
38
  ],
39
39
  )
40
40
 
41
- computation = client.computations.run_capsule(run_params)
41
+ computation = client.computations.run_pipeline(run_params)
42
42
 
43
43
  # Wait for pipeline to finish.
44
44
 
@@ -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.13.0"
8
8
  authors = [
9
9
  { name="Code Ocean", email="dev@codeocean.com" },
10
10
  ]
@@ -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"capsules/{capsule_id}")
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"capsules/{capsule_id}")
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"capsules/{capsule_id}/app_panel", params={"version": version} if version else None)
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"capsules/{capsule_id}/computations")
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"capsules/{capsule_id}/permissions",
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"capsules/{capsule_id}/data_assets",
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"capsules/{capsule_id}/data_assets/",
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"capsules/{capsule_id}/archive",
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("capsules/search", json=search_params.to_dict())
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.0.0"
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:
@@ -274,6 +274,9 @@ class Computations:
274
274
 
275
275
  return Computation.from_dict(res.json())
276
276
 
277
+ # Alias for run_capsule
278
+ run_pipeline = run_capsule
279
+
277
280
  def wait_until_completed(
278
281
  self,
279
282
  computation: Computation,
@@ -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