coiled 1.135.4.dev4__tar.gz → 1.135.4.dev18__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 (104) hide show
  1. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/PKG-INFO +1 -1
  2. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/batch.py +36 -18
  3. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/batch/run.py +14 -2
  4. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/filestore.py +38 -4
  5. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/.gitignore +0 -0
  6. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/LICENSE +0 -0
  7. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/README.md +0 -0
  8. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/__init__.py +0 -0
  9. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/__main__.py +0 -0
  10. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/analytics.py +0 -0
  11. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/auth.py +0 -0
  12. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/capture_environment.py +0 -0
  13. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/__init__.py +0 -0
  14. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/batch/__init__.py +0 -0
  15. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/batch/list.py +0 -0
  16. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/batch/logs.py +0 -0
  17. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/batch/status.py +0 -0
  18. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/batch/util.py +0 -0
  19. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/batch/wait.py +0 -0
  20. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/__init__.py +0 -0
  21. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/azure_logs.py +0 -0
  22. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/better_logs.py +0 -0
  23. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/crud.py +0 -0
  24. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/get_address.py +0 -0
  25. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/list.py +0 -0
  26. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/logs.py +0 -0
  27. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/metrics.py +0 -0
  28. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/ssh.py +0 -0
  29. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/cluster/utils.py +0 -0
  30. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/config.py +0 -0
  31. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/core.py +0 -0
  32. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/curl.py +0 -0
  33. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/diagnostics.py +0 -0
  34. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/env.py +0 -0
  35. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/file.py +0 -0
  36. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/__init__.py +0 -0
  37. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/examples/__init__.py +0 -0
  38. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/examples/exit.py +0 -0
  39. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/examples/hello_world.py +0 -0
  40. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/examples/nyc_parquet.py +0 -0
  41. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/examples/pytorch.py +0 -0
  42. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/examples/xarray_nwm.py +0 -0
  43. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/hello.py +0 -0
  44. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/scripts/fill_ipython.py +0 -0
  45. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/scripts/nyc_parquet.py +0 -0
  46. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/scripts/pytorch.py +0 -0
  47. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/scripts/xarray_nwm.py +0 -0
  48. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/hello/utils.py +0 -0
  49. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/login.py +0 -0
  50. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/mpi.py +0 -0
  51. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/notebook/__init__.py +0 -0
  52. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/notebook/notebook.py +0 -0
  53. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/package_sync.py +0 -0
  54. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/prefect.py +0 -0
  55. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/prefect_serve.py +0 -0
  56. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/run.py +0 -0
  57. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/__init__.py +0 -0
  58. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/amp.py +0 -0
  59. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/aws.py +0 -0
  60. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/azure.py +0 -0
  61. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/entry.py +0 -0
  62. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/gcp.py +0 -0
  63. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/prometheus.py +0 -0
  64. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/setup/util.py +0 -0
  65. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/sync.py +0 -0
  66. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cli/utils.py +0 -0
  67. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/cluster.py +0 -0
  68. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/coiled.yaml +0 -0
  69. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/compatibility.py +0 -0
  70. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/config.py +0 -0
  71. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/context.py +0 -0
  72. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/core.py +0 -0
  73. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/credentials/__init__.py +0 -0
  74. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/credentials/aws.py +0 -0
  75. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/credentials/google.py +0 -0
  76. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/errors.py +0 -0
  77. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/exceptions.py +0 -0
  78. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/extensions/__init__.py +0 -0
  79. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/extensions/prefect/__init__.py +0 -0
  80. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/extensions/prefect/runners.py +0 -0
  81. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/extensions/prefect/workers.py +0 -0
  82. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/function.py +0 -0
  83. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/plugins.py +0 -0
  84. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/prefect.py +0 -0
  85. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/pypi_conda_map.py +0 -0
  86. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/scan.py +0 -0
  87. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/software.py +0 -0
  88. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/software_utils.py +0 -0
  89. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/spans.py +0 -0
  90. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/spark.py +0 -0
  91. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/types.py +0 -0
  92. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/utils.py +0 -0
  93. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/__init__.py +0 -0
  94. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/cluster.py +0 -0
  95. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/cluster_comms.py +0 -0
  96. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/core.py +0 -0
  97. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/cwi_log_link.py +0 -0
  98. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/states.py +0 -0
  99. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/widgets/__init__.py +0 -0
  100. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/widgets/interface.py +0 -0
  101. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/widgets/rich.py +0 -0
  102. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/v2/widgets/util.py +0 -0
  103. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/coiled/websockets.py +0 -0
  104. {coiled-1.135.4.dev4 → coiled-1.135.4.dev18}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: coiled
3
- Version: 1.135.4.dev4
3
+ Version: 1.135.4.dev18
4
4
  Summary: Python client for coiled.io dask clusters
5
5
  Project-URL: Homepage, https://coiled.io
6
6
  Maintainer-email: Coiled <info@coiled.io>
@@ -2,8 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import shlex
4
4
  import time
5
+ from contextlib import nullcontext
5
6
 
6
7
  from aiohttp import ServerDisconnectedError
8
+ from typing_extensions import Literal
7
9
 
