modal 1.0.3.dev10__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/__init__.py +0 -2
- modal/__main__.py +3 -4
- modal/_billing.py +80 -0
- modal/_clustered_functions.py +7 -3
- modal/_clustered_functions.pyi +15 -3
- modal/_container_entrypoint.py +51 -69
- modal/_functions.py +508 -240
- modal/_grpc_client.py +171 -0
- modal/_load_context.py +105 -0
- modal/_object.py +81 -21
- modal/_output.py +58 -45
- modal/_partial_function.py +48 -73
- modal/_pty.py +7 -3
- modal/_resolver.py +26 -46
- modal/_runtime/asgi.py +4 -3
- modal/_runtime/container_io_manager.py +358 -220
- modal/_runtime/container_io_manager.pyi +296 -101
- modal/_runtime/execution_context.py +18 -2
- modal/_runtime/execution_context.pyi +64 -7
- modal/_runtime/gpu_memory_snapshot.py +262 -57
- modal/_runtime/user_code_imports.py +28 -58
- modal/_serialization.py +90 -6
- modal/_traceback.py +42 -1
- modal/_tunnel.pyi +380 -12
- modal/_utils/async_utils.py +84 -29
- modal/_utils/auth_token_manager.py +111 -0
- modal/_utils/blob_utils.py +181 -58
- modal/_utils/deprecation.py +19 -0
- modal/_utils/function_utils.py +91 -47
- modal/_utils/grpc_utils.py +89 -66
- 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 +256 -88
- modal/app.pyi +909 -92
- modal/billing.py +5 -0
- modal/builder/2025.06.txt +18 -0
- modal/builder/PREVIEW.txt +18 -0
- modal/builder/base-images.json +58 -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 +11 -12
- 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 +91 -23
- modal/cli/secret.py +48 -22
- modal/cli/token.py +7 -8
- modal/cli/utils.py +4 -7
- modal/cli/volume.py +31 -25
- modal/client.py +15 -85
- modal/client.pyi +183 -62
- modal/cloud_bucket_mount.py +5 -3
- modal/cloud_bucket_mount.pyi +197 -5
- modal/cls.py +200 -126
- modal/cls.pyi +446 -68
- modal/config.py +29 -11
- modal/container_process.py +319 -19
- modal/container_process.pyi +190 -20
- modal/dict.py +290 -71
- modal/dict.pyi +835 -83
- modal/environments.py +15 -27
- modal/environments.pyi +46 -24
- modal/exception.py +14 -2
- modal/experimental/__init__.py +194 -40
- modal/experimental/flash.py +618 -0
- modal/experimental/flash.pyi +380 -0
- modal/experimental/ipython.py +11 -7
- modal/file_io.py +29 -36
- modal/file_io.pyi +251 -53
- modal/file_pattern_matcher.py +56 -16
- modal/functions.pyi +673 -92
- modal/gpu.py +1 -1
- modal/image.py +528 -176
- modal/image.pyi +1572 -145
- modal/io_streams.py +458 -128
- modal/io_streams.pyi +433 -52
- modal/mount.py +216 -151
- modal/mount.pyi +225 -78
- modal/network_file_system.py +45 -62
- modal/network_file_system.pyi +277 -56
- modal/object.pyi +93 -17
- modal/parallel_map.py +942 -129
- modal/parallel_map.pyi +294 -15
- modal/partial_function.py +0 -2
- modal/partial_function.pyi +234 -19
- modal/proxy.py +17 -8
- modal/proxy.pyi +36 -3
- modal/queue.py +270 -65
- modal/queue.pyi +817 -57
- modal/runner.py +115 -101
- modal/runner.pyi +205 -49
- modal/sandbox.py +512 -136
- modal/sandbox.pyi +845 -111
- modal/schedule.py +1 -1
- modal/secret.py +300 -70
- modal/secret.pyi +589 -34
- modal/serving.py +7 -11
- modal/serving.pyi +7 -8
- modal/snapshot.py +11 -8
- modal/snapshot.pyi +25 -4
- modal/token_flow.py +4 -4
- modal/token_flow.pyi +28 -8
- modal/volume.py +416 -158
- modal/volume.pyi +1117 -121
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +10 -9
- modal-1.2.3.dev7.dist-info/RECORD +195 -0
- modal_docs/mdmd/mdmd.py +17 -4
- modal_proto/api.proto +534 -79
- modal_proto/api_grpc.py +337 -1
- modal_proto/api_pb2.py +1522 -968
- modal_proto/api_pb2.pyi +1619 -134
- modal_proto/api_pb2_grpc.py +699 -4
- modal_proto/api_pb2_grpc.pyi +226 -14
- modal_proto/modal_api_grpc.py +175 -154
- 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/requirements/PREVIEW.txt +0 -16
- modal/requirements/base-images.json +0 -26
- modal-1.0.3.dev10.dist-info/RECORD +0 -179
- 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-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/client.py
CHANGED
|
@@ -6,14 +6,7 @@ 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
|
|
@@ -27,7 +20,8 @@ from modal_version import __version__
|
|
|
27
20
|
from ._traceback import print_server_warnings
|
|
28
21
|
from ._utils import async_utils
|
|
29
22
|
from ._utils.async_utils import TaskContext, synchronize_api
|
|
30
|
-
from ._utils.
|
|
23
|
+
from ._utils.auth_token_manager import _AuthTokenManager
|
|
24
|
+
from ._utils.grpc_utils import ConnectionManager
|
|
31
25
|
from .config import _check_config, _is_remote, config, logger
|
|
32
26
|
from .exception import AuthError, ClientClosed
|
|
33
27
|
|
|
@@ -78,6 +72,7 @@ class _Client:
|
|
|
78
72
|
_cancellation_context: TaskContext
|
|
79
73
|
_cancellation_context_event_loop: asyncio.AbstractEventLoop = None
|
|
80
74
|
_stub: Optional[api_grpc.ModalClientStub]
|
|
75
|
+
_auth_token_manager: _AuthTokenManager = None
|
|
81
76
|
_snapshotted: bool
|
|
82
77
|
|
|
83
78
|
def __init__(
|
|
@@ -96,6 +91,7 @@ class _Client:
|
|
|
96
91
|
self.version = version
|
|
97
92
|
self._closed = False
|
|
98
93
|
self._stub: Optional[modal_api_grpc.ModalClientModal] = None
|
|
94
|
+
self._auth_token_manager: Optional[_AuthTokenManager] = None
|
|
99
95
|
self._snapshotted = False
|
|
100
96
|
self._owner_pid = None
|
|
101
97
|
|
|
@@ -133,9 +129,9 @@ class _Client:
|
|
|
133
129
|
self._cancellation_context = TaskContext(grace=0.5) # allow running rpcs to finish in 0.5s when closing client
|
|
134
130
|
self._cancellation_context_event_loop = asyncio.get_running_loop()
|
|
135
131
|
await self._cancellation_context.__aenter__()
|
|
136
|
-
|
|
137
132
|
self._connection_manager = ConnectionManager(client=self, metadata=metadata)
|
|
138
133
|
self._stub = await self.get_stub(self.server_url)
|
|
134
|
+
self._auth_token_manager = _AuthTokenManager(self.stub)
|
|
139
135
|
self._owner_pid = os.getpid()
|
|
140
136
|
|
|
141
137
|
async def _close(self, prep_for_restore: bool = False):
|
|
@@ -155,7 +151,7 @@ class _Client:
|
|
|
155
151
|
async def hello(self):
|
|
156
152
|
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
|
157
153
|
logger.debug(f"Client ({id(self)}): Starting")
|
|
158
|
-
resp = await
|
|
154
|
+
resp = await self.stub.ClientHello(empty_pb2.Empty())
|
|
159
155
|
print_server_warnings(resp.server_warnings)
|
|
160
156
|
|
|
161
157
|
async def __aenter__(self):
|
|
@@ -264,6 +260,14 @@ class _Client:
|
|
|
264
260
|
# Just used from tests.
|
|
265
261
|
cls._client_from_env = client
|
|
266
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
|
+
|
|
267
271
|
async def _call_safely(self, coro, readable_method: str):
|
|
268
272
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
269
273
|
|
|
@@ -350,77 +354,3 @@ class _Client:
|
|
|
350
354
|
|
|
351
355
|
|
|
352
356
|
Client = synchronize_api(_Client)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
class UnaryUnaryWrapper(Generic[RequestType, ResponseType]):
|
|
356
|
-
# Calls a grpclib.UnaryUnaryMethod using a specific Client instance, respecting
|
|
357
|
-
# if that client is closed etc. and possibly introducing Modal-specific retry logic
|
|
358
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
|
359
|
-
client: _Client
|
|
360
|
-
|
|
361
|
-
def __init__(
|
|
362
|
-
self,
|
|
363
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
364
|
-
client: _Client,
|
|
365
|
-
server_url: str,
|
|
366
|
-
):
|
|
367
|
-
self.wrapped_method = wrapped_method
|
|
368
|
-
self.client = client
|
|
369
|
-
self.server_url = server_url
|
|
370
|
-
|
|
371
|
-
@property
|
|
372
|
-
def name(self) -> str:
|
|
373
|
-
return self.wrapped_method.name
|
|
374
|
-
|
|
375
|
-
async def __call__(
|
|
376
|
-
self,
|
|
377
|
-
req: RequestType,
|
|
378
|
-
*,
|
|
379
|
-
timeout: Optional[float] = None,
|
|
380
|
-
metadata: Optional[_MetadataLike] = None,
|
|
381
|
-
) -> ResponseType:
|
|
382
|
-
if self.client._snapshotted:
|
|
383
|
-
logger.debug(f"refreshing client after snapshot for {self.name.rsplit('/', 1)[1]}")
|
|
384
|
-
self.client = await _Client.from_env()
|
|
385
|
-
|
|
386
|
-
# Note: We override the grpclib method's channel (see grpclib's code [1]). I think this is fine
|
|
387
|
-
# since grpclib's code doesn't seem to change very much, but we could also recreate the
|
|
388
|
-
# grpclib stub if we aren't comfortable with this. The downside is then we need to cache
|
|
389
|
-
# the grpclib stub so the rest of our code becomes a bit more complicated.
|
|
390
|
-
#
|
|
391
|
-
# We need to override the channel because after the process is forked or the client is
|
|
392
|
-
# snapshotted, the existing channel may be stale / unusable.
|
|
393
|
-
#
|
|
394
|
-
# [1]: https://github.com/vmagamedov/grpclib/blob/62f968a4c84e3f64e6966097574ff0a59969ea9b/grpclib/client.py#L844
|
|
395
|
-
self.wrapped_method.channel = await self.client._get_channel(self.server_url)
|
|
396
|
-
return await self.client._call_unary(self.wrapped_method, req, timeout=timeout, metadata=metadata)
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
class UnaryStreamWrapper(Generic[RequestType, ResponseType]):
|
|
400
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
|
|
401
|
-
|
|
402
|
-
def __init__(
|
|
403
|
-
self,
|
|
404
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
405
|
-
client: _Client,
|
|
406
|
-
server_url: str,
|
|
407
|
-
):
|
|
408
|
-
self.wrapped_method = wrapped_method
|
|
409
|
-
self.client = client
|
|
410
|
-
self.server_url = server_url
|
|
411
|
-
|
|
412
|
-
@property
|
|
413
|
-
def name(self) -> str:
|
|
414
|
-
return self.wrapped_method.name
|
|
415
|
-
|
|
416
|
-
async def unary_stream(
|
|
417
|
-
self,
|
|
418
|
-
request,
|
|
419
|
-
metadata: Optional[Any] = None,
|
|
420
|
-
):
|
|
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
|
-
self.wrapped_method.channel = await self.client._get_channel(self.server_url)
|
|
425
|
-
async for response in self.client._call_stream(self.wrapped_method, request, metadata=metadata):
|
|
426
|
-
yield response
|
modal/client.pyi
CHANGED
|
@@ -4,6 +4,7 @@ import collections.abc
|
|
|
4
4
|
import google.protobuf.message
|
|
5
5
|
import grpclib.client
|
|
6
6
|
import modal._utils.async_utils
|
|
7
|
+
import modal._utils.auth_token_manager
|
|
7
8
|
import modal_proto.api_grpc
|
|
8
9
|
import modal_proto.modal_api_grpc
|
|
9
10
|
import synchronicity.combined_types
|
|
@@ -24,6 +25,7 @@ class _Client:
|
|
|
24
25
|
_cancellation_context: modal._utils.async_utils.TaskContext
|
|
25
26
|
_cancellation_context_event_loop: asyncio.events.AbstractEventLoop
|
|
26
27
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
|
28
|
+
_auth_token_manager: modal._utils.auth_token_manager._AuthTokenManager
|
|
27
29
|
_snapshotted: bool
|
|
28
30
|
|
|
29
31
|
def __init__(
|
|
@@ -31,28 +33,95 @@ class _Client:
|
|
|
31
33
|
server_url: str,
|
|
32
34
|
client_type: int,
|
|
33
35
|
credentials: typing.Optional[tuple[str, str]],
|
|
34
|
-
version: str = "1.
|
|
35
|
-
):
|
|
36
|
+
version: str = "1.2.3.dev7",
|
|
37
|
+
):
|
|
38
|
+
"""mdmd:hidden
|
|
39
|
+
The Modal client object is not intended to be instantiated directly by users.
|
|
40
|
+
"""
|
|
41
|
+
...
|
|
42
|
+
|
|
36
43
|
def is_closed(self) -> bool: ...
|
|
37
44
|
@property
|
|
38
|
-
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
39
|
-
|
|
45
|
+
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
46
|
+
"""mdmd:hidden
|
|
47
|
+
The default stub. Stubs can safely be used across forks / client snapshots.
|
|
48
|
+
|
|
49
|
+
This is useful if you want to make requests to the default Modal server in us-east, for example
|
|
50
|
+
control plane requests.
|
|
51
|
+
|
|
52
|
+
This is equivalent to client.get_stub(default_server_url), but it's cached, so it's a bit faster.
|
|
53
|
+
"""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
async def get_stub(self, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
57
|
+
"""mdmd:hidden
|
|
58
|
+
Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
|
|
59
|
+
|
|
60
|
+
This is useful if you want to make requests to a regional Modal server, for example low-latency
|
|
61
|
+
function calls in us-west.
|
|
62
|
+
|
|
63
|
+
This function is O(n) where n is the number of RPCs in ModalClient.
|
|
64
|
+
"""
|
|
65
|
+
...
|
|
66
|
+
|
|
40
67
|
async def _open(self): ...
|
|
41
68
|
async def _close(self, prep_for_restore: bool = False): ...
|
|
42
|
-
async def hello(self):
|
|
69
|
+
async def hello(self):
|
|
70
|
+
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
|
71
|
+
...
|
|
72
|
+
|
|
43
73
|
async def __aenter__(self): ...
|
|
44
74
|
async def __aexit__(self, exc_type, exc, tb): ...
|
|
45
75
|
@classmethod
|
|
46
|
-
def anonymous(cls, server_url: str) -> typing.AsyncContextManager[_Client]:
|
|
76
|
+
def anonymous(cls, server_url: str) -> typing.AsyncContextManager[_Client]:
|
|
77
|
+
"""mdmd:hidden
|
|
78
|
+
Create a connection with no credentials; to be used for token creation.
|
|
79
|
+
"""
|
|
80
|
+
...
|
|
81
|
+
|
|
47
82
|
@classmethod
|
|
48
|
-
async def from_env(cls, _override_config=None) -> _Client:
|
|
83
|
+
async def from_env(cls, _override_config=None) -> _Client:
|
|
84
|
+
"""mdmd:hidden
|
|
85
|
+
Singleton that is instantiated from the Modal config and reused on subsequent calls.
|
|
86
|
+
"""
|
|
87
|
+
...
|
|
88
|
+
|
|
49
89
|
@classmethod
|
|
50
|
-
async def from_credentials(cls, token_id: str, token_secret: str) -> _Client:
|
|
90
|
+
async def from_credentials(cls, token_id: str, token_secret: str) -> _Client:
|
|
91
|
+
"""Constructor based on token credentials; useful for managing Modal on behalf of third-party users.
|
|
92
|
+
|
|
93
|
+
**Usage:**
|
|
94
|
+
|
|
95
|
+
```python notest
|
|
96
|
+
client = modal.Client.from_credentials("my_token_id", "my_token_secret")
|
|
97
|
+
|
|
98
|
+
modal.Sandbox.create("echo", "hi", client=client, app=app)
|
|
99
|
+
```
|
|
100
|
+
"""
|
|
101
|
+
...
|
|
102
|
+
|
|
51
103
|
@classmethod
|
|
52
|
-
async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
|
104
|
+
async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
|
105
|
+
"""mdmd:hidden
|
|
106
|
+
Check whether can the client can connect to this server with these credentials; raise if not.
|
|
107
|
+
"""
|
|
108
|
+
...
|
|
109
|
+
|
|
53
110
|
@classmethod
|
|
54
|
-
def set_env_client(cls, client: typing.Optional[_Client]):
|
|
55
|
-
|
|
111
|
+
def set_env_client(cls, client: typing.Optional[_Client]):
|
|
112
|
+
"""mdmd:hidden"""
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
async def get_input_plane_metadata(self, input_plane_region: str) -> list[tuple[str, str]]: ...
|
|
116
|
+
async def _call_safely(self, coro, readable_method: str):
|
|
117
|
+
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
118
|
+
|
|
119
|
+
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
|
120
|
+
* Logs warning if call is made outside of the event loop that the client is running in,
|
|
121
|
+
and execute without the cancellation context in that case
|
|
122
|
+
"""
|
|
123
|
+
...
|
|
124
|
+
|
|
56
125
|
async def _reset_on_pid_change(self): ...
|
|
57
126
|
async def _get_channel(self, server_url: str) -> grpclib.client.Channel: ...
|
|
58
127
|
async def _call_unary(
|
|
@@ -87,6 +156,7 @@ class Client:
|
|
|
87
156
|
_cancellation_context: modal._utils.async_utils.TaskContext
|
|
88
157
|
_cancellation_context_event_loop: asyncio.events.AbstractEventLoop
|
|
89
158
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
|
159
|
+
_auth_token_manager: modal._utils.auth_token_manager._AuthTokenManager
|
|
90
160
|
_snapshotted: bool
|
|
91
161
|
|
|
92
162
|
def __init__(
|
|
@@ -94,15 +164,48 @@ class Client:
|
|
|
94
164
|
server_url: str,
|
|
95
165
|
client_type: int,
|
|
96
166
|
credentials: typing.Optional[tuple[str, str]],
|
|
97
|
-
version: str = "1.
|
|
98
|
-
):
|
|
167
|
+
version: str = "1.2.3.dev7",
|
|
168
|
+
):
|
|
169
|
+
"""mdmd:hidden
|
|
170
|
+
The Modal client object is not intended to be instantiated directly by users.
|
|
171
|
+
"""
|
|
172
|
+
...
|
|
173
|
+
|
|
99
174
|
def is_closed(self) -> bool: ...
|
|
100
175
|
@property
|
|
101
|
-
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
176
|
+
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
177
|
+
"""mdmd:hidden
|
|
178
|
+
The default stub. Stubs can safely be used across forks / client snapshots.
|
|
179
|
+
|
|
180
|
+
This is useful if you want to make requests to the default Modal server in us-east, for example
|
|
181
|
+
control plane requests.
|
|
182
|
+
|
|
183
|
+
This is equivalent to client.get_stub(default_server_url), but it's cached, so it's a bit faster.
|
|
184
|
+
"""
|
|
185
|
+
...
|
|
102
186
|
|
|
103
187
|
class __get_stub_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
104
|
-
def __call__(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
105
|
-
|
|
188
|
+
def __call__(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
189
|
+
"""mdmd:hidden
|
|
190
|
+
Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
|
|
191
|
+
|
|
192
|
+
This is useful if you want to make requests to a regional Modal server, for example low-latency
|
|
193
|
+
function calls in us-west.
|
|
194
|
+
|
|
195
|
+
This function is O(n) where n is the number of RPCs in ModalClient.
|
|
196
|
+
"""
|
|
197
|
+
...
|
|
198
|
+
|
|
199
|
+
async def aio(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
|
200
|
+
"""mdmd:hidden
|
|
201
|
+
Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
|
|
202
|
+
|
|
203
|
+
This is useful if you want to make requests to a regional Modal server, for example low-latency
|
|
204
|
+
function calls in us-west.
|
|
205
|
+
|
|
206
|
+
This function is O(n) where n is the number of RPCs in ModalClient.
|
|
207
|
+
"""
|
|
208
|
+
...
|
|
106
209
|
|
|
107
210
|
get_stub: __get_stub_spec[typing_extensions.Self]
|
|
108
211
|
|
|
@@ -119,8 +222,13 @@ class Client:
|
|
|
119
222
|
_close: ___close_spec[typing_extensions.Self]
|
|
120
223
|
|
|
121
224
|
class __hello_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
122
|
-
def __call__(self, /):
|
|
123
|
-
|
|
225
|
+
def __call__(self, /):
|
|
226
|
+
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
|
227
|
+
...
|
|
228
|
+
|
|
229
|
+
async def aio(self, /):
|
|
230
|
+
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
|
231
|
+
...
|
|
124
232
|
|
|
125
233
|
hello: __hello_spec[typing_extensions.Self]
|
|
126
234
|
|
|
@@ -129,19 +237,69 @@ class Client:
|
|
|
129
237
|
def __exit__(self, exc_type, exc, tb): ...
|
|
130
238
|
async def __aexit__(self, exc_type, exc, tb): ...
|
|
131
239
|
@classmethod
|
|
132
|
-
def anonymous(cls, server_url: str) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Client]:
|
|
240
|
+
def anonymous(cls, server_url: str) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Client]:
|
|
241
|
+
"""mdmd:hidden
|
|
242
|
+
Create a connection with no credentials; to be used for token creation.
|
|
243
|
+
"""
|
|
244
|
+
...
|
|
245
|
+
|
|
133
246
|
@classmethod
|
|
134
|
-
def from_env(cls, _override_config=None) -> Client:
|
|
247
|
+
def from_env(cls, _override_config=None) -> Client:
|
|
248
|
+
"""mdmd:hidden
|
|
249
|
+
Singleton that is instantiated from the Modal config and reused on subsequent calls.
|
|
250
|
+
"""
|
|
251
|
+
...
|
|
252
|
+
|
|
135
253
|
@classmethod
|
|
136
|
-
def from_credentials(cls, token_id: str, token_secret: str) -> Client:
|
|
254
|
+
def from_credentials(cls, token_id: str, token_secret: str) -> Client:
|
|
255
|
+
"""Constructor based on token credentials; useful for managing Modal on behalf of third-party users.
|
|
256
|
+
|
|
257
|
+
**Usage:**
|
|
258
|
+
|
|
259
|
+
```python notest
|
|
260
|
+
client = modal.Client.from_credentials("my_token_id", "my_token_secret")
|
|
261
|
+
|
|
262
|
+
modal.Sandbox.create("echo", "hi", client=client, app=app)
|
|
263
|
+
```
|
|
264
|
+
"""
|
|
265
|
+
...
|
|
266
|
+
|
|
137
267
|
@classmethod
|
|
138
|
-
def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
|
268
|
+
def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
|
269
|
+
"""mdmd:hidden
|
|
270
|
+
Check whether can the client can connect to this server with these credentials; raise if not.
|
|
271
|
+
"""
|
|
272
|
+
...
|
|
273
|
+
|
|
139
274
|
@classmethod
|
|
140
|
-
def set_env_client(cls, client: typing.Optional[Client]):
|
|
275
|
+
def set_env_client(cls, client: typing.Optional[Client]):
|
|
276
|
+
"""mdmd:hidden"""
|
|
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]
|
|
141
284
|
|
|
142
285
|
class ___call_safely_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
143
|
-
def __call__(self, /, coro, readable_method: str):
|
|
144
|
-
|
|
286
|
+
def __call__(self, /, coro, readable_method: str):
|
|
287
|
+
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
288
|
+
|
|
289
|
+
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
|
290
|
+
* Logs warning if call is made outside of the event loop that the client is running in,
|
|
291
|
+
and execute without the cancellation context in that case
|
|
292
|
+
"""
|
|
293
|
+
...
|
|
294
|
+
|
|
295
|
+
async def aio(self, /, coro, readable_method: str):
|
|
296
|
+
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
297
|
+
|
|
298
|
+
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
|
299
|
+
* Logs warning if call is made outside of the event loop that the client is running in,
|
|
300
|
+
and execute without the cancellation context in that case
|
|
301
|
+
"""
|
|
302
|
+
...
|
|
145
303
|
|
|
146
304
|
_call_safely: ___call_safely_spec[typing_extensions.Self]
|
|
147
305
|
|
|
@@ -181,43 +339,6 @@ class Client:
|
|
|
181
339
|
],
|
|
182
340
|
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
|
183
341
|
|
|
184
|
-
class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
|
185
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
|
186
|
-
client: _Client
|
|
187
|
-
|
|
188
|
-
def __init__(
|
|
189
|
-
self,
|
|
190
|
-
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
191
|
-
client: _Client,
|
|
192
|
-
server_url: str,
|
|
193
|
-
): ...
|
|
194
|
-
@property
|
|
195
|
-
def name(self) -> str: ...
|
|
196
|
-
async def __call__(
|
|
197
|
-
self,
|
|
198
|
-
req: RequestType,
|
|
199
|
-
*,
|
|
200
|
-
timeout: typing.Optional[float] = None,
|
|
201
|
-
metadata: typing.Union[
|
|
202
|
-
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
|
203
|
-
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
|
204
|
-
None,
|
|
205
|
-
] = None,
|
|
206
|
-
) -> ResponseType: ...
|
|
207
|
-
|
|
208
|
-
class UnaryStreamWrapper(typing.Generic[RequestType, ResponseType]):
|
|
209
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
|
|
210
|
-
|
|
211
|
-
def __init__(
|
|
212
|
-
self,
|
|
213
|
-
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
214
|
-
client: _Client,
|
|
215
|
-
server_url: str,
|
|
216
|
-
): ...
|
|
217
|
-
@property
|
|
218
|
-
def name(self) -> str: ...
|
|
219
|
-
def unary_stream(self, request, metadata: typing.Optional[typing.Any] = None): ...
|
|
220
|
-
|
|
221
342
|
HEARTBEAT_INTERVAL: float
|
|
222
343
|
|
|
223
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
|
|