modal 1.0.6.dev58__py3-none-any.whl → 1.2.3.dev7__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.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- modal/__main__.py +3 -4
- modal/_billing.py +80 -0
- modal/_clustered_functions.py +7 -3
- modal/_clustered_functions.pyi +4 -2
- modal/_container_entrypoint.py +41 -49
- modal/_functions.py +424 -195
- modal/_grpc_client.py +171 -0
- modal/_load_context.py +105 -0
- modal/_object.py +68 -20
- modal/_output.py +58 -45
- modal/_partial_function.py +36 -11
- modal/_pty.py +7 -3
- modal/_resolver.py +21 -35
- modal/_runtime/asgi.py +4 -3
- modal/_runtime/container_io_manager.py +301 -186
- modal/_runtime/container_io_manager.pyi +70 -61
- modal/_runtime/execution_context.py +18 -2
- modal/_runtime/execution_context.pyi +4 -1
- modal/_runtime/gpu_memory_snapshot.py +170 -63
- modal/_runtime/user_code_imports.py +28 -58
- modal/_serialization.py +57 -1
- modal/_utils/async_utils.py +33 -12
- modal/_utils/auth_token_manager.py +2 -5
- modal/_utils/blob_utils.py +110 -53
- modal/_utils/function_utils.py +49 -42
- modal/_utils/grpc_utils.py +80 -50
- modal/_utils/mount_utils.py +26 -1
- modal/_utils/name_utils.py +17 -3
- modal/_utils/task_command_router_client.py +536 -0
- modal/_utils/time_utils.py +34 -6
- modal/app.py +219 -83
- modal/app.pyi +229 -56
- modal/billing.py +5 -0
- modal/{requirements → builder}/2025.06.txt +1 -0
- modal/{requirements → builder}/PREVIEW.txt +1 -0
- modal/cli/_download.py +19 -3
- modal/cli/_traceback.py +3 -2
- modal/cli/app.py +4 -4
- modal/cli/cluster.py +15 -7
- modal/cli/config.py +5 -3
- modal/cli/container.py +7 -6
- modal/cli/dict.py +22 -16
- modal/cli/entry_point.py +12 -5
- modal/cli/environment.py +5 -4
- modal/cli/import_refs.py +3 -3
- modal/cli/launch.py +102 -5
- modal/cli/network_file_system.py +9 -13
- modal/cli/profile.py +3 -2
- modal/cli/programs/launch_instance_ssh.py +94 -0
- modal/cli/programs/run_jupyter.py +1 -1
- modal/cli/programs/run_marimo.py +95 -0
- modal/cli/programs/vscode.py +1 -1
- modal/cli/queues.py +57 -26
- modal/cli/run.py +58 -16
- modal/cli/secret.py +48 -22
- modal/cli/utils.py +3 -4
- modal/cli/volume.py +28 -25
- modal/client.py +13 -116
- modal/client.pyi +9 -91
- modal/cloud_bucket_mount.py +5 -3
- modal/cloud_bucket_mount.pyi +5 -1
- modal/cls.py +130 -102
- modal/cls.pyi +45 -85
- modal/config.py +29 -10
- modal/container_process.py +291 -13
- modal/container_process.pyi +95 -32
- modal/dict.py +282 -63
- modal/dict.pyi +423 -73
- modal/environments.py +15 -27
- modal/environments.pyi +5 -15
- modal/exception.py +8 -0
- modal/experimental/__init__.py +143 -38
- modal/experimental/flash.py +247 -78
- modal/experimental/flash.pyi +137 -9
- modal/file_io.py +14 -28
- modal/file_io.pyi +2 -2
- modal/file_pattern_matcher.py +25 -16
- modal/functions.pyi +134 -61
- modal/image.py +255 -86
- modal/image.pyi +300 -62
- modal/io_streams.py +436 -126
- modal/io_streams.pyi +236 -171
- modal/mount.py +62 -157
- modal/mount.pyi +45 -172
- modal/network_file_system.py +30 -53
- modal/network_file_system.pyi +16 -76
- modal/object.pyi +42 -8
- modal/parallel_map.py +821 -113
- modal/parallel_map.pyi +134 -0
- modal/partial_function.pyi +4 -1
- modal/proxy.py +16 -7
- modal/proxy.pyi +10 -2
- modal/queue.py +263 -61
- modal/queue.pyi +409 -66
- modal/runner.py +112 -92
- modal/runner.pyi +45 -27
- modal/sandbox.py +451 -124
- modal/sandbox.pyi +513 -67
- modal/secret.py +291 -67
- modal/secret.pyi +425 -19
- modal/serving.py +7 -11
- modal/serving.pyi +7 -8
- modal/snapshot.py +11 -8
- modal/token_flow.py +4 -4
- modal/volume.py +344 -98
- modal/volume.pyi +464 -68
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
- modal-1.2.3.dev7.dist-info/RECORD +195 -0
- modal_docs/mdmd/mdmd.py +11 -1
- modal_proto/api.proto +399 -67
- modal_proto/api_grpc.py +241 -1
- modal_proto/api_pb2.py +1395 -1000
- modal_proto/api_pb2.pyi +1239 -79
- modal_proto/api_pb2_grpc.py +499 -4
- modal_proto/api_pb2_grpc.pyi +162 -14
- modal_proto/modal_api_grpc.py +175 -160
- modal_proto/sandbox_router.proto +145 -0
- modal_proto/sandbox_router_grpc.py +105 -0
- modal_proto/sandbox_router_pb2.py +149 -0
- modal_proto/sandbox_router_pb2.pyi +333 -0
- modal_proto/sandbox_router_pb2_grpc.py +203 -0
- modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
- modal_proto/task_command_router.proto +144 -0
- modal_proto/task_command_router_grpc.py +105 -0
- modal_proto/task_command_router_pb2.py +149 -0
- modal_proto/task_command_router_pb2.pyi +333 -0
- modal_proto/task_command_router_pb2_grpc.py +203 -0
- modal_proto/task_command_router_pb2_grpc.pyi +75 -0
- modal_version/__init__.py +1 -1
- modal-1.0.6.dev58.dist-info/RECORD +0 -183
- modal_proto/modal_options_grpc.py +0 -3
- modal_proto/options.proto +0 -19
- modal_proto/options_grpc.py +0 -3
- modal_proto/options_pb2.py +0 -35
- modal_proto/options_pb2.pyi +0 -20
- modal_proto/options_pb2_grpc.py +0 -4
- modal_proto/options_pb2_grpc.pyi +0 -7
- /modal/{requirements → builder}/2023.12.312.txt +0 -0
- /modal/{requirements → builder}/2023.12.txt +0 -0
- /modal/{requirements → builder}/2024.04.txt +0 -0
- /modal/{requirements → builder}/2024.10.txt +0 -0
- /modal/{requirements → builder}/README.md +0 -0
- /modal/{requirements → builder}/base-images.json +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/cli/volume.py
CHANGED
|
@@ -7,18 +7,15 @@ from typing import Optional
|
|
|
7
7
|
import typer
|
|
8
8
|
from click import UsageError
|
|
9
9
|
from grpclib import GRPCError, Status
|
|
10
|
-
from rich.console import Console
|
|
11
10
|
from rich.syntax import Syntax
|
|
12
11
|
from typer import Argument, Option, Typer
|
|
13
12
|
|
|
14
13
|
import modal
|
|
15
|
-
from modal._output import OutputManager, ProgressHandler
|
|
14
|
+
from modal._output import OutputManager, ProgressHandler, make_console
|
|
16
15
|
from modal._utils.async_utils import synchronizer
|
|
17
|
-
from modal._utils.
|
|
18
|
-
from modal._utils.time_utils import timestamp_to_local
|
|
16
|
+
from modal._utils.time_utils import timestamp_to_localized_str
|
|
19
17
|
from modal.cli._download import _volume_download
|
|
20
18
|
from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
|
|
21
|
-
from modal.client import _Client
|
|
22
19
|
from modal.environments import ensure_env
|
|
23
20
|
from modal.volume import _AbstractVolumeUploadContextManager, _Volume
|
|
24
21
|
from modal_proto import api_pb2
|
|
@@ -57,14 +54,14 @@ def create(
|
|
|
57
54
|
version: Optional[int] = Option(default=None, help="VolumeFS version. (Experimental)"),
|
|
58
55
|
):
|
|
59
56
|
env_name = ensure_env(env)
|
|
60
|
-
modal.Volume.
|
|
57
|
+
modal.Volume.objects.create(name, environment_name=env, version=version)
|
|
61
58
|
usage_code = f"""
|
|
62
59
|
@app.function(volumes={{"/my_vol": modal.Volume.from_name("{name}")}})
|
|
63
60
|
def some_func():
|
|
64
61
|
os.listdir("/my_vol")
|
|
65
62
|
"""
|
|
66
63
|
|
|
67
|
-
console =
|
|
64
|
+
console = make_console()
|
|
68
65
|
console.print(f"Created Volume '{name}' in environment '{env_name}'. \n\nCode example:\n")
|
|
69
66
|
usage = Syntax(usage_code, "python")
|
|
70
67
|
console.print(usage)
|
|
@@ -96,10 +93,16 @@ async def get(
|
|
|
96
93
|
ensure_env(env)
|
|
97
94
|
destination = Path(local_destination)
|
|
98
95
|
volume = _Volume.from_name(volume_name, environment_name=env)
|
|
99
|
-
console =
|
|
96
|
+
console = make_console()
|
|
100
97
|
progress_handler = ProgressHandler(type="download", console=console)
|
|
101
98
|
with progress_handler.live:
|
|
102
|
-
await _volume_download(
|
|
99
|
+
await _volume_download(
|
|
100
|
+
volume=volume,
|
|
101
|
+
remote_path=remote_path,
|
|
102
|
+
local_destination=destination,
|
|
103
|
+
overwrite=force,
|
|
104
|
+
progress_cb=progress_handler.progress,
|
|
105
|
+
)
|
|
103
106
|
console.print(OutputManager.step_completed("Finished downloading files to local!"))
|
|
104
107
|
|
|
105
108
|
|
|
@@ -111,14 +114,13 @@ async def get(
|
|
|
111
114
|
@synchronizer.create_blocking
|
|
112
115
|
async def list_(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
|
|
113
116
|
env = ensure_env(env)
|
|
114
|
-
|
|
115
|
-
response = await retry_transient_errors(client.stub.VolumeList, api_pb2.VolumeListRequest(environment_name=env))
|
|
116
|
-
env_part = f" in environment '{env}'" if env else ""
|
|
117
|
-
column_names = ["Name", "Created at"]
|
|
117
|
+
volumes = await _Volume.objects.list(environment_name=env)
|
|
118
118
|
rows = []
|
|
119
|
-
for
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
for obj in volumes:
|
|
120
|
+
info = await obj.info()
|
|
121
|
+
rows.append((info.name, timestamp_to_localized_str(info.created_at.timestamp(), json), info.created_by))
|
|
122
|
+
|
|
123
|
+
display_table(["Name", "Created at", "Created by"], rows, json)
|
|
122
124
|
|
|
123
125
|
|
|
124
126
|
@volume_cli.command(
|
|
@@ -164,7 +166,7 @@ async def ls(
|
|
|
164
166
|
(
|
|
165
167
|
entry.path.encode("unicode_escape").decode("utf-8"),
|
|
166
168
|
filetype,
|
|
167
|
-
|
|
169
|
+
timestamp_to_localized_str(entry.mtime, False),
|
|
168
170
|
humanize_filesize(entry.size),
|
|
169
171
|
)
|
|
170
172
|
)
|
|
@@ -197,7 +199,7 @@ async def put(
|
|
|
197
199
|
|
|
198
200
|
if remote_path.endswith("/"):
|
|
199
201
|
remote_path = remote_path + os.path.basename(local_path)
|
|
200
|
-
console =
|
|
202
|
+
console = make_console()
|
|
201
203
|
progress_handler = ProgressHandler(type="upload", console=console)
|
|
202
204
|
|
|
203
205
|
if Path(local_path).is_dir():
|
|
@@ -245,7 +247,7 @@ async def rm(
|
|
|
245
247
|
):
|
|
246
248
|
ensure_env(env)
|
|
247
249
|
volume = _Volume.from_name(volume_name, environment_name=env)
|
|
248
|
-
console =
|
|
250
|
+
console = make_console()
|
|
249
251
|
try:
|
|
250
252
|
await volume.remove_file(remote_path, recursive=recursive)
|
|
251
253
|
console.print(OutputManager.step_completed(f"{remote_path} was deleted successfully!"))
|
|
@@ -278,25 +280,26 @@ async def cp(
|
|
|
278
280
|
|
|
279
281
|
@volume_cli.command(
|
|
280
282
|
name="delete",
|
|
281
|
-
help="Delete a named
|
|
283
|
+
help="Delete a named Volume and all of its data.",
|
|
282
284
|
rich_help_panel="Management",
|
|
283
285
|
)
|
|
284
286
|
@synchronizer.create_blocking
|
|
285
287
|
async def delete(
|
|
286
|
-
|
|
288
|
+
name: str = Argument(help="Name of the modal.Volume to be deleted. Case sensitive"),
|
|
289
|
+
*,
|
|
290
|
+
allow_missing: bool = Option(False, "--allow-missing", help="Don't error if the Volume doesn't exist."),
|
|
287
291
|
yes: bool = YES_OPTION,
|
|
288
292
|
env: Optional[str] = ENV_OPTION,
|
|
289
293
|
):
|
|
290
|
-
|
|
291
|
-
await _Volume.from_name(volume_name, environment_name=env).hydrate()
|
|
294
|
+
env = ensure_env(env)
|
|
292
295
|
if not yes:
|
|
293
296
|
typer.confirm(
|
|
294
|
-
f"Are you sure you want to irrevocably delete the modal.Volume '{
|
|
297
|
+
f"Are you sure you want to irrevocably delete the modal.Volume '{name}'?",
|
|
295
298
|
default=False,
|
|
296
299
|
abort=True,
|
|
297
300
|
)
|
|
298
301
|
|
|
299
|
-
await _Volume.delete(
|
|
302
|
+
await _Volume.objects.delete(name, environment_name=env, allow_missing=allow_missing)
|
|
300
303
|
|
|
301
304
|
|
|
302
305
|
@volume_cli.command(
|
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):
|
|
@@ -268,6 +260,14 @@ class _Client:
|
|
|
268
260
|
# Just used from tests.
|
|
269
261
|
cls._client_from_env = client
|
|
270
262
|
|
|
263
|
+
async def get_input_plane_metadata(self, input_plane_region: str) -> list[tuple[str, str]]:
|
|
264
|
+
assert self._auth_token_manager, "Client must have an instance of auth token manager."
|
|
265
|
+
token = await self._auth_token_manager.get_token()
|
|
266
|
+
return [
|
|
267
|
+
("x-modal-input-plane-region", input_plane_region),
|
|
268
|
+
("x-modal-auth-token", token),
|
|
269
|
+
]
|
|
270
|
+
|
|
271
271
|
async def _call_safely(self, coro, readable_method: str):
|
|
272
272
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
273
273
|
|
|
@@ -354,106 +354,3 @@ class _Client:
|
|
|
354
354
|
|
|
355
355
|
|
|
356
356
|
Client = synchronize_api(_Client)
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
class grpc_error_converter:
|
|
360
|
-
def __enter__(self):
|
|
361
|
-
pass
|
|
362
|
-
|
|
363
|
-
def __exit__(self, exc_type, exc, traceback) -> bool:
|
|
364
|
-
# skip all internal frames from grpclib
|
|
365
|
-
use_full_traceback = config.get("traceback")
|
|
366
|
-
with suppress_tb_frames(1):
|
|
367
|
-
if isinstance(exc, GRPCError):
|
|
368
|
-
if exc.status == Status.NOT_FOUND:
|
|
369
|
-
if use_full_traceback:
|
|
370
|
-
raise NotFoundError(exc.message)
|
|
371
|
-
else:
|
|
372
|
-
raise NotFoundError(exc.message) from None # from None to skip the grpc-internal cause
|
|
373
|
-
|
|
374
|
-
if not use_full_traceback:
|
|
375
|
-
# just include the frame in grpclib that actually raises the GRPCError
|
|
376
|
-
tb = exc.__traceback__
|
|
377
|
-
while tb.tb_next:
|
|
378
|
-
tb = tb.tb_next
|
|
379
|
-
exc.with_traceback(tb)
|
|
380
|
-
raise exc from None # from None to skip the grpc-internal cause
|
|
381
|
-
raise exc
|
|
382
|
-
|
|
383
|
-
return False
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
class UnaryUnaryWrapper(Generic[RequestType, ResponseType]):
|
|
387
|
-
# Calls a grpclib.UnaryUnaryMethod using a specific Client instance, respecting
|
|
388
|
-
# if that client is closed etc. and possibly introducing Modal-specific retry logic
|
|
389
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
|
390
|
-
client: _Client
|
|
391
|
-
|
|
392
|
-
def __init__(
|
|
393
|
-
self,
|
|
394
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
395
|
-
client: _Client,
|
|
396
|
-
server_url: str,
|
|
397
|
-
):
|
|
398
|
-
self.wrapped_method = wrapped_method
|
|
399
|
-
self.client = client
|
|
400
|
-
self.server_url = server_url
|
|
401
|
-
|
|
402
|
-
@property
|
|
403
|
-
def name(self) -> str:
|
|
404
|
-
return self.wrapped_method.name
|
|
405
|
-
|
|
406
|
-
async def __call__(
|
|
407
|
-
self,
|
|
408
|
-
req: RequestType,
|
|
409
|
-
*,
|
|
410
|
-
timeout: Optional[float] = None,
|
|
411
|
-
metadata: Optional[_MetadataLike] = None,
|
|
412
|
-
) -> ResponseType:
|
|
413
|
-
if self.client._snapshotted:
|
|
414
|
-
logger.debug(f"refreshing client after snapshot for {self.name.rsplit('/', 1)[1]}")
|
|
415
|
-
self.client = await _Client.from_env()
|
|
416
|
-
|
|
417
|
-
# Note: We override the grpclib method's channel (see grpclib's code [1]). I think this is fine
|
|
418
|
-
# since grpclib's code doesn't seem to change very much, but we could also recreate the
|
|
419
|
-
# grpclib stub if we aren't comfortable with this. The downside is then we need to cache
|
|
420
|
-
# the grpclib stub so the rest of our code becomes a bit more complicated.
|
|
421
|
-
#
|
|
422
|
-
# We need to override the channel because after the process is forked or the client is
|
|
423
|
-
# snapshotted, the existing channel may be stale / unusable.
|
|
424
|
-
#
|
|
425
|
-
# [1]: https://github.com/vmagamedov/grpclib/blob/62f968a4c84e3f64e6966097574ff0a59969ea9b/grpclib/client.py#L844
|
|
426
|
-
self.wrapped_method.channel = await self.client._get_channel(self.server_url)
|
|
427
|
-
with suppress_tb_frames(1), grpc_error_converter():
|
|
428
|
-
return await self.client._call_unary(self.wrapped_method, req, timeout=timeout, metadata=metadata)
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
class UnaryStreamWrapper(Generic[RequestType, ResponseType]):
|
|
432
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
|
|
433
|
-
|
|
434
|
-
def __init__(
|
|
435
|
-
self,
|
|
436
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
437
|
-
client: _Client,
|
|
438
|
-
server_url: str,
|
|
439
|
-
):
|
|
440
|
-
self.wrapped_method = wrapped_method
|
|
441
|
-
self.client = client
|
|
442
|
-
self.server_url = server_url
|
|
443
|
-
|
|
444
|
-
@property
|
|
445
|
-
def name(self) -> str:
|
|
446
|
-
return self.wrapped_method.name
|
|
447
|
-
|
|
448
|
-
async def unary_stream(
|
|
449
|
-
self,
|
|
450
|
-
request,
|
|
451
|
-
metadata: Optional[Any] = None,
|
|
452
|
-
):
|
|
453
|
-
if self.client._snapshotted:
|
|
454
|
-
logger.debug(f"refreshing client after snapshot for {self.name.rsplit('/', 1)[1]}")
|
|
455
|
-
self.client = await _Client.from_env()
|
|
456
|
-
self.wrapped_method.channel = await self.client._get_channel(self.server_url)
|
|
457
|
-
async for response in self.client._call_stream(self.wrapped_method, request, metadata=metadata):
|
|
458
|
-
yield response
|
|
459
|
-
|
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.
|
|
36
|
+
version: str = "1.2.3.dev7",
|
|
37
37
|
):
|
|
38
38
|
"""mdmd:hidden
|
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -112,6 +112,7 @@ class _Client:
|
|
|
112
112
|
"""mdmd:hidden"""
|
|
113
113
|
...
|
|
114
114
|
|
|
115
|
+
async def get_input_plane_metadata(self, input_plane_region: str) -> list[tuple[str, str]]: ...
|
|
115
116
|
async def _call_safely(self, coro, readable_method: str):
|
|
116
117
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
117
118
|
|
|
@@ -163,7 +164,7 @@ class Client:
|
|
|
163
164
|
server_url: str,
|
|
164
165
|
client_type: int,
|
|
165
166
|
credentials: typing.Optional[tuple[str, str]],
|
|
166
|
-
version: str = "1.
|
|
167
|
+
version: str = "1.2.3.dev7",
|
|
167
168
|
):
|
|
168
169
|
"""mdmd:hidden
|
|
169
170
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -275,6 +276,12 @@ class Client:
|
|
|
275
276
|
"""mdmd:hidden"""
|
|
276
277
|
...
|
|
277
278
|
|
|
279
|
+
class __get_input_plane_metadata_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
280
|
+
def __call__(self, /, input_plane_region: str) -> list[tuple[str, str]]: ...
|
|
281
|
+
async def aio(self, /, input_plane_region: str) -> list[tuple[str, str]]: ...
|
|
282
|
+
|
|
283
|
+
get_input_plane_metadata: __get_input_plane_metadata_spec[typing_extensions.Self]
|
|
284
|
+
|
|
278
285
|
class ___call_safely_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
279
286
|
def __call__(self, /, coro, readable_method: str):
|
|
280
287
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
@@ -332,95 +339,6 @@ class Client:
|
|
|
332
339
|
],
|
|
333
340
|
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
|
334
341
|
|
|
335
|
-
class grpc_error_converter:
|
|
336
|
-
def __enter__(self): ...
|
|
337
|
-
def __exit__(self, exc_type, exc, traceback) -> bool: ...
|
|
338
|
-
|
|
339
|
-
class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
|
340
|
-
"""Abstract base class for generic types.
|
|
341
|
-
|
|
342
|
-
A generic type is typically declared by inheriting from
|
|
343
|
-
this class parameterized with one or more type variables.
|
|
344
|
-
For example, a generic mapping type might be defined as::
|
|
345
|
-
|
|
346
|
-
class Mapping(Generic[KT, VT]):
|
|
347
|
-
def __getitem__(self, key: KT) -> VT:
|
|
348
|
-
...
|
|
349
|
-
# Etc.
|
|
350
|
-
|
|
351
|
-
This class can then be used as follows::
|
|
352
|
-
|
|
353
|
-
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
|
354
|
-
try:
|
|
355
|
-
return mapping[key]
|
|
356
|
-
except KeyError:
|
|
357
|
-
return default
|
|
358
|
-
"""
|
|
359
|
-
|
|
360
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
|
361
|
-
client: _Client
|
|
362
|
-
|
|
363
|
-
def __init__(
|
|
364
|
-
self,
|
|
365
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
366
|
-
client: _Client,
|
|
367
|
-
server_url: str,
|
|
368
|
-
):
|
|
369
|
-
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
370
|
-
...
|
|
371
|
-
|
|
372
|
-
@property
|
|
373
|
-
def name(self) -> str: ...
|
|
374
|
-
async def __call__(
|
|
375
|
-
self,
|
|
376
|
-
req: RequestType,
|
|
377
|
-
*,
|
|
378
|
-
timeout: typing.Optional[float] = None,
|
|
379
|
-
metadata: typing.Union[
|
|
380
|
-
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
|
381
|
-
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
|
382
|
-
None,
|
|
383
|
-
] = None,
|
|
384
|
-
) -> ResponseType:
|
|
385
|
-
"""Call self as a function."""
|
|
386
|
-
...
|
|
387
|
-
|
|
388
|
-
class UnaryStreamWrapper(typing.Generic[RequestType, ResponseType]):
|
|
389
|
-
"""Abstract base class for generic types.
|
|
390
|
-
|
|
391
|
-
A generic type is typically declared by inheriting from
|
|
392
|
-
this class parameterized with one or more type variables.
|
|
393
|
-
For example, a generic mapping type might be defined as::
|
|
394
|
-
|
|
395
|
-
class Mapping(Generic[KT, VT]):
|
|
396
|
-
def __getitem__(self, key: KT) -> VT:
|
|
397
|
-
...
|
|
398
|
-
# Etc.
|
|
399
|
-
|
|
400
|
-
This class can then be used as follows::
|
|
401
|
-
|
|
402
|
-
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
|
403
|
-
try:
|
|
404
|
-
return mapping[key]
|
|
405
|
-
except KeyError:
|
|
406
|
-
return default
|
|
407
|
-
"""
|
|
408
|
-
|
|
409
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
|
|
410
|
-
|
|
411
|
-
def __init__(
|
|
412
|
-
self,
|
|
413
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
414
|
-
client: _Client,
|
|
415
|
-
server_url: str,
|
|
416
|
-
):
|
|
417
|
-
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
418
|
-
...
|
|
419
|
-
|
|
420
|
-
@property
|
|
421
|
-
def name(self) -> str: ...
|
|
422
|
-
def unary_stream(self, request, metadata: typing.Optional[typing.Any] = None): ...
|
|
423
|
-
|
|
424
342
|
HEARTBEAT_INTERVAL: float
|
|
425
343
|
|
|
426
344
|
HEARTBEAT_TIMEOUT: float
|
modal/cloud_bucket_mount.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Copyright Modal Labs 2022
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional, Sequence
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
5
|
|
|
6
6
|
from modal_proto import api_pb2
|
|
@@ -117,9 +117,10 @@ class _CloudBucketMount:
|
|
|
117
117
|
|
|
118
118
|
read_only: bool = False
|
|
119
119
|
requester_pays: bool = False
|
|
120
|
+
force_path_style: bool = False
|
|
120
121
|
|
|
121
122
|
|
|
122
|
-
def cloud_bucket_mounts_to_proto(mounts:
|
|
123
|
+
def cloud_bucket_mounts_to_proto(mounts: Sequence[tuple[str, _CloudBucketMount]]) -> list[api_pb2.CloudBucketMount]:
|
|
123
124
|
"""Helper function to convert `CloudBucketMount` to a list of protobufs that can be passed to the server."""
|
|
124
125
|
cloud_bucket_mounts: list[api_pb2.CloudBucketMount] = []
|
|
125
126
|
|
|
@@ -132,7 +133,7 @@ def cloud_bucket_mounts_to_proto(mounts: list[tuple[str, _CloudBucketMount]]) ->
|
|
|
132
133
|
elif parse_result.hostname.endswith("storage.googleapis.com"):
|
|
133
134
|
bucket_type = api_pb2.CloudBucketMount.BucketType.GCP
|
|
134
135
|
else:
|
|
135
|
-
logger.
|
|
136
|
+
logger.info(
|
|
136
137
|
"CloudBucketMount received unrecognized bucket endpoint URL. "
|
|
137
138
|
"Assuming AWS S3 configuration as fallback."
|
|
138
139
|
)
|
|
@@ -159,6 +160,7 @@ def cloud_bucket_mounts_to_proto(mounts: list[tuple[str, _CloudBucketMount]]) ->
|
|
|
159
160
|
requester_pays=mount.requester_pays,
|
|
160
161
|
key_prefix=key_prefix,
|
|
161
162
|
oidc_auth_role_arn=mount.oidc_auth_role_arn,
|
|
163
|
+
force_path_style=mount.force_path_style,
|
|
162
164
|
)
|
|
163
165
|
cloud_bucket_mounts.append(cloud_bucket_mount)
|
|
164
166
|
|
modal/cloud_bucket_mount.pyi
CHANGED
|
@@ -99,6 +99,7 @@ class _CloudBucketMount:
|
|
|
99
99
|
oidc_auth_role_arn: typing.Optional[str]
|
|
100
100
|
read_only: bool
|
|
101
101
|
requester_pays: bool
|
|
102
|
+
force_path_style: bool
|
|
102
103
|
|
|
103
104
|
def __init__(
|
|
104
105
|
self,
|
|
@@ -109,6 +110,7 @@ class _CloudBucketMount:
|
|
|
109
110
|
oidc_auth_role_arn: typing.Optional[str] = None,
|
|
110
111
|
read_only: bool = False,
|
|
111
112
|
requester_pays: bool = False,
|
|
113
|
+
force_path_style: bool = False,
|
|
112
114
|
) -> None:
|
|
113
115
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
114
116
|
...
|
|
@@ -122,7 +124,7 @@ class _CloudBucketMount:
|
|
|
122
124
|
...
|
|
123
125
|
|
|
124
126
|
def cloud_bucket_mounts_to_proto(
|
|
125
|
-
mounts:
|
|
127
|
+
mounts: typing.Sequence[tuple[str, _CloudBucketMount]],
|
|
126
128
|
) -> list[modal_proto.api_pb2.CloudBucketMount]:
|
|
127
129
|
"""Helper function to convert `CloudBucketMount` to a list of protobufs that can be passed to the server."""
|
|
128
130
|
...
|
|
@@ -224,6 +226,7 @@ class CloudBucketMount:
|
|
|
224
226
|
oidc_auth_role_arn: typing.Optional[str]
|
|
225
227
|
read_only: bool
|
|
226
228
|
requester_pays: bool
|
|
229
|
+
force_path_style: bool
|
|
227
230
|
|
|
228
231
|
def __init__(
|
|
229
232
|
self,
|
|
@@ -234,6 +237,7 @@ class CloudBucketMount:
|
|
|
234
237
|
oidc_auth_role_arn: typing.Optional[str] = None,
|
|
235
238
|
read_only: bool = False,
|
|
236
239
|
requester_pays: bool = False,
|
|
240
|
+
force_path_style: bool = False,
|
|
237
241
|
) -> None: ...
|
|
238
242
|
def __repr__(self): ...
|
|
239
243
|
def __eq__(self, other): ...
|