modAL 1.2.5.dev24__py3-none-any.whl → 1.2.5.dev26__py3-none-any.whl

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.
modal/cli/shell.py CHANGED
@@ -1,9 +1,9 @@
1
1
  # Copyright Modal Labs 2022
2
+ import inspect
2
3
  import platform
3
4
  import shlex
4
- from functools import partial
5
5
  from pathlib import Path, PurePosixPath
6
- from typing import Optional
6
+ from typing import Any, Callable, Optional
7
7
 
8
8
  import typer
9
9
  from click import ClickException
@@ -16,8 +16,10 @@ from ..functions import Function
16
16
  from ..image import Image
17
17
  from ..mount import _Mount
18
18
  from ..runner import interactive_shell
19
+ from ..sandbox import Sandbox
19
20
  from ..secret import Secret
20
21
  from ..volume import Volume
22
+ from .container import exec
21
23
  from .import_refs import (
22
24
  MethodReference,
23
25
  import_and_filter,
@@ -27,6 +29,171 @@ from .run import _get_runnable_list
27
29
  from .utils import ENV_OPTION, is_tty
28
30
 
29
31
 
32
+ def _params_from_signature(
33
+ func: Callable[..., Any],
34
+ ) -> dict[str, typer.models.ParameterInfo]:
35
+ sig = inspect.signature(func)
36
+ params = {param_name: param.default for param_name, param in sig.parameters.items()}
37
+ assert all(isinstance(param, typer.models.ParameterInfo) for param in params.values()), (
38
+ f"All params to {func.__name__} must be of type typer.models.ParameterInfo."
39
+ )
40
+ return params
41
+
42
+
43
+ def _passed_forbidden_args(
44
+ param_objs: dict[str, typer.models.ParameterInfo],
45
+ passed_args: dict[str, Any],
46
+ allowed: Callable[[str], bool],
47
+ ) -> list[str]:
48
+ """Check which forbidden arguments were passed with non-default values."""
49
+ passed_forbidden: list[str] = []
50
+ for param_name, param_obj in param_objs.items():
51
+ if allowed(param_name):
52
+ continue
53
+
54
+ assert param_obj.param_decls is not None, "All params must be typer.models.ParameterInfo, and have param_decls."
55
+
56
+ if passed_args.get(param_name) != param_obj.default:
57
+ passed_forbidden.append("/".join(param_obj.param_decls))
58
+
59
+ return passed_forbidden
60
+
61
+
62
+ def _is_valid_modal_id(ref: str, prefix: str) -> bool:
63
+ assert prefix.endswith("-")
64
+ return ref.startswith(prefix) and len(ref[len(prefix) :]) > 0 and ref[len(prefix) :].isalnum()
65
+
66
+
67
+ def _is_running_container_ref(ref: Optional[str]) -> bool:
68
+ if ref is None:
69
+ return False
70
+ return _is_valid_modal_id(ref, "sb-") or _is_valid_modal_id(ref, "ta-")
71
+
72
+
73
+ def _start_shell_in_running_container(ref: str, cmd: str, pty: bool) -> None:
74
+ if _is_valid_modal_id(ref, "sb-"):
75
+ try:
76
+ sandbox = Sandbox.from_id(ref)
77
+ ref = sandbox._get_task_id()
78
+ except NotFoundError as e:
79
+ raise ClickException(f"Sandbox '{ref}' not found (is it still running?)")
80
+ except Exception as e:
81
+ raise ClickException(f"Error connecting to Sandbox '{ref}': {str(e)}")
82
+
83
+ assert _is_valid_modal_id(ref, "ta-")
84
+ try:
85
+ exec(container_id=ref, command=shlex.split(cmd), pty=pty)
86
+ except NotFoundError as e:
87
+ raise ClickException(f"Container '{ref}' not found (is it still running?)")
88
+ except Exception as e:
89
+ raise ClickException(f"Error connecting to container '{ref}': {str(e)}")
90
+
91
+
92
+ def _function_spec_from_ref(ref: str, use_module_mode: bool) -> _FunctionSpec:
93
+ import_ref = parse_import_ref(ref, use_module_mode=use_module_mode)
94
+ runnable, all_usable_commands = import_and_filter(
95
+ import_ref, base_cmd="modal shell", accept_local_entrypoint=False, accept_webhook=True
96
+ )
97
+ if not runnable:
98
+ help_header = (
99
+ "Specify a Modal function to start a shell session for. E.g.\n"
100
+ f"> modal shell {import_ref.file_or_module}::my_function"
101
+ )
102
+
103
+ if all_usable_commands:
104
+ help_footer = f"The selected module '{import_ref.file_or_module}' has the following choices:\n\n"
105
+ help_footer += _get_runnable_list(all_usable_commands)
106
+ else:
107
+ help_footer = f"The selected module '{import_ref.file_or_module}' has no Modal functions or classes."
108
+
109
+ raise ClickException(f"{help_header}\n\n{help_footer}")
110
+
111
+ if isinstance(runnable, MethodReference):
112
+ # TODO: let users specify a class instead of a method, since they use the same environment
113
+ class_service_function = runnable.cls._get_class_service_function()
114
+ return class_service_function.spec
115
+ elif isinstance(runnable, Function):
116
+ return runnable.spec
117
+
118
+ raise ValueError("Referenced entity is not a Modal Function or Cls")
119
+
120
+
121
+ def _start_shell_from_function_spec(
122
+ app: App,
123
+ cmds: list[str],
124
+ env: str,
125
+ timeout: int,
126
+ function_spec: _FunctionSpec,
127
+ pty: bool,
128
+ ) -> None:
129
+ interactive_shell(
130
+ app,
131
+ cmds=cmds,
132
+ environment_name=env,
133
+ timeout=timeout,
134
+ image=function_spec.image,
135
+ mounts=function_spec.mounts,
136
+ secrets=function_spec.secrets,
137
+ network_file_systems=function_spec.network_file_systems,
138
+ gpu=function_spec.gpus,
139
+ cloud=function_spec.cloud,
140
+ cpu=function_spec.cpu,
141
+ memory=function_spec.memory,
142
+ volumes=function_spec.volumes,
143
+ region=function_spec.scheduler_placement.regions if function_spec.scheduler_placement else None,
144
+ pty=pty,
145
+ proxy=function_spec.proxy,
146
+ )
147
+
148
+
149
+ def _start_shell_from_image(
150
+ app: App,
151
+ cmds: list[str],
152
+ env: str,
153
+ timeout: int,
154
+ modal_image: Optional[Image],
155
+ volume: list[str],
156
+ secret: list[str],
157
+ add_local: list[str],
158
+ cpu: Optional[int],
159
+ memory: Optional[int],
160
+ gpu: Optional[str],
161
+ cloud: Optional[str],
162
+ region: Optional[str],
163
+ pty: bool,
164
+ ) -> None:
165
+ volumes = {f"/mnt/{vol}": Volume.from_name(vol) for vol in volume}
166
+ secrets = [Secret.from_name(s) for s in secret]
167
+
168
+ mounts = []
169
+ for local_path_str in add_local:
170
+ local_path = Path(local_path_str).expanduser().resolve()
171
+ remote_path = PurePosixPath(f"/mnt/{local_path.name}")
172
+
173
+ if local_path.is_dir():
174
+ m = _Mount._from_local_dir(local_path, remote_path=remote_path)
175
+ else:
176
+ m = _Mount._from_local_file(local_path, remote_path=remote_path)
177
+ mounts.append(m)
178
+
179
+ interactive_shell(
180
+ app,
181
+ cmds=cmds,
182
+ environment_name=env,
183
+ timeout=timeout,
184
+ image=modal_image,
185
+ mounts=mounts,
186
+ cpu=cpu,
187
+ memory=memory,
188
+ gpu=gpu,
189
+ cloud=cloud,
190
+ volumes=volumes,
191
+ secrets=secrets,
192
+ region=region.split(",") if region else [],
193
+ pty=pty,
194
+ )
195
+
196
+
30
197
  def shell(
31
198
  ref: Optional[str] = typer.Argument(
32
199
  default=None,
@@ -36,51 +203,59 @@ def shell(
36
203
  ),
37
204
  ),
38
205
  cmd: str = typer.Option("/bin/bash", "-c", "--cmd", help="Command to run inside the Modal image."),
39
- env: str = ENV_OPTION,
206
+ env: Optional[str] = ENV_OPTION,
40
207
  image: Optional[str] = typer.Option(
41
- default=None, help="Container image tag for inside the shell (if not using REF)."
208
+ None, "--image", help="Container image tag for inside the shell (if not using REF)."
42
209
  ),
43
- add_python: Optional[str] = typer.Option(default=None, help="Add Python to the image (if not using REF)."),
210
+ add_python: Optional[str] = typer.Option(None, "--add-python", help="Add Python to the image (if not using REF)."),
44
211
  volume: Optional[list[str]] = typer.Option(
45
- default=None,
212
+ None,
213
+ "--volume",
46
214
  help=(
47
215
  "Name of a `modal.Volume` to mount inside the shell at `/mnt/{name}` (if not using REF)."
48
216
  " Can be used multiple times."
49
217
  ),
50
218
  ),
51
219
  add_local: Optional[list[str]] = typer.Option(
52
- default=None,
220
+ None,
221
+ "--add-local",
53
222
  help=(
54
223
  "Local file or directory to mount inside the shell at `/mnt/{basename}` (if not using REF)."
55
224
  " Can be used multiple times."
56
225
  ),
57
226
  ),
58
227
  secret: Optional[list[str]] = typer.Option(
59
- default=None,
228
+ None,
229
+ "--secret",
60
230
  help=("Name of a `modal.Secret` to mount inside the shell (if not using REF). Can be used multiple times."),
61
231
  ),
62
- cpu: Optional[int] = typer.Option(default=None, help="Number of CPUs to allocate to the shell (if not using REF)."),
232
+ cpu: Optional[int] = typer.Option(
233
+ None, "--cpu", help="Number of CPUs to allocate to the shell (if not using REF)."
234
+ ),
63
235
  memory: Optional[int] = typer.Option(
64
- default=None, help="Memory to allocate for the shell, in MiB (if not using REF)."
236
+ None, "--memory", help="Memory to allocate for the shell, in MiB (if not using REF)."
65
237
  ),
66
238
  gpu: Optional[str] = typer.Option(
67
- default=None,
239
+ None,
240
+ "--gpu",
68
241
  help="GPUs to request for the shell, if any. Examples are `any`, `a10g`, `a100:4` (if not using REF).",
69
242
  ),
70
243
  cloud: Optional[str] = typer.Option(
71
- default=None,
244
+ None,
245
+ "--cloud",
72
246
  help=(
73
247
  "Cloud provider to run the shell on. Possible values are `aws`, `gcp`, `oci`, `auto` (if not using REF)."
74
248
  ),
75
249
  ),
76
250
  region: Optional[str] = typer.Option(
77
- default=None,
251
+ None,
252
+ "--region",
78
253
  help=(
79
254
  "Region(s) to run the container on. "
80
255
  "Can be a single region or a comma-separated list to choose from (if not using REF)."
81
256
  ),
82
257
  ),
83
- pty: Optional[bool] = typer.Option(default=None, help="Run the command using a PTY."),
258
+ pty: Optional[bool] = typer.Option(None, "--pty", help="Run the command using a PTY."),
84
259
  use_module_mode: bool = typer.Option(
85
260
  False, "-m", help="Interpret argument as a Python module path instead of a file/script path"
86
261
  ),
@@ -127,111 +302,74 @@ def shell(
127
302
  modal shell sb-abc123xyz
128
303
  ```
129
304
  """
130
- env = ensure_env(env)
131
-
132
305
  if pty is None:
133
306
  pty = is_tty()
134
307
 
135
308
  if platform.system() == "Windows":
136
309
  raise InvalidError("`modal shell` is currently not supported on Windows")
137
310
 
311
+ param_objs = _params_from_signature(shell)
312
+
313
+ if ref is not None and _is_running_container_ref(ref):
314
+ # We're attaching to an already running container or Sandbox.
315
+ if passed_forbidden := _passed_forbidden_args(
316
+ param_objs, locals(), allowed=lambda p: p in {"cmd", "pty", "ref"}
317
+ ):
318
+ raise ClickException(
319
+ f"Cannot specify container configuration arguments ({', '.join(passed_forbidden)}) "
320
+ f"when attaching to an already running container or Sandbox ('{ref}')."
321
+ )
322
+
323
+ _start_shell_in_running_container(ref, cmd, pty)
324
+ return
325
+
326
+ # We're not attaching to an existing container, so we need to create a new one.
327
+ env = ensure_env(env)
138
328
  app = App("modal shell")
139
329
 
140
- if ref is not None:
141
- # `modal shell` with a sandbox ID gets the task_id, that's then handled by the `ta-*` flow below.
142
- if ref.startswith("sb-") and len(ref[3:]) > 0 and ref[3:].isalnum():
143
- from ..sandbox import Sandbox
144
-
145
- try:
146
- sandbox = Sandbox.from_id(ref)
147
- task_id = sandbox._get_task_id()
148
- ref = task_id
149
- except NotFoundError as e:
150
- raise ClickException(f"Sandbox '{ref}' not found")
151
- except Exception as e:
152
- raise ClickException(f"Error connecting to sandbox '{ref}': {str(e)}")
153
-
154
- # `modal shell` with a container ID is a special case, alias for `modal container exec`.
155
- if ref.startswith("ta-") and len(ref[3:]) > 0 and ref[3:].isalnum():
156
- from .container import exec
157
-
158
- exec(container_id=ref, command=shlex.split(cmd), pty=pty)
159
- return
160
-
161
- import_ref = parse_import_ref(ref, use_module_mode=use_module_mode)
162
- runnable, all_usable_commands = import_and_filter(
163
- import_ref, base_cmd="modal shell", accept_local_entrypoint=False, accept_webhook=True
164
- )
165
- if not runnable:
166
- help_header = (
167
- "Specify a Modal function to start a shell session for. E.g.\n"
168
- f"> modal shell {import_ref.file_or_module}::my_function"
330
+ # NB: invoking under bash makes --cmd a lot more flexible.
331
+ cmds = shlex.split(f'/bin/bash -c "{cmd}"')
332
+ timeout = 3600
333
+
334
+ if ref is not None and not _is_valid_modal_id(ref, "im-"):
335
+ # If ref it not a Modal Image ID, then it's a function reference, and we'll start a new container from its spec.
336
+ if passed_forbidden := _passed_forbidden_args(
337
+ param_objs, locals(), allowed=lambda p: p in {"cmd", "env", "pty", "ref", "use_module_mode"}
338
+ ):
339
+ raise ClickException(
340
+ f"Cannot specify container configuration arguments ({', '.join(passed_forbidden)}) "
341
+ f"when starting a new container from a function reference ('{ref}')."
169
342
  )
170
343
 
171
- if all_usable_commands:
172
- help_footer = f"The selected module '{import_ref.file_or_module}' has the following choices:\n\n"
173
- help_footer += _get_runnable_list(all_usable_commands)
174
- else:
175
- help_footer = f"The selected module '{import_ref.file_or_module}' has no Modal functions or classes."
176
-
177
- raise ClickException(f"{help_header}\n\n{help_footer}")
178
-
179
- function_spec: _FunctionSpec
180
- if isinstance(runnable, MethodReference):
181
- # TODO: let users specify a class instead of a method, since they use the same environment
182
- class_service_function = runnable.cls._get_class_service_function()
183
- function_spec = class_service_function.spec
184
- elif isinstance(runnable, Function):
185
- function_spec = runnable.spec
186
- else:
187
- raise ValueError("Referenced entity is not a Modal function or class")
188
-
189
- start_shell = partial(
190
- interactive_shell,
191
- image=function_spec.image,
192
- mounts=function_spec.mounts,
193
- secrets=function_spec.secrets,
194
- network_file_systems=function_spec.network_file_systems,
195
- gpu=function_spec.gpus,
196
- cloud=function_spec.cloud,
197
- cpu=function_spec.cpu,
198
- memory=function_spec.memory,
199
- volumes=function_spec.volumes,
200
- region=function_spec.scheduler_placement.regions if function_spec.scheduler_placement else None,
201
- pty=pty,
202
- proxy=function_spec.proxy,
203
- )
344
+ function_spec = _function_spec_from_ref(ref, use_module_mode)
345
+ _start_shell_from_function_spec(app, cmds, env, timeout, function_spec, pty)
346
+ return
347
+
348
+ if ref is not None and _is_valid_modal_id(ref, "im-"):
349
+ if passed_forbidden := _passed_forbidden_args(
350
+ param_objs, locals(), allowed=lambda p: p not in {"add_python", "image"}
351
+ ):
352
+ raise ClickException(
353
+ f"Cannot specify {', '.join(passed_forbidden)} argument(s) "
354
+ f"when starting a new container from a Modal Image ID ('{ref}')."
355
+ )
356
+ modal_image = Image.from_id(ref)
204
357
  else:
205
358
  modal_image = Image.from_registry(image, add_python=add_python) if image else None
206
- volumes = {} if volume is None else {f"/mnt/{vol}": Volume.from_name(vol) for vol in volume}
207
- secrets = [] if secret is None else [Secret.from_name(s) for s in secret]
208
-
209
- mounts = []
210
- if add_local:
211
- for local_path_str in add_local:
212
- local_path = Path(local_path_str).expanduser().resolve()
213
- remote_path = PurePosixPath(f"/mnt/{local_path.name}")
214
-
215
- if local_path.is_dir():
216
- m = _Mount._from_local_dir(local_path, remote_path=remote_path)
217
- else:
218
- m = _Mount._from_local_file(local_path, remote_path=remote_path)
219
- mounts.append(m)
220
-
221
- start_shell = partial(
222
- interactive_shell,
223
- image=modal_image,
224
- mounts=mounts,
225
- cpu=cpu,
226
- memory=memory,
227
- gpu=gpu,
228
- cloud=cloud,
229
- volumes=volumes,
230
- secrets=secrets,
231
- region=region.split(",") if region else [],
232
- pty=pty,
233
- )
234
359
 
235
- # NB: invoking under bash makes --cmd a lot more flexible.
236
- cmds = shlex.split(f'/bin/bash -c "{cmd}"')
237
- start_shell(app, cmds=cmds, environment_name=env, timeout=3600)
360
+ _start_shell_from_image(
361
+ app,
362
+ cmds,
363
+ env,
364
+ timeout,
365
+ modal_image,
366
+ volume or [],
367
+ secret or [],
368
+ add_local or [],
369
+ cpu,
370
+ memory,
371
+ gpu,
372
+ cloud,
373
+ region,
374
+ pty,
375
+ )
modal/client.pyi CHANGED
@@ -32,7 +32,7 @@ class _Client:
32
32
  server_url: str,
33
33
  client_type: int,
34
34
  credentials: typing.Optional[tuple[str, str]],
35
- version: str = "1.2.5.dev24",
35
+ version: str = "1.2.5.dev26",
36
36
  ):
37
37
  """mdmd:hidden
38
38
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.2.5.dev24",
166
+ version: str = "1.2.5.dev26",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.5.dev24
3
+ Version: 1.2.5.dev26
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -26,7 +26,7 @@ modal/app.pyi,sha256=nI_Tibj-rI5I7dJFJnczbHvcmVzCvvCih0tMzoQlr_Y,50835
26
26
  modal/billing.py,sha256=zmQ3bcCJlwa4KD1IA_QgdWpm1pn13c-7qfy79iEauYI,195
27
27
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
28
28
  modal/client.py,sha256=k1CqQ_0t4-vHylkqYZgYCnClg-FT0QrTPAKivL01A_k,14354
29
- modal/client.pyi,sha256=JX95R72YyYt-ILECQ0kaLmLTunhGjbW0FE46mNfLLl8,13148
29
+ modal/client.pyi,sha256=TEaW0lozAoWfvbNPNpDbB6_0aMeQiSUNPg7TxH3oC9Q,13148
30
30
  modal/cloud_bucket_mount.py,sha256=cvVj5Rt4nDHoQbTneoJPTaAcnl_Ab_mrsjnnzd0v9OI,6013
31
31
  modal/cloud_bucket_mount.pyi,sha256=vXusGMOUk9HjCs4lhxteuCK4xLQclPfugGWKo7LECEA,7539
32
32
  modal/cls.py,sha256=4T__mtYlQUnkatif1Wnr8Ld9DN_ySydHj2UwhRg89ok,41984
@@ -147,7 +147,7 @@ modal/cli/profile.py,sha256=g8X6tFFK9ccKyu2he9Yu19WLSLNdztzECgmIV__XJFs,3257
147
147
  modal/cli/queues.py,sha256=S5dqk6lEd_2fJolHqoN9iRs6EkeTa0k7KqJWBBGS23k,6170
148
148
  modal/cli/run.py,sha256=Uftci2utrO6HJH4-JS_gRuy-d5Dfpt-PcXbc_JkiRVY,18378
149
149
  modal/cli/secret.py,sha256=zkCztov8c4x8CI8G0EoyieUfv9XLQQtVXV8xeMLK-Os,8044
150
- modal/cli/shell.py,sha256=SllhtMrxrGCjJcS5wlHGFUSRwGoKNepKcPqdfNsISt4,8660
150
+ modal/cli/shell.py,sha256=L5Dw8yeuu9-f6vNlBGbUT4oh9AVSK4GuB5J6m6vYLBk,12613
151
151
  modal/cli/token.py,sha256=NAmQzKBfEHkcldWKeFxAVIqQBoo1RTp7_A4yc7-8qM0,1911
152
152
  modal/cli/utils.py,sha256=aUXDU9_VgcJrGaGRy4bGf4dqwKYXHCpoO27x4m_bpuo,3293
153
153
  modal/cli/volume.py,sha256=wJchJyIEw9lTKiNUNTgR4I3DPYHYJBu50gU_SsecP0w,10877
@@ -160,7 +160,7 @@ modal/experimental/__init__.py,sha256=GOuo3t4-Le0ZVddiHIOw0MQxkdTJge-bVbhDvgnblm
160
160
  modal/experimental/flash.py,sha256=GJZwSr-4OHlP6gCIZdcbsSb8D8WX9xJwMphjwcfsRoU,31262
161
161
  modal/experimental/flash.pyi,sha256=q4a3mdgL0jzATPL2tS7LpuyulYzF1vUSE453S9xc8n8,16965
162
162
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
163
- modal-1.2.5.dev24.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
163
+ modal-1.2.5.dev26.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
164
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
165
165
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
166
166
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -176,22 +176,16 @@ modal_proto/api_pb2_grpc.py,sha256=Hqw9jcbhpr-W6jsfog_tGU55ouZjITxGvA-DGNBqOLA,2
176
176
  modal_proto/api_pb2_grpc.pyi,sha256=QLJ58ANCx147HeGJva58h0MTCLIDs9JmVjrx8bDdwlg,67776
177
177
  modal_proto/modal_api_grpc.py,sha256=MqaBZB2ZqYj6XTIgI_p5dOPjt9gKT4pAJNd_WXR3W84,21295
178
178
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
179
- modal_proto/sandbox_router.proto,sha256=o6LZWekz2h7uAhBlHcu_EqXnc5ptxm-4j_ZUFVGAJFk,5161
180
- modal_proto/sandbox_router_grpc.py,sha256=27daOTX2N5hADDG-5Qnn4Yj3VfekyJwrDUkrQ12mPuU,5004
181
- modal_proto/sandbox_router_pb2.py,sha256=-AuG6ZRjGDjKgS1J4nSleqkS1W0HgpLjs7ahPnYwUHc,10860
182
- modal_proto/sandbox_router_pb2.pyi,sha256=9KzzS0-goV3p12hWZrLUHGzZEgrzpa4AazEUOd_xK9g,14899
183
- modal_proto/sandbox_router_pb2_grpc.py,sha256=8kPFo84rpRHfeRdZuHyoWuuIAphGmjM0s3ygGJSBVck,10006
184
- modal_proto/sandbox_router_pb2_grpc.pyi,sha256=iAr1ornlIEYP7pfrFMrw91-F3PNs7amH6z6J2oCwYxk,3276
185
179
  modal_proto/task_command_router.proto,sha256=xAVsFyIff6Yvp-PoS3kwyag2G73tBht2KtF4CWKeewI,5000
186
180
  modal_proto/task_command_router_grpc.py,sha256=qxuVUaVwLtCUZGHnYYqUHPvzYepEwt_hXaq2KndPwys,5097
187
181
  modal_proto/task_command_router_pb2.py,sha256=_pD2ZpU0bNzhwBdzmLoLyLtAtftI_AgxwnowxvQbtvk,10616
188
182
  modal_proto/task_command_router_pb2.pyi,sha256=EyDgXPLr7alqjXYERV8w_MPuO404x0uCppmSkrfE9IE,14589
189
183
  modal_proto/task_command_router_pb2_grpc.py,sha256=uEQ0HdrCp8v-9bB5yIic9muA8spCShLHY6Bz9cCgOUE,10114
190
184
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=s3Yxsrawdj4nr8vqQqsAxyX6ilWaGbdECy425KKbLIA,3301
191
- modal_version/__init__.py,sha256=SHHQC5xr-U7-6iEfX2brvTGLLIV9ISS6rXBSGfoZDd8,121
185
+ modal_version/__init__.py,sha256=QwcxGoIGazH2S3MlRUTbpYKS43JrnMyY_BDVYdiOW94,121
192
186
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
193
- modal-1.2.5.dev24.dist-info/METADATA,sha256=TRrHXpW966lipczKqKoGqKp5GIWLrjQnyROIhOQPJzA,2490
194
- modal-1.2.5.dev24.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
195
- modal-1.2.5.dev24.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
196
- modal-1.2.5.dev24.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
197
- modal-1.2.5.dev24.dist-info/RECORD,,
187
+ modal-1.2.5.dev26.dist-info/METADATA,sha256=wSCg8IoU-NwUTlR_rZVB9iJciNtLqlhffzil6k9fy8Y,2490
188
+ modal-1.2.5.dev26.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
189
+ modal-1.2.5.dev26.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
190
+ modal-1.2.5.dev26.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
191
+ modal-1.2.5.dev26.dist-info/RECORD,,
modal_version/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.2.5.dev24"
4
+ __version__ = "1.2.5.dev26"
@@ -1,145 +0,0 @@
1
- syntax = "proto3";
2
-
3
- option go_package = "github.com/modal-labs/modal/go/proto";
4
-
5
- import "modal_proto/api.proto";
6
-
7
- package modal.sandbox_router;
8
-
9
- enum SandboxExecStderrConfig {
10
- // The output will be discarded.
11
- SANDBOX_EXEC_STDERR_CONFIG_DEVNULL = 0;
12
- // The output will be streamed to the client.
13
- SANDBOX_EXEC_STDERR_CONFIG_PIPE = 1;
14
- // A special value that can be used to indicate that the stderr stream should
15
- // be merged with the stdout stream.
16
- SANDBOX_EXEC_STDERR_CONFIG_STDOUT = 2;
17
- }
18
-
19
- enum SandboxExecStdioFileDescriptor {
20
- // Read from stdout.
21
- SANDBOX_EXEC_STDIO_FILE_DESCRIPTOR_STDOUT = 0;
22
- // Read from stderr.
23
- SANDBOX_EXEC_STDIO_FILE_DESCRIPTOR_STDERR = 1;
24
- }
25
-
26
- enum SandboxExecStdoutConfig {
27
- // The output will be discarded.
28
- SANDBOX_EXEC_STDOUT_CONFIG_DEVNULL = 0;
29
- // The output will be streamed to the client.
30
- SANDBOX_EXEC_STDOUT_CONFIG_PIPE = 1;
31
- }
32
-
33
- message SandboxExecPollRequest {
34
- // The task ID of the sandbox running the exec'd command.
35
- string task_id = 1;
36
- // The execution ID of the command to wait on.
37
- string exec_id = 2;
38
- }
39
-
40
- // The response to a SandboxExecPollRequest. If the exec'd command has not
41
- // completed, exit_status will be unset.
42
- message SandboxExecPollResponse {
43
- oneof exit_status {
44
- // The exit code of the command.
45
- int32 code = 1;
46
- // The signal that terminated the command.
47
- int32 signal = 2;
48
- }
49
- // TODO(saltzm): Give a way for the user to distinguish between normal exit
50
- // and termination by Modal (due to sandbox timeout, exec exceeded deadline, etc.)
51
- }
52
-
53
- message SandboxExecStartRequest {
54
- // The task ID of the sandbox to execute the command in.
55
- string task_id = 1;
56
- // Execution ID. This ID will be used to identify the execution for other
57
- // requests and ensure exec commands are idempotent.
58
- string exec_id = 2;
59
- // Command arguments to execute.
60
- repeated string command_args= 3;
61
- // Configures how the stdout of the command will be handled.
62
- SandboxExecStdoutConfig stdout_config = 4;
63
- // Configures how the stderr of the command will be handled.
64
- SandboxExecStderrConfig stderr_config = 5;
65
- // Timeout in seconds for the exec'd command to exit. If the command does not
66
- // exit within this duration, the command will be killed. This is NOT the
67
- // timeout for the ExecStartRequest RPC to complete.
68
- optional uint32 timeout_secs = 6;
69
- // Working directory for the command.
70
- optional string workdir = 7;
71
- // Secret IDs to mount into the sandbox.
72
- repeated string secret_ids = 8;
73
- // PTY info for the command.
74
- optional modal.client.PTYInfo pty_info = 9;
75
- // Enable debugging capabilities on the container runtime. Used only for
76
- // internal debugging.
77
- bool runtime_debug = 10;
78
- }
79
-
80
- message SandboxExecStartResponse { }
81
-
82
- message SandboxExecStdinWriteRequest {
83
- // The task ID of the sandbox running the exec'd command.
84
- string task_id = 1;
85
- // The execution ID of the command to write to.
86
- string exec_id = 2;
87
- // The offset to start writing to. This is used to resume writing from the
88
- // last write position if the connection is closed and reopened.
89
- uint64 offset = 3;
90
- bytes data = 4;
91
- // If true, close the stdin stream after writing any provided data.
92
- // This signals EOF to the exec'd process.
93
- bool eof = 5;
94
- }
95
-
96
- message SandboxExecStdinWriteResponse { }
97
-
98
-
99
- message SandboxExecStdioReadRequest {
100
- // The task ID of the sandbox running the exec'd command.
101
- string task_id = 1;
102
- // The execution ID of the command to read from.
103
- string exec_id = 2;
104
- // The offset to start reading from. This is used to resume reading from the
105
- // last read position if the connection is closed and reopened.
106
- uint64 offset = 3;
107
- // Which file descriptor to read from.
108
- SandboxExecStdioFileDescriptor file_descriptor = 4;
109
- }
110
-
111
- message SandboxExecStdioReadResponse {
112
- // The data read from the file descriptor.
113
- bytes data = 1;
114
- }
115
-
116
- message SandboxExecWaitRequest {
117
- // The task ID of the sandbox running the exec'd command.
118
- string task_id = 1;
119
- // The execution ID of the command to wait on.
120
- string exec_id = 2;
121
- }
122
-
123
- message SandboxExecWaitResponse {
124
- oneof exit_status {
125
- // The exit code of the command.
126
- int32 code = 1;
127
- // The signal that terminated the command.
128
- int32 signal = 2;
129
- }
130
- // TODO(saltzm): Give a way for the user to distinguish between normal exit
131
- // and termination by Modal (due to sandbox timeout, exec exceeded deadline, etc.)
132
- }
133
-
134
- service SandboxRouter {
135
- // Poll for the exit status of an exec'd command.
136
- rpc SandboxExecPoll(SandboxExecPollRequest) returns (SandboxExecPollResponse);
137
- // Execute a command in the sandbox.
138
- rpc SandboxExecStart(SandboxExecStartRequest) returns (SandboxExecStartResponse);
139
- // Write to the stdin stream of an exec'd command.
140
- rpc SandboxExecStdinWrite(SandboxExecStdinWriteRequest) returns (SandboxExecStdinWriteResponse);
141
- // Get a stream of output from the stdout or stderr stream of an exec'd command.
142
- rpc SandboxExecStdioRead(SandboxExecStdioReadRequest) returns (stream SandboxExecStdioReadResponse);
143
- // Wait for an exec'd command to exit and return the exit code.
144
- rpc SandboxExecWait(SandboxExecWaitRequest) returns (SandboxExecWaitResponse);
145
- }