modal 0.68.31__py3-none-any.whl → 0.68.50__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/_runtime/asgi.py +11 -4
- modal/_traceback.py +6 -2
- modal/_utils/deprecation.py +45 -0
- modal/_utils/function_utils.py +24 -13
- modal/app.py +5 -4
- modal/app.pyi +3 -3
- 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/dict.py +11 -17
- modal/dict.pyi +8 -12
- modal/environments.py +10 -7
- modal/environments.pyi +6 -6
- modal/functions.py +11 -7
- modal/functions.pyi +7 -5
- modal/gpu.py +22 -0
- modal/image.py +0 -15
- modal/image.pyi +0 -26
- modal/mount.py +14 -8
- modal/mount.pyi +4 -4
- modal/network_file_system.py +12 -19
- modal/network_file_system.pyi +8 -12
- modal/partial_function.py +0 -29
- modal/queue.py +11 -17
- modal/queue.pyi +8 -12
- modal/sandbox.py +2 -0
- modal/secret.py +7 -4
- modal/secret.pyi +5 -5
- modal/volume.py +11 -17
- modal/volume.pyi +8 -12
- {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/METADATA +2 -2
- {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/RECORD +48 -48
- modal_proto/api.proto +9 -0
- modal_proto/api_grpc.py +16 -0
- modal_proto/api_pb2.py +785 -765
- modal_proto/api_pb2.pyi +30 -0
- modal_proto/api_pb2_grpc.py +33 -0
- modal_proto/api_pb2_grpc.pyi +10 -0
- modal_proto/modal_api_grpc.py +1 -0
- modal_version/_version_generated.py +1 -1
- {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/LICENSE +0 -0
- {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/WHEEL +0 -0
- {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/entry_points.txt +0 -0
- {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/top_level.txt +0 -0
modal/environments.py
CHANGED
@@ -10,6 +10,7 @@ from modal_proto import api_pb2
|
|
10
10
|
|
11
11
|
from ._resolver import Resolver
|
12
12
|
from ._utils.async_utils import synchronize_api, synchronizer
|
13
|
+
from ._utils.deprecation import renamed_parameter
|
13
14
|
from ._utils.grpc_utils import retry_transient_errors
|
14
15
|
from ._utils.name_utils import check_object_name
|
15
16
|
from .client import _Client
|
@@ -52,21 +53,22 @@ class _Environment(_Object, type_prefix="en"):
|
|
52
53
|
)
|
53
54
|
|
54
55
|
@staticmethod
|
56
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
55
57
|
async def from_name(
|
56
|
-
|
58
|
+
name: str,
|
57
59
|
create_if_missing: bool = False,
|
58
60
|
):
|
59
|
-
if
|
60
|
-
# Allow null
|
61
|
+
if name:
|
62
|
+
# Allow null names for the case where we want to look up the "default" environment,
|
61
63
|
# which is defined by the server. It feels messy to have "from_name" without a name, though?
|
62
64
|
# We're adding this mostly for internal use right now. We could consider an environment-only
|
63
65
|
# alternate constructor, like `Environment.get_default`, rather than exposing "unnamed"
|
64
66
|
# environments as part of public API when we make this class more useful.
|
65
|
-
check_object_name(
|
67
|
+
check_object_name(name, "Environment")
|
66
68
|
|
67
69
|
async def _load(self: _Environment, resolver: Resolver, existing_object_id: Optional[str]):
|
68
70
|
request = api_pb2.EnvironmentGetOrCreateRequest(
|
69
|
-
deployment_name=
|
71
|
+
deployment_name=name,
|
70
72
|
object_creation_type=(
|
71
73
|
api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING
|
72
74
|
if create_if_missing
|
@@ -81,12 +83,13 @@ class _Environment(_Object, type_prefix="en"):
|
|
81
83
|
return _Environment._from_loader(_load, "Environment()", is_another_app=True, hydrate_lazily=True)
|
82
84
|
|
83
85
|
@staticmethod
|
86
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
84
87
|
async def lookup(
|
85
|
-
|
88
|
+
name: str,
|
86
89
|
client: Optional[_Client] = None,
|
87
90
|
create_if_missing: bool = False,
|
88
91
|
):
|
89
|
-
obj = await _Environment.from_name(
|
92
|
+
obj = await _Environment.from_name(name, create_if_missing=create_if_missing)
|
90
93
|
if client is None:
|
91
94
|
client = await _Client.from_env()
|
92
95
|
resolver = Resolver(client=client)
|
modal/environments.pyi
CHANGED
@@ -22,10 +22,10 @@ class _Environment(modal.object._Object):
|
|
22
22
|
def __init__(self): ...
|
23
23
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
24
24
|
@staticmethod
|
25
|
-
async def from_name(
|
25
|
+
async def from_name(name: str, create_if_missing: bool = False): ...
|
26
26
|
@staticmethod
|
27
27
|
async def lookup(
|
28
|
-
|
28
|
+
name: str, client: typing.Optional[modal.client._Client] = None, create_if_missing: bool = False
|
29
29
|
): ...
|
30
30
|
|
31
31
|
class Environment(modal.object.Object):
|
@@ -35,17 +35,17 @@ class Environment(modal.object.Object):
|
|
35
35
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
36
36
|
|
37
37
|
class __from_name_spec(typing_extensions.Protocol):
|
38
|
-
def __call__(self,
|
39
|
-
async def aio(self,
|
38
|
+
def __call__(self, name: str, create_if_missing: bool = False): ...
|
39
|
+
async def aio(self, name: str, create_if_missing: bool = False): ...
|
40
40
|
|
41
41
|
from_name: __from_name_spec
|
42
42
|
|
43
43
|
class __lookup_spec(typing_extensions.Protocol):
|
44
44
|
def __call__(
|
45
|
-
self,
|
45
|
+
self, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
|
46
46
|
): ...
|
47
47
|
async def aio(
|
48
|
-
self,
|
48
|
+
self, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
|
49
49
|
): ...
|
50
50
|
|
51
51
|
lookup: __lookup_spec
|
modal/functions.py
CHANGED
@@ -22,7 +22,6 @@ from grpclib import GRPCError, Status
|
|
22
22
|
from synchronicity.combined_types import MethodWithAio
|
23
23
|
from synchronicity.exceptions import UserCodeException
|
24
24
|
|
25
|
-
from modal._utils.async_utils import aclosing
|
26
25
|
from modal_proto import api_pb2
|
27
26
|
from modal_proto.modal_api_grpc import ModalClientModal
|
28
27
|
|
@@ -35,13 +34,14 @@ from ._serialization import serialize, serialize_proto_params
|
|
35
34
|
from ._traceback import print_server_warnings
|
36
35
|
from ._utils.async_utils import (
|
37
36
|
TaskContext,
|
37
|
+
aclosing,
|
38
38
|
async_merge,
|
39
39
|
callable_to_agen,
|
40
40
|
synchronize_api,
|
41
41
|
synchronizer,
|
42
42
|
warn_if_generator_is_not_consumed,
|
43
43
|
)
|
44
|
-
from ._utils.deprecation import deprecation_warning
|
44
|
+
from ._utils.deprecation import deprecation_warning, renamed_parameter
|
45
45
|
from ._utils.function_utils import (
|
46
46
|
ATTEMPT_TIMEOUT_GRACE_PERIOD,
|
47
47
|
OUTPUTS_TIMEOUT,
|
@@ -346,6 +346,7 @@ class _FunctionSpec:
|
|
346
346
|
memory: Optional[Union[int, tuple[int, int]]]
|
347
347
|
ephemeral_disk: Optional[int]
|
348
348
|
scheduler_placement: Optional[SchedulerPlacement]
|
349
|
+
proxy: Optional[_Proxy]
|
349
350
|
|
350
351
|
|
351
352
|
P = typing_extensions.ParamSpec("P")
|
@@ -530,6 +531,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
530
531
|
memory=memory,
|
531
532
|
ephemeral_disk=ephemeral_disk,
|
532
533
|
scheduler_placement=scheduler_placement,
|
534
|
+
proxy=proxy,
|
533
535
|
)
|
534
536
|
|
535
537
|
if info.user_cls and not is_auto_snapshot:
|
@@ -751,7 +753,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
751
753
|
mount_ids=loaded_mount_ids,
|
752
754
|
secret_ids=[secret.object_id for secret in secrets],
|
753
755
|
image_id=(image.object_id if image else ""),
|
754
|
-
definition_type=info.
|
756
|
+
definition_type=info.get_definition_type(),
|
755
757
|
function_serialized=function_serialized or b"",
|
756
758
|
class_serialized=class_serialized or b"",
|
757
759
|
function_type=function_type,
|
@@ -1022,10 +1024,11 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1022
1024
|
await retry_transient_errors(self._client.stub.FunctionUpdateSchedulingParams, request)
|
1023
1025
|
|
1024
1026
|
@classmethod
|
1027
|
+
@renamed_parameter((2024, 12, 18), "tag", "name")
|
1025
1028
|
def from_name(
|
1026
1029
|
cls: type["_Function"],
|
1027
1030
|
app_name: str,
|
1028
|
-
|
1031
|
+
name: str,
|
1029
1032
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
1030
1033
|
environment_name: Optional[str] = None,
|
1031
1034
|
) -> "_Function":
|
@@ -1044,7 +1047,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1044
1047
|
assert resolver.client and resolver.client.stub
|
1045
1048
|
request = api_pb2.FunctionGetRequest(
|
1046
1049
|
app_name=app_name,
|
1047
|
-
object_tag=
|
1050
|
+
object_tag=name,
|
1048
1051
|
namespace=namespace,
|
1049
1052
|
environment_name=_get_environment_name(environment_name, resolver) or "",
|
1050
1053
|
)
|
@@ -1064,9 +1067,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1064
1067
|
return cls._from_loader(_load_remote, rep, is_another_app=True, hydrate_lazily=True)
|
1065
1068
|
|
1066
1069
|
@staticmethod
|
1070
|
+
@renamed_parameter((2024, 12, 18), "tag", "name")
|
1067
1071
|
async def lookup(
|
1068
1072
|
app_name: str,
|
1069
|
-
|
1073
|
+
name: str,
|
1070
1074
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
1071
1075
|
client: Optional[_Client] = None,
|
1072
1076
|
environment_name: Optional[str] = None,
|
@@ -1080,7 +1084,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1080
1084
|
f = modal.Function.lookup("other-app", "function")
|
1081
1085
|
```
|
1082
1086
|
"""
|
1083
|
-
obj = _Function.from_name(app_name,
|
1087
|
+
obj = _Function.from_name(app_name, name, namespace=namespace, environment_name=environment_name)
|
1084
1088
|
if client is None:
|
1085
1089
|
client = await _Client.from_env()
|
1086
1090
|
resolver = Resolver(client=client)
|
modal/functions.pyi
CHANGED
@@ -100,6 +100,7 @@ class _FunctionSpec:
|
|
100
100
|
memory: typing.Union[int, tuple[int, int], None]
|
101
101
|
ephemeral_disk: typing.Optional[int]
|
102
102
|
scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement]
|
103
|
+
proxy: typing.Optional[modal.proxy._Proxy]
|
103
104
|
|
104
105
|
def __init__(
|
105
106
|
self,
|
@@ -121,6 +122,7 @@ class _FunctionSpec:
|
|
121
122
|
memory: typing.Union[int, tuple[int, int], None],
|
122
123
|
ephemeral_disk: typing.Optional[int],
|
123
124
|
scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement],
|
125
|
+
proxy: typing.Optional[modal.proxy._Proxy],
|
124
126
|
) -> None: ...
|
125
127
|
def __repr__(self): ...
|
126
128
|
def __eq__(self, other): ...
|
@@ -206,12 +208,12 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.
|
|
206
208
|
async def keep_warm(self, warm_pool_size: int) -> None: ...
|
207
209
|
@classmethod
|
208
210
|
def from_name(
|
209
|
-
cls: type[_Function], app_name: str,
|
211
|
+
cls: type[_Function], app_name: str, name: str, namespace=1, environment_name: typing.Optional[str] = None
|
210
212
|
) -> _Function: ...
|
211
213
|
@staticmethod
|
212
214
|
async def lookup(
|
213
215
|
app_name: str,
|
214
|
-
|
216
|
+
name: str,
|
215
217
|
namespace=1,
|
216
218
|
client: typing.Optional[modal.client._Client] = None,
|
217
219
|
environment_name: typing.Optional[str] = None,
|
@@ -381,14 +383,14 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
381
383
|
|
382
384
|
@classmethod
|
383
385
|
def from_name(
|
384
|
-
cls: type[Function], app_name: str,
|
386
|
+
cls: type[Function], app_name: str, name: str, namespace=1, environment_name: typing.Optional[str] = None
|
385
387
|
) -> Function: ...
|
386
388
|
|
387
389
|
class __lookup_spec(typing_extensions.Protocol):
|
388
390
|
def __call__(
|
389
391
|
self,
|
390
392
|
app_name: str,
|
391
|
-
|
393
|
+
name: str,
|
392
394
|
namespace=1,
|
393
395
|
client: typing.Optional[modal.client.Client] = None,
|
394
396
|
environment_name: typing.Optional[str] = None,
|
@@ -396,7 +398,7 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
396
398
|
async def aio(
|
397
399
|
self,
|
398
400
|
app_name: str,
|
399
|
-
|
401
|
+
name: str,
|
400
402
|
namespace=1,
|
401
403
|
client: typing.Optional[modal.client.Client] = None,
|
402
404
|
environment_name: typing.Optional[str] = None,
|
modal/gpu.py
CHANGED
@@ -137,6 +137,27 @@ class H100(_GPUConfig):
|
|
137
137
|
return f"GPU(H100, count={self.count})"
|
138
138
|
|
139
139
|
|
140
|
+
class L40S(_GPUConfig):
|
141
|
+
"""
|
142
|
+
[NVIDIA L40S](https://www.nvidia.com/en-us/data-center/l40s/) GPU class.
|
143
|
+
|
144
|
+
The L40S is a data center GPU for the Ada Lovelace architecture. It has 48 GB of on-chip
|
145
|
+
GDDR6 RAM and enhanced support for FP8 precision.
|
146
|
+
"""
|
147
|
+
|
148
|
+
def __init__(
|
149
|
+
self,
|
150
|
+
*,
|
151
|
+
# Number of GPUs per container. Defaults to 1.
|
152
|
+
# Useful if you have very large models that don't fit on a single GPU.
|
153
|
+
count: int = 1,
|
154
|
+
):
|
155
|
+
super().__init__(api_pb2.GPU_TYPE_L40S, count)
|
156
|
+
|
157
|
+
def __repr__(self):
|
158
|
+
return f"GPU(L40S, count={self.count})"
|
159
|
+
|
160
|
+
|
140
161
|
class Any(_GPUConfig):
|
141
162
|
"""Selects any one of the GPU classes available within Modal, according to availability."""
|
142
163
|
|
@@ -154,6 +175,7 @@ STRING_TO_GPU_CONFIG: dict[str, Callable] = {
|
|
154
175
|
"a100-80gb": lambda: A100(size="80GB"),
|
155
176
|
"h100": H100,
|
156
177
|
"a10g": A10G,
|
178
|
+
"l40s": L40S,
|
157
179
|
"any": Any,
|
158
180
|
}
|
159
181
|
display_string_to_config = "\n".join(f'- "{key}" → `{c()}`' for key, c in STRING_TO_GPU_CONFIG.items() if key != "inf2")
|
modal/image.py
CHANGED
@@ -571,21 +571,6 @@ class _Image(_Object, type_prefix="im"):
|
|
571
571
|
obj.force_build = force_build
|
572
572
|
return obj
|
573
573
|
|
574
|
-
def extend(self, **kwargs) -> "_Image":
|
575
|
-
"""mdmd:hidden"""
|
576
|
-
deprecation_error(
|
577
|
-
(2024, 3, 7),
|
578
|
-
"`Image.extend` is deprecated; please use a higher-level method, such as `Image.dockerfile_commands`.",
|
579
|
-
)
|
580
|
-
|
581
|
-
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
582
|
-
return DockerfileSpec(
|
583
|
-
commands=kwargs.pop("dockerfile_commands", []),
|
584
|
-
context_files=kwargs.pop("context_files", {}),
|
585
|
-
)
|
586
|
-
|
587
|
-
return _Image._from_args(base_images={"base": self}, dockerfile_function=build_dockerfile, **kwargs)
|
588
|
-
|
589
574
|
def copy_mount(self, mount: _Mount, remote_path: Union[str, Path] = ".") -> "_Image":
|
590
575
|
"""Copy the entire contents of a `modal.Mount` into an image.
|
591
576
|
Useful when files only available locally are required during the image
|
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
|
@@ -364,19 +351,6 @@ class Image(modal.object.Object):
|
|
364
351
|
_namespace: int = 1,
|
365
352
|
_do_assert_no_mount_layers: bool = True,
|
366
353
|
): ...
|
367
|
-
def extend(
|
368
|
-
self,
|
369
|
-
*,
|
370
|
-
secrets: typing.Optional[collections.abc.Sequence[modal.secret.Secret]] = None,
|
371
|
-
gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
|
372
|
-
build_function: typing.Optional[modal.functions.Function] = None,
|
373
|
-
build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
|
374
|
-
image_registry_config: typing.Optional[_ImageRegistryConfig] = None,
|
375
|
-
context_mount: typing.Optional[modal.mount.Mount] = None,
|
376
|
-
force_build: bool = False,
|
377
|
-
_namespace: int = 1,
|
378
|
-
_do_assert_no_mount_layers: bool = True,
|
379
|
-
) -> Image: ...
|
380
354
|
def copy_mount(self, mount: modal.mount.Mount, remote_path: typing.Union[str, pathlib.Path] = ".") -> Image: ...
|
381
355
|
def add_local_file(
|
382
356
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: str, *, copy: bool = False
|
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
|
@@ -104,7 +105,7 @@ class _MountFile(_MountEntry):
|
|
104
105
|
return str(self.local_file)
|
105
106
|
|
106
107
|
def get_files_to_upload(self):
|
107
|
-
local_file = self.local_file.
|
108
|
+
local_file = self.local_file.resolve()
|
108
109
|
if not local_file.exists():
|
109
110
|
raise FileNotFoundError(local_file)
|
110
111
|
|
@@ -130,6 +131,8 @@ class _MountDir(_MountEntry):
|
|
130
131
|
return str(self.local_dir.expanduser().absolute())
|
131
132
|
|
132
133
|
def get_files_to_upload(self):
|
134
|
+
# we can't use .resolve() eagerly here since that could end up "renaming" symlinked files
|
135
|
+
# see test_mount_directory_with_symlinked_file
|
133
136
|
local_dir = self.local_dir.expanduser().absolute()
|
134
137
|
|
135
138
|
if not local_dir.exists():
|
@@ -144,10 +147,11 @@ class _MountDir(_MountEntry):
|
|
144
147
|
gen = (dir_entry.path for dir_entry in os.scandir(local_dir) if dir_entry.is_file())
|
145
148
|
|
146
149
|
for local_filename in gen:
|
147
|
-
|
148
|
-
|
150
|
+
local_path = Path(local_filename)
|
151
|
+
if not self.ignore(local_path):
|
152
|
+
local_relpath = local_path.expanduser().absolute().relative_to(local_dir)
|
149
153
|
mount_path = self.remote_path / local_relpath.as_posix()
|
150
|
-
yield
|
154
|
+
yield local_path.resolve(), mount_path
|
151
155
|
|
152
156
|
def watch_entry(self):
|
153
157
|
return self.local_dir.resolve().expanduser(), None
|
@@ -623,8 +627,9 @@ class _Mount(_Object, type_prefix="mo"):
|
|
623
627
|
return mount
|
624
628
|
|
625
629
|
@staticmethod
|
630
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
626
631
|
def from_name(
|
627
|
-
|
632
|
+
name: str,
|
628
633
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
629
634
|
environment_name: Optional[str] = None,
|
630
635
|
) -> "_Mount":
|
@@ -632,7 +637,7 @@ class _Mount(_Object, type_prefix="mo"):
|
|
632
637
|
|
633
638
|
async def _load(provider: _Mount, resolver: Resolver, existing_object_id: Optional[str]):
|
634
639
|
req = api_pb2.MountGetOrCreateRequest(
|
635
|
-
deployment_name=
|
640
|
+
deployment_name=name,
|
636
641
|
namespace=namespace,
|
637
642
|
environment_name=_get_environment_name(environment_name, resolver),
|
638
643
|
)
|
@@ -642,15 +647,16 @@ class _Mount(_Object, type_prefix="mo"):
|
|
642
647
|
return _Mount._from_loader(_load, "Mount()")
|
643
648
|
|
644
649
|
@classmethod
|
650
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
645
651
|
async def lookup(
|
646
652
|
cls: type["_Mount"],
|
647
|
-
|
653
|
+
name: str,
|
648
654
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
649
655
|
client: Optional[_Client] = None,
|
650
656
|
environment_name: Optional[str] = None,
|
651
657
|
) -> "_Mount":
|
652
658
|
"""mdmd:hidden"""
|
653
|
-
obj = _Mount.from_name(
|
659
|
+
obj = _Mount.from_name(name, namespace=namespace, environment_name=environment_name)
|
654
660
|
if client is None:
|
655
661
|
client = await _Client.from_env()
|
656
662
|
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,7 +15,7 @@ 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
|
18
|
+
from ._utils.deprecation import renamed_parameter
|
19
19
|
from ._utils.grpc_utils import retry_transient_errors
|
20
20
|
from ._utils.hash_utils import get_sha256_hex
|
21
21
|
from ._utils.name_utils import check_object_name
|
@@ -91,18 +91,9 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
91
91
|
"""
|
92
92
|
|
93
93
|
@staticmethod
|
94
|
-
|
95
|
-
"""mdmd:hidden"""
|
96
|
-
message = (
|
97
|
-
"`NetworkFileSystem.new` is deprecated."
|
98
|
-
" Please use `NetworkFileSystem.from_name` (for persisted)"
|
99
|
-
" or `NetworkFileSystem.ephemeral` (for ephemeral) network filesystems instead."
|
100
|
-
)
|
101
|
-
deprecation_error((2024, 3, 20), message)
|
102
|
-
|
103
|
-
@staticmethod
|
94
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
104
95
|
def from_name(
|
105
|
-
|
96
|
+
name: str,
|
106
97
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
107
98
|
environment_name: Optional[str] = None,
|
108
99
|
create_if_missing: bool = False,
|
@@ -121,11 +112,11 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
121
112
|
pass
|
122
113
|
```
|
123
114
|
"""
|
124
|
-
check_object_name(
|
115
|
+
check_object_name(name, "NetworkFileSystem")
|
125
116
|
|
126
117
|
async def _load(self: _NetworkFileSystem, resolver: Resolver, existing_object_id: Optional[str]):
|
127
118
|
req = api_pb2.SharedVolumeGetOrCreateRequest(
|
128
|
-
deployment_name=
|
119
|
+
deployment_name=name,
|
129
120
|
namespace=namespace,
|
130
121
|
environment_name=_get_environment_name(environment_name, resolver),
|
131
122
|
object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
|
@@ -136,7 +127,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
136
127
|
except GRPCError as exc:
|
137
128
|
if exc.status == Status.NOT_FOUND and exc.message == "App has wrong entity vo":
|
138
129
|
raise InvalidError(
|
139
|
-
f"Attempted to mount: `{
|
130
|
+
f"Attempted to mount: `{name}` as a NetworkFileSystem " + "which already exists as a Volume"
|
140
131
|
)
|
141
132
|
raise
|
142
133
|
|
@@ -176,8 +167,9 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
176
167
|
yield cls._new_hydrated(response.shared_volume_id, client, None, is_another_app=True)
|
177
168
|
|
178
169
|
@staticmethod
|
170
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
179
171
|
async def lookup(
|
180
|
-
|
172
|
+
name: str,
|
181
173
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
182
174
|
client: Optional[_Client] = None,
|
183
175
|
environment_name: Optional[str] = None,
|
@@ -194,7 +186,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
194
186
|
```
|
195
187
|
"""
|
196
188
|
obj = _NetworkFileSystem.from_name(
|
197
|
-
|
189
|
+
name, namespace=namespace, environment_name=environment_name, create_if_missing=create_if_missing
|
198
190
|
)
|
199
191
|
if client is None:
|
200
192
|
client = await _Client.from_env()
|
@@ -223,8 +215,9 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
223
215
|
return resp.shared_volume_id
|
224
216
|
|
225
217
|
@staticmethod
|
226
|
-
|
227
|
-
|
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)
|
228
221
|
req = api_pb2.SharedVolumeDeleteRequest(shared_volume_id=obj.object_id)
|
229
222
|
await retry_transient_errors(obj._client.stub.SharedVolumeDelete, req)
|
230
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
@@ -138,29 +138,6 @@ PartialFunction = synchronize_api(_PartialFunction)
|
|
138
138
|
def _find_partial_methods_for_user_cls(user_cls: type[Any], flags: int) -> dict[str, _PartialFunction]:
|
139
139
|
"""Grabs all method on a user class, and returns partials. Includes legacy methods."""
|
140
140
|
|
141
|
-
# Build up a list of legacy attributes to check
|
142
|
-
check_attrs: list[str] = []
|
143
|
-
if flags & _PartialFunctionFlags.BUILD:
|
144
|
-
check_attrs += ["__build__", "__abuild__"]
|
145
|
-
if flags & _PartialFunctionFlags.ENTER_POST_SNAPSHOT:
|
146
|
-
check_attrs += ["__enter__", "__aenter__"]
|
147
|
-
if flags & _PartialFunctionFlags.EXIT:
|
148
|
-
check_attrs += ["__exit__", "__aexit__"]
|
149
|
-
|
150
|
-
# Grab legacy lifecycle methods
|
151
|
-
for attr in check_attrs:
|
152
|
-
if hasattr(user_cls, attr):
|
153
|
-
suggested = attr.strip("_")
|
154
|
-
if is_async := suggested.startswith("a"):
|
155
|
-
suggested = suggested[1:]
|
156
|
-
async_suggestion = " (on an async method)" if is_async else ""
|
157
|
-
message = (
|
158
|
-
f"Using `{attr}` methods for class lifecycle management is deprecated."
|
159
|
-
f" Please try using the `modal.{suggested}` decorator{async_suggestion} instead."
|
160
|
-
" See https://modal.com/docs/guide/lifecycle-functions for more information."
|
161
|
-
)
|
162
|
-
deprecation_error((2024, 2, 21), message)
|
163
|
-
|
164
141
|
partial_functions: dict[str, _PartialFunction] = {}
|
165
142
|
for parent_cls in reversed(user_cls.mro()):
|
166
143
|
if parent_cls is not object:
|
@@ -634,12 +611,6 @@ def _exit(_warn_parentheses_missing=None) -> Callable[[ExitHandlerType], _Partia
|
|
634
611
|
if isinstance(f, _PartialFunction):
|
635
612
|
_disallow_wrapping_method(f, "exit")
|
636
613
|
|
637
|
-
if callable_has_non_self_params(f):
|
638
|
-
message = (
|
639
|
-
"Support for decorating parameterized methods with `@exit` has been deprecated."
|
640
|
-
" Please update your code by removing the parameters."
|
641
|
-
)
|
642
|
-
deprecation_error((2024, 2, 23), message)
|
643
614
|
return _PartialFunction(f, _PartialFunctionFlags.EXIT)
|
644
615
|
|
645
616
|
return wrapper
|