modal 0.70.2__py3-none-any.whl → 0.71.5__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/_container_entrypoint.py +23 -15
- modal/_runtime/container_io_manager.py +1 -6
- modal/_utils/function_utils.py +10 -1
- modal/app.py +25 -23
- modal/app.pyi +6 -2
- modal/cli/run.py +1 -1
- modal/client.pyi +2 -2
- modal/exception.py +6 -0
- modal/experimental.py +3 -0
- modal/file_io.py +86 -0
- modal/file_io.pyi +59 -0
- modal/functions.py +20 -5
- modal/functions.pyi +8 -8
- modal/io_streams.py +15 -27
- modal/io_streams_helper.py +53 -0
- modal/runner.py +26 -33
- modal/runner.pyi +6 -3
- modal/running_app.py +4 -11
- modal/sandbox.py +19 -6
- modal/sandbox.pyi +25 -0
- {modal-0.70.2.dist-info → modal-0.71.5.dist-info}/METADATA +1 -1
- {modal-0.70.2.dist-info → modal-0.71.5.dist-info}/RECORD +35 -34
- modal_proto/api.proto +13 -0
- modal_proto/api_grpc.py +16 -0
- modal_proto/api_pb2.py +456 -436
- modal_proto/api_pb2.pyi +41 -1
- modal_proto/api_pb2_grpc.py +34 -1
- modal_proto/api_pb2_grpc.pyi +13 -3
- modal_proto/modal_api_grpc.py +1 -0
- modal_version/__init__.py +1 -1
- modal_version/_version_generated.py +2 -2
- {modal-0.70.2.dist-info → modal-0.71.5.dist-info}/LICENSE +0 -0
- {modal-0.70.2.dist-info → modal-0.71.5.dist-info}/WHEEL +0 -0
- {modal-0.70.2.dist-info → modal-0.71.5.dist-info}/entry_points.txt +0 -0
- {modal-0.70.2.dist-info → modal-0.71.5.dist-info}/top_level.txt +0 -0
modal/io_streams.py
CHANGED
@@ -14,7 +14,8 @@ from typing import (
|
|
14
14
|
from grpclib import Status
|
15
15
|
from grpclib.exceptions import GRPCError, StreamTerminatedError
|
16
16
|
|
17
|
-
from modal.exception import
|
17
|
+
from modal.exception import InvalidError
|
18
|
+
from modal.io_streams_helper import consume_stream_with_retries
|
18
19
|
from modal_proto import api_pb2
|
19
20
|
|
20
21
|
from ._utils.async_utils import synchronize_api
|
@@ -176,34 +177,21 @@ class _StreamReader(Generic[T]):
|
|
176
177
|
if self._stream_type == StreamType.DEVNULL:
|
177
178
|
return
|
178
179
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
180
|
+
def item_handler(item: Optional[bytes]):
|
181
|
+
if self._stream_type == StreamType.STDOUT and item is not None:
|
182
|
+
print(item.decode("utf-8"), end="")
|
183
|
+
elif self._stream_type == StreamType.PIPE:
|
184
|
+
self._container_process_buffer.append(item)
|
184
185
|
|
185
|
-
|
186
|
-
|
187
|
-
print(message.decode("utf-8"), end="")
|
188
|
-
elif self._stream_type == StreamType.PIPE:
|
189
|
-
self._container_process_buffer.append(message)
|
190
|
-
if message is None:
|
191
|
-
completed = True
|
192
|
-
break
|
186
|
+
def completion_check(item: Optional[bytes]):
|
187
|
+
return item is None
|
193
188
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
continue
|
201
|
-
elif isinstance(exc, StreamTerminatedError):
|
202
|
-
continue
|
203
|
-
elif isinstance(exc, ClientClosed):
|
204
|
-
# If the client was closed, the user has triggered a cleanup.
|
205
|
-
break
|
206
|
-
raise exc
|
189
|
+
iterator = _container_process_logs_iterator(self._object_id, self._file_descriptor, self._client)
|
190
|
+
await consume_stream_with_retries(
|
191
|
+
iterator,
|
192
|
+
item_handler,
|
193
|
+
completion_check,
|
194
|
+
)
|
207
195
|
|
208
196
|
async def _stream_container_process(self) -> AsyncGenerator[tuple[Optional[bytes], str], None]:
|
209
197
|
"""Streams the container process buffer to the reader."""
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright Modal Labs 2024
|
2
|
+
import asyncio
|
3
|
+
from typing import AsyncIterator, Callable, TypeVar
|
4
|
+
|
5
|
+
from grpclib.exceptions import GRPCError, StreamTerminatedError
|
6
|
+
|
7
|
+
from modal.exception import ClientClosed
|
8
|
+
|
9
|
+
from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES
|
10
|
+
|
11
|
+
T = TypeVar("T")
|
12
|
+
|
13
|
+
|
14
|
+
async def consume_stream_with_retries(
|
15
|
+
stream: AsyncIterator[T],
|
16
|
+
item_handler: Callable[[T], None],
|
17
|
+
completion_check: Callable[[T], bool],
|
18
|
+
max_retries: int = 10,
|
19
|
+
retry_delay: float = 1.0,
|
20
|
+
) -> None:
|
21
|
+
"""mdmd:hidden
|
22
|
+
Helper function to consume a stream with retry logic for transient errors.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
stream_generator: Function that returns an AsyncIterator to consume
|
26
|
+
item_handler: Callback function to handle each item from the stream
|
27
|
+
completion_check: Callback function to check if the stream is complete
|
28
|
+
max_retries: Maximum number of retry attempts
|
29
|
+
retry_delay: Delay in seconds between retries
|
30
|
+
"""
|
31
|
+
completed = False
|
32
|
+
retries_remaining = max_retries
|
33
|
+
|
34
|
+
while not completed:
|
35
|
+
try:
|
36
|
+
async for item in stream:
|
37
|
+
item_handler(item)
|
38
|
+
if completion_check(item):
|
39
|
+
completed = True
|
40
|
+
break
|
41
|
+
|
42
|
+
except (GRPCError, StreamTerminatedError, ClientClosed) as exc:
|
43
|
+
if retries_remaining > 0:
|
44
|
+
retries_remaining -= 1
|
45
|
+
if isinstance(exc, GRPCError):
|
46
|
+
if exc.status in RETRYABLE_GRPC_STATUS_CODES:
|
47
|
+
await asyncio.sleep(retry_delay)
|
48
|
+
continue
|
49
|
+
elif isinstance(exc, StreamTerminatedError):
|
50
|
+
continue
|
51
|
+
elif isinstance(exc, ClientClosed):
|
52
|
+
break
|
53
|
+
raise
|
modal/runner.py
CHANGED
@@ -6,7 +6,7 @@ import time
|
|
6
6
|
import typing
|
7
7
|
from collections.abc import AsyncGenerator
|
8
8
|
from multiprocessing.synchronize import Event
|
9
|
-
from typing import TYPE_CHECKING, Any,
|
9
|
+
from typing import TYPE_CHECKING, Any, Optional, TypeVar
|
10
10
|
|
11
11
|
from grpclib import GRPCError, Status
|
12
12
|
from synchronicity.async_wrap import asynccontextmanager
|
@@ -64,7 +64,6 @@ async def _init_local_app_existing(client: _Client, existing_app_id: str, enviro
|
|
64
64
|
return running_app_from_layout(
|
65
65
|
existing_app_id,
|
66
66
|
obj_resp.app_layout,
|
67
|
-
client,
|
68
67
|
app_page_url=app_page_url,
|
69
68
|
)
|
70
69
|
|
@@ -89,10 +88,8 @@ async def _init_local_app_new(
|
|
89
88
|
logger.debug(f"Created new app with id {app_resp.app_id}")
|
90
89
|
return RunningApp(
|
91
90
|
app_resp.app_id,
|
92
|
-
client=client,
|
93
91
|
app_page_url=app_resp.app_page_url,
|
94
92
|
app_logs_url=app_resp.app_logs_url,
|
95
|
-
environment_name=environment_name,
|
96
93
|
interactive=interactive,
|
97
94
|
)
|
98
95
|
|
@@ -124,10 +121,12 @@ async def _init_local_app_from_name(
|
|
124
121
|
async def _create_all_objects(
|
125
122
|
client: _Client,
|
126
123
|
running_app: RunningApp,
|
127
|
-
|
124
|
+
functions: dict[str, _Function],
|
125
|
+
classes: dict[str, _Cls],
|
128
126
|
environment_name: str,
|
129
127
|
) -> None:
|
130
128
|
"""Create objects that have been defined but not created on the server."""
|
129
|
+
indexed_objects: dict[str, _Object] = {**functions, **classes}
|
131
130
|
resolver = Resolver(
|
132
131
|
client,
|
133
132
|
environment_name=environment_name,
|
@@ -135,8 +134,9 @@ async def _create_all_objects(
|
|
135
134
|
)
|
136
135
|
with resolver.display():
|
137
136
|
# Get current objects, and reset all objects
|
138
|
-
tag_to_object_id = running_app.
|
139
|
-
running_app.
|
137
|
+
tag_to_object_id = {**running_app.function_ids, **running_app.class_ids}
|
138
|
+
running_app.function_ids = {}
|
139
|
+
running_app.class_ids = {}
|
140
140
|
|
141
141
|
# Assign all objects
|
142
142
|
for tag, obj in indexed_objects.items():
|
@@ -163,7 +163,12 @@ async def _create_all_objects(
|
|
163
163
|
async def _load(tag, obj):
|
164
164
|
existing_object_id = tag_to_object_id.get(tag)
|
165
165
|
await resolver.load(obj, existing_object_id)
|
166
|
-
|
166
|
+
if _Function._is_id_type(obj.object_id):
|
167
|
+
running_app.function_ids[tag] = obj.object_id
|
168
|
+
elif _Cls._is_id_type(obj.object_id):
|
169
|
+
running_app.class_ids[tag] = obj.object_id
|
170
|
+
else:
|
171
|
+
raise RuntimeError(f"Unexpected object {obj.object_id}")
|
167
172
|
|
168
173
|
await TaskContext.gather(*(_load(tag, obj) for tag, obj in indexed_objects.items()))
|
169
174
|
|
@@ -172,30 +177,22 @@ async def _publish_app(
|
|
172
177
|
client: _Client,
|
173
178
|
running_app: RunningApp,
|
174
179
|
app_state: int, # api_pb2.AppState.value
|
175
|
-
|
180
|
+
functions: dict[str, _Function],
|
181
|
+
classes: dict[str, _Cls],
|
176
182
|
name: str = "", # Only relevant for deployments
|
177
183
|
tag: str = "", # Only relevant for deployments
|
178
184
|
) -> tuple[str, list[api_pb2.Warning]]:
|
179
185
|
"""Wrapper for AppPublish RPC."""
|
180
186
|
|
181
|
-
|
182
|
-
# function_ids / class_ids rather than the current tag_to_object_id (i.e. "indexed_objects")
|
183
|
-
def filter_values(full_dict: dict[str, V], condition: Callable[[V], bool]) -> dict[str, V]:
|
184
|
-
return {k: v for k, v in full_dict.items() if condition(v)}
|
185
|
-
|
186
|
-
function_ids = filter_values(running_app.tag_to_object_id, _Function._is_id_type)
|
187
|
-
class_ids = filter_values(running_app.tag_to_object_id, _Cls._is_id_type)
|
188
|
-
|
189
|
-
function_objs = filter_values(indexed_objects, lambda v: v.object_id in function_ids.values())
|
190
|
-
definition_ids = {obj.object_id: obj._get_metadata().definition_id for obj in function_objs.values()} # type: ignore
|
187
|
+
definition_ids = {obj.object_id: obj._get_metadata().definition_id for obj in functions.values()} # type: ignore
|
191
188
|
|
192
189
|
request = api_pb2.AppPublishRequest(
|
193
190
|
app_id=running_app.app_id,
|
194
191
|
name=name,
|
195
192
|
deployment_tag=tag,
|
196
193
|
app_state=app_state, # type: ignore : should be a api_pb2.AppState.value
|
197
|
-
function_ids=function_ids,
|
198
|
-
class_ids=class_ids,
|
194
|
+
function_ids=running_app.function_ids,
|
195
|
+
class_ids=running_app.class_ids,
|
199
196
|
definition_ids=definition_ids,
|
200
197
|
)
|
201
198
|
try:
|
@@ -329,13 +326,11 @@ async def _run_app(
|
|
329
326
|
)
|
330
327
|
|
331
328
|
try:
|
332
|
-
indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
|
333
|
-
|
334
329
|
# Create all members
|
335
|
-
await _create_all_objects(client, running_app,
|
330
|
+
await _create_all_objects(client, running_app, app._functions, app._classes, environment_name)
|
336
331
|
|
337
332
|
# Publish the app
|
338
|
-
await _publish_app(client, running_app, app_state,
|
333
|
+
await _publish_app(client, running_app, app_state, app._functions, app._classes)
|
339
334
|
except asyncio.CancelledError as e:
|
340
335
|
# this typically happens on sigint/ctrl-C during setup (the KeyboardInterrupt happens in the main thread)
|
341
336
|
if output_mgr := _get_output_manager():
|
@@ -428,18 +423,17 @@ async def _serve_update(
|
|
428
423
|
try:
|
429
424
|
running_app: RunningApp = await _init_local_app_existing(client, existing_app_id, environment_name)
|
430
425
|
|
431
|
-
indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
|
432
|
-
|
433
426
|
# Create objects
|
434
427
|
await _create_all_objects(
|
435
428
|
client,
|
436
429
|
running_app,
|
437
|
-
|
430
|
+
app._functions,
|
431
|
+
app._classes,
|
438
432
|
environment_name,
|
439
433
|
)
|
440
434
|
|
441
435
|
# Publish the updated app
|
442
|
-
await _publish_app(client, running_app, api_pb2.APP_STATE_UNSPECIFIED,
|
436
|
+
await _publish_app(client, running_app, api_pb2.APP_STATE_UNSPECIFIED, app._functions, app._classes)
|
443
437
|
|
444
438
|
# Communicate to the parent process
|
445
439
|
is_ready.set()
|
@@ -527,19 +521,18 @@ async def _deploy_app(
|
|
527
521
|
|
528
522
|
tc.infinite_loop(heartbeat, sleep=HEARTBEAT_INTERVAL)
|
529
523
|
|
530
|
-
indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
|
531
|
-
|
532
524
|
try:
|
533
525
|
# Create all members
|
534
526
|
await _create_all_objects(
|
535
527
|
client,
|
536
528
|
running_app,
|
537
|
-
|
529
|
+
app._functions,
|
530
|
+
app._classes,
|
538
531
|
environment_name=environment_name,
|
539
532
|
)
|
540
533
|
|
541
534
|
app_url, warnings = await _publish_app(
|
542
|
-
client, running_app, api_pb2.APP_STATE_DEPLOYED,
|
535
|
+
client, running_app, api_pb2.APP_STATE_DEPLOYED, app._functions, app._classes, name, tag
|
543
536
|
)
|
544
537
|
except Exception as e:
|
545
538
|
# Note that AppClientDisconnect only stops the app if it's still initializing, and is a no-op otherwise.
|
modal/runner.pyi
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import modal.client
|
2
|
-
import modal.
|
2
|
+
import modal.cls
|
3
|
+
import modal.functions
|
3
4
|
import modal.running_app
|
4
5
|
import modal_proto.api_pb2
|
5
6
|
import multiprocessing.synchronize
|
@@ -28,14 +29,16 @@ async def _init_local_app_from_name(
|
|
28
29
|
async def _create_all_objects(
|
29
30
|
client: modal.client._Client,
|
30
31
|
running_app: modal.running_app.RunningApp,
|
31
|
-
|
32
|
+
functions: dict[str, modal.functions._Function],
|
33
|
+
classes: dict[str, modal.cls._Cls],
|
32
34
|
environment_name: str,
|
33
35
|
) -> None: ...
|
34
36
|
async def _publish_app(
|
35
37
|
client: modal.client._Client,
|
36
38
|
running_app: modal.running_app.RunningApp,
|
37
39
|
app_state: int,
|
38
|
-
|
40
|
+
functions: dict[str, modal.functions._Function],
|
41
|
+
classes: dict[str, modal.cls._Cls],
|
39
42
|
name: str = "",
|
40
43
|
tag: str = "",
|
41
44
|
) -> tuple[str, list[modal_proto.api_pb2.Warning]]: ...
|
modal/running_app.py
CHANGED
@@ -7,17 +7,14 @@ from google.protobuf.message import Message
|
|
7
7
|
from modal._utils.grpc_utils import get_proto_oneof
|
8
8
|
from modal_proto import api_pb2
|
9
9
|
|
10
|
-
from .client import _Client
|
11
|
-
|
12
10
|
|
13
11
|
@dataclass
|
14
12
|
class RunningApp:
|
15
13
|
app_id: str
|
16
|
-
client: _Client
|
17
|
-
environment_name: Optional[str] = None
|
18
14
|
app_page_url: Optional[str] = None
|
19
15
|
app_logs_url: Optional[str] = None
|
20
|
-
|
16
|
+
function_ids: dict[str, str] = field(default_factory=dict)
|
17
|
+
class_ids: dict[str, str] = field(default_factory=dict)
|
21
18
|
object_handle_metadata: dict[str, Optional[Message]] = field(default_factory=dict)
|
22
19
|
interactive: bool = False
|
23
20
|
|
@@ -25,11 +22,8 @@ class RunningApp:
|
|
25
22
|
def running_app_from_layout(
|
26
23
|
app_id: str,
|
27
24
|
app_layout: api_pb2.AppLayout,
|
28
|
-
client: _Client,
|
29
|
-
environment_name: Optional[str] = None,
|
30
25
|
app_page_url: Optional[str] = None,
|
31
26
|
) -> RunningApp:
|
32
|
-
tag_to_object_id = dict(**app_layout.function_ids, **app_layout.class_ids)
|
33
27
|
object_handle_metadata = {}
|
34
28
|
for obj in app_layout.objects:
|
35
29
|
handle_metadata: Optional[Message] = get_proto_oneof(obj, "handle_metadata_oneof")
|
@@ -37,9 +31,8 @@ def running_app_from_layout(
|
|
37
31
|
|
38
32
|
return RunningApp(
|
39
33
|
app_id,
|
40
|
-
|
41
|
-
|
42
|
-
tag_to_object_id=tag_to_object_id,
|
34
|
+
function_ids=dict(app_layout.function_ids),
|
35
|
+
class_ids=dict(app_layout.class_ids),
|
43
36
|
object_handle_metadata=object_handle_metadata,
|
44
37
|
app_page_url=app_page_url,
|
45
38
|
)
|
modal/sandbox.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
import asyncio
|
3
3
|
import os
|
4
4
|
from collections.abc import AsyncGenerator, Sequence
|
5
|
-
from typing import TYPE_CHECKING, Literal, Optional, Union, overload
|
5
|
+
from typing import TYPE_CHECKING, AsyncIterator, Literal, Optional, Union, overload
|
6
6
|
|
7
7
|
if TYPE_CHECKING:
|
8
8
|
import _typeshed
|
@@ -26,7 +26,7 @@ from .client import _Client
|
|
26
26
|
from .config import config
|
27
27
|
from .container_process import _ContainerProcess
|
28
28
|
from .exception import ExecutionError, InvalidError, SandboxTerminatedError, SandboxTimeoutError
|
29
|
-
from .file_io import _FileIO
|
29
|
+
from .file_io import FileWatchEvent, FileWatchEventType, _FileIO
|
30
30
|
from .gpu import GPU_T
|
31
31
|
from .image import _Image
|
32
32
|
from .io_streams import StreamReader, StreamWriter, _StreamReader, _StreamWriter
|
@@ -276,9 +276,9 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
276
276
|
|
277
277
|
app_id = app.app_id
|
278
278
|
app_client = app._client
|
279
|
-
elif _App.
|
280
|
-
app_id =
|
281
|
-
app_client =
|
279
|
+
elif (container_app := _App._get_container_app()) is not None:
|
280
|
+
app_id = container_app.app_id
|
281
|
+
app_client = container_app._client
|
282
282
|
else:
|
283
283
|
arglist = ", ".join(repr(s) for s in entrypoint_args)
|
284
284
|
deprecation_error(
|
@@ -320,7 +320,9 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
320
320
|
resp = await retry_transient_errors(client.stub.SandboxWait, req)
|
321
321
|
|
322
322
|
obj = _Sandbox._new_hydrated(sandbox_id, client, None)
|
323
|
-
|
323
|
+
|
324
|
+
if resp.result.status:
|
325
|
+
obj._result = resp.result
|
324
326
|
|
325
327
|
return obj
|
326
328
|
|
@@ -579,6 +581,17 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
579
581
|
task_id = await self._get_task_id()
|
580
582
|
return await _FileIO.rm(path, self._client, task_id, recursive)
|
581
583
|
|
584
|
+
async def watch(
|
585
|
+
self,
|
586
|
+
path: str,
|
587
|
+
filter: Optional[list[FileWatchEventType]] = None,
|
588
|
+
recursive: Optional[bool] = None,
|
589
|
+
timeout: Optional[int] = None,
|
590
|
+
) -> AsyncIterator[FileWatchEvent]:
|
591
|
+
task_id = await self._get_task_id()
|
592
|
+
async for event in _FileIO.watch(path, self._client, task_id, filter, recursive, timeout):
|
593
|
+
yield event
|
594
|
+
|
582
595
|
@property
|
583
596
|
def stdout(self) -> _StreamReader[str]:
|
584
597
|
"""
|
modal/sandbox.pyi
CHANGED
@@ -131,6 +131,13 @@ class _Sandbox(modal.object._Object):
|
|
131
131
|
async def ls(self, path: str) -> list[str]: ...
|
132
132
|
async def mkdir(self, path: str, parents: bool = False) -> None: ...
|
133
133
|
async def rm(self, path: str, recursive: bool = False) -> None: ...
|
134
|
+
def watch(
|
135
|
+
self,
|
136
|
+
path: str,
|
137
|
+
filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
|
138
|
+
recursive: typing.Optional[bool] = None,
|
139
|
+
timeout: typing.Optional[int] = None,
|
140
|
+
) -> typing.AsyncIterator[modal.file_io.FileWatchEvent]: ...
|
134
141
|
@property
|
135
142
|
def stdout(self) -> modal.io_streams._StreamReader[str]: ...
|
136
143
|
@property
|
@@ -388,6 +395,24 @@ class Sandbox(modal.object.Object):
|
|
388
395
|
|
389
396
|
rm: __rm_spec
|
390
397
|
|
398
|
+
class __watch_spec(typing_extensions.Protocol):
|
399
|
+
def __call__(
|
400
|
+
self,
|
401
|
+
path: str,
|
402
|
+
filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
|
403
|
+
recursive: typing.Optional[bool] = None,
|
404
|
+
timeout: typing.Optional[int] = None,
|
405
|
+
) -> typing.Iterator[modal.file_io.FileWatchEvent]: ...
|
406
|
+
def aio(
|
407
|
+
self,
|
408
|
+
path: str,
|
409
|
+
filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
|
410
|
+
recursive: typing.Optional[bool] = None,
|
411
|
+
timeout: typing.Optional[int] = None,
|
412
|
+
) -> typing.AsyncIterator[modal.file_io.FileWatchEvent]: ...
|
413
|
+
|
414
|
+
watch: __watch_spec
|
415
|
+
|
391
416
|
@property
|
392
417
|
def stdout(self) -> modal.io_streams.StreamReader[str]: ...
|
393
418
|
@property
|
@@ -2,7 +2,7 @@ modal/__init__.py,sha256=3NJLLHb0TRc2tc68kf8NHzORx38GbtbZvPEWDWrQ6N4,2234
|
|
2
2
|
modal/__main__.py,sha256=scYhGFqh8OJcVDo-VOxIT6CCwxOgzgflYWMnIZiMRqE,2871
|
3
3
|
modal/_clustered_functions.py,sha256=kTf-9YBXY88NutC1akI-gCbvf01RhMPCw-zoOI_YIUE,2700
|
4
4
|
modal/_clustered_functions.pyi,sha256=vllkegc99A0jrUOWa8mdlSbdp6uz36TsHhGxysAOpaQ,771
|
5
|
-
modal/_container_entrypoint.py,sha256
|
5
|
+
modal/_container_entrypoint.py,sha256=-zUa567FgOKmF0TtFWQ6DgehUD2CMfABDBQ8oLSpjyc,29171
|
6
6
|
modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
|
7
7
|
modal/_location.py,sha256=S3lSxIU3h9HkWpkJ3Pwo0pqjIOSB1fjeSgUsY3x7eec,1202
|
8
8
|
modal/_output.py,sha256=0fWX_KQwhER--U81ys16CL-pA5A-LN20C0EZjElKGJQ,25410
|
@@ -15,11 +15,11 @@ modal/_traceback.py,sha256=IZQzB3fVlUfMHOSyKUgw0H6qv4yHnpyq-XVCNZKfUdA,5023
|
|
15
15
|
modal/_tunnel.py,sha256=o-jJhS4vQ6-XswDhHcJWGMZZmD03SC0e9i8fEu1JTjo,6310
|
16
16
|
modal/_tunnel.pyi,sha256=JmmDYAy9F1FpgJ_hWx0xkom2nTOFQjn4mTPYlU3PFo4,1245
|
17
17
|
modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
|
18
|
-
modal/app.py,sha256=
|
19
|
-
modal/app.pyi,sha256=
|
18
|
+
modal/app.py,sha256=vEE0cK5QPF6_cdW5AJvcuWxz5KmeprHwBEtlDkVRHgE,45582
|
19
|
+
modal/app.pyi,sha256=Gx7gxjfQ70sxhbwfpx1VjvzEON-ZEMTJ_Vy8qt0oQvo,25302
|
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=pjZG1Z_8vLGkYtlrkEBAkl9tV9wqACVl0a3qWxHPacQ,7278
|
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=3hjb0JcoPjxKZNeK22f5rR43bZRBjoRI7_EMZXY7YrE,31172
|
@@ -31,18 +31,19 @@ modal/dict.py,sha256=ei9jsA5iTj4UFGPJxTAed6vjd49W47ezDtj0koUmVts,12497
|
|
31
31
|
modal/dict.pyi,sha256=VmbzxltA2vFlIHZCxpNGtd-ieXwcUwdw3iyy3WCweqU,7115
|
32
32
|
modal/environments.py,sha256=wbv9ttFCbzATGfwcmvYiG608PfHovx0AQmawsg-jmic,6660
|
33
33
|
modal/environments.pyi,sha256=rF7oaaELoSNuoD6qImGnIbuGPtgWwR5SlcExyYJ61hQ,3515
|
34
|
-
modal/exception.py,sha256=
|
35
|
-
modal/experimental.py,sha256=
|
36
|
-
modal/file_io.py,sha256=
|
37
|
-
modal/file_io.pyi,sha256=
|
34
|
+
modal/exception.py,sha256=4JyO-SACaLNDe2QC48EjsK8GMkZ8AgEurZ8j1YdRu8E,5263
|
35
|
+
modal/experimental.py,sha256=npfKbyMpI41uZZs9HW_QiB3E4ykWfDXZbACXXbw6qeA,2385
|
36
|
+
modal/file_io.py,sha256=ZR8VBCDsDt5uB9TNN9XbEh7sniJzM_5YL47m8WP0m5c,19617
|
37
|
+
modal/file_io.pyi,sha256=79Fg75BjmMEeCX0Lx-Z8C4XSNPCotwNdK6ZLIDFm2f4,9770
|
38
38
|
modal/file_pattern_matcher.py,sha256=LaI7Paxg0xR9D-D7Tgc60xR0w1KZee22LjGbFie1Vms,5571
|
39
|
-
modal/functions.py,sha256=
|
40
|
-
modal/functions.pyi,sha256=
|
39
|
+
modal/functions.py,sha256=3uJPbrEAWhpFfLfUnoRjGmvEUC-_wVh-8yNJBx8eVeM,68249
|
40
|
+
modal/functions.pyi,sha256=LiSDgH-X7jcZ56pAoLMwo3x9Dzdp_3Sd7W5MVAJPoCg,25407
|
41
41
|
modal/gpu.py,sha256=MTxj6ql8EpgfBg8YmZ5a1cLznyuZFssX1qXbEX4LKVM,7503
|
42
42
|
modal/image.py,sha256=Krvcsclomp9YsqSNwFj2FoAyg10OvU47RDnsNCwjGbQ,84550
|
43
43
|
modal/image.pyi,sha256=1fgGvsL5Rb0Sa-_2OCgIyJ_QgHcL0_9MWD_oY7cyFFM,24937
|
44
|
-
modal/io_streams.py,sha256=
|
44
|
+
modal/io_streams.py,sha256=Xxc5grJiO94nBA48FFWH3S3g6SPR0xFVgZ_DZ1oFmvI,14428
|
45
45
|
modal/io_streams.pyi,sha256=bCCVSxkMcosYd8O3PQDDwJw7TQ8JEcnYonLJ5t27TQs,4804
|
46
|
+
modal/io_streams_helper.py,sha256=B5Ui56ph7LkRpZX0tAF80Q-gOMsvPPLx5bpIPX0kgDc,1772
|
46
47
|
modal/mount.py,sha256=wOr-2vmKImsE3lHBII8hL2gYy5ng46R58QwId4JultQ,29313
|
47
48
|
modal/mount.pyi,sha256=FiNV1wIKFvd0ZMZ0tm1mz6ZSA5Hjsge-kFSA5tPWfcI,10503
|
48
49
|
modal/network_file_system.py,sha256=INj1TfN_Fsmabmlte7anvey1epodjbMmjBW_TIJSST4,14406
|
@@ -60,11 +61,11 @@ modal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
61
|
modal/queue.py,sha256=zMUQtdAyqZzBg-2iAo3c3G54HLP7TEWfVhiQXLjewb4,18556
|
61
62
|
modal/queue.pyi,sha256=gGV97pWelSSYqMV9Bl4ys3mSP7q82fS71oqSWeAwyDE,9818
|
62
63
|
modal/retries.py,sha256=HKR2Q9aNPWkMjQ5nwobqYTuZaSuw0a8lI2zrtY5IW98,5230
|
63
|
-
modal/runner.py,sha256=
|
64
|
-
modal/runner.pyi,sha256=
|
65
|
-
modal/running_app.py,sha256=
|
66
|
-
modal/sandbox.py,sha256=
|
67
|
-
modal/sandbox.pyi,sha256=
|
64
|
+
modal/runner.py,sha256=mhqgRdjD5cUDpBQIokiX7OCfVblpGV6aWmZ-WvWJgGg,24114
|
65
|
+
modal/runner.pyi,sha256=YmP4EOCNjjkwSIPi2Gl6hF_ji_ytkxz9dw3iB9KXaOI,5275
|
66
|
+
modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
|
67
|
+
modal/sandbox.py,sha256=H63K3MTcgdb9KkKc79sMKe6UzFMGBNI7HB2TUwBbE_U,28609
|
68
|
+
modal/sandbox.pyi,sha256=lceWDeXqzdeRc1iIuM5YmpoZlBJcVBpQO1Jc3AT1AQI,20809
|
68
69
|
modal/schedule.py,sha256=0ZFpKs1bOxeo5n3HZjoL7OE2ktsb-_oGtq-WJEPO4tY,2615
|
69
70
|
modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
|
70
71
|
modal/secret.py,sha256=lebVTZi4fC9PXQpLVmsvgQLzy-2Kzxv1WBD0Jr2wsxQ,10117
|
@@ -78,7 +79,7 @@ modal/volume.py,sha256=T-pLxCYqmqRO6OolpAXlPxomMu0RWjti2e4kUpaj2cQ,29229
|
|
78
79
|
modal/volume.pyi,sha256=eekb2dnAAwFK_NO9ciAOOTthl8NP1iAmMFrCGgjDA2k,11100
|
79
80
|
modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
80
81
|
modal/_runtime/asgi.py,sha256=Mjs859pSgOmtZL-YmEsSKN557v1A2Ax_5-ERgPfj55E,21920
|
81
|
-
modal/_runtime/container_io_manager.py,sha256=
|
82
|
+
modal/_runtime/container_io_manager.py,sha256=HgDLjE78yy1P7WZTmsEVDf89YnFFWG63Ddes8uYLVDY,43764
|
82
83
|
modal/_runtime/execution_context.py,sha256=E6ofm6j1POXGPxS841X3V7JU6NheVb8OkQc7JpLq4Kg,2712
|
83
84
|
modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
|
84
85
|
modal/_runtime/user_code_imports.py,sha256=n4CQOojzSdf0jwSKSy6MEnVX3IWl3t3Dq54-x9VS2Ds,14663
|
@@ -89,7 +90,7 @@ modal/_utils/blob_utils.py,sha256=N66LtZI8PpCkZ7maA7GLW5CAmYUoNJdG-GjaAUR4_NQ,14
|
|
89
90
|
modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
|
90
91
|
modal/_utils/deprecation.py,sha256=dycySRBxyZf3ITzEqPNM6MxXTk9-0VVLA8oCPQ5j_Os,3426
|
91
92
|
modal/_utils/docker_utils.py,sha256=FLz1q0YicL6i_Iq-4inkgDVFfEINVG6YPT2s_P6ly0o,2264
|
92
|
-
modal/_utils/function_utils.py,sha256=
|
93
|
+
modal/_utils/function_utils.py,sha256=q68HhFH16MwhHRnGD8jvIgqDjduRQVp3a_qMWXPyrgU,25518
|
93
94
|
modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
|
94
95
|
modal/_utils/grpc_utils.py,sha256=PPB5ay-vXencXNIWPVw5modr3EH7gfq2QPcO5YJ1lMU,7737
|
95
96
|
modal/_utils/hash_utils.py,sha256=zg3J6OGxTFGSFri1qQ12giDz90lWk8bzaxCTUCRtiX4,3034
|
@@ -119,7 +120,7 @@ modal/cli/launch.py,sha256=44oOlGB0KYDBMfuIlkhW2uzjkWbHhDRR64UOEnGwsJ4,2989
|
|
119
120
|
modal/cli/network_file_system.py,sha256=o6VLTgN4xn5XUiNPBfxYec-5uWCgYrDmfFFLM1ZW_eE,8180
|
120
121
|
modal/cli/profile.py,sha256=rLXfjJObfPNjaZvNfHGIKqs7y9bGYyGe-K7V0w-Ni0M,3110
|
121
122
|
modal/cli/queues.py,sha256=MIh2OsliNE2QeL1erubfsRsNuG4fxqcqWA2vgIfQ4Mg,4494
|
122
|
-
modal/cli/run.py,sha256=
|
123
|
+
modal/cli/run.py,sha256=QMs3BVSkq8jve76BqgstGt4C6jilbDD9gpCCRPuHUy0,17879
|
123
124
|
modal/cli/secret.py,sha256=uQpwYrMY98iMCWeZOQTcktOYhPTZ8IHnyealDc2CZqo,4206
|
124
125
|
modal/cli/token.py,sha256=mxSgOWakXG6N71hQb1ko61XAR9ZGkTMZD-Txn7gmTac,1924
|
125
126
|
modal/cli/utils.py,sha256=hZmjyzcPjDnQSkLvycZD2LhGdcsfdZshs_rOU78EpvI,3717
|
@@ -148,13 +149,13 @@ modal_global_objects/mounts/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0
|
|
148
149
|
modal_global_objects/mounts/modal_client_package.py,sha256=W0E_yShsRojPzWm6LtIQqNVolapdnrZkm2hVEQuZK_4,767
|
149
150
|
modal_global_objects/mounts/python_standalone.py,sha256=SL_riIxpd8mP4i4CLDCWiFFNj0Ltknm9c_UIGfX5d60,1836
|
150
151
|
modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
151
|
-
modal_proto/api.proto,sha256=
|
152
|
-
modal_proto/api_grpc.py,sha256=
|
153
|
-
modal_proto/api_pb2.py,sha256=
|
154
|
-
modal_proto/api_pb2.pyi,sha256=
|
155
|
-
modal_proto/api_pb2_grpc.py,sha256=
|
156
|
-
modal_proto/api_pb2_grpc.pyi,sha256=
|
157
|
-
modal_proto/modal_api_grpc.py,sha256=
|
152
|
+
modal_proto/api.proto,sha256=yxqa3djtNDAD6_AolYTUR_BM5w2apPN6UIXcvjRAFvY,80137
|
153
|
+
modal_proto/api_grpc.py,sha256=MLsxlZXikcv36aSs0iVZywIOANMWstVZMWJGQTDjdM0,102823
|
154
|
+
modal_proto/api_pb2.py,sha256=Mt0QjSEeSNi9vbpK8LoEAfrCF2obuDyJdFWAob2zYFw,294928
|
155
|
+
modal_proto/api_pb2.pyi,sha256=uSsIePIeIK06rkW3rPHSOnpKoagr5bhDWuXjk73W_l8,393499
|
156
|
+
modal_proto/api_pb2_grpc.py,sha256=4d5SwJPLldCqqHz_BGqmJLlX-BMG592WFKUQ6IBg5rg,222415
|
157
|
+
modal_proto/api_pb2_grpc.pyi,sha256=C4c3jndI6TKEgVnx3vAmT86In4T9JKmB1CImiye_aQk,51822
|
158
|
+
modal_proto/modal_api_grpc.py,sha256=eeH1vHXgwO768tM7DXqvj-P37u15SI00gZtm8_EK15I,13732
|
158
159
|
modal_proto/modal_options_grpc.py,sha256=qJ1cuwA54oRqrdTyPTbvfhFZYd9HhJKK5UCwt523r3Y,120
|
159
160
|
modal_proto/options.proto,sha256=a-siq4swVbZPfaFRXAipRZzGP2bq8OsdUvjlyzAeodQ,488
|
160
161
|
modal_proto/options_grpc.py,sha256=M18X3d-8F_cNYSVM3I25dUTO5rZ0rd-vCCfynfh13Nc,125
|
@@ -163,12 +164,12 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
|
|
163
164
|
modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
164
165
|
modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
|
165
166
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
166
|
-
modal_version/__init__.py,sha256=
|
167
|
+
modal_version/__init__.py,sha256=BEBWj9tcbFUwzEjUrqly601rauw5cYsHdcmJHs3iu0s,470
|
167
168
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
168
|
-
modal_version/_version_generated.py,sha256
|
169
|
-
modal-0.
|
170
|
-
modal-0.
|
171
|
-
modal-0.
|
172
|
-
modal-0.
|
173
|
-
modal-0.
|
174
|
-
modal-0.
|
169
|
+
modal_version/_version_generated.py,sha256=Ryi8NC5BNPKr4cGNqMzFAc_4pmvMQHAGNycNZBu1EBI,148
|
170
|
+
modal-0.71.5.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
171
|
+
modal-0.71.5.dist-info/METADATA,sha256=-r6kJm_xHIGyfR0rcCVEpqeh7hJcMF9WlwUEaGzcm8k,2328
|
172
|
+
modal-0.71.5.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
173
|
+
modal-0.71.5.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
174
|
+
modal-0.71.5.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
175
|
+
modal-0.71.5.dist-info/RECORD,,
|
modal_proto/api.proto
CHANGED
@@ -1479,6 +1479,8 @@ message FunctionGetOutputsRequest {
|
|
1479
1479
|
string last_entry_id = 6;
|
1480
1480
|
bool clear_on_success = 7; // expires *any* remaining outputs soon after this call, not just the returned ones
|
1481
1481
|
double requested_at = 8; // Used for waypoints.
|
1482
|
+
// The jwts the client expects the server to be processing. This is optional and used for sync inputs only.
|
1483
|
+
repeated string input_jwts = 9;
|
1482
1484
|
}
|
1483
1485
|
|
1484
1486
|
message FunctionGetOutputsResponse {
|
@@ -1678,6 +1680,7 @@ message GenericResult { // Used for both tasks and function outputs
|
|
1678
1680
|
// Used when the user's function fails to initialize (ex. S3 mount failed due to invalid credentials).
|
1679
1681
|
// Terminates the function and all remaining inputs.
|
1680
1682
|
GENERIC_STATUS_INIT_FAILURE = 5;
|
1683
|
+
GENERIC_STATUS_INTERNAL_FAILURE = 6;
|
1681
1684
|
}
|
1682
1685
|
|
1683
1686
|
GenericStatus status = 1; // Status of the task or function output.
|
@@ -1727,6 +1730,15 @@ message ImageContextFile {
|
|
1727
1730
|
bytes data = 2;
|
1728
1731
|
}
|
1729
1732
|
|
1733
|
+
message ImageFromIdRequest {
|
1734
|
+
string image_id = 1;
|
1735
|
+
}
|
1736
|
+
|
1737
|
+
message ImageFromIdResponse {
|
1738
|
+
string image_id = 1;
|
1739
|
+
ImageMetadata metadata = 2;
|
1740
|
+
}
|
1741
|
+
|
1730
1742
|
message ImageGetOrCreateRequest {
|
1731
1743
|
Image image = 2;
|
1732
1744
|
string app_id = 4 [ (modal.options.audit_target_attr) = true ];
|
@@ -2794,6 +2806,7 @@ service ModalClient {
|
|
2794
2806
|
rpc FunctionUpdateSchedulingParams(FunctionUpdateSchedulingParamsRequest) returns (FunctionUpdateSchedulingParamsResponse);
|
2795
2807
|
|
2796
2808
|
// Images
|
2809
|
+
rpc ImageFromId(ImageFromIdRequest) returns (ImageFromIdResponse);
|
2797
2810
|
rpc ImageGetOrCreate(ImageGetOrCreateRequest) returns (ImageGetOrCreateResponse);
|
2798
2811
|
rpc ImageJoinStreaming(ImageJoinStreamingRequest) returns (stream ImageJoinStreamingResponse);
|
2799
2812
|
|