modal 0.68.22__py3-none-any.whl → 0.68.25__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/run.py +25 -5
- modal/client.pyi +2 -2
- modal/file_io.py +2 -3
- modal/file_io.pyi +5 -8
- modal/functions.pyi +6 -6
- {modal-0.68.22.dist-info → modal-0.68.25.dist-info}/METADATA +1 -1
- {modal-0.68.22.dist-info → modal-0.68.25.dist-info}/RECORD +15 -15
- modal_proto/api.proto +7 -0
- modal_proto/api_pb2.py +781 -745
- modal_proto/api_pb2.pyi +61 -2
- modal_version/_version_generated.py +1 -1
- {modal-0.68.22.dist-info → modal-0.68.25.dist-info}/LICENSE +0 -0
- {modal-0.68.22.dist-info → modal-0.68.25.dist-info}/WHEEL +0 -0
- {modal-0.68.22.dist-info → modal-0.68.25.dist-info}/entry_points.txt +0 -0
- {modal-0.68.22.dist-info → modal-0.68.25.dist-info}/top_level.txt +0 -0
modal/cli/run.py
CHANGED
@@ -133,6 +133,18 @@ def _get_clean_app_description(func_ref: str) -> str:
|
|
133
133
|
return " ".join(sys.argv)
|
134
134
|
|
135
135
|
|
136
|
+
def _write_local_result(result_path: str, res: Any):
|
137
|
+
if isinstance(res, str):
|
138
|
+
mode = "wt"
|
139
|
+
elif isinstance(res, bytes):
|
140
|
+
mode = "wb"
|
141
|
+
else:
|
142
|
+
res_type = type(res).__name__
|
143
|
+
raise InvalidError(f"Function must return str or bytes when using `--write-result`; got {res_type}.")
|
144
|
+
with open(result_path, mode) as fid:
|
145
|
+
fid.write(res)
|
146
|
+
|
147
|
+
|
136
148
|
def _get_click_command_for_function(app: App, function_tag):
|
137
149
|
function = app.registered_functions.get(function_tag)
|
138
150
|
if not function or (isinstance(function, Function) and function.info.user_cls is not None):
|
@@ -177,7 +189,7 @@ def _get_click_command_for_function(app: App, function_tag):
|
|
177
189
|
interactive=ctx.obj["interactive"],
|
178
190
|
):
|
179
191
|
if cls is None:
|
180
|
-
function.remote(**kwargs)
|
192
|
+
res = function.remote(**kwargs)
|
181
193
|
else:
|
182
194
|
# unpool class and method arguments
|
183
195
|
# TODO(erikbern): this code is a bit hacky
|
@@ -186,7 +198,10 @@ def _get_click_command_for_function(app: App, function_tag):
|
|
186
198
|
|
187
199
|
instance = cls(**cls_kwargs)
|
188
200
|
method: Function = getattr(instance, method_name)
|
189
|
-
method.remote(**fun_kwargs)
|
201
|
+
res = method.remote(**fun_kwargs)
|
202
|
+
|
203
|
+
if result_path := ctx.obj["result_path"]:
|
204
|
+
_write_local_result(result_path, res)
|
190
205
|
|
191
206
|
with_click_options = _add_click_options(f, signature)
|
192
207
|
return click.command(with_click_options)
|
@@ -214,12 +229,15 @@ def _get_click_command_for_local_entrypoint(app: App, entrypoint: LocalEntrypoin
|
|
214
229
|
):
|
215
230
|
try:
|
216
231
|
if isasync:
|
217
|
-
asyncio.run(func(*args, **kwargs))
|
232
|
+
res = asyncio.run(func(*args, **kwargs))
|
218
233
|
else:
|
219
|
-
func(*args, **kwargs)
|
234
|
+
res = func(*args, **kwargs)
|
220
235
|
except Exception as exc:
|
221
236
|
raise _CliUserExecutionError(inspect.getsourcefile(func)) from exc
|
222
237
|
|
238
|
+
if result_path := ctx.obj["result_path"]:
|
239
|
+
_write_local_result(result_path, res)
|
240
|
+
|
223
241
|
with_click_options = _add_click_options(f, _get_signature(func))
|
224
242
|
return click.command(with_click_options)
|
225
243
|
|
@@ -248,12 +266,13 @@ class RunGroup(click.Group):
|
|
248
266
|
cls=RunGroup,
|
249
267
|
subcommand_metavar="FUNC_REF",
|
250
268
|
)
|
269
|
+
@click.option("-w", "--write-result", help="Write return value (which must be str or bytes) to this local path.")
|
251
270
|
@click.option("-q", "--quiet", is_flag=True, help="Don't show Modal progress indicators.")
|
252
271
|
@click.option("-d", "--detach", is_flag=True, help="Don't stop the app if the local process dies or disconnects.")
|
253
272
|
@click.option("-i", "--interactive", is_flag=True, help="Run the app in interactive mode.")
|
254
273
|
@click.option("-e", "--env", help=ENV_OPTION_HELP, default=None)
|
255
274
|
@click.pass_context
|
256
|
-
def run(ctx, detach, quiet, interactive, env):
|
275
|
+
def run(ctx, write_result, detach, quiet, interactive, env):
|
257
276
|
"""Run a Modal function or local entrypoint.
|
258
277
|
|
259
278
|
`FUNC_REF` should be of the format `{file or module}::{function name}`.
|
@@ -284,6 +303,7 @@ def run(ctx, detach, quiet, interactive, env):
|
|
284
303
|
```
|
285
304
|
"""
|
286
305
|
ctx.ensure_object(dict)
|
306
|
+
ctx.obj["result_path"] = write_result
|
287
307
|
ctx.obj["detach"] = detach # if subcommand would be a click command...
|
288
308
|
ctx.obj["show_progress"] = False if quiet else True
|
289
309
|
ctx.obj["interactive"] = interactive
|
modal/client.pyi
CHANGED
@@ -26,7 +26,7 @@ class _Client:
|
|
26
26
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
27
27
|
|
28
28
|
def __init__(
|
29
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.
|
29
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.25"
|
30
30
|
): ...
|
31
31
|
def is_closed(self) -> bool: ...
|
32
32
|
@property
|
@@ -81,7 +81,7 @@ class Client:
|
|
81
81
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
82
82
|
|
83
83
|
def __init__(
|
84
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.
|
84
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.25"
|
85
85
|
): ...
|
86
86
|
def is_closed(self) -> bool: ...
|
87
87
|
@property
|
modal/file_io.py
CHANGED
@@ -367,11 +367,10 @@ class _FileIO(Generic[T]):
|
|
367
367
|
if self._closed:
|
368
368
|
raise ValueError("I/O operation on closed file")
|
369
369
|
|
370
|
-
def
|
371
|
-
self._check_closed()
|
370
|
+
async def __aenter__(self) -> "_FileIO":
|
372
371
|
return self
|
373
372
|
|
374
|
-
async def
|
373
|
+
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
|
375
374
|
await self._close()
|
376
375
|
|
377
376
|
|
modal/file_io.pyi
CHANGED
@@ -48,8 +48,8 @@ class _FileIO(typing.Generic[T]):
|
|
48
48
|
def _check_writable(self) -> None: ...
|
49
49
|
def _check_readable(self) -> None: ...
|
50
50
|
def _check_closed(self) -> None: ...
|
51
|
-
def
|
52
|
-
async def
|
51
|
+
async def __aenter__(self) -> _FileIO: ...
|
52
|
+
async def __aexit__(self, exc_type, exc_value, traceback) -> None: ...
|
53
53
|
|
54
54
|
class __delete_bytes_spec(typing_extensions.Protocol):
|
55
55
|
def __call__(self, file: FileIO, start: typing.Optional[int] = None, end: typing.Optional[int] = None) -> None: ...
|
@@ -177,9 +177,6 @@ class FileIO(typing.Generic[T]):
|
|
177
177
|
def _check_readable(self) -> None: ...
|
178
178
|
def _check_closed(self) -> None: ...
|
179
179
|
def __enter__(self) -> FileIO: ...
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
async def aio(self, exc_type, exc_value, traceback) -> None: ...
|
184
|
-
|
185
|
-
__exit__: ____exit___spec
|
180
|
+
async def __aenter__(self) -> FileIO: ...
|
181
|
+
def __exit__(self, exc_type, exc_value, traceback) -> None: ...
|
182
|
+
async def __aexit__(self, exc_type, exc_value, traceback) -> None: ...
|
modal/functions.pyi
CHANGED
@@ -456,11 +456,11 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
456
456
|
|
457
457
|
_call_generator_nowait: ___call_generator_nowait_spec
|
458
458
|
|
459
|
-
class __remote_spec(typing_extensions.Protocol[
|
459
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
460
460
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
461
461
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
462
462
|
|
463
|
-
remote: __remote_spec[
|
463
|
+
remote: __remote_spec[ReturnType, P]
|
464
464
|
|
465
465
|
class __remote_gen_spec(typing_extensions.Protocol):
|
466
466
|
def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
|
@@ -473,17 +473,17 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
473
473
|
def _get_obj(self) -> typing.Optional[modal.cls.Obj]: ...
|
474
474
|
def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType: ...
|
475
475
|
|
476
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
476
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
477
477
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
478
478
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
479
479
|
|
480
|
-
_experimental_spawn: ___experimental_spawn_spec[
|
480
|
+
_experimental_spawn: ___experimental_spawn_spec[ReturnType, P]
|
481
481
|
|
482
|
-
class __spawn_spec(typing_extensions.Protocol[
|
482
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
483
483
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
484
484
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
485
485
|
|
486
|
-
spawn: __spawn_spec[
|
486
|
+
spawn: __spawn_spec[ReturnType, P]
|
487
487
|
|
488
488
|
def get_raw_f(self) -> typing.Callable[..., typing.Any]: ...
|
489
489
|
|
@@ -19,7 +19,7 @@ modal/app.py,sha256=EJ7FUN6rWnSwLJoYJh8nmKg_t-8hdN8_rt0OrkP7JvQ,46084
|
|
19
19
|
modal/app.pyi,sha256=BE5SlR5tRECuc6-e2lUuOknDdov3zxgZ4N0AsLb5ZVQ,25270
|
20
20
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
21
21
|
modal/client.py,sha256=JAnd4-GCN093BwkvOFAK5a6iy5ycxofjpUncMxlrIMw,15253
|
22
|
-
modal/client.pyi,sha256=
|
22
|
+
modal/client.pyi,sha256=bTCfVc8EDreYA_wdgSRtgtQ-amgHCynC0qCNyZbBZVo,7280
|
23
23
|
modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
|
24
24
|
modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
|
25
25
|
modal/cls.py,sha256=ONnrfZ2vPcaY2JuKypPiBA9eTiyg8Qfg-Ull40nn9zs,30956
|
@@ -33,11 +33,11 @@ modal/environments.py,sha256=5cgA-zbm6ngKLsRA19zSOgtgo9-BarJK3FJK0BiF2Lo,6505
|
|
33
33
|
modal/environments.pyi,sha256=XalNpiPkAtHWAvOU2Cotq0ozmtl-Jv0FDsR8h9mr27Q,3521
|
34
34
|
modal/exception.py,sha256=dRK789TD1HaB63kHhu1yZuvS2vP_Vua3iLMBtA6dgqk,7128
|
35
35
|
modal/experimental.py,sha256=jFuNbwrNHos47viMB9q-cHJSvf2RDxDdoEcss9plaZE,2302
|
36
|
-
modal/file_io.py,sha256=
|
37
|
-
modal/file_io.pyi,sha256=
|
36
|
+
modal/file_io.py,sha256=2eoSYpMMYs-FRCQbU7joFvaObuYz6HtEtLBik1hz5Xw,14616
|
37
|
+
modal/file_io.pyi,sha256=QSWGm35no2ApXiy3olLwUiT7jaNKVxIOz0rXQauCg4M,6897
|
38
38
|
modal/file_pattern_matcher.py,sha256=vX6MjWRGdonE4I8QPdjFUnz6moBjSzvgD6417BNQrW4,4021
|
39
39
|
modal/functions.py,sha256=IIdHw0FNOdoMksG1b2zvkn8f-xskhJu07ZvHMey9iq4,67667
|
40
|
-
modal/functions.pyi,sha256=
|
40
|
+
modal/functions.pyi,sha256=bHbJiWW5TbFKKjDn7bSCFvOcUcAjPFqTStS-NAHPSeM,25068
|
41
41
|
modal/gpu.py,sha256=r4rL6uH3UJIQthzYvfWauXNyh01WqCPtKZCmmSX1fd4,6881
|
42
42
|
modal/image.py,sha256=xQAC1gWOG0L77QfVfAbHfLwfVkvMYi2sy0V_Ah7GWPg,82253
|
43
43
|
modal/image.pyi,sha256=8R8Ac9eZ83ocM_1qrFUvH3rCbI5zRnq-Eq0xaAQq4nI,25105
|
@@ -117,7 +117,7 @@ modal/cli/launch.py,sha256=uyI-ouGvYRjHLGxGQ2lYBZq32BiRT1i0L8ksz5iy7K8,2935
|
|
117
117
|
modal/cli/network_file_system.py,sha256=3QbAxKEoRc6RCMsYE3OS-GcuiI4GMkz_wAKsIBbN1qg,8186
|
118
118
|
modal/cli/profile.py,sha256=rLXfjJObfPNjaZvNfHGIKqs7y9bGYyGe-K7V0w-Ni0M,3110
|
119
119
|
modal/cli/queues.py,sha256=MIh2OsliNE2QeL1erubfsRsNuG4fxqcqWA2vgIfQ4Mg,4494
|
120
|
-
modal/cli/run.py,sha256=
|
120
|
+
modal/cli/run.py,sha256=W9isFpIQTNCxKafVfI_PUjxNrDMl5V8N9y15F3wtPXk,17831
|
121
121
|
modal/cli/secret.py,sha256=uQpwYrMY98iMCWeZOQTcktOYhPTZ8IHnyealDc2CZqo,4206
|
122
122
|
modal/cli/token.py,sha256=mxSgOWakXG6N71hQb1ko61XAR9ZGkTMZD-Txn7gmTac,1924
|
123
123
|
modal/cli/utils.py,sha256=hZmjyzcPjDnQSkLvycZD2LhGdcsfdZshs_rOU78EpvI,3717
|
@@ -146,10 +146,10 @@ modal_global_objects/mounts/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0
|
|
146
146
|
modal_global_objects/mounts/modal_client_package.py,sha256=W0E_yShsRojPzWm6LtIQqNVolapdnrZkm2hVEQuZK_4,767
|
147
147
|
modal_global_objects/mounts/python_standalone.py,sha256=SL_riIxpd8mP4i4CLDCWiFFNj0Ltknm9c_UIGfX5d60,1836
|
148
148
|
modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
149
|
-
modal_proto/api.proto,sha256=
|
149
|
+
modal_proto/api.proto,sha256=K1QnKJ3UuBkOJ4bCsrKqn-kDeitAiaoBK8Lq2Wzs28Y,79552
|
150
150
|
modal_proto/api_grpc.py,sha256=DveC4ejFYEhCLiWbQShnmY31_FWGYU675Bmr7nHhsgs,101342
|
151
|
-
modal_proto/api_pb2.py,sha256=
|
152
|
-
modal_proto/api_pb2.pyi,sha256=
|
151
|
+
modal_proto/api_pb2.py,sha256=HoQkd_G9iIPPcXyBQ_50NJFNXxcckZ6VyzGWdaurkgE,292334
|
152
|
+
modal_proto/api_pb2.pyi,sha256=EqUBkMJr0CKILSvS9AwAAOxlyo26ftZBnMOECfOAzTA,390736
|
153
153
|
modal_proto/api_pb2_grpc.py,sha256=2PEP6JPOoTw2rDC5qYjLNuumP68ZwAouRhCoayisAhY,219162
|
154
154
|
modal_proto/api_pb2_grpc.pyi,sha256=uWtCxVEd0cFpOZ1oOGfZNO7Dv45OP4kp09jMnNyx9D4,51098
|
155
155
|
modal_proto/modal_api_grpc.py,sha256=-8mLby_om5MYo6yu1zA_hqhz0yLsQW7k2YWBVZW1iVs,13546
|
@@ -163,10 +163,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
|
|
163
163
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
164
164
|
modal_version/__init__.py,sha256=RT6zPoOdFO99u5Wcxxaoir4ZCuPTbQ22cvzFAXl3vUY,470
|
165
165
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
166
|
-
modal_version/_version_generated.py,sha256=
|
167
|
-
modal-0.68.
|
168
|
-
modal-0.68.
|
169
|
-
modal-0.68.
|
170
|
-
modal-0.68.
|
171
|
-
modal-0.68.
|
172
|
-
modal-0.68.
|
166
|
+
modal_version/_version_generated.py,sha256=DerEU6zVIc7dqruAOfER6IUbfS1shalQMeQgLAXiaN8,149
|
167
|
+
modal-0.68.25.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
168
|
+
modal-0.68.25.dist-info/METADATA,sha256=xBi2AEEVfybma-KaRqmkx_Kb_ZtwrCLZWCvRnogVckc,2329
|
169
|
+
modal-0.68.25.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
170
|
+
modal-0.68.25.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
171
|
+
modal-0.68.25.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
172
|
+
modal-0.68.25.dist-info/RECORD,,
|
modal_proto/api.proto
CHANGED
@@ -360,6 +360,12 @@ message AppHeartbeatRequest {
|
|
360
360
|
string app_id = 1;
|
361
361
|
}
|
362
362
|
|
363
|
+
message AppLayout {
|
364
|
+
repeated Object object = 1;
|
365
|
+
map<string, string> function_ids = 2; // tag -> function id
|
366
|
+
map<string, string> class_ids = 3; // tag -> class id
|
367
|
+
}
|
368
|
+
|
363
369
|
message AppListRequest {
|
364
370
|
string environment_name = 1;
|
365
371
|
}
|
@@ -710,6 +716,7 @@ message ContainerArguments { // This is used to pass data from the worker to th
|
|
710
716
|
string runtime = 11;
|
711
717
|
string environment_name = 13;
|
712
718
|
optional string checkpoint_id = 14;
|
719
|
+
AppLayout app_layout = 15;
|
713
720
|
}
|
714
721
|
|
715
722
|
message ContainerCheckpointRequest {
|