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.
Files changed (48) hide show
  1. modal/_runtime/asgi.py +11 -4
  2. modal/_traceback.py +6 -2
  3. modal/_utils/deprecation.py +45 -0
  4. modal/_utils/function_utils.py +24 -13
  5. modal/app.py +5 -4
  6. modal/app.pyi +3 -3
  7. modal/cli/dict.py +6 -2
  8. modal/cli/network_file_system.py +1 -1
  9. modal/cli/run.py +1 -0
  10. modal/cli/volume.py +1 -1
  11. modal/client.pyi +2 -2
  12. modal/cls.py +15 -9
  13. modal/cls.pyi +5 -5
  14. modal/dict.py +11 -17
  15. modal/dict.pyi +8 -12
  16. modal/environments.py +10 -7
  17. modal/environments.pyi +6 -6
  18. modal/functions.py +11 -7
  19. modal/functions.pyi +7 -5
  20. modal/gpu.py +22 -0
  21. modal/image.py +0 -15
  22. modal/image.pyi +0 -26
  23. modal/mount.py +14 -8
  24. modal/mount.pyi +4 -4
  25. modal/network_file_system.py +12 -19
  26. modal/network_file_system.pyi +8 -12
  27. modal/partial_function.py +0 -29
  28. modal/queue.py +11 -17
  29. modal/queue.pyi +8 -12
  30. modal/sandbox.py +2 -0
  31. modal/secret.py +7 -4
  32. modal/secret.pyi +5 -5
  33. modal/volume.py +11 -17
  34. modal/volume.pyi +8 -12
  35. {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/METADATA +2 -2
  36. {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/RECORD +48 -48
  37. modal_proto/api.proto +9 -0
  38. modal_proto/api_grpc.py +16 -0
  39. modal_proto/api_pb2.py +785 -765
  40. modal_proto/api_pb2.pyi +30 -0
  41. modal_proto/api_pb2_grpc.py +33 -0
  42. modal_proto/api_pb2_grpc.pyi +10 -0
  43. modal_proto/modal_api_grpc.py +1 -0
  44. modal_version/_version_generated.py +1 -1
  45. {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/LICENSE +0 -0
  46. {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/WHEEL +0 -0
  47. {modal-0.68.31.dist-info → modal-0.68.50.dist-info}/entry_points.txt +0 -0
  48. {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
- label: str,
58
+ name: str,
57
59
  create_if_missing: bool = False,
58
60
  ):
59
- if label:
60
- # Allow null labels for the case where we want to look up the "default" environment,
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(label, "Environment")
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=label,
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
- label: str,
88
+ name: str,
86
89
  client: Optional[_Client] = None,
87
90
  create_if_missing: bool = False,
88
91
  ):
89
- obj = await _Environment.from_name(label, create_if_missing=create_if_missing)
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(label: str, create_if_missing: bool = False): ...
25
+ async def from_name(name: str, create_if_missing: bool = False): ...
26
26
  @staticmethod
27
27
  async def lookup(
28
- label: str, client: typing.Optional[modal.client._Client] = None, create_if_missing: bool = False
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, label: str, create_if_missing: bool = False): ...
39
- async def aio(self, label: str, create_if_missing: bool = False): ...
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, label: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
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, label: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
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.definition_type,
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
- tag: str,
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=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
- tag: str,
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, tag, namespace=namespace, environment_name=environment_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, tag: str, namespace=1, environment_name: typing.Optional[str] = None
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
- tag: str,
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, tag: str, namespace=1, environment_name: typing.Optional[str] = None
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
- tag: str,
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
- tag: str,
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.expanduser().absolute()
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
- if not self.ignore(Path(local_filename)):
148
- local_relpath = Path(local_filename).expanduser().absolute().relative_to(local_dir)
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 local_filename, mount_path
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
- label: str,
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=label,
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
- label: str,
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(label, namespace=namespace, environment_name=environment_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(label: str, namespace=1, environment_name: typing.Optional[str] = None) -> _Mount: ...
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
- label: str,
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(label: str, namespace=1, environment_name: typing.Optional[str] = None) -> Mount: ...
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
- label: str,
238
+ name: str,
239
239
  namespace=1,
240
240
  client: typing.Optional[modal.client.Client] = None,
241
241
  environment_name: typing.Optional[str] = None,
@@ -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 deprecation_error
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
- def new(cloud: Optional[str] = None):
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
- label: str,
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(label, "NetworkFileSystem")
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=label,
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: `{label}` as a NetworkFileSystem " + "which already exists as a Volume"
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
- label: str,
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
- label, namespace=namespace, environment_name=environment_name, create_if_missing=create_if_missing
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
- async def delete(label: str, client: Optional[_Client] = None, environment_name: Optional[str] = None):
227
- obj = await _NetworkFileSystem.lookup(label, client=client, environment_name=environment_name)
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
 
@@ -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
- label: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
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
- label: str,
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
- label: str, client: typing.Optional[modal.client._Client] = None, environment_name: typing.Optional[str] = None
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
- label: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
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
- label: str,
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
- label: str,
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
- label: str,
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
- label: str,
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