8
10
  import coiled
9
11
  from coiled.cli.curl import sync_request
@@ -15,6 +17,7 @@ def run(
15
17
  *,
16
18
  name: str | None = None,
17
19
  workspace: str | None = None,
20
+ cloud: coiled.Cloud[Literal[False]] | None = None,
18
21
  software: str | None = None,
19
22
  container: str | None = None,
20
23
  run_on_host: bool | None = None,
@@ -64,6 +67,11 @@ def run(
64
67
 
65
68
  Additional Parameters
66
69
  ---------------------
70
+ cloud
71
+ Authenticated Coiled Cloud client to use for this submission. If not
72
+ provided, a client is created from the current Coiled configuration.
73
+ The client's default workspace is used. ``cloud`` and ``workspace``
74
+ are mutually exclusive.
67
75
  map_over_task_var_dicts
68
76
  takes a list of dictionaries, so you can specify multiple environment variables for each task.
69
77
  For example, ``[{"FOO": 1, "BAR": 2}, {"FOO": 3, "BAR": 4}]`` will pass ``FOO=1 BAR=2`` to one task and
@@ -81,6 +89,9 @@ def run(
81
89
  tag = dict_to_key_val_list(tag)
82
90
  vm_type = [vm_type] if isinstance(vm_type, str) else vm_type
83
91
 
92
+ if cloud is not None and workspace is not None:
93
+ raise ValueError("cloud and workspace are mutually exclusive")
94
+
84
95
  kwargs = dict(
85
96
  name=name,
86
97
  command=command,
@@ -144,25 +155,32 @@ def run(
144
155
  **default_kwargs,
145
156
  }
146
157
 
147
- success = True
148
- exception = None
149
- try:
150
- return _batch_run(default_kwargs, **kwargs)
151
- except Exception as e:
152
- success = False
153
- exception = e
154
- raise
155
- finally:
156
- coiled.add_interaction(
157
- "coiled-batch-python",
158
- success=success,
159
- **error_info_for_tracking(exception),
160
- )
161
-
162
-
163
- def wait_for_job_done(job_id: int, timeout: int | None = None) -> str | None:
158
+ with coiled.Cloud(workspace=workspace) if cloud is None else nullcontext(cloud) as cloud:
159
+ success = True
160
+ exception = None
161
+ try:
162
+ return _batch_run(default_kwargs, cloud=cloud, **kwargs)
163
+ except Exception as e:
164
+ success = False
165
+ exception = e
166
+ raise
167
+ finally:
168
+ tracking_info = error_info_for_tracking(exception)
169
+ _ = cloud.add_interaction(
170
+ action="coiled-batch-python",
171
+ success=success,
172
+ error_message=tracking_info.pop("error_message", None),
173
+ additional_data=tracking_info or None,
174
+ )
175
+
176
+
177
+ def wait_for_job_done(
178
+ job_id: int,
179
+ timeout: int | None = None,
180
+ cloud: coiled.Cloud[Literal[False]] | None = None,
181
+ ) -> str | None:
164
182
  timeout_at = time.monotonic() + timeout if timeout is not None else None
165
- with coiled.Cloud() as cloud:
183
+ with coiled.Cloud() if cloud is None else nullcontext(cloud) as cloud:
166
184
  url = f"{cloud.server}/api/v2/jobs/{job_id}"
167
185
  while timeout_at is None or time.monotonic() < timeout_at:
168
186
  try:
@@ -7,12 +7,14 @@ import logging
7
7
  import os
8
8
  import re
9
9
  import shlex
10
+ from contextlib import nullcontext
10
11
 
11
12
  import click
12
13
  import dask.config
13
14
  from dask.utils import format_bytes, format_time, parse_timedelta
14
15
  from rich.console import Console
15
16
  from rich.panel import Panel
17
+ from typing_extensions import Literal
16
18
 
17
19
  import coiled
18
20
  from coiled.cli.batch.util import load_sidecar_spec
@@ -524,7 +526,13 @@ def batch_run_cli(ctx, **kwargs):
524
526
  )
525
527
 
526
528
 
527
- def _batch_run(default_kwargs, logger=None, from_cli=False, **kwargs) -> dict:
529
+ def _batch_run(
530
+ default_kwargs,
531
+ logger=None,
532
+ from_cli=False,
533
+ cloud: coiled.Cloud[Literal[False]] | None = None,
534
+ **kwargs,
535
+ ) -> dict:
528
536
  command = kwargs["command"]
529
537
  user_files = []
530
538
 
@@ -868,8 +876,9 @@ def _batch_run(default_kwargs, logger=None, from_cli=False, **kwargs) -> dict:
868
876
  "max_retries": kwargs.get("max_retries") or 0,
869
877
  }
870
878
 
871
- with coiled.Cloud(workspace=kwargs["workspace"]) as cloud:
879
+ with coiled.Cloud(workspace=kwargs["workspace"]) if cloud is None else nullcontext(cloud) as cloud:
872
880
  job_spec["workspace"] = cloud.default_workspace
881
+ cluster_kwargs["workspace"] = cloud.default_workspace
873
882
 
874
883
  compressed_data = gzip.compress(json.dumps(job_spec).encode())
875
884
  if len(compressed_data) > 2_400_000:
@@ -914,6 +923,7 @@ def _batch_run(default_kwargs, logger=None, from_cli=False, **kwargs) -> dict:
914
923
  names=[in_fs_name, out_fs_name],
915
924
  workspace=job_spec["workspace"],
916
925
  region=kwargs["region"],
926
+ cloud=cloud,
917
927
  )
918
928
 
919
929
  in_fs = filestores[0]
@@ -929,6 +939,7 @@ def _batch_run(default_kwargs, logger=None, from_cli=False, **kwargs) -> dict:
929
939
  fs=in_fs,
930
940
  local_dir=kwargs.get("local_upload_path") or kwargs.get("local_sync_path"),
931
941
  file_buffers=kwargs.get("buffers_to_upload"),
942
+ cloud=cloud,
932
943
  )
