modal 0.72.5__py3-none-any.whl → 0.72.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modal/_container_entrypoint.py +5 -10
- modal/_object.py +297 -0
- modal/_resolver.py +7 -5
- modal/_runtime/container_io_manager.py +0 -11
- modal/_runtime/user_code_imports.py +7 -7
- modal/_serialization.py +4 -3
- modal/_tunnel.py +1 -1
- modal/app.py +14 -61
- modal/app.pyi +25 -25
- modal/cli/app.py +3 -2
- modal/cli/container.py +1 -1
- modal/cli/import_refs.py +185 -113
- modal/cli/launch.py +10 -5
- modal/cli/programs/run_jupyter.py +2 -2
- modal/cli/programs/vscode.py +3 -3
- modal/cli/run.py +134 -68
- modal/client.py +1 -0
- modal/client.pyi +18 -14
- modal/cloud_bucket_mount.py +4 -0
- modal/cloud_bucket_mount.pyi +4 -0
- modal/cls.py +33 -5
- modal/cls.pyi +20 -5
- modal/container_process.pyi +8 -6
- modal/dict.py +1 -1
- modal/dict.pyi +32 -29
- modal/environments.py +1 -1
- modal/environments.pyi +2 -1
- modal/experimental.py +47 -11
- modal/experimental.pyi +29 -0
- modal/file_io.pyi +30 -28
- modal/file_pattern_matcher.py +3 -4
- modal/functions.py +31 -23
- modal/functions.pyi +57 -50
- modal/gpu.py +19 -26
- modal/image.py +47 -19
- modal/image.pyi +28 -21
- modal/io_streams.pyi +14 -12
- modal/mount.py +14 -5
- modal/mount.pyi +28 -25
- modal/network_file_system.py +7 -7
- modal/network_file_system.pyi +27 -24
- modal/object.py +2 -265
- modal/object.pyi +46 -130
- modal/parallel_map.py +2 -2
- modal/parallel_map.pyi +10 -7
- modal/partial_function.py +22 -3
- modal/partial_function.pyi +45 -27
- modal/proxy.py +1 -1
- modal/proxy.pyi +2 -1
- modal/queue.py +1 -1
- modal/queue.pyi +26 -23
- modal/runner.py +14 -3
- modal/sandbox.py +11 -7
- modal/sandbox.pyi +30 -27
- modal/secret.py +1 -1
- modal/secret.pyi +2 -1
- modal/token_flow.pyi +6 -4
- modal/volume.py +1 -1
- modal/volume.pyi +36 -33
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/METADATA +2 -2
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/RECORD +73 -71
- modal_proto/api.proto +151 -4
- modal_proto/api_grpc.py +113 -0
- modal_proto/api_pb2.py +998 -795
- modal_proto/api_pb2.pyi +430 -11
- modal_proto/api_pb2_grpc.py +233 -1
- modal_proto/api_pb2_grpc.pyi +75 -3
- modal_proto/modal_api_grpc.py +7 -0
- modal_version/_version_generated.py +1 -1
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/LICENSE +0 -0
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/WHEEL +0 -0
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/entry_points.txt +0 -0
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/top_level.txt +0 -0
modal/dict.py
CHANGED
@@ -7,6 +7,7 @@ from synchronicity.async_wrap import asynccontextmanager
|
|
7
7
|
|
8
8
|
from modal_proto import api_pb2
|
9
9
|
|
10
|
+
from ._object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
|
10
11
|
from ._resolver import Resolver
|
11
12
|
from ._serialization import deserialize, serialize
|
12
13
|
from ._utils.async_utils import TaskContext, synchronize_api
|
@@ -16,7 +17,6 @@ from ._utils.name_utils import check_object_name
|
|
16
17
|
from .client import _Client
|
17
18
|
from .config import logger
|
18
19
|
from .exception import RequestSizeError
|
19
|
-
from .object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
|
20
20
|
|
21
21
|
|
22
22
|
def _serialize_dict(data):
|
modal/dict.pyi
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import collections.abc
|
2
|
+
import modal._object
|
2
3
|
import modal.client
|
3
4
|
import modal.object
|
4
5
|
import synchronicity.combined_types
|
@@ -7,7 +8,7 @@ import typing_extensions
|
|
7
8
|
|
8
9
|
def _serialize_dict(data): ...
|
9
10
|
|
10
|
-
class _Dict(modal.
|
11
|
+
class _Dict(modal._object._Object):
|
11
12
|
def __init__(self, data={}): ...
|
12
13
|
@classmethod
|
13
14
|
def ephemeral(
|
@@ -56,6 +57,8 @@ class _Dict(modal.object._Object):
|
|
56
57
|
def values(self) -> collections.abc.AsyncIterator[typing.Any]: ...
|
57
58
|
def items(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
|
58
59
|
|
60
|
+
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
61
|
+
|
59
62
|
class Dict(modal.object.Object):
|
60
63
|
def __init__(self, data={}): ...
|
61
64
|
@classmethod
|
@@ -115,86 +118,86 @@ class Dict(modal.object.Object):
|
|
115
118
|
|
116
119
|
delete: __delete_spec
|
117
120
|
|
118
|
-
class __clear_spec(typing_extensions.Protocol):
|
121
|
+
class __clear_spec(typing_extensions.Protocol[SUPERSELF]):
|
119
122
|
def __call__(self) -> None: ...
|
120
123
|
async def aio(self) -> None: ...
|
121
124
|
|
122
|
-
clear: __clear_spec
|
125
|
+
clear: __clear_spec[typing_extensions.Self]
|
123
126
|
|
124
|
-
class __get_spec(typing_extensions.Protocol):
|
127
|
+
class __get_spec(typing_extensions.Protocol[SUPERSELF]):
|
125
128
|
def __call__(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
|
126
129
|
async def aio(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
|
127
130
|
|
128
|
-
get: __get_spec
|
131
|
+
get: __get_spec[typing_extensions.Self]
|
129
132
|
|
130
|
-
class __contains_spec(typing_extensions.Protocol):
|
133
|
+
class __contains_spec(typing_extensions.Protocol[SUPERSELF]):
|
131
134
|
def __call__(self, key: typing.Any) -> bool: ...
|
132
135
|
async def aio(self, key: typing.Any) -> bool: ...
|
133
136
|
|
134
|
-
contains: __contains_spec
|
137
|
+
contains: __contains_spec[typing_extensions.Self]
|
135
138
|
|
136
|
-
class __len_spec(typing_extensions.Protocol):
|
139
|
+
class __len_spec(typing_extensions.Protocol[SUPERSELF]):
|
137
140
|
def __call__(self) -> int: ...
|
138
141
|
async def aio(self) -> int: ...
|
139
142
|
|
140
|
-
len: __len_spec
|
143
|
+
len: __len_spec[typing_extensions.Self]
|
141
144
|
|
142
|
-
class ____getitem___spec(typing_extensions.Protocol):
|
145
|
+
class ____getitem___spec(typing_extensions.Protocol[SUPERSELF]):
|
143
146
|
def __call__(self, key: typing.Any) -> typing.Any: ...
|
144
147
|
async def aio(self, key: typing.Any) -> typing.Any: ...
|
145
148
|
|
146
|
-
__getitem__: ____getitem___spec
|
149
|
+
__getitem__: ____getitem___spec[typing_extensions.Self]
|
147
150
|
|
148
|
-
class __update_spec(typing_extensions.Protocol):
|
151
|
+
class __update_spec(typing_extensions.Protocol[SUPERSELF]):
|
149
152
|
def __call__(self, **kwargs) -> None: ...
|
150
153
|
async def aio(self, **kwargs) -> None: ...
|
151
154
|
|
152
|
-
update: __update_spec
|
155
|
+
update: __update_spec[typing_extensions.Self]
|
153
156
|
|
154
|
-
class __put_spec(typing_extensions.Protocol):
|
157
|
+
class __put_spec(typing_extensions.Protocol[SUPERSELF]):
|
155
158
|
def __call__(self, key: typing.Any, value: typing.Any) -> None: ...
|
156
159
|
async def aio(self, key: typing.Any, value: typing.Any) -> None: ...
|
157
160
|
|
158
|
-
put: __put_spec
|
161
|
+
put: __put_spec[typing_extensions.Self]
|
159
162
|
|
160
|
-
class ____setitem___spec(typing_extensions.Protocol):
|
163
|
+
class ____setitem___spec(typing_extensions.Protocol[SUPERSELF]):
|
161
164
|
def __call__(self, key: typing.Any, value: typing.Any) -> None: ...
|
162
165
|
async def aio(self, key: typing.Any, value: typing.Any) -> None: ...
|
163
166
|
|
164
|
-
__setitem__: ____setitem___spec
|
167
|
+
__setitem__: ____setitem___spec[typing_extensions.Self]
|
165
168
|
|
166
|
-
class __pop_spec(typing_extensions.Protocol):
|
169
|
+
class __pop_spec(typing_extensions.Protocol[SUPERSELF]):
|
167
170
|
def __call__(self, key: typing.Any) -> typing.Any: ...
|
168
171
|
async def aio(self, key: typing.Any) -> typing.Any: ...
|
169
172
|
|
170
|
-
pop: __pop_spec
|
173
|
+
pop: __pop_spec[typing_extensions.Self]
|
171
174
|
|
172
|
-
class ____delitem___spec(typing_extensions.Protocol):
|
175
|
+
class ____delitem___spec(typing_extensions.Protocol[SUPERSELF]):
|
173
176
|
def __call__(self, key: typing.Any) -> typing.Any: ...
|
174
177
|
async def aio(self, key: typing.Any) -> typing.Any: ...
|
175
178
|
|
176
|
-
__delitem__: ____delitem___spec
|
179
|
+
__delitem__: ____delitem___spec[typing_extensions.Self]
|
177
180
|
|
178
|
-
class ____contains___spec(typing_extensions.Protocol):
|
181
|
+
class ____contains___spec(typing_extensions.Protocol[SUPERSELF]):
|
179
182
|
def __call__(self, key: typing.Any) -> bool: ...
|
180
183
|
async def aio(self, key: typing.Any) -> bool: ...
|
181
184
|
|
182
|
-
__contains__: ____contains___spec
|
185
|
+
__contains__: ____contains___spec[typing_extensions.Self]
|
183
186
|
|
184
|
-
class __keys_spec(typing_extensions.Protocol):
|
187
|
+
class __keys_spec(typing_extensions.Protocol[SUPERSELF]):
|
185
188
|
def __call__(self) -> typing.Iterator[typing.Any]: ...
|
186
189
|
def aio(self) -> collections.abc.AsyncIterator[typing.Any]: ...
|
187
190
|
|
188
|
-
keys: __keys_spec
|
191
|
+
keys: __keys_spec[typing_extensions.Self]
|
189
192
|
|
190
|
-
class __values_spec(typing_extensions.Protocol):
|
193
|
+
class __values_spec(typing_extensions.Protocol[SUPERSELF]):
|
191
194
|
def __call__(self) -> typing.Iterator[typing.Any]: ...
|
192
195
|
def aio(self) -> collections.abc.AsyncIterator[typing.Any]: ...
|
193
196
|
|
194
|
-
values: __values_spec
|
197
|
+
values: __values_spec[typing_extensions.Self]
|
195
198
|
|
196
|
-
class __items_spec(typing_extensions.Protocol):
|
199
|
+
class __items_spec(typing_extensions.Protocol[SUPERSELF]):
|
197
200
|
def __call__(self) -> typing.Iterator[tuple[typing.Any, typing.Any]]: ...
|
198
201
|
def aio(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
|
199
202
|
|
200
|
-
items: __items_spec
|
203
|
+
items: __items_spec[typing_extensions.Self]
|
modal/environments.py
CHANGED
@@ -8,6 +8,7 @@ from google.protobuf.wrappers_pb2 import StringValue
|
|
8
8
|
|
9
9
|
from modal_proto import api_pb2
|
10
10
|
|
11
|
+
from ._object import _Object
|
11
12
|
from ._resolver import Resolver
|
12
13
|
from ._utils.async_utils import synchronize_api, synchronizer
|
13
14
|
from ._utils.deprecation import renamed_parameter
|
@@ -15,7 +16,6 @@ from ._utils.grpc_utils import retry_transient_errors
|
|
15
16
|
from ._utils.name_utils import check_object_name
|
16
17
|
from .client import _Client
|
17
18
|
from .config import config, logger
|
18
|
-
from .object import _Object
|
19
19
|
|
20
20
|
|
21
21
|
@dataclass(frozen=True)
|
modal/environments.pyi
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import google.protobuf.message
|
2
|
+
import modal._object
|
2
3
|
import modal.client
|
3
4
|
import modal.object
|
4
5
|
import modal_proto.api_pb2
|
@@ -16,7 +17,7 @@ class EnvironmentSettings:
|
|
16
17
|
def __delattr__(self, name): ...
|
17
18
|
def __hash__(self): ...
|
18
19
|
|
19
|
-
class _Environment(modal.
|
20
|
+
class _Environment(modal._object._Object):
|
20
21
|
_settings: EnvironmentSettings
|
21
22
|
|
22
23
|
def __init__(self): ...
|
modal/experimental.py
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
|
-
from
|
3
|
-
|
4
|
-
Callable,
|
5
|
-
)
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Callable, Optional
|
6
4
|
|
7
|
-
import
|
8
|
-
from modal.functions import _Function
|
5
|
+
from modal_proto import api_pb2
|
9
6
|
|
7
|
+
from ._clustered_functions import ClusterInfo, get_cluster_info as _get_cluster_info
|
8
|
+
from ._object import _get_environment_name
|
10
9
|
from ._runtime.container_io_manager import _ContainerIOManager
|
11
|
-
from .
|
12
|
-
|
13
|
-
|
10
|
+
from ._utils.async_utils import synchronizer
|
11
|
+
from .client import _Client
|
12
|
+
from .exception import InvalidError
|
13
|
+
from .functions import _Function
|
14
14
|
from .partial_function import _PartialFunction, _PartialFunctionFlags
|
15
15
|
|
16
16
|
|
@@ -65,5 +65,41 @@ def clustered(size: int, broadcast: bool = True):
|
|
65
65
|
return wrapper
|
66
66
|
|
67
67
|
|
68
|
-
def get_cluster_info() ->
|
69
|
-
return
|
68
|
+
def get_cluster_info() -> ClusterInfo:
|
69
|
+
return _get_cluster_info()
|
70
|
+
|
71
|
+
|
72
|
+
@dataclass
|
73
|
+
class AppInfo:
|
74
|
+
app_id: str
|
75
|
+
name: str
|
76
|
+
containers: int
|
77
|
+
|
78
|
+
|
79
|
+
@synchronizer.create_blocking
|
80
|
+
async def list_deployed_apps(environment_name: str = "", client: Optional[_Client] = None) -> list[AppInfo]:
|
81
|
+
"""List deployed Apps along with the number of containers currently running."""
|
82
|
+
# This function exists to provide backwards compatibility for some users who had been
|
83
|
+
# calling into the private function that previously backed the `modal app list` CLI command.
|
84
|
+
# We plan to add more Python API for exposing this sort of information, but we haven't
|
85
|
+
# settled on a design we're happy with yet. In the meantime, this function will continue
|
86
|
+
# to support existing codebases. It's likely that the final API will be different
|
87
|
+
# (e.g. more oriented around the App object). This function should be gracefully deprecated
|
88
|
+
# one the new API is released.
|
89
|
+
client = client or await _Client.from_env()
|
90
|
+
|
91
|
+
resp: api_pb2.AppListResponse = await client.stub.AppList(
|
92
|
+
api_pb2.AppListRequest(environment_name=_get_environment_name(environment_name))
|
93
|
+
)
|
94
|
+
|
95
|
+
app_infos = []
|
96
|
+
for app_stats in resp.apps:
|
97
|
+
if app_stats.state == api_pb2.APP_STATE_DEPLOYED:
|
98
|
+
app_infos.append(
|
99
|
+
AppInfo(
|
100
|
+
app_id=app_stats.app_id,
|
101
|
+
name=app_stats.description,
|
102
|
+
containers=app_stats.n_running_tasks,
|
103
|
+
)
|
104
|
+
)
|
105
|
+
return app_infos
|
modal/experimental.pyi
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
import modal._clustered_functions
|
2
|
+
import modal.client
|
3
|
+
import typing
|
4
|
+
import typing_extensions
|
5
|
+
|
6
|
+
def stop_fetching_inputs(): ...
|
7
|
+
def get_local_input_concurrency(): ...
|
8
|
+
def set_local_input_concurrency(concurrency: int): ...
|
9
|
+
def clustered(size: int, broadcast: bool = True): ...
|
10
|
+
def get_cluster_info() -> modal._clustered_functions.ClusterInfo: ...
|
11
|
+
|
12
|
+
class AppInfo:
|
13
|
+
app_id: str
|
14
|
+
name: str
|
15
|
+
containers: int
|
16
|
+
|
17
|
+
def __init__(self, app_id: str, name: str, containers: int) -> None: ...
|
18
|
+
def __repr__(self): ...
|
19
|
+
def __eq__(self, other): ...
|
20
|
+
|
21
|
+
class __list_deployed_apps_spec(typing_extensions.Protocol):
|
22
|
+
def __call__(
|
23
|
+
self, environment_name: str = "", client: typing.Optional[modal.client.Client] = None
|
24
|
+
) -> list[AppInfo]: ...
|
25
|
+
async def aio(
|
26
|
+
self, environment_name: str = "", client: typing.Optional[modal.client.Client] = None
|
27
|
+
) -> list[AppInfo]: ...
|
28
|
+
|
29
|
+
list_deployed_apps: __list_deployed_apps_spec
|
modal/file_io.pyi
CHANGED
@@ -100,6 +100,8 @@ class __replace_bytes_spec(typing_extensions.Protocol):
|
|
100
100
|
|
101
101
|
replace_bytes: __replace_bytes_spec
|
102
102
|
|
103
|
+
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
104
|
+
|
103
105
|
T_INNER = typing.TypeVar("T_INNER", covariant=True)
|
104
106
|
|
105
107
|
class FileIO(typing.Generic[T]):
|
@@ -112,37 +114,37 @@ class FileIO(typing.Generic[T]):
|
|
112
114
|
def _validate_mode(self, mode: str) -> None: ...
|
113
115
|
def _handle_error(self, error: modal_proto.api_pb2.SystemErrorMessage) -> None: ...
|
114
116
|
|
115
|
-
class ___consume_output_spec(typing_extensions.Protocol):
|
117
|
+
class ___consume_output_spec(typing_extensions.Protocol[SUPERSELF]):
|
116
118
|
def __call__(self, exec_id: str) -> typing.Iterator[typing.Optional[bytes]]: ...
|
117
119
|
def aio(self, exec_id: str) -> typing.AsyncIterator[typing.Optional[bytes]]: ...
|
118
120
|
|
119
|
-
_consume_output: ___consume_output_spec
|
121
|
+
_consume_output: ___consume_output_spec[typing_extensions.Self]
|
120
122
|
|
121
|
-
class ___consume_watch_output_spec(typing_extensions.Protocol):
|
123
|
+
class ___consume_watch_output_spec(typing_extensions.Protocol[SUPERSELF]):
|
122
124
|
def __call__(self, exec_id: str) -> None: ...
|
123
125
|
async def aio(self, exec_id: str) -> None: ...
|
124
126
|
|
125
|
-
_consume_watch_output: ___consume_watch_output_spec
|
127
|
+
_consume_watch_output: ___consume_watch_output_spec[typing_extensions.Self]
|
126
128
|
|
127
|
-
class ___parse_watch_output_spec(typing_extensions.Protocol):
|
129
|
+
class ___parse_watch_output_spec(typing_extensions.Protocol[SUPERSELF]):
|
128
130
|
def __call__(self, event: bytes) -> typing.Optional[FileWatchEvent]: ...
|
129
131
|
async def aio(self, event: bytes) -> typing.Optional[FileWatchEvent]: ...
|
130
132
|
|
131
|
-
_parse_watch_output: ___parse_watch_output_spec
|
133
|
+
_parse_watch_output: ___parse_watch_output_spec[typing_extensions.Self]
|
132
134
|
|
133
|
-
class ___wait_spec(typing_extensions.Protocol):
|
135
|
+
class ___wait_spec(typing_extensions.Protocol[SUPERSELF]):
|
134
136
|
def __call__(self, exec_id: str) -> bytes: ...
|
135
137
|
async def aio(self, exec_id: str) -> bytes: ...
|
136
138
|
|
137
|
-
_wait: ___wait_spec
|
139
|
+
_wait: ___wait_spec[typing_extensions.Self]
|
138
140
|
|
139
141
|
def _validate_type(self, data: typing.Union[bytes, str]) -> None: ...
|
140
142
|
|
141
|
-
class ___open_file_spec(typing_extensions.Protocol):
|
143
|
+
class ___open_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
142
144
|
def __call__(self, path: str, mode: str) -> None: ...
|
143
145
|
async def aio(self, path: str, mode: str) -> None: ...
|
144
146
|
|
145
|
-
_open_file: ___open_file_spec
|
147
|
+
_open_file: ___open_file_spec[typing_extensions.Self]
|
146
148
|
|
147
149
|
@classmethod
|
148
150
|
def create(
|
@@ -153,49 +155,49 @@ class FileIO(typing.Generic[T]):
|
|
153
155
|
task_id: str,
|
154
156
|
) -> FileIO: ...
|
155
157
|
|
156
|
-
class ___make_read_request_spec(typing_extensions.Protocol):
|
158
|
+
class ___make_read_request_spec(typing_extensions.Protocol[SUPERSELF]):
|
157
159
|
def __call__(self, n: typing.Optional[int]) -> bytes: ...
|
158
160
|
async def aio(self, n: typing.Optional[int]) -> bytes: ...
|
159
161
|
|
160
|
-
_make_read_request: ___make_read_request_spec
|
162
|
+
_make_read_request: ___make_read_request_spec[typing_extensions.Self]
|
161
163
|
|
162
|
-
class __read_spec(typing_extensions.Protocol[T_INNER]):
|
164
|
+
class __read_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
|
163
165
|
def __call__(self, n: typing.Optional[int] = None) -> T_INNER: ...
|
164
166
|
async def aio(self, n: typing.Optional[int] = None) -> T_INNER: ...
|
165
167
|
|
166
|
-
read: __read_spec[T]
|
168
|
+
read: __read_spec[T, typing_extensions.Self]
|
167
169
|
|
168
|
-
class __readline_spec(typing_extensions.Protocol[T_INNER]):
|
170
|
+
class __readline_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
|
169
171
|
def __call__(self) -> T_INNER: ...
|
170
172
|
async def aio(self) -> T_INNER: ...
|
171
173
|
|
172
|
-
readline: __readline_spec[T]
|
174
|
+
readline: __readline_spec[T, typing_extensions.Self]
|
173
175
|
|
174
|
-
class __readlines_spec(typing_extensions.Protocol[T_INNER]):
|
176
|
+
class __readlines_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
|
175
177
|
def __call__(self) -> typing.Sequence[T_INNER]: ...
|
176
178
|
async def aio(self) -> typing.Sequence[T_INNER]: ...
|
177
179
|
|
178
|
-
readlines: __readlines_spec[T]
|
180
|
+
readlines: __readlines_spec[T, typing_extensions.Self]
|
179
181
|
|
180
|
-
class __write_spec(typing_extensions.Protocol):
|
182
|
+
class __write_spec(typing_extensions.Protocol[SUPERSELF]):
|
181
183
|
def __call__(self, data: typing.Union[bytes, str]) -> None: ...
|
182
184
|
async def aio(self, data: typing.Union[bytes, str]) -> None: ...
|
183
185
|
|
184
|
-
write: __write_spec
|
186
|
+
write: __write_spec[typing_extensions.Self]
|
185
187
|
|
186
|
-
class __flush_spec(typing_extensions.Protocol):
|
188
|
+
class __flush_spec(typing_extensions.Protocol[SUPERSELF]):
|
187
189
|
def __call__(self) -> None: ...
|
188
190
|
async def aio(self) -> None: ...
|
189
191
|
|
190
|
-
flush: __flush_spec
|
192
|
+
flush: __flush_spec[typing_extensions.Self]
|
191
193
|
|
192
194
|
def _get_whence(self, whence: int): ...
|
193
195
|
|
194
|
-
class __seek_spec(typing_extensions.Protocol):
|
196
|
+
class __seek_spec(typing_extensions.Protocol[SUPERSELF]):
|
195
197
|
def __call__(self, offset: int, whence: int = 0) -> None: ...
|
196
198
|
async def aio(self, offset: int, whence: int = 0) -> None: ...
|
197
199
|
|
198
|
-
seek: __seek_spec
|
200
|
+
seek: __seek_spec[typing_extensions.Self]
|
199
201
|
|
200
202
|
@classmethod
|
201
203
|
def ls(cls, path: str, client: modal.client.Client, task_id: str) -> list[str]: ...
|
@@ -214,17 +216,17 @@ class FileIO(typing.Generic[T]):
|
|
214
216
|
timeout: typing.Optional[int] = None,
|
215
217
|
) -> typing.Iterator[FileWatchEvent]: ...
|
216
218
|
|
217
|
-
class ___close_spec(typing_extensions.Protocol):
|
219
|
+
class ___close_spec(typing_extensions.Protocol[SUPERSELF]):
|
218
220
|
def __call__(self) -> None: ...
|
219
221
|
async def aio(self) -> None: ...
|
220
222
|
|
221
|
-
_close: ___close_spec
|
223
|
+
_close: ___close_spec[typing_extensions.Self]
|
222
224
|
|
223
|
-
class __close_spec(typing_extensions.Protocol):
|
225
|
+
class __close_spec(typing_extensions.Protocol[SUPERSELF]):
|
224
226
|
def __call__(self) -> None: ...
|
225
227
|
async def aio(self) -> None: ...
|
226
228
|
|
227
|
-
close: __close_spec
|
229
|
+
close: __close_spec[typing_extensions.Self]
|
228
230
|
|
229
231
|
def _check_writable(self) -> None: ...
|
230
232
|
def _check_readable(self) -> None: ...
|
modal/file_pattern_matcher.py
CHANGED
@@ -112,7 +112,7 @@ class FilePatternMatcher(_AbstractPatternMatcher):
|
|
112
112
|
self._set_patterns(pattern)
|
113
113
|
|
114
114
|
@classmethod
|
115
|
-
def from_file(cls, file_path: Path) -> "FilePatternMatcher":
|
115
|
+
def from_file(cls, file_path: Union[str, Path]) -> "FilePatternMatcher":
|
116
116
|
"""Initialize a new FilePatternMatcher instance from a file.
|
117
117
|
|
118
118
|
The patterns in the file will be read lazily when the matcher is first used.
|
@@ -122,17 +122,16 @@ class FilePatternMatcher(_AbstractPatternMatcher):
|
|
122
122
|
|
123
123
|
**Usage:**
|
124
124
|
```python
|
125
|
-
from pathlib import Path
|
126
125
|
from modal import FilePatternMatcher
|
127
126
|
|
128
|
-
matcher = FilePatternMatcher.from_file(
|
127
|
+
matcher = FilePatternMatcher.from_file("/path/to/ignorefile")
|
129
128
|
```
|
130
129
|
|
131
130
|
"""
|
132
131
|
uninitialized = cls.__new__(cls)
|
133
132
|
|
134
133
|
def _delayed_init():
|
135
|
-
uninitialized._set_patterns(file_path.read_text("utf8").splitlines())
|
134
|
+
uninitialized._set_patterns(Path(file_path).read_text("utf8").splitlines())
|
136
135
|
uninitialized._delayed_init = None
|
137
136
|
|
138
137
|
uninitialized._delayed_init = _delayed_init
|
modal/functions.py
CHANGED
@@ -26,6 +26,7 @@ from modal_proto import api_pb2
|
|
26
26
|
from modal_proto.modal_api_grpc import ModalClientModal
|
27
27
|
|
28
28
|
from ._location import parse_cloud_provider
|
29
|
+
from ._object import _get_environment_name, _Object, live_method, live_method_gen
|
29
30
|
from ._pty import get_pty_info
|
30
31
|
from ._resolver import Resolver
|
31
32
|
from ._resources import convert_fn_config_to_resources_config
|
@@ -71,7 +72,6 @@ from .gpu import GPU_T, parse_gpu_config
|
|
71
72
|
from .image import _Image
|
72
73
|
from .mount import _get_client_mount, _Mount, get_auto_mounts
|
73
74
|
from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
|
74
|
-
from .object import _get_environment_name, _Object, live_method, live_method_gen
|
75
75
|
from .output import _get_output_manager
|
76
76
|
from .parallel_map import (
|
77
77
|
_for_each_async,
|
@@ -383,12 +383,15 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
383
383
|
_serve_mounts: frozenset[_Mount] # set at load time, only by loader
|
384
384
|
_app: Optional["modal.app._App"] = None
|
385
385
|
_obj: Optional["modal.cls._Obj"] = None # only set for InstanceServiceFunctions and bound instance methods
|
386
|
-
|
386
|
+
|
387
|
+
_webhook_config: Optional[api_pb2.WebhookConfig] = None # this is set in definition scope, only locally
|
388
|
+
_web_url: Optional[str] # this is set on hydration
|
389
|
+
|
387
390
|
_function_name: Optional[str]
|
388
391
|
_is_method: bool
|
389
392
|
_spec: Optional[_FunctionSpec] = None
|
390
393
|
_tag: str
|
391
|
-
_raw_f: Callable[..., Any]
|
394
|
+
_raw_f: Optional[Callable[..., Any]] # this is set to None for a "class service [function]"
|
392
395
|
_build_args: dict
|
393
396
|
|
394
397
|
_is_generator: Optional[bool] = None
|
@@ -474,7 +477,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
474
477
|
_experimental_buffer_containers: Optional[int] = None,
|
475
478
|
_experimental_proxy_ip: Optional[str] = None,
|
476
479
|
_experimental_custom_scaling_factor: Optional[float] = None,
|
477
|
-
) ->
|
480
|
+
) -> "_Function":
|
478
481
|
"""mdmd:hidden"""
|
479
482
|
# Needed to avoid circular imports
|
480
483
|
from .partial_function import _find_partial_methods_for_user_cls, _PartialFunctionFlags
|
@@ -573,7 +576,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
573
576
|
)
|
574
577
|
image = _Image._from_args(
|
575
578
|
base_images={"base": image},
|
576
|
-
build_function=snapshot_function,
|
579
|
+
build_function=snapshot_function, # type: ignore # TODO: separate functions.py and _functions.py
|
577
580
|
force_build=image.force_build or pf.force_build,
|
578
581
|
)
|
579
582
|
|
@@ -785,7 +788,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
785
788
|
task_idle_timeout_secs=container_idle_timeout or 0,
|
786
789
|
concurrency_limit=concurrency_limit or 0,
|
787
790
|
pty_info=pty_info,
|
788
|
-
cloud_provider=cloud_provider,
|
791
|
+
cloud_provider=cloud_provider, # Deprecated at some point
|
792
|
+
cloud_provider_str=cloud.upper() if cloud else "", # Supersedes cloud_provider
|
789
793
|
warm_pool_size=keep_warm or 0,
|
790
794
|
runtime=config.get("function_runtime"),
|
791
795
|
runtime_debug=config.get("function_runtime_debug"),
|
@@ -911,6 +915,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
911
915
|
obj._cluster_size = cluster_size
|
912
916
|
obj._is_method = False
|
913
917
|
obj._spec = function_spec # needed for modal shell
|
918
|
+
obj._webhook_config = webhook_config # only set locally
|
914
919
|
|
915
920
|
# Used to check whether we should rebuild a modal.Image which uses `run_function`.
|
916
921
|
gpus: list[GPU_T] = gpu if isinstance(gpu, list) else [gpu]
|
@@ -962,7 +967,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
962
967
|
f"The {identity} has not been hydrated with the metadata it needs to run on Modal{reason}."
|
963
968
|
)
|
964
969
|
|
965
|
-
assert parent._client.stub
|
970
|
+
assert parent._client and parent._client.stub
|
966
971
|
|
967
972
|
if can_use_parent:
|
968
973
|
# We can end up here if parent wasn't hydrated when class was instantiated, but has been since.
|
@@ -983,9 +988,9 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
983
988
|
else:
|
984
989
|
serialized_params = serialize((args, kwargs))
|
985
990
|
environment_name = _get_environment_name(None, resolver)
|
986
|
-
assert parent is not None
|
991
|
+
assert parent is not None and parent.is_hydrated
|
987
992
|
req = api_pb2.FunctionBindParamsRequest(
|
988
|
-
function_id=parent.
|
993
|
+
function_id=parent.object_id,
|
989
994
|
serialized_params=serialized_params,
|
990
995
|
function_options=options,
|
991
996
|
environment_name=environment_name
|
@@ -1032,11 +1037,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1032
1037
|
"""
|
1033
1038
|
)
|
1034
1039
|
)
|
1035
|
-
assert self._client and self._client.stub
|
1036
1040
|
request = api_pb2.FunctionUpdateSchedulingParamsRequest(
|
1037
|
-
function_id=self.
|
1041
|
+
function_id=self.object_id, warm_pool_size_override=warm_pool_size
|
1038
1042
|
)
|
1039
|
-
await retry_transient_errors(self.
|
1043
|
+
await retry_transient_errors(self.client.stub.FunctionUpdateSchedulingParams, request)
|
1040
1044
|
|
1041
1045
|
@classmethod
|
1042
1046
|
@renamed_parameter((2024, 12, 18), "tag", "name")
|
@@ -1138,11 +1142,15 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1138
1142
|
assert self._spec
|
1139
1143
|
return self._spec
|
1140
1144
|
|
1145
|
+
def _is_web_endpoint(self) -> bool:
|
1146
|
+
# only defined in definition scope/locally, and not for class methods at the moment
|
1147
|
+
return bool(self._webhook_config and self._webhook_config.type != api_pb2.WEBHOOK_TYPE_UNSPECIFIED)
|
1148
|
+
|
1141
1149
|
def get_build_def(self) -> str:
|
1142
1150
|
"""mdmd:hidden"""
|
1143
1151
|
# Plaintext source and arg definition for the function, so it's part of the image
|
1144
1152
|
# hash. We can't use the cloudpickle hash because it's not very stable.
|
1145
|
-
assert hasattr(self, "_raw_f") and hasattr(self, "_build_args")
|
1153
|
+
assert hasattr(self, "_raw_f") and hasattr(self, "_build_args") and self._raw_f is not None
|
1146
1154
|
return f"{inspect.getsource(self._raw_f)}\n{repr(self._build_args)}"
|
1147
1155
|
|
1148
1156
|
# Live handle methods
|
@@ -1207,12 +1215,13 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1207
1215
|
async def is_generator(self) -> bool:
|
1208
1216
|
"""mdmd:hidden"""
|
1209
1217
|
# hacky: kind of like @live_method, but not hydrating if we have the value already from local source
|
1218
|
+
# TODO(michael) use a common / lightweight method for handling unhydrated metadata properties
|
1210
1219
|
if self._is_generator is not None:
|
1211
1220
|
# this is set if the function or class is local
|
1212
1221
|
return self._is_generator
|
1213
1222
|
|
1214
1223
|
# not set - this is a from_name lookup - hydrate
|
1215
|
-
await self.
|
1224
|
+
await self.hydrate()
|
1216
1225
|
assert self._is_generator is not None # should be set now
|
1217
1226
|
return self._is_generator
|
1218
1227
|
|
@@ -1248,7 +1257,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1248
1257
|
_map_invocation(
|
1249
1258
|
self, # type: ignore
|
1250
1259
|
input_queue,
|
1251
|
-
self.
|
1260
|
+
self.client,
|
1252
1261
|
order_outputs,
|
1253
1262
|
return_exceptions,
|
1254
1263
|
count_update_callback,
|
@@ -1266,7 +1275,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1266
1275
|
self,
|
1267
1276
|
args,
|
1268
1277
|
kwargs,
|
1269
|
-
client=self.
|
1278
|
+
client=self.client,
|
1270
1279
|
function_call_invocation_type=function_call_invocation_type,
|
1271
1280
|
)
|
1272
1281
|
|
@@ -1276,7 +1285,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1276
1285
|
self, args, kwargs, function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType"
|
1277
1286
|
) -> _Invocation:
|
1278
1287
|
return await _Invocation.create(
|
1279
|
-
self, args, kwargs, client=self.
|
1288
|
+
self, args, kwargs, client=self.client, function_call_invocation_type=function_call_invocation_type
|
1280
1289
|
)
|
1281
1290
|
|
1282
1291
|
@warn_if_generator_is_not_consumed()
|
@@ -1287,7 +1296,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1287
1296
|
self,
|
1288
1297
|
args,
|
1289
1298
|
kwargs,
|
1290
|
-
client=self.
|
1299
|
+
client=self.client,
|
1291
1300
|
function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_SYNC_LEGACY,
|
1292
1301
|
)
|
1293
1302
|
async for res in invocation.run_generator():
|
@@ -1303,7 +1312,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1303
1312
|
self,
|
1304
1313
|
args,
|
1305
1314
|
kwargs,
|
1306
|
-
client=self.
|
1315
|
+
client=self.client,
|
1307
1316
|
function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC_LEGACY,
|
1308
1317
|
)
|
1309
1318
|
|
@@ -1452,14 +1461,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1452
1461
|
|
1453
1462
|
def get_raw_f(self) -> Callable[..., Any]:
|
1454
1463
|
"""Return the inner Python object wrapped by this Modal Function."""
|
1464
|
+
assert self._raw_f is not None
|
1455
1465
|
return self._raw_f
|
1456
1466
|
|
1457
1467
|
@live_method
|
1458
1468
|
async def get_current_stats(self) -> FunctionStats:
|
1459
1469
|
"""Return a `FunctionStats` object describing the current function's queue and runner counts."""
|
1460
|
-
assert self._client.stub
|
1461
1470
|
resp = await retry_transient_errors(
|
1462
|
-
self.
|
1471
|
+
self.client.stub.FunctionGetCurrentStats,
|
1463
1472
|
api_pb2.FunctionGetCurrentStatsRequest(function_id=self.object_id),
|
1464
1473
|
total_timeout=10.0,
|
1465
1474
|
)
|
@@ -1491,8 +1500,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
1491
1500
|
_is_generator: bool = False
|
1492
1501
|
|
1493
1502
|
def _invocation(self):
|
1494
|
-
|
1495
|
-
return _Invocation(self._client.stub, self.object_id, self._client)
|
1503
|
+
return _Invocation(self.client.stub, self.object_id, self.client)
|
1496
1504
|
|
1497
1505
|
async def get(self, timeout: Optional[float] = None) -> ReturnType:
|
1498
1506
|
"""Get the result of the function call.
|