modal 0.67.6__py3-none-any.whl → 0.67.8__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 +2 -2
- modal/_clustered_functions.pyi +2 -2
- modal/_container_entrypoint.py +5 -4
- modal/_output.py +29 -28
- modal/_pty.py +2 -2
- modal/_resolver.py +6 -5
- modal/_resources.py +3 -3
- modal/_runtime/asgi.py +7 -6
- modal/_runtime/container_io_manager.py +22 -26
- modal/_runtime/execution_context.py +2 -2
- modal/_runtime/telemetry.py +1 -2
- modal/_runtime/user_code_imports.py +11 -13
- modal/_serialization.py +3 -7
- modal/_traceback.py +5 -5
- modal/_tunnel.py +4 -3
- modal/_tunnel.pyi +2 -2
- modal/_utils/async_utils.py +8 -15
- modal/_utils/blob_utils.py +4 -3
- modal/_utils/function_utils.py +14 -10
- modal/_utils/grpc_testing.py +7 -6
- modal/_utils/grpc_utils.py +2 -3
- modal/_utils/hash_utils.py +2 -2
- modal/_utils/mount_utils.py +5 -4
- modal/_utils/package_utils.py +2 -3
- modal/_utils/pattern_matcher.py +6 -6
- modal/_utils/rand_pb_testing.py +3 -3
- modal/_utils/shell_utils.py +2 -1
- modal/_vendor/a2wsgi_wsgi.py +62 -72
- modal/_vendor/cloudpickle.py +1 -1
- modal/_watcher.py +8 -7
- modal/app.py +29 -34
- modal/app.pyi +102 -97
- modal/call_graph.py +6 -6
- modal/cli/_download.py +3 -2
- modal/cli/_traceback.py +4 -4
- modal/cli/app.py +4 -4
- modal/cli/container.py +4 -4
- modal/cli/dict.py +1 -1
- modal/cli/environment.py +2 -3
- modal/cli/launch.py +2 -2
- modal/cli/network_file_system.py +1 -1
- modal/cli/profile.py +1 -1
- modal/cli/programs/run_jupyter.py +2 -2
- modal/cli/programs/vscode.py +3 -3
- modal/cli/queues.py +1 -1
- modal/cli/run.py +6 -6
- modal/cli/secret.py +3 -3
- modal/cli/utils.py +2 -1
- modal/cli/volume.py +3 -3
- modal/client.py +6 -11
- modal/client.pyi +18 -27
- modal/cloud_bucket_mount.py +3 -3
- modal/cloud_bucket_mount.pyi +2 -2
- modal/cls.py +16 -15
- modal/cls.pyi +23 -22
- modal/config.py +2 -2
- modal/dict.py +4 -3
- modal/dict.pyi +10 -9
- modal/environments.py +3 -3
- modal/environments.pyi +3 -3
- modal/exception.py +2 -3
- modal/functions.py +20 -27
- modal/functions.pyi +44 -47
- modal/image.py +45 -48
- modal/image.pyi +102 -101
- modal/io_streams.py +4 -7
- modal/io_streams.pyi +14 -13
- modal/mount.py +23 -22
- modal/mount.pyi +28 -29
- modal/network_file_system.py +7 -6
- modal/network_file_system.pyi +12 -11
- modal/object.py +9 -8
- modal/object.pyi +47 -34
- modal/output.py +2 -1
- modal/parallel_map.py +4 -4
- modal/partial_function.py +9 -13
- modal/partial_function.pyi +17 -18
- modal/queue.py +9 -8
- modal/queue.pyi +23 -22
- modal/runner.py +8 -7
- modal/runner.pyi +8 -14
- modal/running_app.py +3 -3
- modal/sandbox.py +14 -13
- modal/sandbox.pyi +67 -72
- modal/scheduler_placement.py +2 -1
- modal/secret.py +7 -7
- modal/secret.pyi +12 -12
- modal/serving.py +4 -3
- modal/serving.pyi +5 -4
- modal/token_flow.py +3 -2
- modal/token_flow.pyi +3 -3
- modal/volume.py +7 -12
- modal/volume.pyi +17 -16
- {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/METADATA +1 -1
- modal-0.67.8.dist-info/RECORD +168 -0
- modal_docs/mdmd/signatures.py +1 -2
- modal_version/_version_generated.py +1 -1
- modal-0.67.6.dist-info/RECORD +0 -168
- {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/LICENSE +0 -0
- {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/WHEEL +0 -0
- {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/entry_points.txt +0 -0
- {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/top_level.txt +0 -0
modal/cli/profile.py
CHANGED
@@ -28,7 +28,7 @@ def current():
|
|
28
28
|
|
29
29
|
@profile_cli.command(name="list", help="Show all Modal profiles and highlight the active one.")
|
30
30
|
@synchronizer.create_blocking
|
31
|
-
async def
|
31
|
+
async def list_(json: Optional[bool] = False):
|
32
32
|
config = Config()
|
33
33
|
profiles = config_profiles()
|
34
34
|
lookup_coros = [
|
@@ -8,12 +8,12 @@ import subprocess
|
|
8
8
|
import threading
|
9
9
|
import time
|
10
10
|
import webbrowser
|
11
|
-
from typing import Any
|
11
|
+
from typing import Any
|
12
12
|
|
13
13
|
from modal import App, Image, Mount, Queue, Secret, Volume, forward
|
14
14
|
|
15
15
|
# Passed by `modal launch` locally via CLI, plumbed to remote runner through secrets.
|
16
|
-
args:
|
16
|
+
args: dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))
|
17
17
|
|
18
18
|
|
19
19
|
app = App()
|
modal/cli/programs/vscode.py
CHANGED
@@ -8,12 +8,12 @@ import subprocess
|
|
8
8
|
import threading
|
9
9
|
import time
|
10
10
|
import webbrowser
|
11
|
-
from typing import Any
|
11
|
+
from typing import Any
|
12
12
|
|
13
13
|
from modal import App, Image, Mount, Queue, Secret, Volume, forward
|
14
14
|
|
15
15
|
# Passed by `modal launch` locally via CLI, plumbed to remote runner through secrets.
|
16
|
-
args:
|
16
|
+
args: dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))
|
17
17
|
|
18
18
|
|
19
19
|
app = App()
|
@@ -41,7 +41,7 @@ volume = (
|
|
41
41
|
volumes = {"/home/coder/volume": volume} if volume else {}
|
42
42
|
|
43
43
|
|
44
|
-
def wait_for_port(data:
|
44
|
+
def wait_for_port(data: tuple[str, str], q: Queue):
|
45
45
|
start_time = time.monotonic()
|
46
46
|
while True:
|
47
47
|
try:
|
modal/cli/queues.py
CHANGED
@@ -58,7 +58,7 @@ async def delete(name: str, *, yes: bool = YES_OPTION, env: Optional[str] = ENV_
|
|
58
58
|
|
59
59
|
@queue_cli.command(name="list", rich_help_panel="Management")
|
60
60
|
@synchronizer.create_blocking
|
61
|
-
async def
|
61
|
+
async def list_(*, json: bool = False, env: Optional[str] = ENV_OPTION):
|
62
62
|
"""List all named Queues."""
|
63
63
|
env = ensure_env(env)
|
64
64
|
|
modal/cli/run.py
CHANGED
@@ -9,7 +9,7 @@ import sys
|
|
9
9
|
import time
|
10
10
|
import typing
|
11
11
|
from functools import partial
|
12
|
-
from typing import Any, Callable,
|
12
|
+
from typing import Any, Callable, Optional, get_type_hints
|
13
13
|
|
14
14
|
import click
|
15
15
|
import typer
|
@@ -60,7 +60,7 @@ class NoParserAvailable(InvalidError):
|
|
60
60
|
pass
|
61
61
|
|
62
62
|
|
63
|
-
def _get_signature(f: Callable[..., Any], is_method: bool = False) ->
|
63
|
+
def _get_signature(f: Callable[..., Any], is_method: bool = False) -> dict[str, ParameterMetadata]:
|
64
64
|
try:
|
65
65
|
type_hints = get_type_hints(f)
|
66
66
|
except Exception as exc:
|
@@ -71,7 +71,7 @@ def _get_signature(f: Callable[..., Any], is_method: bool = False) -> Dict[str,
|
|
71
71
|
if is_method:
|
72
72
|
self = None # Dummy, doesn't matter
|
73
73
|
f = functools.partial(f, self)
|
74
|
-
signature:
|
74
|
+
signature: dict[str, ParameterMetadata] = {}
|
75
75
|
for param in inspect.signature(f).parameters.values():
|
76
76
|
signature[param.name] = {
|
77
77
|
"name": param.name,
|
@@ -98,7 +98,7 @@ def _get_param_type_as_str(annot: Any) -> str:
|
|
98
98
|
return annot_str
|
99
99
|
|
100
100
|
|
101
|
-
def _add_click_options(func, signature:
|
101
|
+
def _add_click_options(func, signature: dict[str, ParameterMetadata]):
|
102
102
|
"""Adds @click.option based on function signature
|
103
103
|
|
104
104
|
Kind of like typer, but using options instead of positional arguments
|
@@ -148,7 +148,7 @@ def _get_click_command_for_function(app: App, function_tag):
|
|
148
148
|
if function.is_generator:
|
149
149
|
raise InvalidError("`modal run` is not supported for generator functions")
|
150
150
|
|
151
|
-
signature:
|
151
|
+
signature: dict[str, ParameterMetadata]
|
152
152
|
cls: Optional[Cls] = None
|
153
153
|
if function.info.user_cls is not None:
|
154
154
|
cls = typing.cast(Cls, app.registered_classes[class_name])
|
@@ -364,7 +364,7 @@ def shell(
|
|
364
364
|
default=None, help="Container image tag for inside the shell (if not using REF)."
|
365
365
|
),
|
366
366
|
add_python: Optional[str] = typer.Option(default=None, help="Add Python to the image (if not using REF)."),
|
367
|
-
volume: Optional[
|
367
|
+
volume: Optional[list[str]] = typer.Option(
|
368
368
|
default=None,
|
369
369
|
help=(
|
370
370
|
"Name of a `modal.Volume` to mount inside the shell at `/mnt/{name}` (if not using REF)."
|
modal/cli/secret.py
CHANGED
@@ -3,7 +3,7 @@ import os
|
|
3
3
|
import platform
|
4
4
|
import subprocess
|
5
5
|
from tempfile import NamedTemporaryFile
|
6
|
-
from typing import
|
6
|
+
from typing import Optional
|
7
7
|
|
8
8
|
import click
|
9
9
|
import typer
|
@@ -23,7 +23,7 @@ secret_cli = typer.Typer(name="secret", help="Manage secrets.", no_args_is_help=
|
|
23
23
|
|
24
24
|
@secret_cli.command("list", help="List your published secrets.")
|
25
25
|
@synchronizer.create_blocking
|
26
|
-
async def
|
26
|
+
async def list_(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
|
27
27
|
env = ensure_env(env)
|
28
28
|
client = await _Client.from_env()
|
29
29
|
response = await retry_transient_errors(client.stub.SecretList, api_pb2.SecretListRequest(environment_name=env))
|
@@ -47,7 +47,7 @@ async def list(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
|
|
47
47
|
@synchronizer.create_blocking
|
48
48
|
async def create(
|
49
49
|
secret_name,
|
50
|
-
keyvalues:
|
50
|
+
keyvalues: list[str] = typer.Argument(..., help="Space-separated KEY=VALUE items"),
|
51
51
|
env: Optional[str] = ENV_OPTION,
|
52
52
|
force: bool = typer.Option(False, "--force", help="Overwrite the secret if it already exists."),
|
53
53
|
):
|
modal/cli/utils.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
2
|
import asyncio
|
3
|
+
from collections.abc import Sequence
|
3
4
|
from datetime import datetime
|
4
5
|
from json import dumps
|
5
|
-
from typing import Optional,
|
6
|
+
from typing import Optional, Union
|
6
7
|
|
7
8
|
import typer
|
8
9
|
from click import UsageError
|
modal/cli/volume.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
import os
|
3
3
|
import sys
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import
|
5
|
+
from typing import Optional
|
6
6
|
|
7
7
|
import typer
|
8
8
|
from click import UsageError
|
@@ -108,7 +108,7 @@ async def get(
|
|
108
108
|
rich_help_panel="Management",
|
109
109
|
)
|
110
110
|
@synchronizer.create_blocking
|
111
|
-
async def
|
111
|
+
async def list_(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
|
112
112
|
env = ensure_env(env)
|
113
113
|
client = await _Client.from_env()
|
114
114
|
response = await retry_transient_errors(client.stub.VolumeList, api_pb2.VolumeListRequest(environment_name=env))
|
@@ -257,7 +257,7 @@ async def rm(
|
|
257
257
|
@synchronizer.create_blocking
|
258
258
|
async def cp(
|
259
259
|
volume_name: str,
|
260
|
-
paths:
|
260
|
+
paths: list[str], # accepts multiple paths, last path is treated as destination path
|
261
261
|
env: Optional[str] = ENV_OPTION,
|
262
262
|
):
|
263
263
|
ensure_env(env)
|
modal/client.py
CHANGED
@@ -3,17 +3,12 @@ import asyncio
|
|
3
3
|
import os
|
4
4
|
import platform
|
5
5
|
import warnings
|
6
|
+
from collections.abc import AsyncGenerator, AsyncIterator, Collection, Mapping
|
6
7
|
from typing import (
|
7
8
|
Any,
|
8
|
-
AsyncGenerator,
|
9
|
-
AsyncIterator,
|
10
9
|
ClassVar,
|
11
|
-
Collection,
|
12
|
-
Dict,
|
13
10
|
Generic,
|
14
|
-
Mapping,
|
15
11
|
Optional,
|
16
|
-
Tuple,
|
17
12
|
TypeVar,
|
18
13
|
Union,
|
19
14
|
)
|
@@ -40,7 +35,7 @@ CLIENT_CREATE_ATTEMPT_TIMEOUT: float = 4.0
|
|
40
35
|
CLIENT_CREATE_TOTAL_TIMEOUT: float = 15.0
|
41
36
|
|
42
37
|
|
43
|
-
def _get_metadata(client_type: int, credentials: Optional[
|
38
|
+
def _get_metadata(client_type: int, credentials: Optional[tuple[str, str]], version: str) -> dict[str, str]:
|
44
39
|
# This implements a simplified version of platform.platform() that's still machine-readable
|
45
40
|
uname: platform.uname_result = platform.uname()
|
46
41
|
if uname.system == "Darwin":
|
@@ -69,7 +64,7 @@ def _get_metadata(client_type: int, credentials: Optional[Tuple[str, str]], vers
|
|
69
64
|
|
70
65
|
ReturnType = TypeVar("ReturnType")
|
71
66
|
_Value = Union[str, bytes]
|
72
|
-
_MetadataLike = Union[Mapping[str, _Value], Collection[
|
67
|
+
_MetadataLike = Union[Mapping[str, _Value], Collection[tuple[str, _Value]]]
|
73
68
|
RequestType = TypeVar("RequestType", bound=Message)
|
74
69
|
ResponseType = TypeVar("ResponseType", bound=Message)
|
75
70
|
|
@@ -85,7 +80,7 @@ class _Client:
|
|
85
80
|
self,
|
86
81
|
server_url: str,
|
87
82
|
client_type: int,
|
88
|
-
credentials: Optional[
|
83
|
+
credentials: Optional[tuple[str, str]],
|
89
84
|
version: str = __version__,
|
90
85
|
):
|
91
86
|
"""mdmd:hidden
|
@@ -201,7 +196,7 @@ class _Client:
|
|
201
196
|
else:
|
202
197
|
c = config
|
203
198
|
|
204
|
-
credentials: Optional[
|
199
|
+
credentials: Optional[tuple[str, str]]
|
205
200
|
|
206
201
|
if cls._client_from_env_lock is None:
|
207
202
|
cls._client_from_env_lock = asyncio.Lock()
|
@@ -266,7 +261,7 @@ class _Client:
|
|
266
261
|
return client
|
267
262
|
|
268
263
|
@classmethod
|
269
|
-
async def verify(cls, server_url: str, credentials:
|
264
|
+
async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
270
265
|
"""mdmd:hidden
|
271
266
|
Check whether can the client can connect to this server with these credentials; raise if not.
|
272
267
|
"""
|
modal/client.pyi
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import asyncio.events
|
2
2
|
import asyncio.locks
|
3
|
+
import collections.abc
|
3
4
|
import google.protobuf.message
|
4
5
|
import grpclib.client
|
5
6
|
import modal._utils.async_utils
|
@@ -9,9 +10,7 @@ import synchronicity.combined_types
|
|
9
10
|
import typing
|
10
11
|
import typing_extensions
|
11
12
|
|
12
|
-
def _get_metadata(
|
13
|
-
client_type: int, credentials: typing.Optional[typing.Tuple[str, str]], version: str
|
14
|
-
) -> typing.Dict[str, str]: ...
|
13
|
+
def _get_metadata(client_type: int, credentials: typing.Optional[tuple[str, str]], version: str) -> dict[str, str]: ...
|
15
14
|
|
16
15
|
ReturnType = typing.TypeVar("ReturnType")
|
17
16
|
|
@@ -27,11 +26,7 @@ class _Client:
|
|
27
26
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
28
27
|
|
29
28
|
def __init__(
|
30
|
-
self,
|
31
|
-
server_url: str,
|
32
|
-
client_type: int,
|
33
|
-
credentials: typing.Optional[typing.Tuple[str, str]],
|
34
|
-
version: str = "0.67.6",
|
29
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.8"
|
35
30
|
): ...
|
36
31
|
def is_closed(self) -> bool: ...
|
37
32
|
@property
|
@@ -48,7 +43,7 @@ class _Client:
|
|
48
43
|
@classmethod
|
49
44
|
async def from_credentials(cls, token_id: str, token_secret: str) -> _Client: ...
|
50
45
|
@classmethod
|
51
|
-
async def verify(cls, server_url: str, credentials:
|
46
|
+
async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None: ...
|
52
47
|
@classmethod
|
53
48
|
def set_env_client(cls, client: typing.Optional[_Client]): ...
|
54
49
|
async def _call_safely(self, coro, readable_method: str): ...
|
@@ -61,8 +56,8 @@ class _Client:
|
|
61
56
|
*,
|
62
57
|
timeout: typing.Optional[float] = None,
|
63
58
|
metadata: typing.Union[
|
64
|
-
|
65
|
-
|
59
|
+
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
60
|
+
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
66
61
|
None,
|
67
62
|
] = None,
|
68
63
|
) -> typing.Any: ...
|
@@ -72,11 +67,11 @@ class _Client:
|
|
72
67
|
request: typing.Any,
|
73
68
|
*,
|
74
69
|
metadata: typing.Union[
|
75
|
-
|
76
|
-
|
70
|
+
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
71
|
+
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
77
72
|
None,
|
78
73
|
],
|
79
|
-
) ->
|
74
|
+
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
80
75
|
|
81
76
|
class Client:
|
82
77
|
_client_from_env: typing.ClassVar[typing.Optional[Client]]
|
@@ -86,11 +81,7 @@ class Client:
|
|
86
81
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
87
82
|
|
88
83
|
def __init__(
|
89
|
-
self,
|
90
|
-
server_url: str,
|
91
|
-
client_type: int,
|
92
|
-
credentials: typing.Optional[typing.Tuple[str, str]],
|
93
|
-
version: str = "0.67.6",
|
84
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.8"
|
94
85
|
): ...
|
95
86
|
def is_closed(self) -> bool: ...
|
96
87
|
@property
|
@@ -125,7 +116,7 @@ class Client:
|
|
125
116
|
@classmethod
|
126
117
|
def from_credentials(cls, token_id: str, token_secret: str) -> Client: ...
|
127
118
|
@classmethod
|
128
|
-
def verify(cls, server_url: str, credentials:
|
119
|
+
def verify(cls, server_url: str, credentials: tuple[str, str]) -> None: ...
|
129
120
|
@classmethod
|
130
121
|
def set_env_client(cls, client: typing.Optional[Client]): ...
|
131
122
|
|
@@ -154,8 +145,8 @@ class Client:
|
|
154
145
|
*,
|
155
146
|
timeout: typing.Optional[float] = None,
|
156
147
|
metadata: typing.Union[
|
157
|
-
|
158
|
-
|
148
|
+
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
149
|
+
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
159
150
|
None,
|
160
151
|
] = None,
|
161
152
|
) -> typing.Any: ...
|
@@ -165,11 +156,11 @@ class Client:
|
|
165
156
|
request: typing.Any,
|
166
157
|
*,
|
167
158
|
metadata: typing.Union[
|
168
|
-
|
169
|
-
|
159
|
+
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
160
|
+
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
170
161
|
None,
|
171
162
|
],
|
172
|
-
) ->
|
163
|
+
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
173
164
|
|
174
165
|
class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
175
166
|
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
@@ -184,8 +175,8 @@ class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
|
184
175
|
*,
|
185
176
|
timeout: typing.Optional[float] = None,
|
186
177
|
metadata: typing.Union[
|
187
|
-
|
188
|
-
|
178
|
+
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
179
|
+
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
189
180
|
None,
|
190
181
|
] = None,
|
191
182
|
) -> ResponseType: ...
|
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
|
3
|
+
from typing import Optional
|
4
4
|
from urllib.parse import urlparse
|
5
5
|
|
6
6
|
from modal_proto import api_pb2
|
@@ -116,9 +116,9 @@ class _CloudBucketMount:
|
|
116
116
|
requester_pays: bool = False
|
117
117
|
|
118
118
|
|
119
|
-
def cloud_bucket_mounts_to_proto(mounts:
|
119
|
+
def cloud_bucket_mounts_to_proto(mounts: list[tuple[str, _CloudBucketMount]]) -> list[api_pb2.CloudBucketMount]:
|
120
120
|
"""Helper function to convert `CloudBucketMount` to a list of protobufs that can be passed to the server."""
|
121
|
-
cloud_bucket_mounts:
|
121
|
+
cloud_bucket_mounts: list[api_pb2.CloudBucketMount] = []
|
122
122
|
|
123
123
|
for path, mount in mounts:
|
124
124
|
# crude mapping from mount arguments to type.
|
modal/cloud_bucket_mount.pyi
CHANGED
@@ -23,8 +23,8 @@ class _CloudBucketMount:
|
|
23
23
|
def __eq__(self, other): ...
|
24
24
|
|
25
25
|
def cloud_bucket_mounts_to_proto(
|
26
|
-
mounts:
|
27
|
-
) ->
|
26
|
+
mounts: list[tuple[str, _CloudBucketMount]],
|
27
|
+
) -> list[modal_proto.api_pb2.CloudBucketMount]: ...
|
28
28
|
|
29
29
|
class CloudBucketMount:
|
30
30
|
bucket_name: str
|
modal/cls.py
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
import inspect
|
3
3
|
import os
|
4
4
|
import typing
|
5
|
-
from
|
5
|
+
from collections.abc import Collection
|
6
|
+
from typing import Any, Callable, Optional, TypeVar, Union
|
6
7
|
|
7
8
|
from google.protobuf.message import Message
|
8
9
|
from grpclib import GRPCError, Status
|
@@ -76,10 +77,10 @@ class _Obj:
|
|
76
77
|
|
77
78
|
All this class does is to return `Function` objects."""
|
78
79
|
|
79
|
-
_functions:
|
80
|
+
_functions: dict[str, _Function]
|
80
81
|
_entered: bool
|
81
82
|
_user_cls_instance: Optional[Any] = None
|
82
|
-
_construction_args:
|
83
|
+
_construction_args: tuple[tuple, dict[str, Any]]
|
83
84
|
|
84
85
|
_instance_service_function: Optional[_Function]
|
85
86
|
|
@@ -91,7 +92,7 @@ class _Obj:
|
|
91
92
|
self,
|
92
93
|
user_cls: type,
|
93
94
|
class_service_function: Optional[_Function], # only None for <v0.63 classes
|
94
|
-
classbound_methods:
|
95
|
+
classbound_methods: dict[str, _Function],
|
95
96
|
from_other_workspace: bool,
|
96
97
|
options: Optional[api_pb2.FunctionOptions],
|
97
98
|
args,
|
@@ -244,9 +245,9 @@ class _Cls(_Object, type_prefix="cs"):
|
|
244
245
|
_class_service_function: Optional[
|
245
246
|
_Function
|
246
247
|
] # The _Function serving *all* methods of the class, used for version >=v0.63
|
247
|
-
_method_functions: Optional[
|
248
|
+
_method_functions: Optional[dict[str, _Function]] = None # Placeholder _Functions for each method
|
248
249
|
_options: Optional[api_pb2.FunctionOptions]
|
249
|
-
_callables:
|
250
|
+
_callables: dict[str, Callable[..., Any]]
|
250
251
|
_from_other_workspace: Optional[bool] # Functions require FunctionBindParams before invocation.
|
251
252
|
_app: Optional["modal.app._App"] = None # not set for lookups
|
252
253
|
|
@@ -265,7 +266,7 @@ class _Cls(_Object, type_prefix="cs"):
|
|
265
266
|
self._callables = other._callables
|
266
267
|
self._from_other_workspace = other._from_other_workspace
|
267
268
|
|
268
|
-
def _get_partial_functions(self) ->
|
269
|
+
def _get_partial_functions(self) -> dict[str, _PartialFunction]:
|
269
270
|
if not self._user_cls:
|
270
271
|
raise AttributeError("You can only get the partial functions of a local Cls instance")
|
271
272
|
return _find_partial_methods_for_user_cls(self._user_cls, _PartialFunctionFlags.all())
|
@@ -344,8 +345,8 @@ class _Cls(_Object, type_prefix="cs"):
|
|
344
345
|
# validate signature
|
345
346
|
_Cls.validate_construction_mechanism(user_cls)
|
346
347
|
|
347
|
-
method_functions:
|
348
|
-
partial_functions:
|
348
|
+
method_functions: dict[str, _Function] = {}
|
349
|
+
partial_functions: dict[str, _PartialFunction] = _find_partial_methods_for_user_cls(
|
349
350
|
user_cls, _PartialFunctionFlags.FUNCTION
|
350
351
|
)
|
351
352
|
|
@@ -361,11 +362,11 @@ class _Cls(_Object, type_prefix="cs"):
|
|
361
362
|
partial_function.wrapped = True
|
362
363
|
|
363
364
|
# Get all callables
|
364
|
-
callables:
|
365
|
+
callables: dict[str, Callable] = {
|
365
366
|
k: pf.raw_f for k, pf in _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.all()).items()
|
366
367
|
}
|
367
368
|
|
368
|
-
def _deps() ->
|
369
|
+
def _deps() -> list[_Function]:
|
369
370
|
return [class_service_function]
|
370
371
|
|
371
372
|
async def _load(self: "_Cls", resolver: Resolver, existing_object_id: Optional[str]):
|
@@ -392,7 +393,7 @@ class _Cls(_Object, type_prefix="cs"):
|
|
392
393
|
|
393
394
|
@classmethod
|
394
395
|
def from_name(
|
395
|
-
cls:
|
396
|
+
cls: type["_Cls"],
|
396
397
|
app_name: str,
|
397
398
|
tag: str,
|
398
399
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
@@ -454,11 +455,11 @@ class _Cls(_Object, type_prefix="cs"):
|
|
454
455
|
|
455
456
|
def with_options(
|
456
457
|
self: "_Cls",
|
457
|
-
cpu: Optional[Union[float,
|
458
|
-
memory: Optional[Union[int,
|
458
|
+
cpu: Optional[Union[float, tuple[float, float]]] = None,
|
459
|
+
memory: Optional[Union[int, tuple[int, int]]] = None,
|
459
460
|
gpu: GPU_T = None,
|
460
461
|
secrets: Collection[_Secret] = (),
|
461
|
-
volumes:
|
462
|
+
volumes: dict[Union[str, os.PathLike], _Volume] = {},
|
462
463
|
retries: Optional[Union[int, Retries]] = None,
|
463
464
|
timeout: Optional[int] = None,
|
464
465
|
concurrency_limit: Optional[int] = None,
|
modal/cls.pyi
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import collections.abc
|
1
2
|
import google.protobuf.message
|
2
3
|
import inspect
|
3
4
|
import modal.app
|
@@ -20,10 +21,10 @@ def _use_annotation_parameters(user_cls) -> bool: ...
|
|
20
21
|
def _get_class_constructor_signature(user_cls: type) -> inspect.Signature: ...
|
21
22
|
|
22
23
|
class _Obj:
|
23
|
-
_functions:
|
24
|
+
_functions: dict[str, modal.functions._Function]
|
24
25
|
_entered: bool
|
25
26
|
_user_cls_instance: typing.Optional[typing.Any]
|
26
|
-
_construction_args:
|
27
|
+
_construction_args: tuple[tuple, dict[str, typing.Any]]
|
27
28
|
_instance_service_function: typing.Optional[modal.functions._Function]
|
28
29
|
|
29
30
|
def _uses_common_service_function(self): ...
|
@@ -31,7 +32,7 @@ class _Obj:
|
|
31
32
|
self,
|
32
33
|
user_cls: type,
|
33
34
|
class_service_function: typing.Optional[modal.functions._Function],
|
34
|
-
classbound_methods:
|
35
|
+
classbound_methods: dict[str, modal.functions._Function],
|
35
36
|
from_other_workspace: bool,
|
36
37
|
options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
|
37
38
|
args,
|
@@ -49,17 +50,17 @@ class _Obj:
|
|
49
50
|
def __getattr__(self, k): ...
|
50
51
|
|
51
52
|
class Obj:
|
52
|
-
_functions:
|
53
|
+
_functions: dict[str, modal.functions.Function]
|
53
54
|
_entered: bool
|
54
55
|
_user_cls_instance: typing.Optional[typing.Any]
|
55
|
-
_construction_args:
|
56
|
+
_construction_args: tuple[tuple, dict[str, typing.Any]]
|
56
57
|
_instance_service_function: typing.Optional[modal.functions.Function]
|
57
58
|
|
58
59
|
def __init__(
|
59
60
|
self,
|
60
61
|
user_cls: type,
|
61
62
|
class_service_function: typing.Optional[modal.functions.Function],
|
62
|
-
classbound_methods:
|
63
|
+
classbound_methods: dict[str, modal.functions.Function],
|
63
64
|
from_other_workspace: bool,
|
64
65
|
options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
|
65
66
|
args,
|
@@ -86,15 +87,15 @@ class Obj:
|
|
86
87
|
class _Cls(modal.object._Object):
|
87
88
|
_user_cls: typing.Optional[type]
|
88
89
|
_class_service_function: typing.Optional[modal.functions._Function]
|
89
|
-
_method_functions: typing.Optional[
|
90
|
+
_method_functions: typing.Optional[dict[str, modal.functions._Function]]
|
90
91
|
_options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
|
91
|
-
_callables:
|
92
|
+
_callables: dict[str, typing.Callable[..., typing.Any]]
|
92
93
|
_from_other_workspace: typing.Optional[bool]
|
93
94
|
_app: typing.Optional[modal.app._App]
|
94
95
|
|
95
96
|
def _initialize_from_empty(self): ...
|
96
97
|
def _initialize_from_other(self, other: _Cls): ...
|
97
|
-
def _get_partial_functions(self) ->
|
98
|
+
def _get_partial_functions(self) -> dict[str, modal.partial_function._PartialFunction]: ...
|
98
99
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
99
100
|
@staticmethod
|
100
101
|
def validate_construction_mechanism(user_cls): ...
|
@@ -103,7 +104,7 @@ class _Cls(modal.object._Object):
|
|
103
104
|
def _uses_common_service_function(self): ...
|
104
105
|
@classmethod
|
105
106
|
def from_name(
|
106
|
-
cls:
|
107
|
+
cls: type[_Cls],
|
107
108
|
app_name: str,
|
108
109
|
tag: str,
|
109
110
|
namespace=1,
|
@@ -112,11 +113,11 @@ class _Cls(modal.object._Object):
|
|
112
113
|
) -> _Cls: ...
|
113
114
|
def with_options(
|
114
115
|
self: _Cls,
|
115
|
-
cpu: typing.Union[float,
|
116
|
-
memory: typing.Union[int,
|
116
|
+
cpu: typing.Union[float, tuple[float, float], None] = None,
|
117
|
+
memory: typing.Union[int, tuple[int, int], None] = None,
|
117
118
|
gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
|
118
|
-
secrets:
|
119
|
-
volumes:
|
119
|
+
secrets: collections.abc.Collection[modal.secret._Secret] = (),
|
120
|
+
volumes: dict[typing.Union[str, os.PathLike], modal.volume._Volume] = {},
|
120
121
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
121
122
|
timeout: typing.Optional[int] = None,
|
122
123
|
concurrency_limit: typing.Optional[int] = None,
|
@@ -138,16 +139,16 @@ class _Cls(modal.object._Object):
|
|
138
139
|
class Cls(modal.object.Object):
|
139
140
|
_user_cls: typing.Optional[type]
|
140
141
|
_class_service_function: typing.Optional[modal.functions.Function]
|
141
|
-
_method_functions: typing.Optional[
|
142
|
+
_method_functions: typing.Optional[dict[str, modal.functions.Function]]
|
142
143
|
_options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
|
143
|
-
_callables:
|
144
|
+
_callables: dict[str, typing.Callable[..., typing.Any]]
|
144
145
|
_from_other_workspace: typing.Optional[bool]
|
145
146
|
_app: typing.Optional[modal.app.App]
|
146
147
|
|
147
148
|
def __init__(self, *args, **kwargs): ...
|
148
149
|
def _initialize_from_empty(self): ...
|
149
150
|
def _initialize_from_other(self, other: Cls): ...
|
150
|
-
def _get_partial_functions(self) ->
|
151
|
+
def _get_partial_functions(self) -> dict[str, modal.partial_function.PartialFunction]: ...
|
151
152
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
152
153
|
@staticmethod
|
153
154
|
def validate_construction_mechanism(user_cls): ...
|
@@ -156,7 +157,7 @@ class Cls(modal.object.Object):
|
|
156
157
|
def _uses_common_service_function(self): ...
|
157
158
|
@classmethod
|
158
159
|
def from_name(
|
159
|
-
cls:
|
160
|
+
cls: type[Cls],
|
160
161
|
app_name: str,
|
161
162
|
tag: str,
|
162
163
|
namespace=1,
|
@@ -165,11 +166,11 @@ class Cls(modal.object.Object):
|
|
165
166
|
) -> Cls: ...
|
166
167
|
def with_options(
|
167
168
|
self: Cls,
|
168
|
-
cpu: typing.Union[float,
|
169
|
-
memory: typing.Union[int,
|
169
|
+
cpu: typing.Union[float, tuple[float, float], None] = None,
|
170
|
+
memory: typing.Union[int, tuple[int, int], None] = None,
|
170
171
|
gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
|
171
|
-
secrets:
|
172
|
-
volumes:
|
172
|
+
secrets: collections.abc.Collection[modal.secret.Secret] = (),
|
173
|
+
volumes: dict[typing.Union[str, os.PathLike], modal.volume.Volume] = {},
|
173
174
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
174
175
|
timeout: typing.Optional[int] = None,
|
175
176
|
concurrency_limit: typing.Optional[int] = None,
|
modal/config.py
CHANGED
@@ -80,7 +80,7 @@ import os
|
|
80
80
|
import typing
|
81
81
|
import warnings
|
82
82
|
from textwrap import dedent
|
83
|
-
from typing import Any,
|
83
|
+
from typing import Any, Optional
|
84
84
|
|
85
85
|
from google.protobuf.empty_pb2 import Empty
|
86
86
|
|
@@ -282,7 +282,7 @@ configure_logger(logger, config["loglevel"], config["log_format"])
|
|
282
282
|
|
283
283
|
|
284
284
|
def _store_user_config(
|
285
|
-
new_settings:
|
285
|
+
new_settings: dict[str, Any], profile: Optional[str] = None, active_profile: Optional[str] = None
|
286
286
|
):
|
287
287
|
"""Internal method, used by the CLI to set tokens."""
|
288
288
|
if profile is None:
|