933
944
 
934
945
  # Run the job on a cluster
@@ -944,6 +955,7 @@ def _batch_run(default_kwargs, logger=None, from_cli=False, **kwargs) -> dict:
944
955
  FilestoreManager.attach_filestores_to_cluster(
945
956
  cluster_id=cluster.cluster_id,
946
957
  attachments=filestores_to_attach,
958
+ cloud=cloud,
947
959
  )
948
960
 
949
961
  if logger:
@@ -3,13 +3,16 @@ from __future__ import annotations
3
3
  import io
4
4
  import os
5
5
  import time
6
+ from contextlib import nullcontext
6
7
  from pathlib import Path
8
+ from typing import Any
7
9
 
8
10
  import httpx
9
11
  from rich.align import Align
10
12
  from rich.console import Group
11
13
  from rich.prompt import Confirm
12
14
  from rich.status import Status
15
+ from typing_extensions import Literal
13
16
 
14
17
  import coiled
15
18
  from coiled.cli.curl import sync_request
@@ -111,7 +114,7 @@ def download_from_filestore_with_ui(fs, into=".", name_includes=None):
111
114
  )
112
115
 
113
116
 
114
- def upload_to_filestore_with_ui(fs, local_dir, file_buffers=None):
117
+ def upload_to_filestore_with_ui(fs, local_dir, file_buffers=None, cloud=None):
115
118
  # TODO (future enhancement) send write status
116
119
  # this is tricky because status is stored on the "attachment" object, which might not exist yet
117
120
  # because we want to be able to upload files before cluster has been created
@@ -160,7 +163,7 @@ def upload_to_filestore_with_ui(fs, local_dir, file_buffers=None):
160
163
  ])
161
164
 
162
165
  # files_for_upload is type list[dict] where each dict has "relative_path" key
163
- upload_info = FilestoreManager.get_signed_upload_urls(fs["id"], files_for_upload=files)
166
+ upload_info = FilestoreManager.get_signed_upload_urls(fs["id"], files_for_upload=files, cloud=cloud)
164
167
 
165
168
  upload_urls = upload_info.get("urls")
166
169
  existing_blobs = upload_info.get("existing")
@@ -440,10 +443,41 @@ class FilestoreManagerWithoutHttp:
440
443
  class FilestoreManager(FilestoreManagerWithoutHttp):
441
444
  http2 = True
442
445
 
446
+ @classmethod
447
+ def get_or_create_filestores(cls, names, workspace, region, cloud=None):
448
+ return cls.make_req(
449
+ "/api/v2/filestore/list",
450
+ post=True,
451
+ data={"names": names, "workspace": workspace, "region": region},
452
+ cloud=cloud,
453
+ ).get("filestores")
454
+
455
+ @classmethod
456
+ def get_signed_upload_urls(cls, fs_id, files_for_upload, cloud=None):
457
+ paths = [f["relative_path"] for f in files_for_upload] # relative paths
458
+ return cls.make_req(
459
+ f"/api/v2/filestore/fs/{fs_id}/signed-urls/upload",
460
+ post=True,
461
+ data={"paths": paths},
462
+ cloud=cloud,
463
+ )
464
+
465
+ @classmethod
466
+ def attach_filestores_to_cluster(cls, cluster_id, attachments, cloud=None):
467
+ return cls.make_req(
468
+ "/api/v2/filestore/attach",
469
+ post=True,
470
+ data={
471
+ "cluster_id": cluster_id,
472
+ "attachments": attachments,
473
+ },
474
+ cloud=cloud,
475
+ )
476
+
443
477
  @staticmethod
444
- def make_req(api_path, post=False, data=None):
478
+ def make_req(api_path, post=False, data=None, cloud: coiled.Cloud[Literal[False]] | None = None) -> Any:
445
479
  workspace = (data or {}).get("workspace")
446
- with coiled.Cloud(workspace=workspace) as cloud:
480
+ with coiled.Cloud(workspace=workspace) if cloud is None else nullcontext(cloud) as cloud:
447
481
  url = f"{cloud.server}{api_path}"
448
482
  response = sync_request(
449
483
  cloud=cloud,
File without changes
File without changes
File without changes