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/parallel_map.pyi
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import collections.abc
|
1
2
|
import modal._utils.async_utils
|
2
3
|
import modal.client
|
3
4
|
import modal.functions
|
@@ -9,26 +10,28 @@ class _SynchronizedQueue:
|
|
9
10
|
async def put(self, item): ...
|
10
11
|
async def get(self): ...
|
11
12
|
|
13
|
+
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
14
|
+
|
12
15
|
class SynchronizedQueue:
|
13
16
|
def __init__(self, /, *args, **kwargs): ...
|
14
17
|
|
15
|
-
class __init_spec(typing_extensions.Protocol):
|
18
|
+
class __init_spec(typing_extensions.Protocol[SUPERSELF]):
|
16
19
|
def __call__(self): ...
|
17
20
|
async def aio(self): ...
|
18
21
|
|
19
|
-
init: __init_spec
|
22
|
+
init: __init_spec[typing_extensions.Self]
|
20
23
|
|
21
|
-
class __put_spec(typing_extensions.Protocol):
|
24
|
+
class __put_spec(typing_extensions.Protocol[SUPERSELF]):
|
22
25
|
def __call__(self, item): ...
|
23
26
|
async def aio(self, item): ...
|
24
27
|
|
25
|
-
put: __put_spec
|
28
|
+
put: __put_spec[typing_extensions.Self]
|
26
29
|
|
27
|
-
class __get_spec(typing_extensions.Protocol):
|
30
|
+
class __get_spec(typing_extensions.Protocol[SUPERSELF]):
|
28
31
|
def __call__(self): ...
|
29
32
|
async def aio(self): ...
|
30
33
|
|
31
|
-
get: __get_spec
|
34
|
+
get: __get_spec[typing_extensions.Self]
|
32
35
|
|
33
36
|
class _OutputValue:
|
34
37
|
value: typing.Any
|
@@ -43,7 +46,7 @@ def _map_invocation(
|
|
43
46
|
client: modal.client._Client,
|
44
47
|
order_outputs: bool,
|
45
48
|
return_exceptions: bool,
|
46
|
-
count_update_callback: typing.Optional[
|
49
|
+
count_update_callback: typing.Optional[collections.abc.Callable[[int, int], None]],
|
47
50
|
): ...
|
48
51
|
def _map_sync(
|
49
52
|
self, *input_iterators, kwargs={}, order_outputs: bool = True, return_exceptions: bool = False
|
modal/partial_function.py
CHANGED
@@ -89,6 +89,14 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
89
89
|
self.force_build = force_build
|
90
90
|
self.build_timeout = build_timeout
|
91
91
|
|
92
|
+
def _get_raw_f(self) -> Callable[P, ReturnType]:
|
93
|
+
return self.raw_f
|
94
|
+
|
95
|
+
def _is_web_endpoint(self) -> bool:
|
96
|
+
if self.webhook_config is None:
|
97
|
+
return False
|
98
|
+
return self.webhook_config.type != api_pb2.WEBHOOK_TYPE_UNSPECIFIED
|
99
|
+
|
92
100
|
def __get__(self, obj, objtype=None) -> _Function[P, ReturnType, OriginalReturnType]:
|
93
101
|
k = self.raw_f.__name__
|
94
102
|
if obj: # accessing the method on an instance of a class, e.g. `MyClass().fun``
|
@@ -530,10 +538,12 @@ def _build(
|
|
530
538
|
_warn_parentheses_missing=None, *, force: bool = False, timeout: int = 86400
|
531
539
|
) -> Callable[[Union[Callable[[Any], Any], _PartialFunction]], _PartialFunction]:
|
532
540
|
"""
|
533
|
-
Decorator for methods that
|
534
|
-
in a `modal.Image`.
|
541
|
+
Decorator for methods that execute at _build time_ to create a new Image layer.
|
535
542
|
|
536
|
-
|
543
|
+
**Deprecated**: This function is deprecated. We recommend using `modal.Volume`
|
544
|
+
to store large assets (such as model weights) instead of writing them to the
|
545
|
+
Image during the build process. For other use cases, you can replace this
|
546
|
+
decorator with the `Image.run_function` method.
|
537
547
|
|
538
548
|
**Usage**
|
539
549
|
|
@@ -552,6 +562,15 @@ def _build(
|
|
552
562
|
if _warn_parentheses_missing is not None:
|
553
563
|
raise InvalidError("Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@build()`.")
|
554
564
|
|
565
|
+
deprecation_warning(
|
566
|
+
(2025, 1, 15),
|
567
|
+
"The `@modal.build` decorator is deprecated and will be removed in a future release."
|
568
|
+
"\n\nWe now recommend storing large assets (such as model weights) using a `modal.Volume`"
|
569
|
+
" instead of writing them directly into the `modal.Image` filesystem."
|
570
|
+
" For other use cases we recommend using `Image.run_function` instead."
|
571
|
+
"\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
|
572
|
+
)
|
573
|
+
|
555
574
|
def wrapper(f: Union[Callable[[Any], Any], _PartialFunction]) -> _PartialFunction:
|
556
575
|
if isinstance(f, _PartialFunction):
|
557
576
|
_disallow_wrapping_method(f, "build")
|
modal/partial_function.pyi
CHANGED
@@ -25,7 +25,7 @@ ReturnType = typing.TypeVar("ReturnType", covariant=True)
|
|
25
25
|
OriginalReturnType = typing.TypeVar("OriginalReturnType", covariant=True)
|
26
26
|
|
27
27
|
class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
28
|
-
raw_f:
|
28
|
+
raw_f: collections.abc.Callable[P, ReturnType]
|
29
29
|
flags: _PartialFunctionFlags
|
30
30
|
webhook_config: typing.Optional[modal_proto.api_pb2.WebhookConfig]
|
31
31
|
is_generator: bool
|
@@ -38,7 +38,7 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
38
38
|
|
39
39
|
def __init__(
|
40
40
|
self,
|
41
|
-
raw_f:
|
41
|
+
raw_f: collections.abc.Callable[P, ReturnType],
|
42
42
|
flags: _PartialFunctionFlags,
|
43
43
|
webhook_config: typing.Optional[modal_proto.api_pb2.WebhookConfig] = None,
|
44
44
|
is_generator: typing.Optional[bool] = None,
|
@@ -49,12 +49,14 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
49
49
|
force_build: bool = False,
|
50
50
|
build_timeout: typing.Optional[int] = None,
|
51
51
|
): ...
|
52
|
+
def _get_raw_f(self) -> collections.abc.Callable[P, ReturnType]: ...
|
53
|
+
def _is_web_endpoint(self) -> bool: ...
|
52
54
|
def __get__(self, obj, objtype=None) -> modal.functions._Function[P, ReturnType, OriginalReturnType]: ...
|
53
55
|
def __del__(self): ...
|
54
56
|
def add_flags(self, flags) -> _PartialFunction: ...
|
55
57
|
|
56
58
|
class PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
57
|
-
raw_f:
|
59
|
+
raw_f: collections.abc.Callable[P, ReturnType]
|
58
60
|
flags: _PartialFunctionFlags
|
59
61
|
webhook_config: typing.Optional[modal_proto.api_pb2.WebhookConfig]
|
60
62
|
is_generator: bool
|
@@ -67,7 +69,7 @@ class PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
67
69
|
|
68
70
|
def __init__(
|
69
71
|
self,
|
70
|
-
raw_f:
|
72
|
+
raw_f: collections.abc.Callable[P, ReturnType],
|
71
73
|
flags: _PartialFunctionFlags,
|
72
74
|
webhook_config: typing.Optional[modal_proto.api_pb2.WebhookConfig] = None,
|
73
75
|
is_generator: typing.Optional[bool] = None,
|
@@ -78,12 +80,16 @@ class PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
78
80
|
force_build: bool = False,
|
79
81
|
build_timeout: typing.Optional[int] = None,
|
80
82
|
): ...
|
83
|
+
def _get_raw_f(self) -> collections.abc.Callable[P, ReturnType]: ...
|
84
|
+
def _is_web_endpoint(self) -> bool: ...
|
81
85
|
def __get__(self, obj, objtype=None) -> modal.functions.Function[P, ReturnType, OriginalReturnType]: ...
|
82
86
|
def __del__(self): ...
|
83
87
|
def add_flags(self, flags) -> PartialFunction: ...
|
84
88
|
|
85
89
|
def _find_partial_methods_for_user_cls(user_cls: type[typing.Any], flags: int) -> dict[str, _PartialFunction]: ...
|
86
|
-
def _find_callables_for_obj(
|
90
|
+
def _find_callables_for_obj(
|
91
|
+
user_obj: typing.Any, flags: int
|
92
|
+
) -> dict[str, collections.abc.Callable[..., typing.Any]]: ...
|
87
93
|
|
88
94
|
class _MethodDecoratorType:
|
89
95
|
@typing.overload
|
@@ -93,13 +99,13 @@ class _MethodDecoratorType:
|
|
93
99
|
@typing.overload
|
94
100
|
def __call__(
|
95
101
|
self,
|
96
|
-
func:
|
102
|
+
func: collections.abc.Callable[
|
97
103
|
typing_extensions.Concatenate[typing.Any, P], collections.abc.Coroutine[typing.Any, typing.Any, ReturnType]
|
98
104
|
],
|
99
105
|
) -> PartialFunction[P, ReturnType, collections.abc.Coroutine[typing.Any, typing.Any, ReturnType]]: ...
|
100
106
|
@typing.overload
|
101
107
|
def __call__(
|
102
|
-
self, func:
|
108
|
+
self, func: collections.abc.Callable[typing_extensions.Concatenate[typing.Any, P], ReturnType]
|
103
109
|
) -> PartialFunction[P, ReturnType, ReturnType]: ...
|
104
110
|
|
105
111
|
def _method(
|
@@ -120,7 +126,9 @@ def _web_endpoint(
|
|
120
126
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
121
127
|
requires_proxy_auth: bool = False,
|
122
128
|
wait_for_response: bool = True,
|
123
|
-
) ->
|
129
|
+
) -> collections.abc.Callable[
|
130
|
+
[collections.abc.Callable[P, ReturnType]], _PartialFunction[P, ReturnType, ReturnType]
|
131
|
+
]: ...
|
124
132
|
def _asgi_app(
|
125
133
|
_warn_parentheses_missing=None,
|
126
134
|
*,
|
@@ -128,7 +136,7 @@ def _asgi_app(
|
|
128
136
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
129
137
|
requires_proxy_auth: bool = False,
|
130
138
|
wait_for_response: bool = True,
|
131
|
-
) ->
|
139
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _PartialFunction]: ...
|
132
140
|
def _wsgi_app(
|
133
141
|
_warn_parentheses_missing=None,
|
134
142
|
*,
|
@@ -136,7 +144,7 @@ def _wsgi_app(
|
|
136
144
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
137
145
|
requires_proxy_auth: bool = False,
|
138
146
|
wait_for_response: bool = True,
|
139
|
-
) ->
|
147
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _PartialFunction]: ...
|
140
148
|
def _web_server(
|
141
149
|
port: int,
|
142
150
|
*,
|
@@ -144,31 +152,35 @@ def _web_server(
|
|
144
152
|
label: typing.Optional[str] = None,
|
145
153
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
146
154
|
requires_proxy_auth: bool = False,
|
147
|
-
) ->
|
155
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _PartialFunction]: ...
|
148
156
|
def _disallow_wrapping_method(f: _PartialFunction, wrapper: str) -> None: ...
|
149
157
|
def _build(
|
150
158
|
_warn_parentheses_missing=None, *, force: bool = False, timeout: int = 86400
|
151
|
-
) ->
|
159
|
+
) -> collections.abc.Callable[
|
160
|
+
[typing.Union[collections.abc.Callable[[typing.Any], typing.Any], _PartialFunction]], _PartialFunction
|
161
|
+
]: ...
|
152
162
|
def _enter(
|
153
163
|
_warn_parentheses_missing=None, *, snap: bool = False
|
154
|
-
) ->
|
164
|
+
) -> collections.abc.Callable[
|
165
|
+
[typing.Union[collections.abc.Callable[[typing.Any], typing.Any], _PartialFunction]], _PartialFunction
|
166
|
+
]: ...
|
155
167
|
def _exit(
|
156
168
|
_warn_parentheses_missing=None,
|
157
|
-
) ->
|
169
|
+
) -> collections.abc.Callable[
|
158
170
|
[
|
159
171
|
typing.Union[
|
160
|
-
|
172
|
+
collections.abc.Callable[
|
161
173
|
[typing.Any, typing.Optional[type[BaseException]], typing.Optional[BaseException], typing.Any],
|
162
174
|
typing.Any,
|
163
175
|
],
|
164
|
-
|
176
|
+
collections.abc.Callable[[typing.Any], typing.Any],
|
165
177
|
]
|
166
178
|
],
|
167
179
|
_PartialFunction,
|
168
180
|
]: ...
|
169
181
|
def _batched(
|
170
182
|
_warn_parentheses_missing=None, *, max_batch_size: int, wait_ms: int
|
171
|
-
) ->
|
183
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _PartialFunction]: ...
|
172
184
|
def method(
|
173
185
|
_warn_parentheses_missing=None,
|
174
186
|
*,
|
@@ -184,7 +196,9 @@ def web_endpoint(
|
|
184
196
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
185
197
|
requires_proxy_auth: bool = False,
|
186
198
|
wait_for_response: bool = True,
|
187
|
-
) ->
|
199
|
+
) -> collections.abc.Callable[
|
200
|
+
[collections.abc.Callable[P, ReturnType]], PartialFunction[P, ReturnType, ReturnType]
|
201
|
+
]: ...
|
188
202
|
def asgi_app(
|
189
203
|
_warn_parentheses_missing=None,
|
190
204
|
*,
|
@@ -192,7 +206,7 @@ def asgi_app(
|
|
192
206
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
193
207
|
requires_proxy_auth: bool = False,
|
194
208
|
wait_for_response: bool = True,
|
195
|
-
) ->
|
209
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], PartialFunction]: ...
|
196
210
|
def wsgi_app(
|
197
211
|
_warn_parentheses_missing=None,
|
198
212
|
*,
|
@@ -200,7 +214,7 @@ def wsgi_app(
|
|
200
214
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
201
215
|
requires_proxy_auth: bool = False,
|
202
216
|
wait_for_response: bool = True,
|
203
|
-
) ->
|
217
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], PartialFunction]: ...
|
204
218
|
def web_server(
|
205
219
|
port: int,
|
206
220
|
*,
|
@@ -208,27 +222,31 @@ def web_server(
|
|
208
222
|
label: typing.Optional[str] = None,
|
209
223
|
custom_domains: typing.Optional[collections.abc.Iterable[str]] = None,
|
210
224
|
requires_proxy_auth: bool = False,
|
211
|
-
) ->
|
225
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], PartialFunction]: ...
|
212
226
|
def build(
|
213
227
|
_warn_parentheses_missing=None, *, force: bool = False, timeout: int = 86400
|
214
|
-
) ->
|
228
|
+
) -> collections.abc.Callable[
|
229
|
+
[typing.Union[collections.abc.Callable[[typing.Any], typing.Any], PartialFunction]], PartialFunction
|
230
|
+
]: ...
|
215
231
|
def enter(
|
216
232
|
_warn_parentheses_missing=None, *, snap: bool = False
|
217
|
-
) ->
|
233
|
+
) -> collections.abc.Callable[
|
234
|
+
[typing.Union[collections.abc.Callable[[typing.Any], typing.Any], PartialFunction]], PartialFunction
|
235
|
+
]: ...
|
218
236
|
def exit(
|
219
237
|
_warn_parentheses_missing=None,
|
220
|
-
) ->
|
238
|
+
) -> collections.abc.Callable[
|
221
239
|
[
|
222
240
|
typing.Union[
|
223
|
-
|
241
|
+
collections.abc.Callable[
|
224
242
|
[typing.Any, typing.Optional[type[BaseException]], typing.Optional[BaseException], typing.Any],
|
225
243
|
typing.Any,
|
226
244
|
],
|
227
|
-
|
245
|
+
collections.abc.Callable[[typing.Any], typing.Any],
|
228
246
|
]
|
229
247
|
],
|
230
248
|
PartialFunction,
|
231
249
|
]: ...
|
232
250
|
def batched(
|
233
251
|
_warn_parentheses_missing=None, *, max_batch_size: int, wait_ms: int
|
234
|
-
) ->
|
252
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], PartialFunction]: ...
|
modal/proxy.py
CHANGED
@@ -3,9 +3,9 @@ from typing import Optional
|
|
3
3
|
|
4
4
|
from modal_proto import api_pb2
|
5
5
|
|
6
|
+
from ._object import _get_environment_name, _Object
|
6
7
|
from ._resolver import Resolver
|
7
8
|
from ._utils.async_utils import synchronize_api
|
8
|
-
from .object import _get_environment_name, _Object
|
9
9
|
|
10
10
|
|
11
11
|
class _Proxy(_Object, type_prefix="pr"):
|
modal/proxy.pyi
CHANGED
modal/queue.py
CHANGED
@@ -10,6 +10,7 @@ from synchronicity.async_wrap import asynccontextmanager
|
|
10
10
|
|
11
11
|
from modal_proto import api_pb2
|
12
12
|
|
13
|
+
from ._object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
|
13
14
|
from ._resolver import Resolver
|
14
15
|
from ._serialization import deserialize, serialize
|
15
16
|
from ._utils.async_utils import TaskContext, synchronize_api, warn_if_generator_is_not_consumed
|
@@ -18,7 +19,6 @@ from ._utils.grpc_utils import retry_transient_errors
|
|
18
19
|
from ._utils.name_utils import check_object_name
|
19
20
|
from .client import _Client
|
20
21
|
from .exception import InvalidError, RequestSizeError
|
21
|
-
from .object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
|
22
22
|
|
23
23
|
|
24
24
|
class _Queue(_Object, type_prefix="qu"):
|
modal/queue.pyi
CHANGED
@@ -1,11 +1,12 @@
|
|
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
|
5
6
|
import typing
|
6
7
|
import typing_extensions
|
7
8
|
|
8
|
-
class _Queue(modal.
|
9
|
+
class _Queue(modal._object._Object):
|
9
10
|
def __init__(self): ...
|
10
11
|
@staticmethod
|
11
12
|
def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
|
@@ -84,6 +85,8 @@ class _Queue(modal.object._Object):
|
|
84
85
|
self, *, partition: typing.Optional[str] = None, item_poll_timeout: float = 0.0
|
85
86
|
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
86
87
|
|
88
|
+
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
89
|
+
|
87
90
|
class Queue(modal.object.Object):
|
88
91
|
def __init__(self): ...
|
89
92
|
@staticmethod
|
@@ -138,13 +141,13 @@ class Queue(modal.object.Object):
|
|
138
141
|
|
139
142
|
delete: __delete_spec
|
140
143
|
|
141
|
-
class ___get_nonblocking_spec(typing_extensions.Protocol):
|
144
|
+
class ___get_nonblocking_spec(typing_extensions.Protocol[SUPERSELF]):
|
142
145
|
def __call__(self, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
|
143
146
|
async def aio(self, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
|
144
147
|
|
145
|
-
_get_nonblocking: ___get_nonblocking_spec
|
148
|
+
_get_nonblocking: ___get_nonblocking_spec[typing_extensions.Self]
|
146
149
|
|
147
|
-
class ___get_blocking_spec(typing_extensions.Protocol):
|
150
|
+
class ___get_blocking_spec(typing_extensions.Protocol[SUPERSELF]):
|
148
151
|
def __call__(
|
149
152
|
self, partition: typing.Optional[str], timeout: typing.Optional[float], n_values: int
|
150
153
|
) -> list[typing.Any]: ...
|
@@ -152,15 +155,15 @@ class Queue(modal.object.Object):
|
|
152
155
|
self, partition: typing.Optional[str], timeout: typing.Optional[float], n_values: int
|
153
156
|
) -> list[typing.Any]: ...
|
154
157
|
|
155
|
-
_get_blocking: ___get_blocking_spec
|
158
|
+
_get_blocking: ___get_blocking_spec[typing_extensions.Self]
|
156
159
|
|
157
|
-
class __clear_spec(typing_extensions.Protocol):
|
160
|
+
class __clear_spec(typing_extensions.Protocol[SUPERSELF]):
|
158
161
|
def __call__(self, *, partition: typing.Optional[str] = None, all: bool = False) -> None: ...
|
159
162
|
async def aio(self, *, partition: typing.Optional[str] = None, all: bool = False) -> None: ...
|
160
163
|
|
161
|
-
clear: __clear_spec
|
164
|
+
clear: __clear_spec[typing_extensions.Self]
|
162
165
|
|
163
|
-
class __get_spec(typing_extensions.Protocol):
|
166
|
+
class __get_spec(typing_extensions.Protocol[SUPERSELF]):
|
164
167
|
def __call__(
|
165
168
|
self, block: bool = True, timeout: typing.Optional[float] = None, *, partition: typing.Optional[str] = None
|
166
169
|
) -> typing.Optional[typing.Any]: ...
|
@@ -168,9 +171,9 @@ class Queue(modal.object.Object):
|
|
168
171
|
self, block: bool = True, timeout: typing.Optional[float] = None, *, partition: typing.Optional[str] = None
|
169
172
|
) -> typing.Optional[typing.Any]: ...
|
170
173
|
|
171
|
-
get: __get_spec
|
174
|
+
get: __get_spec[typing_extensions.Self]
|
172
175
|
|
173
|
-
class __get_many_spec(typing_extensions.Protocol):
|
176
|
+
class __get_many_spec(typing_extensions.Protocol[SUPERSELF]):
|
174
177
|
def __call__(
|
175
178
|
self,
|
176
179
|
n_values: int,
|
@@ -188,9 +191,9 @@ class Queue(modal.object.Object):
|
|
188
191
|
partition: typing.Optional[str] = None,
|
189
192
|
) -> list[typing.Any]: ...
|
190
193
|
|
191
|
-
get_many: __get_many_spec
|
194
|
+
get_many: __get_many_spec[typing_extensions.Self]
|
192
195
|
|
193
|
-
class __put_spec(typing_extensions.Protocol):
|
196
|
+
class __put_spec(typing_extensions.Protocol[SUPERSELF]):
|
194
197
|
def __call__(
|
195
198
|
self,
|
196
199
|
v: typing.Any,
|
@@ -210,9 +213,9 @@ class Queue(modal.object.Object):
|
|
210
213
|
partition_ttl: int = 86400,
|
211
214
|
) -> None: ...
|
212
215
|
|
213
|
-
put: __put_spec
|
216
|
+
put: __put_spec[typing_extensions.Self]
|
214
217
|
|
215
|
-
class __put_many_spec(typing_extensions.Protocol):
|
218
|
+
class __put_many_spec(typing_extensions.Protocol[SUPERSELF]):
|
216
219
|
def __call__(
|
217
220
|
self,
|
218
221
|
vs: list[typing.Any],
|
@@ -232,9 +235,9 @@ class Queue(modal.object.Object):
|
|
232
235
|
partition_ttl: int = 86400,
|
233
236
|
) -> None: ...
|
234
237
|
|
235
|
-
put_many: __put_many_spec
|
238
|
+
put_many: __put_many_spec[typing_extensions.Self]
|
236
239
|
|
237
|
-
class ___put_many_blocking_spec(typing_extensions.Protocol):
|
240
|
+
class ___put_many_blocking_spec(typing_extensions.Protocol[SUPERSELF]):
|
238
241
|
def __call__(
|
239
242
|
self,
|
240
243
|
partition: typing.Optional[str],
|
@@ -250,21 +253,21 @@ class Queue(modal.object.Object):
|
|
250
253
|
timeout: typing.Optional[float] = None,
|
251
254
|
): ...
|
252
255
|
|
253
|
-
_put_many_blocking: ___put_many_blocking_spec
|
256
|
+
_put_many_blocking: ___put_many_blocking_spec[typing_extensions.Self]
|
254
257
|
|
255
|
-
class ___put_many_nonblocking_spec(typing_extensions.Protocol):
|
258
|
+
class ___put_many_nonblocking_spec(typing_extensions.Protocol[SUPERSELF]):
|
256
259
|
def __call__(self, partition: typing.Optional[str], partition_ttl: int, vs: list[typing.Any]): ...
|
257
260
|
async def aio(self, partition: typing.Optional[str], partition_ttl: int, vs: list[typing.Any]): ...
|
258
261
|
|
259
|
-
_put_many_nonblocking: ___put_many_nonblocking_spec
|
262
|
+
_put_many_nonblocking: ___put_many_nonblocking_spec[typing_extensions.Self]
|
260
263
|
|
261
|
-
class __len_spec(typing_extensions.Protocol):
|
264
|
+
class __len_spec(typing_extensions.Protocol[SUPERSELF]):
|
262
265
|
def __call__(self, *, partition: typing.Optional[str] = None, total: bool = False) -> int: ...
|
263
266
|
async def aio(self, *, partition: typing.Optional[str] = None, total: bool = False) -> int: ...
|
264
267
|
|
265
|
-
len: __len_spec
|
268
|
+
len: __len_spec[typing_extensions.Self]
|
266
269
|
|
267
|
-
class __iterate_spec(typing_extensions.Protocol):
|
270
|
+
class __iterate_spec(typing_extensions.Protocol[SUPERSELF]):
|
268
271
|
def __call__(
|
269
272
|
self, *, partition: typing.Optional[str] = None, item_poll_timeout: float = 0.0
|
270
273
|
) -> typing.Generator[typing.Any, None, None]: ...
|
@@ -272,4 +275,4 @@ class Queue(modal.object.Object):
|
|
272
275
|
self, *, partition: typing.Optional[str] = None, item_poll_timeout: float = 0.0
|
273
276
|
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
274
277
|
|
275
|
-
iterate: __iterate_spec
|
278
|
+
iterate: __iterate_spec[typing_extensions.Self]
|
modal/runner.py
CHANGED
@@ -4,6 +4,7 @@ import dataclasses
|
|
4
4
|
import os
|
5
5
|
import time
|
6
6
|
import typing
|
7
|
+
import warnings
|
7
8
|
from collections.abc import AsyncGenerator
|
8
9
|
from multiprocessing.synchronize import Event
|
9
10
|
from typing import TYPE_CHECKING, Any, Optional, TypeVar
|
@@ -14,6 +15,7 @@ from synchronicity.async_wrap import asynccontextmanager
|
|
14
15
|
import modal_proto.api_pb2
|
15
16
|
from modal_proto import api_pb2
|
16
17
|
|
18
|
+
from ._object import _get_environment_name, _Object
|
17
19
|
from ._pty import get_pty_info
|
18
20
|
from ._resolver import Resolver
|
19
21
|
from ._runtime.execution_context import is_local
|
@@ -28,7 +30,6 @@ from .config import config, logger
|
|
28
30
|
from .environments import _get_environment_cached
|
29
31
|
from .exception import InteractiveTimeoutError, InvalidError, RemoteError, _CliUserExecutionError
|
30
32
|
from .functions import _Function
|
31
|
-
from .object import _get_environment_name, _Object
|
32
33
|
from .output import _get_output_manager, enable_output
|
33
34
|
from .running_app import RunningApp, running_app_from_layout
|
34
35
|
from .sandbox import _Sandbox
|
@@ -155,7 +156,7 @@ async def _create_all_objects(
|
|
155
156
|
# this is to ensure that directly referenced functions from the global scope has
|
156
157
|
# ids associated with them when they are serialized into other functions
|
157
158
|
await resolver.preload(obj, existing_object_id)
|
158
|
-
if obj.
|
159
|
+
if obj.is_hydrated:
|
159
160
|
tag_to_object_id[tag] = obj.object_id
|
160
161
|
|
161
162
|
await TaskContext.gather(*(_preload(tag, obj) for tag, obj in indexed_objects.items()))
|
@@ -287,6 +288,16 @@ async def _run_app(
|
|
287
288
|
client = await _Client.from_env()
|
288
289
|
|
289
290
|
app_state = api_pb2.APP_STATE_DETACHED if detach else api_pb2.APP_STATE_EPHEMERAL
|
291
|
+
|
292
|
+
output_mgr = _get_output_manager()
|
293
|
+
if interactive and output_mgr is None:
|
294
|
+
warnings.warn(
|
295
|
+
"Interactive mode is disabled because no output manager is active. "
|
296
|
+
"Use 'with modal.enable_output():' to enable interactive mode and see logs.",
|
297
|
+
stacklevel=2,
|
298
|
+
)
|
299
|
+
interactive = False
|
300
|
+
|
290
301
|
running_app: RunningApp = await _init_local_app_new(
|
291
302
|
client,
|
292
303
|
app.description or "",
|
@@ -306,7 +317,7 @@ async def _run_app(
|
|
306
317
|
tc.infinite_loop(heartbeat, sleep=HEARTBEAT_INTERVAL, log_exception=not detach)
|
307
318
|
logs_loop: Optional[asyncio.Task] = None
|
308
319
|
|
309
|
-
if output_mgr
|
320
|
+
if output_mgr is not None:
|
310
321
|
# Defer import so this module is rich-safe
|
311
322
|
# TODO(michael): The get_app_logs_loop function is itself rich-safe aside from accepting an OutputManager
|
312
323
|
# as an argument, so with some refactoring we could avoid the need for this deferred import.
|
modal/sandbox.py
CHANGED
@@ -16,9 +16,10 @@ from modal.volume import _Volume
|
|
16
16
|
from modal_proto import api_pb2
|
17
17
|
|
18
18
|
from ._location import parse_cloud_provider
|
19
|
+
from ._object import _get_environment_name, _Object
|
19
20
|
from ._resolver import Resolver
|
20
21
|
from ._resources import convert_fn_config_to_resources_config
|
21
|
-
from ._utils.async_utils import synchronize_api
|
22
|
+
from ._utils.async_utils import TaskContext, synchronize_api
|
22
23
|
from ._utils.deprecation import deprecation_error
|
23
24
|
from ._utils.grpc_utils import retry_transient_errors
|
24
25
|
from ._utils.mount_utils import validate_network_file_systems, validate_volumes
|
@@ -32,7 +33,6 @@ from .image import _Image
|
|
32
33
|
from .io_streams import StreamReader, StreamWriter, _StreamReader, _StreamWriter
|
33
34
|
from .mount import _Mount
|
34
35
|
from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
|
35
|
-
from .object import _get_environment_name, _Object
|
36
36
|
from .proxy import _Proxy
|
37
37
|
from .scheduler_placement import SchedulerPlacement
|
38
38
|
from .secret import _Secret
|
@@ -165,7 +165,8 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
165
165
|
resources=convert_fn_config_to_resources_config(
|
166
166
|
cpu=cpu, memory=memory, gpu=gpu, ephemeral_disk=ephemeral_disk
|
167
167
|
),
|
168
|
-
cloud_provider=parse_cloud_provider(cloud) if cloud else None,
|
168
|
+
cloud_provider=parse_cloud_provider(cloud) if cloud else None, # Deprecated at some point
|
169
|
+
cloud_provider_str=cloud.upper() if cloud else None, # Supersedes cloud_provider
|
169
170
|
nfs_mounts=network_file_system_mount_protos(validated_network_file_systems, False),
|
170
171
|
runtime_debug=config.get("function_runtime_debug"),
|
171
172
|
cloud_bucket_mounts=cloud_bucket_mounts_to_proto(cloud_bucket_mounts),
|
@@ -350,6 +351,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
350
351
|
Returns an [`Image`](https://modal.com/docs/reference/modal.Image) object which
|
351
352
|
can be used to spawn a new Sandbox with the same filesystem.
|
352
353
|
"""
|
354
|
+
await self._get_task_id() # Ensure the sandbox has started
|
353
355
|
req = api_pb2.SandboxSnapshotFsRequest(sandbox_id=self.object_id, timeout=timeout)
|
354
356
|
resp = await retry_transient_errors(self._client.stub.SandboxSnapshotFs, req)
|
355
357
|
|
@@ -360,10 +362,12 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
360
362
|
metadata = resp.image_metadata
|
361
363
|
|
362
364
|
async def _load(self: _Image, resolver: Resolver, existing_object_id: Optional[str]):
|
363
|
-
|
365
|
+
# no need to hydrate again since we do it eagerly below
|
366
|
+
pass
|
364
367
|
|
365
368
|
rep = "Image()"
|
366
|
-
image = _Image._from_loader(_load, rep)
|
369
|
+
image = _Image._from_loader(_load, rep, hydrate_lazily=True)
|
370
|
+
image._hydrate(image_id, self._client, metadata) # hydrating eagerly since we have all of the data
|
367
371
|
|
368
372
|
return image
|
369
373
|
|
@@ -513,8 +517,8 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
513
517
|
raise InvalidError(f"workdir must be an absolute path, got: {workdir}")
|
514
518
|
|
515
519
|
# Force secret resolution so we can pass the secret IDs to the backend.
|
516
|
-
for secret in secrets
|
517
|
-
|
520
|
+
secret_coros = [secret.hydrate(client=self._client) for secret in secrets]
|
521
|
+
await TaskContext.gather(*secret_coros)
|
518
522
|
|
519
523
|
task_id = await self._get_task_id()
|
520
524
|
req = api_pb2.ContainerExecRequest(
|