modal 1.2.1.dev8__py3-none-any.whl → 1.2.2.dev19__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/_clustered_functions.py +1 -3
- modal/_container_entrypoint.py +4 -1
- modal/_functions.py +33 -49
- modal/_grpc_client.py +148 -0
- modal/_output.py +3 -4
- modal/_partial_function.py +22 -2
- modal/_runtime/container_io_manager.py +21 -22
- modal/_utils/async_utils.py +12 -3
- modal/_utils/auth_token_manager.py +1 -4
- modal/_utils/blob_utils.py +3 -4
- modal/_utils/function_utils.py +4 -0
- modal/_utils/grpc_utils.py +80 -51
- modal/_utils/mount_utils.py +26 -1
- modal/_utils/task_command_router_client.py +536 -0
- modal/app.py +7 -5
- modal/cli/cluster.py +4 -2
- modal/cli/config.py +3 -1
- modal/cli/container.py +5 -4
- modal/cli/entry_point.py +1 -0
- modal/cli/launch.py +1 -2
- modal/cli/network_file_system.py +1 -4
- modal/cli/queues.py +1 -2
- modal/cli/secret.py +1 -2
- modal/client.py +5 -115
- modal/client.pyi +2 -91
- modal/cls.py +1 -2
- modal/config.py +3 -1
- modal/container_process.py +287 -11
- modal/container_process.pyi +95 -32
- modal/dict.py +12 -12
- modal/environments.py +1 -2
- modal/exception.py +4 -0
- modal/experimental/__init__.py +2 -3
- modal/experimental/flash.py +27 -57
- modal/experimental/flash.pyi +6 -20
- modal/file_io.py +13 -27
- modal/functions.pyi +6 -6
- modal/image.py +24 -3
- modal/image.pyi +4 -0
- modal/io_streams.py +433 -127
- modal/io_streams.pyi +236 -171
- modal/mount.py +4 -4
- modal/network_file_system.py +5 -6
- modal/parallel_map.py +29 -31
- modal/parallel_map.pyi +3 -9
- modal/partial_function.pyi +4 -1
- modal/queue.py +17 -18
- modal/runner.py +12 -11
- modal/sandbox.py +148 -42
- modal/sandbox.pyi +139 -0
- modal/secret.py +4 -5
- modal/snapshot.py +1 -4
- modal/token_flow.py +1 -1
- modal/volume.py +22 -22
- {modal-1.2.1.dev8.dist-info → modal-1.2.2.dev19.dist-info}/METADATA +1 -1
- {modal-1.2.1.dev8.dist-info → modal-1.2.2.dev19.dist-info}/RECORD +70 -68
- modal_proto/api.proto +2 -24
- modal_proto/api_grpc.py +0 -32
- modal_proto/api_pb2.py +838 -878
- modal_proto/api_pb2.pyi +8 -70
- modal_proto/api_pb2_grpc.py +0 -67
- modal_proto/api_pb2_grpc.pyi +0 -22
- modal_proto/modal_api_grpc.py +175 -177
- modal_proto/sandbox_router.proto +0 -4
- modal_proto/sandbox_router_pb2.pyi +0 -4
- modal_version/__init__.py +1 -1
- {modal-1.2.1.dev8.dist-info → modal-1.2.2.dev19.dist-info}/WHEEL +0 -0
- {modal-1.2.1.dev8.dist-info → modal-1.2.2.dev19.dist-info}/entry_points.txt +0 -0
- {modal-1.2.1.dev8.dist-info → modal-1.2.2.dev19.dist-info}/licenses/LICENSE +0 -0
- {modal-1.2.1.dev8.dist-info → modal-1.2.2.dev19.dist-info}/top_level.txt +0 -0
modal/cli/network_file_system.py
CHANGED
|
@@ -15,7 +15,6 @@ import modal
|
|
|
15
15
|
from modal._location import display_location
|
|
16
16
|
from modal._output import OutputManager, ProgressHandler, make_console
|
|
17
17
|
from modal._utils.async_utils import synchronizer
|
|
18
|
-
from modal._utils.grpc_utils import retry_transient_errors
|
|
19
18
|
from modal._utils.time_utils import timestamp_to_localized_str
|
|
20
19
|
from modal.cli._download import _volume_download
|
|
21
20
|
from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
|
|
@@ -33,9 +32,7 @@ async def list_(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
|
|
|
33
32
|
env = ensure_env(env)
|
|
34
33
|
|
|
35
34
|
client = await _Client.from_env()
|
|
36
|
-
response = await
|
|
37
|
-
client.stub.SharedVolumeList, api_pb2.SharedVolumeListRequest(environment_name=env)
|
|
38
|
-
)
|
|
35
|
+
response = await client.stub.SharedVolumeList(api_pb2.SharedVolumeListRequest(environment_name=env))
|
|
39
36
|
env_part = f" in environment '{env}'" if env else ""
|
|
40
37
|
column_names = ["Name", "Location", "Created at"]
|
|
41
38
|
rows = []
|
modal/cli/queues.py
CHANGED
|
@@ -8,7 +8,6 @@ from typer import Argument, Option, Typer
|
|
|
8
8
|
from modal._output import make_console
|
|
9
9
|
from modal._resolver import Resolver
|
|
10
10
|
from modal._utils.async_utils import synchronizer
|
|
11
|
-
from modal._utils.grpc_utils import retry_transient_errors
|
|
12
11
|
from modal._utils.time_utils import timestamp_to_localized_str
|
|
13
12
|
from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
|
|
14
13
|
from modal.client import _Client
|
|
@@ -83,7 +82,7 @@ async def list_(*, json: bool = False, env: Optional[str] = ENV_OPTION):
|
|
|
83
82
|
max_page_size = 100
|
|
84
83
|
pagination = api_pb2.ListPagination(max_objects=max_page_size, created_before=created_before)
|
|
85
84
|
req = api_pb2.QueueListRequest(environment_name=env, pagination=pagination, total_size_limit=max_total_size)
|
|
86
|
-
resp = await
|
|
85
|
+
resp = await client.stub.QueueList(req)
|
|
87
86
|
items.extend(resp.queues)
|
|
88
87
|
return len(resp.queues) < max_page_size
|
|
89
88
|
|
modal/cli/secret.py
CHANGED
|
@@ -15,7 +15,6 @@ from typer import Argument, Option
|
|
|
15
15
|
|
|
16
16
|
from modal._output import make_console
|
|
17
17
|
from modal._utils.async_utils import synchronizer
|
|
18
|
-
from modal._utils.grpc_utils import retry_transient_errors
|
|
19
18
|
from modal._utils.time_utils import timestamp_to_localized_str
|
|
20
19
|
from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
|
|
21
20
|
from modal.client import _Client
|
|
@@ -44,7 +43,7 @@ async def list_(env: Optional[str] = ENV_OPTION, json: bool = False):
|
|
|
44
43
|
max_page_size = 100
|
|
45
44
|
pagination = api_pb2.ListPagination(max_objects=max_page_size, created_before=created_before)
|
|
46
45
|
req = api_pb2.SecretListRequest(environment_name=env, pagination=pagination)
|
|
47
|
-
resp = await
|
|
46
|
+
resp = await client.stub.SecretList(req)
|
|
48
47
|
items.extend(resp.items)
|
|
49
48
|
return len(resp.items) < max_page_size
|
|
50
49
|
|
modal/client.py
CHANGED
|
@@ -6,32 +6,24 @@ import sys
|
|
|
6
6
|
import urllib.parse
|
|
7
7
|
import warnings
|
|
8
8
|
from collections.abc import AsyncGenerator, AsyncIterator, Collection, Mapping
|
|
9
|
-
from typing import
|
|
10
|
-
Any,
|
|
11
|
-
ClassVar,
|
|
12
|
-
Generic,
|
|
13
|
-
Optional,
|
|
14
|
-
TypeVar,
|
|
15
|
-
Union,
|
|
16
|
-
)
|
|
9
|
+
from typing import Any, ClassVar, Optional, TypeVar, Union
|
|
17
10
|
|
|
18
11
|
import grpclib.client
|
|
19
12
|
from google.protobuf import empty_pb2
|
|
20
13
|
from google.protobuf.message import Message
|
|
21
|
-
from grpclib import GRPCError, Status
|
|
22
14
|
from synchronicity.async_wrap import asynccontextmanager
|
|
23
15
|
|
|
24
16
|
from modal._utils.async_utils import synchronizer
|
|
25
17
|
from modal_proto import api_grpc, api_pb2, modal_api_grpc
|
|
26
18
|
from modal_version import __version__
|
|
27
19
|
|
|
28
|
-
from ._traceback import print_server_warnings
|
|
20
|
+
from ._traceback import print_server_warnings
|
|
29
21
|
from ._utils import async_utils
|
|
30
22
|
from ._utils.async_utils import TaskContext, synchronize_api
|
|
31
23
|
from ._utils.auth_token_manager import _AuthTokenManager
|
|
32
|
-
from ._utils.grpc_utils import ConnectionManager
|
|
24
|
+
from ._utils.grpc_utils import ConnectionManager
|
|
33
25
|
from .config import _check_config, _is_remote, config, logger
|
|
34
|
-
from .exception import AuthError, ClientClosed
|
|
26
|
+
from .exception import AuthError, ClientClosed
|
|
35
27
|
|
|
36
28
|
HEARTBEAT_INTERVAL: float = config.get("heartbeat_interval")
|
|
37
29
|
HEARTBEAT_TIMEOUT: float = HEARTBEAT_INTERVAL + 0.1
|
|
@@ -159,7 +151,7 @@ class _Client:
|
|
|
159
151
|
async def hello(self):
|
|
160
152
|
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
|
161
153
|
logger.debug(f"Client ({id(self)}): Starting")
|
|
162
|
-
resp = await
|
|
154
|
+
resp = await self.stub.ClientHello(empty_pb2.Empty())
|
|
163
155
|
print_server_warnings(resp.server_warnings)
|
|
164
156
|
|
|
165
157
|
async def __aenter__(self):
|
|
@@ -362,105 +354,3 @@ class _Client:
|
|
|
362
354
|
|
|
363
355
|
|
|
364
356
|
Client = synchronize_api(_Client)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
class grpc_error_converter:
|
|
368
|
-
def __enter__(self):
|
|
369
|
-
pass
|
|
370
|
-
|
|
371
|
-
def __exit__(self, exc_type, exc, traceback) -> bool:
|
|
372
|
-
# skip all internal frames from grpclib
|
|
373
|
-
use_full_traceback = config.get("traceback")
|
|
374
|
-
with suppress_tb_frames(1):
|
|
375
|
-
if isinstance(exc, GRPCError):
|
|
376
|
-
if exc.status == Status.NOT_FOUND:
|
|
377
|
-
if use_full_traceback:
|
|
378
|
-
raise NotFoundError(exc.message)
|
|
379
|
-
else:
|
|
380
|
-
raise NotFoundError(exc.message) from None # from None to skip the grpc-internal cause
|
|
381
|
-
|
|
382
|
-
if not use_full_traceback:
|
|
383
|
-
# just include the frame in grpclib that actually raises the GRPCError
|
|
384
|
-
tb = exc.__traceback__
|
|
385
|
-
while tb.tb_next:
|
|
386
|
-
tb = tb.tb_next
|
|
387
|
-
exc.with_traceback(tb)
|
|
388
|
-
raise exc from None # from None to skip the grpc-internal cause
|
|
389
|
-
raise exc
|
|
390
|
-
|
|
391
|
-
return False
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
class UnaryUnaryWrapper(Generic[RequestType, ResponseType]):
|
|
395
|
-
# Calls a grpclib.UnaryUnaryMethod using a specific Client instance, respecting
|
|
396
|
-
# if that client is closed etc. and possibly introducing Modal-specific retry logic
|
|
397
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
|
398
|
-
client: _Client
|
|
399
|
-
|
|
400
|
-
def __init__(
|
|
401
|
-
self,
|
|
402
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
403
|
-
client: _Client,
|
|
404
|
-
server_url: str,
|
|
405
|
-
):
|
|
406
|
-
self.wrapped_method = wrapped_method
|
|
407
|
-
self.client = client
|
|
408
|
-
self.server_url = server_url
|
|
409
|
-
|
|
410
|
-
@property
|
|
411
|
-
def name(self) -> str:
|
|
412
|
-
return self.wrapped_method.name
|
|
413
|
-
|
|
414
|
-
async def __call__(
|
|
415
|
-
self,
|
|
416
|
-
req: RequestType,
|
|
417
|
-
*,
|
|
418
|
-
timeout: Optional[float] = None,
|
|
419
|
-
metadata: Optional[_MetadataLike] = None,
|
|
420
|
-
) -> ResponseType:
|
|
421
|
-
if self.client._snapshotted:
|
|
422
|
-
logger.debug(f"refreshing client after snapshot for {self.name.rsplit('/', 1)[1]}")
|
|
423
|
-
self.client = await _Client.from_env()
|
|
424
|
-
|
|
425
|
-
# Note: We override the grpclib method's channel (see grpclib's code [1]). I think this is fine
|
|
426
|
-
# since grpclib's code doesn't seem to change very much, but we could also recreate the
|
|
427
|
-
# grpclib stub if we aren't comfortable with this. The downside is then we need to cache
|
|
428
|
-
# the grpclib stub so the rest of our code becomes a bit more complicated.
|
|
429
|
-
#
|
|
430
|
-
# We need to override the channel because after the process is forked or the client is
|
|
431
|
-
# snapshotted, the existing channel may be stale / unusable.
|
|
432
|
-
#
|
|
433
|
-
# [1]: https://github.com/vmagamedov/grpclib/blob/62f968a4c84e3f64e6966097574ff0a59969ea9b/grpclib/client.py#L844
|
|
434
|
-
self.wrapped_method.channel = await self.client._get_channel(self.server_url)
|
|
435
|
-
with suppress_tb_frames(1), grpc_error_converter():
|
|
436
|
-
return await self.client._call_unary(self.wrapped_method, req, timeout=timeout, metadata=metadata)
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
class UnaryStreamWrapper(Generic[RequestType, ResponseType]):
|
|
440
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
|
|
441
|
-
|
|
442
|
-
def __init__(
|
|
443
|
-
self,
|
|
444
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
445
|
-
client: _Client,
|
|
446
|
-
server_url: str,
|
|
447
|
-
):
|
|
448
|
-
self.wrapped_method = wrapped_method
|
|
449
|
-
self.client = client
|
|
450
|
-
self.server_url = server_url
|
|
451
|
-
|
|
452
|
-
@property
|
|
453
|
-
def name(self) -> str:
|
|
454
|
-
return self.wrapped_method.name
|
|
455
|
-
|
|
456
|
-
async def unary_stream(
|
|
457
|
-
self,
|
|
458
|
-
request,
|
|
459
|
-
metadata: Optional[Any] = None,
|
|
460
|
-
):
|
|
461
|
-
if self.client._snapshotted:
|
|
462
|
-
logger.debug(f"refreshing client after snapshot for {self.name.rsplit('/', 1)[1]}")
|
|
463
|
-
self.client = await _Client.from_env()
|
|
464
|
-
self.wrapped_method.channel = await self.client._get_channel(self.server_url)
|
|
465
|
-
async for response in self.client._call_stream(self.wrapped_method, request, metadata=metadata):
|
|
466
|
-
yield response
|
modal/client.pyi
CHANGED
|
@@ -33,7 +33,7 @@ class _Client:
|
|
|
33
33
|
server_url: str,
|
|
34
34
|
client_type: int,
|
|
35
35
|
credentials: typing.Optional[tuple[str, str]],
|
|
36
|
-
version: str = "1.2.
|
|
36
|
+
version: str = "1.2.2.dev19",
|
|
37
37
|
):
|
|
38
38
|
"""mdmd:hidden
|
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -164,7 +164,7 @@ class Client:
|
|
|
164
164
|
server_url: str,
|
|
165
165
|
client_type: int,
|
|
166
166
|
credentials: typing.Optional[tuple[str, str]],
|
|
167
|
-
version: str = "1.2.
|
|
167
|
+
version: str = "1.2.2.dev19",
|
|
168
168
|
):
|
|
169
169
|
"""mdmd:hidden
|
|
170
170
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -339,95 +339,6 @@ class Client:
|
|
|
339
339
|
],
|
|
340
340
|
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
|
341
341
|
|
|
342
|
-
class grpc_error_converter:
|
|
343
|
-
def __enter__(self): ...
|
|
344
|
-
def __exit__(self, exc_type, exc, traceback) -> bool: ...
|
|
345
|
-
|
|
346
|
-
class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
|
347
|
-
"""Abstract base class for generic types.
|
|
348
|
-
|
|
349
|
-
A generic type is typically declared by inheriting from
|
|
350
|
-
this class parameterized with one or more type variables.
|
|
351
|
-
For example, a generic mapping type might be defined as::
|
|
352
|
-
|
|
353
|
-
class Mapping(Generic[KT, VT]):
|
|
354
|
-
def __getitem__(self, key: KT) -> VT:
|
|
355
|
-
...
|
|
356
|
-
# Etc.
|
|
357
|
-
|
|
358
|
-
This class can then be used as follows::
|
|
359
|
-
|
|
360
|
-
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
|
361
|
-
try:
|
|
362
|
-
return mapping[key]
|
|
363
|
-
except KeyError:
|
|
364
|
-
return default
|
|
365
|
-
"""
|
|
366
|
-
|
|
367
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
|
368
|
-
client: _Client
|
|
369
|
-
|
|
370
|
-
def __init__(
|
|
371
|
-
self,
|
|
372
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
373
|
-
client: _Client,
|
|
374
|
-
server_url: str,
|
|
375
|
-
):
|
|
376
|
-
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
377
|
-
...
|
|
378
|
-
|
|
379
|
-
@property
|
|
380
|
-
def name(self) -> str: ...
|
|
381
|
-
async def __call__(
|
|
382
|
-
self,
|
|
383
|
-
req: RequestType,
|
|
384
|
-
*,
|
|
385
|
-
timeout: typing.Optional[float] = None,
|
|
386
|
-
metadata: typing.Union[
|
|
387
|
-
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
|
388
|
-
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
|
389
|
-
None,
|
|
390
|
-
] = None,
|
|
391
|
-
) -> ResponseType:
|
|
392
|
-
"""Call self as a function."""
|
|
393
|
-
...
|
|
394
|
-
|
|
395
|
-
class UnaryStreamWrapper(typing.Generic[RequestType, ResponseType]):
|
|
396
|
-
"""Abstract base class for generic types.
|
|
397
|
-
|
|
398
|
-
A generic type is typically declared by inheriting from
|
|
399
|
-
this class parameterized with one or more type variables.
|
|
400
|
-
For example, a generic mapping type might be defined as::
|
|
401
|
-
|
|
402
|
-
class Mapping(Generic[KT, VT]):
|
|
403
|
-
def __getitem__(self, key: KT) -> VT:
|
|
404
|
-
...
|
|
405
|
-
# Etc.
|
|
406
|
-
|
|
407
|
-
This class can then be used as follows::
|
|
408
|
-
|
|
409
|
-
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
|
410
|
-
try:
|
|
411
|
-
return mapping[key]
|
|
412
|
-
except KeyError:
|
|
413
|
-
return default
|
|
414
|
-
"""
|
|
415
|
-
|
|
416
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
|
|
417
|
-
|
|
418
|
-
def __init__(
|
|
419
|
-
self,
|
|
420
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
421
|
-
client: _Client,
|
|
422
|
-
server_url: str,
|
|
423
|
-
):
|
|
424
|
-
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
425
|
-
...
|
|
426
|
-
|
|
427
|
-
@property
|
|
428
|
-
def name(self) -> str: ...
|
|
429
|
-
def unary_stream(self, request, metadata: typing.Optional[typing.Any] = None): ...
|
|
430
|
-
|
|
431
342
|
HEARTBEAT_INTERVAL: float
|
|
432
343
|
|
|
433
344
|
HEARTBEAT_TIMEOUT: float
|
modal/cls.py
CHANGED
|
@@ -30,7 +30,6 @@ from ._utils.deprecation import (
|
|
|
30
30
|
warn_if_passing_namespace,
|
|
31
31
|
warn_on_renamed_autoscaler_settings,
|
|
32
32
|
)
|
|
33
|
-
from ._utils.grpc_utils import retry_transient_errors
|
|
34
33
|
from ._utils.mount_utils import validate_volumes
|
|
35
34
|
from .cloud_bucket_mount import _CloudBucketMount
|
|
36
35
|
from .config import config
|
|
@@ -643,7 +642,7 @@ More information on class parameterization can be found here: https://modal.com/
|
|
|
643
642
|
only_class_function=True,
|
|
644
643
|
)
|
|
645
644
|
try:
|
|
646
|
-
response = await
|
|
645
|
+
response = await resolver.client.stub.ClassGet(request)
|
|
647
646
|
except NotFoundError as exc:
|
|
648
647
|
env_context = f" (in the '{environment_name}' environment)" if environment_name else ""
|
|
649
648
|
raise NotFoundError(
|
modal/config.py
CHANGED
|
@@ -147,7 +147,7 @@ async def _lookup_workspace(server_url: str, token_id: str, token_secret: str) -
|
|
|
147
147
|
|
|
148
148
|
credentials = (token_id, token_secret)
|
|
149
149
|
async with _Client(server_url, api_pb2.CLIENT_TYPE_CLIENT, credentials) as client:
|
|
150
|
-
return await client.stub.WorkspaceNameLookup(Empty(), timeout=3)
|
|
150
|
+
return await client.stub.WorkspaceNameLookup(Empty(), retry=None, timeout=3)
|
|
151
151
|
|
|
152
152
|
|
|
153
153
|
def config_profiles():
|
|
@@ -247,6 +247,8 @@ _SETTINGS = {
|
|
|
247
247
|
"traceback": _Setting(False, transform=_to_boolean),
|
|
248
248
|
"image_builder_version": _Setting(),
|
|
249
249
|
"strict_parameters": _Setting(False, transform=_to_boolean), # For internal/experimental use
|
|
250
|
+
# Allow insecure TLS for the task command router when running locally (testing/dev only)
|
|
251
|
+
"task_command_router_insecure": _Setting(False, transform=_to_boolean),
|
|
250
252
|
"snapshot_debug": _Setting(False, transform=_to_boolean),
|
|
251
253
|
"cuda_checkpoint_path": _Setting("/__modal/.bin/cuda-checkpoint"), # Used for snapshotting GPU memory.
|
|
252
254
|
"build_validation": _Setting("error", transform=_check_value(["error", "warn", "ignore"])),
|