modal 0.68.24__py3-none-any.whl → 0.68.42__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/_traceback.py +6 -2
- modal/_utils/deprecation.py +89 -0
- modal/app.py +15 -34
- modal/app.pyi +6 -7
- modal/cli/app.py +1 -1
- modal/cli/dict.py +6 -2
- modal/cli/network_file_system.py +1 -1
- modal/cli/run.py +1 -0
- modal/cli/volume.py +1 -1
- modal/client.pyi +2 -2
- modal/cls.py +15 -9
- modal/cls.pyi +5 -5
- modal/config.py +2 -1
- modal/container_process.py +2 -1
- modal/dict.py +12 -17
- modal/dict.pyi +8 -12
- modal/environments.py +10 -7
- modal/environments.pyi +6 -6
- modal/exception.py +0 -54
- modal/file_io.py +54 -7
- modal/file_io.pyi +18 -8
- modal/file_pattern_matcher.py +48 -15
- modal/functions.py +11 -13
- modal/functions.pyi +18 -12
- modal/image.py +21 -20
- modal/image.pyi +16 -28
- modal/mount.py +7 -4
- modal/mount.pyi +4 -4
- modal/network_file_system.py +13 -19
- modal/network_file_system.pyi +8 -12
- modal/partial_function.py +2 -30
- modal/queue.py +12 -17
- modal/queue.pyi +8 -12
- modal/runner.py +2 -7
- modal/sandbox.py +25 -13
- modal/sandbox.pyi +21 -0
- modal/secret.py +7 -4
- modal/secret.pyi +5 -5
- modal/serving.py +1 -1
- modal/volume.py +12 -17
- modal/volume.pyi +8 -12
- {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/METADATA +2 -2
- {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/RECORD +51 -50
- modal_proto/api.proto +1 -1
- modal_proto/api_pb2.py +750 -750
- modal_proto/api_pb2.pyi +4 -4
- modal_version/_version_generated.py +1 -1
- {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/LICENSE +0 -0
- {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/WHEEL +0 -0
- {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/entry_points.txt +0 -0
- {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/top_level.txt +0 -0
modal/image.pyi
CHANGED
@@ -92,19 +92,6 @@ class _Image(modal.object._Object):
|
|
92
92
|
_namespace: int = 1,
|
93
93
|
_do_assert_no_mount_layers: bool = True,
|
94
94
|
): ...
|
95
|
-
def extend(
|
96
|
-
self,
|
97
|
-
*,
|
98
|
-
secrets: typing.Optional[collections.abc.Sequence[modal.secret._Secret]] = None,
|
99
|
-
gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
|
100
|
-
build_function: typing.Optional[modal.functions._Function] = None,
|
101
|
-
build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
|
102
|
-
image_registry_config: typing.Optional[_ImageRegistryConfig] = None,
|
103
|
-
context_mount: typing.Optional[modal.mount._Mount] = None,
|
104
|
-
force_build: bool = False,
|
105
|
-
_namespace: int = 1,
|
106
|
-
_do_assert_no_mount_layers: bool = True,
|
107
|
-
) -> _Image: ...
|
108
95
|
def copy_mount(self, mount: modal.mount._Mount, remote_path: typing.Union[str, pathlib.Path] = ".") -> _Image: ...
|
109
96
|
def add_local_file(
|
110
97
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: str, *, copy: bool = False
|
@@ -120,7 +107,14 @@ class _Image(modal.object._Object):
|
|
120
107
|
def copy_local_file(
|
121
108
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
|
122
109
|
) -> _Image: ...
|
123
|
-
def add_local_python_source(
|
110
|
+
def add_local_python_source(
|
111
|
+
self,
|
112
|
+
*module_names: str,
|
113
|
+
copy: bool = False,
|
114
|
+
ignore: typing.Union[
|
115
|
+
collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
|
116
|
+
] = modal.file_pattern_matcher.NON_PYTHON_FILES,
|
117
|
+
) -> _Image: ...
|
124
118
|
def copy_local_dir(
|
125
119
|
self,
|
126
120
|
local_path: typing.Union[str, pathlib.Path],
|
@@ -357,19 +351,6 @@ class Image(modal.object.Object):
|
|
357
351
|
_namespace: int = 1,
|
358
352
|
_do_assert_no_mount_layers: bool = True,
|
359
353
|
): ...
|
360
|
-
def extend(
|
361
|
-
self,
|
362
|
-
*,
|
363
|
-
secrets: typing.Optional[collections.abc.Sequence[modal.secret.Secret]] = None,
|
364
|
-
gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
|
365
|
-
build_function: typing.Optional[modal.functions.Function] = None,
|
366
|
-
build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
|
367
|
-
image_registry_config: typing.Optional[_ImageRegistryConfig] = None,
|
368
|
-
context_mount: typing.Optional[modal.mount.Mount] = None,
|
369
|
-
force_build: bool = False,
|
370
|
-
_namespace: int = 1,
|
371
|
-
_do_assert_no_mount_layers: bool = True,
|
372
|
-
) -> Image: ...
|
373
354
|
def copy_mount(self, mount: modal.mount.Mount, remote_path: typing.Union[str, pathlib.Path] = ".") -> Image: ...
|
374
355
|
def add_local_file(
|
375
356
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: str, *, copy: bool = False
|
@@ -385,7 +366,14 @@ class Image(modal.object.Object):
|
|
385
366
|
def copy_local_file(
|
386
367
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
|
387
368
|
) -> Image: ...
|
388
|
-
def add_local_python_source(
|
369
|
+
def add_local_python_source(
|
370
|
+
self,
|
371
|
+
*module_names: str,
|
372
|
+
copy: bool = False,
|
373
|
+
ignore: typing.Union[
|
374
|
+
collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
|
375
|
+
] = modal.file_pattern_matcher.NON_PYTHON_FILES,
|
376
|
+
) -> Image: ...
|
389
377
|
def copy_local_dir(
|
390
378
|
self,
|
391
379
|
local_path: typing.Union[str, pathlib.Path],
|
modal/mount.py
CHANGED
@@ -22,6 +22,7 @@ from modal_version import __version__
|
|
22
22
|
from ._resolver import Resolver
|
23
23
|
from ._utils.async_utils import aclosing, async_map, synchronize_api
|
24
24
|
from ._utils.blob_utils import FileUploadSpec, blob_upload_file, get_file_upload_spec_from_path
|
25
|
+
from ._utils.deprecation import renamed_parameter
|
25
26
|
from ._utils.grpc_utils import retry_transient_errors
|
26
27
|
from ._utils.name_utils import check_object_name
|
27
28
|
from ._utils.package_utils import get_module_mount_info
|
@@ -623,8 +624,9 @@ class _Mount(_Object, type_prefix="mo"):
|
|
623
624
|
return mount
|
624
625
|
|
625
626
|
@staticmethod
|
627
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
626
628
|
def from_name(
|
627
|
-
|
629
|
+
name: str,
|
628
630
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
629
631
|
environment_name: Optional[str] = None,
|
630
632
|
) -> "_Mount":
|
@@ -632,7 +634,7 @@ class _Mount(_Object, type_prefix="mo"):
|
|
632
634
|
|
633
635
|
async def _load(provider: _Mount, resolver: Resolver, existing_object_id: Optional[str]):
|
634
636
|
req = api_pb2.MountGetOrCreateRequest(
|
635
|
-
deployment_name=
|
637
|
+
deployment_name=name,
|
636
638
|
namespace=namespace,
|
637
639
|
environment_name=_get_environment_name(environment_name, resolver),
|
638
640
|
)
|
@@ -642,15 +644,16 @@ class _Mount(_Object, type_prefix="mo"):
|
|
642
644
|
return _Mount._from_loader(_load, "Mount()")
|
643
645
|
|
644
646
|
@classmethod
|
647
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
645
648
|
async def lookup(
|
646
649
|
cls: type["_Mount"],
|
647
|
-
|
650
|
+
name: str,
|
648
651
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
649
652
|
client: Optional[_Client] = None,
|
650
653
|
environment_name: Optional[str] = None,
|
651
654
|
) -> "_Mount":
|
652
655
|
"""mdmd:hidden"""
|
653
|
-
obj = _Mount.from_name(
|
656
|
+
obj = _Mount.from_name(name, namespace=namespace, environment_name=environment_name)
|
654
657
|
if client is None:
|
655
658
|
client = await _Client.from_env()
|
656
659
|
resolver = Resolver(client=client)
|
modal/mount.pyi
CHANGED
@@ -139,11 +139,11 @@ class _Mount(modal.object._Object):
|
|
139
139
|
ignore: typing.Union[typing.Sequence[str], typing.Callable[[pathlib.Path], bool], None] = None,
|
140
140
|
) -> _Mount: ...
|
141
141
|
@staticmethod
|
142
|
-
def from_name(
|
142
|
+
def from_name(name: str, namespace=1, environment_name: typing.Optional[str] = None) -> _Mount: ...
|
143
143
|
@classmethod
|
144
144
|
async def lookup(
|
145
145
|
cls: type[_Mount],
|
146
|
-
|
146
|
+
name: str,
|
147
147
|
namespace=1,
|
148
148
|
client: typing.Optional[modal.client._Client] = None,
|
149
149
|
environment_name: typing.Optional[str] = None,
|
@@ -231,11 +231,11 @@ class Mount(modal.object.Object):
|
|
231
231
|
ignore: typing.Union[typing.Sequence[str], typing.Callable[[pathlib.Path], bool], None] = None,
|
232
232
|
) -> Mount: ...
|
233
233
|
@staticmethod
|
234
|
-
def from_name(
|
234
|
+
def from_name(name: str, namespace=1, environment_name: typing.Optional[str] = None) -> Mount: ...
|
235
235
|
@classmethod
|
236
236
|
def lookup(
|
237
237
|
cls: type[Mount],
|
238
|
-
|
238
|
+
name: str,
|
239
239
|
namespace=1,
|
240
240
|
client: typing.Optional[modal.client.Client] = None,
|
241
241
|
environment_name: typing.Optional[str] = None,
|
modal/network_file_system.py
CHANGED
@@ -15,11 +15,12 @@ from modal_proto import api_pb2
|
|
15
15
|
from ._resolver import Resolver
|
16
16
|
from ._utils.async_utils import TaskContext, aclosing, async_map, sync_or_async_iter, synchronize_api
|
17
17
|
from ._utils.blob_utils import LARGE_FILE_LIMIT, blob_iter, blob_upload_file
|
18
|
+
from ._utils.deprecation import renamed_parameter
|
18
19
|
from ._utils.grpc_utils import retry_transient_errors
|
19
20
|
from ._utils.hash_utils import get_sha256_hex
|
20
21
|
from ._utils.name_utils import check_object_name
|
21
22
|
from .client import _Client
|
22
|
-
from .exception import InvalidError
|
23
|
+
from .exception import InvalidError
|
23
24
|
from .object import (
|
24
25
|
EPHEMERAL_OBJECT_HEARTBEAT_SLEEP,
|
25
26
|
_get_environment_name,
|
@@ -90,18 +91,9 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
90
91
|
"""
|
91
92
|
|
92
93
|
@staticmethod
|
93
|
-
|
94
|
-
"""mdmd:hidden"""
|
95
|
-
message = (
|
96
|
-
"`NetworkFileSystem.new` is deprecated."
|
97
|
-
" Please use `NetworkFileSystem.from_name` (for persisted)"
|
98
|
-
" or `NetworkFileSystem.ephemeral` (for ephemeral) network filesystems instead."
|
99
|
-
)
|
100
|
-
deprecation_error((2024, 3, 20), message)
|
101
|
-
|
102
|
-
@staticmethod
|
94
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
103
95
|
def from_name(
|
104
|
-
|
96
|
+
name: str,
|
105
97
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
106
98
|
environment_name: Optional[str] = None,
|
107
99
|
create_if_missing: bool = False,
|
@@ -120,11 +112,11 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
120
112
|
pass
|
121
113
|
```
|
122
114
|
"""
|
123
|
-
check_object_name(
|
115
|
+
check_object_name(name, "NetworkFileSystem")
|
124
116
|
|
125
117
|
async def _load(self: _NetworkFileSystem, resolver: Resolver, existing_object_id: Optional[str]):
|
126
118
|
req = api_pb2.SharedVolumeGetOrCreateRequest(
|
127
|
-
deployment_name=
|
119
|
+
deployment_name=name,
|
128
120
|
namespace=namespace,
|
129
121
|
environment_name=_get_environment_name(environment_name, resolver),
|
130
122
|
object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
|
@@ -135,7 +127,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
135
127
|
except GRPCError as exc:
|
136
128
|
if exc.status == Status.NOT_FOUND and exc.message == "App has wrong entity vo":
|
137
129
|
raise InvalidError(
|
138
|
-
f"Attempted to mount: `{
|
130
|
+
f"Attempted to mount: `{name}` as a NetworkFileSystem " + "which already exists as a Volume"
|
139
131
|
)
|
140
132
|
raise
|
141
133
|
|
@@ -175,8 +167,9 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
175
167
|
yield cls._new_hydrated(response.shared_volume_id, client, None, is_another_app=True)
|
176
168
|
|
177
169
|
@staticmethod
|
170
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
178
171
|
async def lookup(
|
179
|
-
|
172
|
+
name: str,
|
180
173
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
181
174
|
client: Optional[_Client] = None,
|
182
175
|
environment_name: Optional[str] = None,
|
@@ -193,7 +186,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
193
186
|
```
|
194
187
|
"""
|
195
188
|
obj = _NetworkFileSystem.from_name(
|
196
|
-
|
189
|
+
name, namespace=namespace, environment_name=environment_name, create_if_missing=create_if_missing
|
197
190
|
)
|
198
191
|
if client is None:
|
199
192
|
client = await _Client.from_env()
|
@@ -222,8 +215,9 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
222
215
|
return resp.shared_volume_id
|
223
216
|
|
224
217
|
@staticmethod
|
225
|
-
|
226
|
-
|
218
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
219
|
+
async def delete(name: str, client: Optional[_Client] = None, environment_name: Optional[str] = None):
|
220
|
+
obj = await _NetworkFileSystem.lookup(name, client=client, environment_name=environment_name)
|
227
221
|
req = api_pb2.SharedVolumeDeleteRequest(shared_volume_id=obj.object_id)
|
228
222
|
await retry_transient_errors(obj._client.stub.SharedVolumeDelete, req)
|
229
223
|
|
modal/network_file_system.pyi
CHANGED
@@ -13,11 +13,9 @@ def network_file_system_mount_protos(
|
|
13
13
|
) -> list[modal_proto.api_pb2.SharedVolumeMount]: ...
|
14
14
|
|
15
15
|
class _NetworkFileSystem(modal.object._Object):
|
16
|
-
@staticmethod
|
17
|
-
def new(cloud: typing.Optional[str] = None): ...
|
18
16
|
@staticmethod
|
19
17
|
def from_name(
|
20
|
-
|
18
|
+
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
21
19
|
) -> _NetworkFileSystem: ...
|
22
20
|
@classmethod
|
23
21
|
def ephemeral(
|
@@ -28,7 +26,7 @@ class _NetworkFileSystem(modal.object._Object):
|
|
28
26
|
) -> typing.AsyncContextManager[_NetworkFileSystem]: ...
|
29
27
|
@staticmethod
|
30
28
|
async def lookup(
|
31
|
-
|
29
|
+
name: str,
|
32
30
|
namespace=1,
|
33
31
|
client: typing.Optional[modal.client._Client] = None,
|
34
32
|
environment_name: typing.Optional[str] = None,
|
@@ -43,7 +41,7 @@ class _NetworkFileSystem(modal.object._Object):
|
|
43
41
|
) -> str: ...
|
44
42
|
@staticmethod
|
45
43
|
async def delete(
|
46
|
-
|
44
|
+
name: str, client: typing.Optional[modal.client._Client] = None, environment_name: typing.Optional[str] = None
|
47
45
|
): ...
|
48
46
|
async def write_file(
|
49
47
|
self,
|
@@ -71,10 +69,8 @@ class _NetworkFileSystem(modal.object._Object):
|
|
71
69
|
class NetworkFileSystem(modal.object.Object):
|
72
70
|
def __init__(self, *args, **kwargs): ...
|
73
71
|
@staticmethod
|
74
|
-
def new(cloud: typing.Optional[str] = None): ...
|
75
|
-
@staticmethod
|
76
72
|
def from_name(
|
77
|
-
|
73
|
+
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
78
74
|
) -> NetworkFileSystem: ...
|
79
75
|
@classmethod
|
80
76
|
def ephemeral(
|
@@ -87,7 +83,7 @@ class NetworkFileSystem(modal.object.Object):
|
|
87
83
|
class __lookup_spec(typing_extensions.Protocol):
|
88
84
|
def __call__(
|
89
85
|
self,
|
90
|
-
|
86
|
+
name: str,
|
91
87
|
namespace=1,
|
92
88
|
client: typing.Optional[modal.client.Client] = None,
|
93
89
|
environment_name: typing.Optional[str] = None,
|
@@ -95,7 +91,7 @@ class NetworkFileSystem(modal.object.Object):
|
|
95
91
|
) -> NetworkFileSystem: ...
|
96
92
|
async def aio(
|
97
93
|
self,
|
98
|
-
|
94
|
+
name: str,
|
99
95
|
namespace=1,
|
100
96
|
client: typing.Optional[modal.client.Client] = None,
|
101
97
|
environment_name: typing.Optional[str] = None,
|
@@ -125,13 +121,13 @@ class NetworkFileSystem(modal.object.Object):
|
|
125
121
|
class __delete_spec(typing_extensions.Protocol):
|
126
122
|
def __call__(
|
127
123
|
self,
|
128
|
-
|
124
|
+
name: str,
|
129
125
|
client: typing.Optional[modal.client.Client] = None,
|
130
126
|
environment_name: typing.Optional[str] = None,
|
131
127
|
): ...
|
132
128
|
async def aio(
|
133
129
|
self,
|
134
|
-
|
130
|
+
name: str,
|
135
131
|
client: typing.Optional[modal.client.Client] = None,
|
136
132
|
environment_name: typing.Optional[str] = None,
|
137
133
|
): ...
|
modal/partial_function.py
CHANGED
@@ -15,9 +15,10 @@ import typing_extensions
|
|
15
15
|
from modal_proto import api_pb2
|
16
16
|
|
17
17
|
from ._utils.async_utils import synchronize_api, synchronizer
|
18
|
+
from ._utils.deprecation import deprecation_error, deprecation_warning
|
18
19
|
from ._utils.function_utils import callable_has_non_self_non_default_params, callable_has_non_self_params
|
19
20
|
from .config import logger
|
20
|
-
from .exception import InvalidError
|
21
|
+
from .exception import InvalidError
|
21
22
|
from .functions import _Function
|
22
23
|
|
23
24
|
MAX_MAX_BATCH_SIZE = 1000
|
@@ -137,29 +138,6 @@ PartialFunction = synchronize_api(_PartialFunction)
|
|
137
138
|
def _find_partial_methods_for_user_cls(user_cls: type[Any], flags: int) -> dict[str, _PartialFunction]:
|
138
139
|
"""Grabs all method on a user class, and returns partials. Includes legacy methods."""
|
139
140
|
|
140
|
-
# Build up a list of legacy attributes to check
|
141
|
-
check_attrs: list[str] = []
|
142
|
-
if flags & _PartialFunctionFlags.BUILD:
|
143
|
-
check_attrs += ["__build__", "__abuild__"]
|
144
|
-
if flags & _PartialFunctionFlags.ENTER_POST_SNAPSHOT:
|
145
|
-
check_attrs += ["__enter__", "__aenter__"]
|
146
|
-
if flags & _PartialFunctionFlags.EXIT:
|
147
|
-
check_attrs += ["__exit__", "__aexit__"]
|
148
|
-
|
149
|
-
# Grab legacy lifecycle methods
|
150
|
-
for attr in check_attrs:
|
151
|
-
if hasattr(user_cls, attr):
|
152
|
-
suggested = attr.strip("_")
|
153
|
-
if is_async := suggested.startswith("a"):
|
154
|
-
suggested = suggested[1:]
|
155
|
-
async_suggestion = " (on an async method)" if is_async else ""
|
156
|
-
message = (
|
157
|
-
f"Using `{attr}` methods for class lifecycle management is deprecated."
|
158
|
-
f" Please try using the `modal.{suggested}` decorator{async_suggestion} instead."
|
159
|
-
" See https://modal.com/docs/guide/lifecycle-functions for more information."
|
160
|
-
)
|
161
|
-
deprecation_error((2024, 2, 21), message)
|
162
|
-
|
163
141
|
partial_functions: dict[str, _PartialFunction] = {}
|
164
142
|
for parent_cls in reversed(user_cls.mro()):
|
165
143
|
if parent_cls is not object:
|
@@ -633,12 +611,6 @@ def _exit(_warn_parentheses_missing=None) -> Callable[[ExitHandlerType], _Partia
|
|
633
611
|
if isinstance(f, _PartialFunction):
|
634
612
|
_disallow_wrapping_method(f, "exit")
|
635
613
|
|
636
|
-
if callable_has_non_self_params(f):
|
637
|
-
message = (
|
638
|
-
"Support for decorating parameterized methods with `@exit` has been deprecated."
|
639
|
-
" Please update your code by removing the parameters."
|
640
|
-
)
|
641
|
-
deprecation_error((2024, 2, 23), message)
|
642
614
|
return _PartialFunction(f, _PartialFunctionFlags.EXIT)
|
643
615
|
|
644
616
|
return wrapper
|
modal/queue.py
CHANGED
@@ -13,10 +13,11 @@ from modal_proto import api_pb2
|
|
13
13
|
from ._resolver import Resolver
|
14
14
|
from ._serialization import deserialize, serialize
|
15
15
|
from ._utils.async_utils import TaskContext, synchronize_api, warn_if_generator_is_not_consumed
|
16
|
+
from ._utils.deprecation import renamed_parameter
|
16
17
|
from ._utils.grpc_utils import retry_transient_errors
|
17
18
|
from ._utils.name_utils import check_object_name
|
18
19
|
from .client import _Client
|
19
|
-
from .exception import InvalidError, RequestSizeError
|
20
|
+
from .exception import InvalidError, RequestSizeError
|
20
21
|
from .object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
|
21
22
|
|
22
23
|
|
@@ -93,15 +94,6 @@ class _Queue(_Object, type_prefix="qu"):
|
|
93
94
|
Partition keys must be non-empty and must not exceed 64 bytes.
|
94
95
|
"""
|
95
96
|
|
96
|
-
@staticmethod
|
97
|
-
def new():
|
98
|
-
"""mdmd:hidden"""
|
99
|
-
message = (
|
100
|
-
"`Queue.new` is deprecated."
|
101
|
-
" Please use `Queue.from_name` (for persisted) or `Queue.ephemeral` (for ephemeral) queues instead."
|
102
|
-
)
|
103
|
-
deprecation_error((2024, 3, 19), message)
|
104
|
-
|
105
97
|
def __init__(self):
|
106
98
|
"""mdmd:hidden"""
|
107
99
|
raise RuntimeError("Queue() is not allowed. Please use `Queue.from_name(...)` or `Queue.ephemeral()` instead.")
|
@@ -153,8 +145,9 @@ class _Queue(_Object, type_prefix="qu"):
|
|
153
145
|
yield cls._new_hydrated(response.queue_id, client, None, is_another_app=True)
|
154
146
|
|
155
147
|
@staticmethod
|
148
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
156
149
|
def from_name(
|
157
|
-
|
150
|
+
name: str,
|
158
151
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
159
152
|
environment_name: Optional[str] = None,
|
160
153
|
create_if_missing: bool = False,
|
@@ -170,11 +163,11 @@ class _Queue(_Object, type_prefix="qu"):
|
|
170
163
|
q.put(123)
|
171
164
|
```
|
172
165
|
"""
|
173
|
-
check_object_name(
|
166
|
+
check_object_name(name, "Queue")
|
174
167
|
|
175
168
|
async def _load(self: _Queue, resolver: Resolver, existing_object_id: Optional[str]):
|
176
169
|
req = api_pb2.QueueGetOrCreateRequest(
|
177
|
-
deployment_name=
|
170
|
+
deployment_name=name,
|
178
171
|
namespace=namespace,
|
179
172
|
environment_name=_get_environment_name(environment_name, resolver),
|
180
173
|
object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
|
@@ -185,8 +178,9 @@ class _Queue(_Object, type_prefix="qu"):
|
|
185
178
|
return _Queue._from_loader(_load, "Queue()", is_another_app=True, hydrate_lazily=True)
|
186
179
|
|
187
180
|
@staticmethod
|
181
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
188
182
|
async def lookup(
|
189
|
-
|
183
|
+
name: str,
|
190
184
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
191
185
|
client: Optional[_Client] = None,
|
192
186
|
environment_name: Optional[str] = None,
|
@@ -203,7 +197,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
203
197
|
```
|
204
198
|
"""
|
205
199
|
obj = _Queue.from_name(
|
206
|
-
|
200
|
+
name, namespace=namespace, environment_name=environment_name, create_if_missing=create_if_missing
|
207
201
|
)
|
208
202
|
if client is None:
|
209
203
|
client = await _Client.from_env()
|
@@ -212,8 +206,9 @@ class _Queue(_Object, type_prefix="qu"):
|
|
212
206
|
return obj
|
213
207
|
|
214
208
|
@staticmethod
|
215
|
-
|
216
|
-
|
209
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
210
|
+
async def delete(name: str, *, client: Optional[_Client] = None, environment_name: Optional[str] = None):
|
211
|
+
obj = await _Queue.lookup(name, client=client, environment_name=environment_name)
|
217
212
|
req = api_pb2.QueueDeleteRequest(queue_id=obj.object_id)
|
218
213
|
await retry_transient_errors(obj._client.stub.QueueDelete, req)
|
219
214
|
|
modal/queue.pyi
CHANGED
@@ -6,8 +6,6 @@ import typing
|
|
6
6
|
import typing_extensions
|
7
7
|
|
8
8
|
class _Queue(modal.object._Object):
|
9
|
-
@staticmethod
|
10
|
-
def new(): ...
|
11
9
|
def __init__(self): ...
|
12
10
|
@staticmethod
|
13
11
|
def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
|
@@ -20,11 +18,11 @@ class _Queue(modal.object._Object):
|
|
20
18
|
) -> typing.AsyncContextManager[_Queue]: ...
|
21
19
|
@staticmethod
|
22
20
|
def from_name(
|
23
|
-
|
21
|
+
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
24
22
|
) -> _Queue: ...
|
25
23
|
@staticmethod
|
26
24
|
async def lookup(
|
27
|
-
|
25
|
+
name: str,
|
28
26
|
namespace=1,
|
29
27
|
client: typing.Optional[modal.client._Client] = None,
|
30
28
|
environment_name: typing.Optional[str] = None,
|
@@ -32,7 +30,7 @@ class _Queue(modal.object._Object):
|
|
32
30
|
) -> _Queue: ...
|
33
31
|
@staticmethod
|
34
32
|
async def delete(
|
35
|
-
|
33
|
+
name: str,
|
36
34
|
*,
|
37
35
|
client: typing.Optional[modal.client._Client] = None,
|
38
36
|
environment_name: typing.Optional[str] = None,
|
@@ -89,8 +87,6 @@ class _Queue(modal.object._Object):
|
|
89
87
|
class Queue(modal.object.Object):
|
90
88
|
def __init__(self): ...
|
91
89
|
@staticmethod
|
92
|
-
def new(): ...
|
93
|
-
@staticmethod
|
94
90
|
def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
|
95
91
|
@classmethod
|
96
92
|
def ephemeral(
|
@@ -101,13 +97,13 @@ class Queue(modal.object.Object):
|
|
101
97
|
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Queue]: ...
|
102
98
|
@staticmethod
|
103
99
|
def from_name(
|
104
|
-
|
100
|
+
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
105
101
|
) -> Queue: ...
|
106
102
|
|
107
103
|
class __lookup_spec(typing_extensions.Protocol):
|
108
104
|
def __call__(
|
109
105
|
self,
|
110
|
-
|
106
|
+
name: str,
|
111
107
|
namespace=1,
|
112
108
|
client: typing.Optional[modal.client.Client] = None,
|
113
109
|
environment_name: typing.Optional[str] = None,
|
@@ -115,7 +111,7 @@ class Queue(modal.object.Object):
|
|
115
111
|
) -> Queue: ...
|
116
112
|
async def aio(
|
117
113
|
self,
|
118
|
-
|
114
|
+
name: str,
|
119
115
|
namespace=1,
|
120
116
|
client: typing.Optional[modal.client.Client] = None,
|
121
117
|
environment_name: typing.Optional[str] = None,
|
@@ -127,14 +123,14 @@ class Queue(modal.object.Object):
|
|
127
123
|
class __delete_spec(typing_extensions.Protocol):
|
128
124
|
def __call__(
|
129
125
|
self,
|
130
|
-
|
126
|
+
name: str,
|
131
127
|
*,
|
132
128
|
client: typing.Optional[modal.client.Client] = None,
|
133
129
|
environment_name: typing.Optional[str] = None,
|
134
130
|
): ...
|
135
131
|
async def aio(
|
136
132
|
self,
|
137
|
-
|
133
|
+
name: str,
|
138
134
|
*,
|
139
135
|
client: typing.Optional[modal.client.Client] = None,
|
140
136
|
environment_name: typing.Optional[str] = None,
|
modal/runner.py
CHANGED
@@ -19,19 +19,14 @@ from ._resolver import Resolver
|
|
19
19
|
from ._runtime.execution_context import is_local
|
20
20
|
from ._traceback import print_server_warnings, traceback_contains_remote_call
|
21
21
|
from ._utils.async_utils import TaskContext, gather_cancel_on_exc, synchronize_api
|
22
|
+
from ._utils.deprecation import deprecation_error
|
22
23
|
from ._utils.grpc_utils import retry_transient_errors
|
23
24
|
from ._utils.name_utils import check_object_name, is_valid_tag
|
24
25
|
from .client import HEARTBEAT_INTERVAL, HEARTBEAT_TIMEOUT, _Client
|
25
26
|
from .cls import _Cls
|
26
27
|
from .config import config, logger
|
27
28
|
from .environments import _get_environment_cached
|
28
|
-
from .exception import
|
29
|
-
InteractiveTimeoutError,
|
30
|
-
InvalidError,
|
31
|
-
RemoteError,
|
32
|
-
_CliUserExecutionError,
|
33
|
-
deprecation_error,
|
34
|
-
)
|
29
|
+
from .exception import InteractiveTimeoutError, InvalidError, RemoteError, _CliUserExecutionError
|
35
30
|
from .functions import _Function
|
36
31
|
from .object import _get_environment_name, _Object
|
37
32
|
from .output import _get_output_manager, enable_output
|
modal/sandbox.py
CHANGED
@@ -19,19 +19,13 @@ from ._location import parse_cloud_provider
|
|
19
19
|
from ._resolver import Resolver
|
20
20
|
from ._resources import convert_fn_config_to_resources_config
|
21
21
|
from ._utils.async_utils import synchronize_api
|
22
|
+
from ._utils.deprecation import deprecation_error
|
22
23
|
from ._utils.grpc_utils import retry_transient_errors
|
23
24
|
from ._utils.mount_utils import validate_network_file_systems, validate_volumes
|
24
25
|
from .client import _Client
|
25
26
|
from .config import config
|
26
27
|
from .container_process import _ContainerProcess
|
27
|
-
from .exception import
|
28
|
-
ExecutionError,
|
29
|
-
InvalidError,
|
30
|
-
SandboxTerminatedError,
|
31
|
-
SandboxTimeoutError,
|
32
|
-
deprecation_error,
|
33
|
-
deprecation_warning,
|
34
|
-
)
|
28
|
+
from .exception import ExecutionError, InvalidError, SandboxTerminatedError, SandboxTimeoutError
|
35
29
|
from .file_io import _FileIO
|
36
30
|
from .gpu import GPU_T
|
37
31
|
from .image import _Image
|
@@ -124,6 +118,8 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
124
118
|
for _, cloud_bucket_mount in cloud_bucket_mounts:
|
125
119
|
if cloud_bucket_mount.secret:
|
126
120
|
deps.append(cloud_bucket_mount.secret)
|
121
|
+
if proxy:
|
122
|
+
deps.append(proxy)
|
127
123
|
return deps
|
128
124
|
|
129
125
|
async def _load(self: _Sandbox, resolver: Resolver, _existing_object_id: Optional[str]):
|
@@ -284,13 +280,14 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
284
280
|
app_id = _App._container_app.app_id
|
285
281
|
app_client = _App._container_app.client
|
286
282
|
else:
|
287
|
-
|
283
|
+
arglist = ", ".join(repr(s) for s in entrypoint_args)
|
284
|
+
deprecation_error(
|
288
285
|
(2024, 9, 14),
|
289
|
-
"Creating a `Sandbox` without an `App` is deprecated.\n"
|
290
|
-
"You may pass in an `App` object, or reference one by name with `App.lookup`:\n"
|
286
|
+
"Creating a `Sandbox` without an `App` is deprecated.\n\n"
|
287
|
+
"You may pass in an `App` object, or reference one by name with `App.lookup`:\n\n"
|
291
288
|
"```\n"
|
292
|
-
"app = modal.App.lookup('
|
293
|
-
"modal.Sandbox.create(
|
289
|
+
"app = modal.App.lookup('sandbox-app', create_if_missing=True)\n"
|
290
|
+
f"sb = modal.Sandbox.create({arglist}, app=app)\n"
|
294
291
|
"```",
|
295
292
|
)
|
296
293
|
|
@@ -567,6 +564,21 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
567
564
|
task_id = await self._get_task_id()
|
568
565
|
return await _FileIO.create(path, mode, self._client, task_id)
|
569
566
|
|
567
|
+
async def ls(self, path: str) -> list[str]:
|
568
|
+
"""List the contents of a directory in the Sandbox."""
|
569
|
+
task_id = await self._get_task_id()
|
570
|
+
return await _FileIO.ls(path, self._client, task_id)
|
571
|
+
|
572
|
+
async def mkdir(self, path: str, parents: bool = False) -> None:
|
573
|
+
"""Create a new directory in the Sandbox."""
|
574
|
+
task_id = await self._get_task_id()
|
575
|
+
return await _FileIO.mkdir(path, self._client, task_id, parents)
|
576
|
+
|
577
|
+
async def rm(self, path: str, recursive: bool = False) -> None:
|
578
|
+
"""Remove a file or directory in the Sandbox."""
|
579
|
+
task_id = await self._get_task_id()
|
580
|
+
return await _FileIO.rm(path, self._client, task_id, recursive)
|
581
|
+
|
570
582
|
@property
|
571
583
|
def stdout(self) -> _StreamReader[str]:
|
572
584
|
"""
|
modal/sandbox.pyi
CHANGED
@@ -128,6 +128,9 @@ class _Sandbox(modal.object._Object):
|
|
128
128
|
async def open(self, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io._FileIO[str]: ...
|
129
129
|
@typing.overload
|
130
130
|
async def open(self, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io._FileIO[bytes]: ...
|
131
|
+
async def ls(self, path: str) -> list[str]: ...
|
132
|
+
async def mkdir(self, path: str, parents: bool = False) -> None: ...
|
133
|
+
async def rm(self, path: str, recursive: bool = False) -> None: ...
|
131
134
|
@property
|
132
135
|
def stdout(self) -> modal.io_streams._StreamReader[str]: ...
|
133
136
|
@property
|
@@ -367,6 +370,24 @@ class Sandbox(modal.object.Object):
|
|
367
370
|
|
368
371
|
open: __open_spec
|
369
372
|
|
373
|
+
class __ls_spec(typing_extensions.Protocol):
|
374
|
+
def __call__(self, path: str) -> list[str]: ...
|
375
|
+
async def aio(self, path: str) -> list[str]: ...
|
376
|
+
|
377
|
+
ls: __ls_spec
|
378
|
+
|
379
|
+
class __mkdir_spec(typing_extensions.Protocol):
|
380
|
+
def __call__(self, path: str, parents: bool = False) -> None: ...
|
381
|
+
async def aio(self, path: str, parents: bool = False) -> None: ...
|
382
|
+
|
383
|
+
mkdir: __mkdir_spec
|
384
|
+
|
385
|
+
class __rm_spec(typing_extensions.Protocol):
|
386
|
+
def __call__(self, path: str, recursive: bool = False) -> None: ...
|
387
|
+
async def aio(self, path: str, recursive: bool = False) -> None: ...
|
388
|
+
|
389
|
+
rm: __rm_spec
|
390
|
+
|
370
391
|
@property
|
371
392
|
def stdout(self) -> modal.io_streams.StreamReader[str]: ...
|
372
393
|
@property
|