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.
Files changed (51) hide show
  1. modal/_traceback.py +6 -2
  2. modal/_utils/deprecation.py +89 -0
  3. modal/app.py +15 -34
  4. modal/app.pyi +6 -7
  5. modal/cli/app.py +1 -1
  6. modal/cli/dict.py +6 -2
  7. modal/cli/network_file_system.py +1 -1
  8. modal/cli/run.py +1 -0
  9. modal/cli/volume.py +1 -1
  10. modal/client.pyi +2 -2
  11. modal/cls.py +15 -9
  12. modal/cls.pyi +5 -5
  13. modal/config.py +2 -1
  14. modal/container_process.py +2 -1
  15. modal/dict.py +12 -17
  16. modal/dict.pyi +8 -12
  17. modal/environments.py +10 -7
  18. modal/environments.pyi +6 -6
  19. modal/exception.py +0 -54
  20. modal/file_io.py +54 -7
  21. modal/file_io.pyi +18 -8
  22. modal/file_pattern_matcher.py +48 -15
  23. modal/functions.py +11 -13
  24. modal/functions.pyi +18 -12
  25. modal/image.py +21 -20
  26. modal/image.pyi +16 -28
  27. modal/mount.py +7 -4
  28. modal/mount.pyi +4 -4
  29. modal/network_file_system.py +13 -19
  30. modal/network_file_system.pyi +8 -12
  31. modal/partial_function.py +2 -30
  32. modal/queue.py +12 -17
  33. modal/queue.pyi +8 -12
  34. modal/runner.py +2 -7
  35. modal/sandbox.py +25 -13
  36. modal/sandbox.pyi +21 -0
  37. modal/secret.py +7 -4
  38. modal/secret.pyi +5 -5
  39. modal/serving.py +1 -1
  40. modal/volume.py +12 -17
  41. modal/volume.pyi +8 -12
  42. {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/METADATA +2 -2
  43. {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/RECORD +51 -50
  44. modal_proto/api.proto +1 -1
  45. modal_proto/api_pb2.py +750 -750
  46. modal_proto/api_pb2.pyi +4 -4
  47. modal_version/_version_generated.py +1 -1
  48. {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/LICENSE +0 -0
  49. {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/WHEEL +0 -0
  50. {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/entry_points.txt +0 -0
  51. {modal-0.68.24.dist-info → modal-0.68.42.dist-info}/top_level.txt +0 -0
modal/_traceback.py CHANGED
@@ -74,11 +74,15 @@ def append_modal_tb(exc: BaseException, tb_dict: TBDictType, line_cache: LineCac
74
74
 
75
75
  def reduce_traceback_to_user_code(tb: Optional[TracebackType], user_source: str) -> TracebackType:
76
76
  """Return a traceback that does not contain modal entrypoint or synchronicity frames."""
77
- # Step forward all the way through the traceback and drop any synchronicity frames
77
+
78
+ # Step forward all the way through the traceback and drop any "Modal support" frames
79
+ def skip_frame(filename: str) -> bool:
80
+ return "/site-packages/synchronicity/" in filename or "modal/_utils/deprecation" in filename
81
+
78
82
  tb_root = tb
79
83
  while tb is not None:
80
84
  while tb.tb_next is not None:
81
- if "/site-packages/synchronicity/" in tb.tb_next.tb_frame.f_code.co_filename:
85
+ if skip_frame(tb.tb_next.tb_frame.f_code.co_filename):
82
86
  tb.tb_next = tb.tb_next.tb_next
83
87
  else:
84
88
  break
@@ -0,0 +1,89 @@
1
+ # Copyright Modal Labs 2024
2
+ import functools
3
+ import sys
4
+ import warnings
5
+ from datetime import date
6
+ from typing import Any, Callable, TypeVar
7
+
8
+ from typing_extensions import ParamSpec # Needed for Python 3.9
9
+
10
+ from ..exception import DeprecationError, PendingDeprecationError
11
+
12
+ _INTERNAL_MODULES = ["modal", "synchronicity"]
13
+
14
+
15
+ def _is_internal_frame(frame):
16
+ module = frame.f_globals["__name__"].split(".")[0]
17
+ return module in _INTERNAL_MODULES
18
+
19
+
20
+ def deprecation_error(deprecated_on: tuple[int, int, int], msg: str):
21
+ raise DeprecationError(f"Deprecated on {date(*deprecated_on)}: {msg}")
22
+
23
+
24
+ def deprecation_warning(
25
+ deprecated_on: tuple[int, int, int], msg: str, *, pending: bool = False, show_source: bool = True
26
+ ) -> None:
27
+ """Issue a Modal deprecation warning with source optionally attributed to user code.
28
+
29
+ See the implementation of the built-in [warnings.warn](https://docs.python.org/3/library/warnings.html#available-functions).
30
+ """
31
+ filename, lineno = "<unknown>", 0
32
+ if show_source:
33
+ # Find the last non-Modal line that triggered the warning
34
+ try:
35
+ frame = sys._getframe()
36
+ while frame is not None and _is_internal_frame(frame):
37
+ frame = frame.f_back
38
+ if frame is not None:
39
+ filename = frame.f_code.co_filename
40
+ lineno = frame.f_lineno
41
+ except ValueError:
42
+ # Use the defaults from above
43
+ pass
44
+
45
+ warning_cls = PendingDeprecationError if pending else DeprecationError
46
+
47
+ # This is a lower-level function that warnings.warn uses
48
+ warnings.warn_explicit(f"{date(*deprecated_on)}: {msg}", warning_cls, filename, lineno)
49
+
50
+
51
+ P = ParamSpec("P")
52
+ R = TypeVar("R")
53
+
54
+
55
+ def renamed_parameter(
56
+ date: tuple[int, int, int],
57
+ old_name: str,
58
+ new_name: str,
59
+ show_source: bool = True,
60
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
61
+ """Decorator for semi-gracefully changing a parameter name.
62
+
63
+ Functions wrapped with this decorator can be defined using only the `new_name` of the parameter.
64
+ If the function is invoked with the `old_name`, the wrapper will pass the value as a keyword
65
+ argument for `new_name` and issue a Modal deprecation warning about the change.
66
+
67
+ Note that this only prevents parameter renamings from breaking code at runtime.
68
+ Type checking will fail when code uses `old_name`. To avoid this, the `old_name` can be
69
+ preserved in the function signature with an `Annotated` type hint indicating the renaming.
70
+ """
71
+
72
+ def decorator(func: Callable[P, R]) -> Callable[P, R]:
73
+ @functools.wraps(func)
74
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
75
+ mut_kwargs: dict[str, Any] = locals()["kwargs"] # Avoid referencing kwargs directly due to bug in sigtools
76
+ if old_name in mut_kwargs:
77
+ mut_kwargs[new_name] = mut_kwargs.pop(old_name)
78
+ func_name = func.__qualname__.removeprefix("_") # Avoid confusion when synchronicity-wrapped
79
+ message = (
80
+ f"The '{old_name}' parameter of `{func_name}` has been renamed to '{new_name}'."
81
+ "\nUsing the old name will become an error in a future release. Please update your code."
82
+ )
83
+ deprecation_warning(date, message, show_source=show_source)
84
+
85
+ return func(*args, **kwargs)
86
+
87
+ return wrapper
88
+
89
+ return decorator
modal/app.py CHANGED
@@ -22,6 +22,7 @@ from modal_proto import api_pb2
22
22
 
23
23
  from ._ipython import is_notebook
24
24
  from ._utils.async_utils import synchronize_api
25
+ from ._utils.deprecation import deprecation_error, deprecation_warning, renamed_parameter
25
26
  from ._utils.function_utils import FunctionInfo, is_global_object, is_method_fn
26
27
  from ._utils.grpc_utils import retry_transient_errors
27
28
  from ._utils.mount_utils import validate_volumes
@@ -29,7 +30,7 @@ from .client import _Client
29
30
  from .cloud_bucket_mount import _CloudBucketMount
30
31
  from .cls import _Cls, parameter
31
32
  from .config import logger
32
- from .exception import ExecutionError, InvalidError, deprecation_error, deprecation_warning
33
+ from .exception import ExecutionError, InvalidError
33
34
  from .functions import Function, _Function
34
35
  from .gpu import GPU_T
35
36
  from .image import _Image
@@ -45,7 +46,6 @@ from .partial_function import (
45
46
  from .proxy import _Proxy
46
47
  from .retries import Retries
47
48
  from .running_app import RunningApp
48
- from .sandbox import _Sandbox
49
49
  from .schedule import Schedule
50
50
  from .scheduler_placement import SchedulerPlacement
51
51
  from .secret import _Secret
@@ -260,8 +260,9 @@ class _App:
260
260
  return self._description
261
261
 
262
262
  @staticmethod
263
+ @renamed_parameter((2024, 12, 18), "label", "name")
263
264
  async def lookup(
264
- label: str,
265
+ name: str,
265
266
  client: Optional[_Client] = None,
266
267
  environment_name: Optional[str] = None,
267
268
  create_if_missing: bool = False,
@@ -283,14 +284,14 @@ class _App:
283
284
  environment_name = _get_environment_name(environment_name)
284
285
 
285
286
  request = api_pb2.AppGetOrCreateRequest(
286
- app_name=label,
287
+ app_name=name,
287
288
  environment_name=environment_name,
288
289
  object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
289
290
  )
290
291
 
291
292
  response = await retry_transient_errors(client.stub.AppGetOrCreate, request)
292
293
 
293
- app = _App(label)
294
+ app = _App(name)
294
295
  app._app_id = response.app_id
295
296
  app._client = client
296
297
  app._running_app = RunningApp(
@@ -964,36 +965,16 @@ class _App:
964
965
  _experimental_scheduler_placement: Optional[
965
966
  SchedulerPlacement
966
967
  ] = None, # Experimental controls over fine-grained scheduling (alpha).
967
- ) -> _Sandbox:
968
- """`App.spawn_sandbox` is deprecated in favor of `Sandbox.create(app=...)`.
969
-
970
- See https://modal.com/docs/guide/sandbox for more info on working with sandboxes.
971
- """
972
- deprecation_warning((2024, 7, 5), _App.spawn_sandbox.__doc__)
973
- if not self._running_app:
974
- raise InvalidError("`app.spawn_sandbox` requires a running app.")
975
-
976
- return await _Sandbox.create(
977
- *entrypoint_args,
978
- app=self,
979
- environment_name=self._running_app.environment_name,
980
- image=image or _default_image,
981
- mounts=mounts,
982
- secrets=secrets,
983
- timeout=timeout,
984
- workdir=workdir,
985
- gpu=gpu,
986
- cloud=cloud,
987
- region=region,
988
- cpu=cpu,
989
- memory=memory,
990
- network_file_systems=network_file_systems,
991
- block_network=block_network,
992
- volumes=volumes,
993
- pty_info=pty_info,
994
- _experimental_scheduler_placement=_experimental_scheduler_placement,
995
- client=self._client,
968
+ ) -> None:
969
+ """mdmd:hidden"""
970
+ arglist = ", ".join(repr(s) for s in entrypoint_args)
971
+ message = (
972
+ "`App.spawn_sandbox` is deprecated.\n\n"
973
+ "Sandboxes can be created using the `Sandbox` object:\n\n"
974
+ f"```\nsb = Sandbox.create({arglist}, app=app)\n```\n\n"
975
+ "See https://modal.com/docs/guide/sandbox for more info on working with sandboxes."
996
976
  )
977
+ deprecation_error((2024, 7, 5), message)
997
978
 
998
979
  def include(self, /, other_app: "_App"):
999
980
  """Include another App's objects in this one.
modal/app.pyi CHANGED
@@ -13,7 +13,6 @@ import modal.partial_function
13
13
  import modal.proxy
14
14
  import modal.retries
15
15
  import modal.running_app
16
- import modal.sandbox
17
16
  import modal.schedule
18
17
  import modal.scheduler_placement
19
18
  import modal.secret
@@ -108,7 +107,7 @@ class _App:
108
107
  def description(self) -> typing.Optional[str]: ...
109
108
  @staticmethod
110
109
  async def lookup(
111
- label: str,
110
+ name: str,
112
111
  client: typing.Optional[modal.client._Client] = None,
113
112
  environment_name: typing.Optional[str] = None,
114
113
  create_if_missing: bool = False,
@@ -261,7 +260,7 @@ class _App:
261
260
  ] = {},
262
261
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
263
262
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
264
- ) -> modal.sandbox._Sandbox: ...
263
+ ) -> None: ...
265
264
  def include(self, /, other_app: _App): ...
266
265
  def _logs(
267
266
  self, client: typing.Optional[modal.client._Client] = None
@@ -307,14 +306,14 @@ class App:
307
306
  class __lookup_spec(typing_extensions.Protocol):
308
307
  def __call__(
309
308
  self,
310
- label: str,
309
+ name: str,
311
310
  client: typing.Optional[modal.client.Client] = None,
312
311
  environment_name: typing.Optional[str] = None,
313
312
  create_if_missing: bool = False,
314
313
  ) -> App: ...
315
314
  async def aio(
316
315
  self,
317
- label: str,
316
+ name: str,
318
317
  client: typing.Optional[modal.client.Client] = None,
319
318
  environment_name: typing.Optional[str] = None,
320
319
  create_if_missing: bool = False,
@@ -491,7 +490,7 @@ class App:
491
490
  ] = {},
492
491
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
493
492
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
494
- ) -> modal.sandbox.Sandbox: ...
493
+ ) -> None: ...
495
494
  async def aio(
496
495
  self,
497
496
  *entrypoint_args: str,
@@ -515,7 +514,7 @@ class App:
515
514
  ] = {},
516
515
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
517
516
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
518
- ) -> modal.sandbox.Sandbox: ...
517
+ ) -> None: ...
519
518
 
520
519
  spawn_sandbox: __spawn_sandbox_spec
521
520
 
modal/cli/app.py CHANGED
@@ -10,9 +10,9 @@ from rich.text import Text
10
10
  from typer import Argument
11
11
 
12
12
  from modal._utils.async_utils import synchronizer
13
+ from modal._utils.deprecation import deprecation_warning
13
14
  from modal.client import _Client
14
15
  from modal.environments import ensure_env
15
- from modal.exception import deprecation_warning
16
16
  from modal.object import _get_environment_name
17
17
  from modal_proto import api_pb2
18
18
 
modal/cli/dict.py CHANGED
@@ -89,6 +89,11 @@ async def get(name: str, key: str, *, env: Optional[str] = ENV_OPTION):
89
89
  console.print(val)
90
90
 
91
91
 
92
+ def _display(input: str, use_repr: bool) -> str:
93
+ val = repr(input) if use_repr else str(input)
94
+ return val[:80] + "..." if len(val) > 80 else val
95
+
96
+
92
97
  @dict_cli.command(name="items", rich_help_panel="Inspection")
93
98
  @synchronizer.create_blocking
94
99
  async def items(
@@ -117,8 +122,7 @@ async def items(
117
122
  if json:
118
123
  display_item = key, val
119
124
  else:
120
- cast = repr if use_repr else str
121
- display_item = cast(key), cast(val) # type: ignore # mypy/issue/12056
125
+ display_item = _display(key, use_repr), _display(val, use_repr) # type: ignore # mypy/issue/12056
122
126
  items.append(display_item)
123
127
 
124
128
  display_table(["Key", "Value"], items, json)
@@ -237,4 +237,4 @@ async def delete(
237
237
  abort=True,
238
238
  )
239
239
 
240
- await _NetworkFileSystem.delete(label=nfs_name, environment_name=env)
240
+ await _NetworkFileSystem.delete(nfs_name, environment_name=env)
modal/cli/run.py CHANGED
@@ -482,6 +482,7 @@ def shell(
482
482
  volumes=function_spec.volumes,
483
483
  region=function_spec.scheduler_placement.proto.regions if function_spec.scheduler_placement else None,
484
484
  pty=pty,
485
+ proxy=function_spec.proxy,
485
486
  )
486
487
  else:
487
488
  modal_image = Image.from_registry(image, add_python=add_python) if image else None
modal/cli/volume.py CHANGED
@@ -286,4 +286,4 @@ async def delete(
286
286
  abort=True,
287
287
  )
288
288
 
289
- await _Volume.delete(label=volume_name, environment_name=env)
289
+ await _Volume.delete(volume_name, environment_name=env)
modal/client.pyi CHANGED
@@ -26,7 +26,7 @@ class _Client:
26
26
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
27
27
 
28
28
  def __init__(
29
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.24"
29
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.42"
30
30
  ): ...
31
31
  def is_closed(self) -> bool: ...
32
32
  @property
@@ -81,7 +81,7 @@ class Client:
81
81
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
82
82
 
83
83
  def __init__(
84
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.24"
84
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.42"
85
85
  ): ...
86
86
  def is_closed(self) -> bool: ...
87
87
  @property
modal/cls.py CHANGED
@@ -16,6 +16,7 @@ from ._resources import convert_fn_config_to_resources_config
16
16
  from ._serialization import check_valid_cls_constructor_arg
17
17
  from ._traceback import print_server_warnings
18
18
  from ._utils.async_utils import synchronize_api, synchronizer
19
+ from ._utils.deprecation import renamed_parameter
19
20
  from ._utils.grpc_utils import retry_transient_errors
20
21
  from ._utils.mount_utils import validate_volumes
21
22
  from .client import _Client
@@ -513,10 +514,11 @@ class _Cls(_Object, type_prefix="cs"):
513
514
  return self._class_service_function is not None
514
515
 
515
516
  @classmethod
517
+ @renamed_parameter((2024, 12, 18), "tag", "name")
516
518
  def from_name(
517
519
  cls: type["_Cls"],
518
520
  app_name: str,
519
- tag: str,
521
+ name: str,
520
522
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
521
523
  environment_name: Optional[str] = None,
522
524
  workspace: Optional[str] = None,
@@ -528,7 +530,7 @@ class _Cls(_Object, type_prefix="cs"):
528
530
  Modal servers until the first time it is actually used.
529
531
 
530
532
  ```python
531
- Class = modal.Cls.from_name("other-app", "Class")
533
+ Model = modal.Cls.from_name("other-app", "Model")
532
534
  ```
533
535
  """
534
536
 
@@ -536,7 +538,7 @@ class _Cls(_Object, type_prefix="cs"):
536
538
  _environment_name = _get_environment_name(environment_name, resolver)
537
539
  request = api_pb2.ClassGetRequest(
538
540
  app_name=app_name,
539
- object_tag=tag,
541
+ object_tag=name,
540
542
  namespace=namespace,
541
543
  environment_name=_environment_name,
542
544
  lookup_published=workspace is not None,
@@ -555,11 +557,11 @@ class _Cls(_Object, type_prefix="cs"):
555
557
 
556
558
  print_server_warnings(response.server_warnings)
557
559
 
558
- class_function_tag = f"{tag}.*" # special name of the base service function for the class
560
+ class_service_name = f"{name}.*" # special name of the base service function for the class
559
561
 
560
562
  class_service_function = _Function.from_name(
561
563
  app_name,
562
- class_function_tag,
564
+ class_service_name,
563
565
  environment_name=_environment_name,
564
566
  )
565
567
  try:
@@ -635,9 +637,10 @@ class _Cls(_Object, type_prefix="cs"):
635
637
  return cls
636
638
 
637
639
  @staticmethod
640
+ @renamed_parameter((2024, 12, 18), "tag", "name")
638
641
  async def lookup(
639
642
  app_name: str,
640
- tag: str,
643
+ name: str,
641
644
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
642
645
  client: Optional[_Client] = None,
643
646
  environment_name: Optional[str] = None,
@@ -649,11 +652,14 @@ class _Cls(_Object, type_prefix="cs"):
649
652
  that will hydrate the local object with metadata from Modal servers.
650
653
 
651
654
  ```python notest
652
- Class = modal.Cls.lookup("other-app", "Class")
653
- obj = Class()
655
+ Model = modal.Cls.lookup("other-app", "Model")
656
+ model = Model()
657
+ model.inference(...)
654
658
  ```
655
659
  """
656
- obj = _Cls.from_name(app_name, tag, namespace=namespace, environment_name=environment_name, workspace=workspace)
660
+ obj = _Cls.from_name(
661
+ app_name, name, namespace=namespace, environment_name=environment_name, workspace=workspace
662
+ )
657
663
  if client is None:
658
664
  client = await _Client.from_env()
659
665
  resolver = Resolver(client=client)
modal/cls.pyi CHANGED
@@ -112,7 +112,7 @@ class _Cls(modal.object._Object):
112
112
  def from_name(
113
113
  cls: type[_Cls],
114
114
  app_name: str,
115
- tag: str,
115
+ name: str,
116
116
  namespace=1,
117
117
  environment_name: typing.Optional[str] = None,
118
118
  workspace: typing.Optional[str] = None,
@@ -133,7 +133,7 @@ class _Cls(modal.object._Object):
133
133
  @staticmethod
134
134
  async def lookup(
135
135
  app_name: str,
136
- tag: str,
136
+ name: str,
137
137
  namespace=1,
138
138
  client: typing.Optional[modal.client._Client] = None,
139
139
  environment_name: typing.Optional[str] = None,
@@ -164,7 +164,7 @@ class Cls(modal.object.Object):
164
164
  def from_name(
165
165
  cls: type[Cls],
166
166
  app_name: str,
167
- tag: str,
167
+ name: str,
168
168
  namespace=1,
169
169
  environment_name: typing.Optional[str] = None,
170
170
  workspace: typing.Optional[str] = None,
@@ -187,7 +187,7 @@ class Cls(modal.object.Object):
187
187
  def __call__(
188
188
  self,
189
189
  app_name: str,
190
- tag: str,
190
+ name: str,
191
191
  namespace=1,
192
192
  client: typing.Optional[modal.client.Client] = None,
193
193
  environment_name: typing.Optional[str] = None,
@@ -196,7 +196,7 @@ class Cls(modal.object.Object):
196
196
  async def aio(
197
197
  self,
198
198
  app_name: str,
199
- tag: str,
199
+ name: str,
200
200
  namespace=1,
201
201
  client: typing.Optional[modal.client.Client] = None,
202
202
  environment_name: typing.Optional[str] = None,
modal/config.py CHANGED
@@ -86,8 +86,9 @@ from google.protobuf.empty_pb2 import Empty
86
86
 
87
87
  from modal_proto import api_pb2
88
88
 
89
+ from ._utils.deprecation import deprecation_error
89
90
  from ._utils.logger import configure_logger
90
- from .exception import InvalidError, deprecation_error
91
+ from .exception import InvalidError
91
92
 
92
93
  # Locate config file and read it
93
94
 
@@ -6,10 +6,11 @@ from typing import Generic, Optional, TypeVar
6
6
  from modal_proto import api_pb2
7
7
 
8
8
  from ._utils.async_utils import TaskContext, synchronize_api
9
+ from ._utils.deprecation import deprecation_error
9
10
  from ._utils.grpc_utils import retry_transient_errors
10
11
  from ._utils.shell_utils import stream_from_stdin, write_to_fd
11
12
  from .client import _Client
12
- from .exception import InteractiveTimeoutError, InvalidError, deprecation_error
13
+ from .exception import InteractiveTimeoutError, InvalidError
13
14
  from .io_streams import _StreamReader, _StreamWriter
14
15
  from .stream_type import StreamType
15
16
 
modal/dict.py CHANGED
@@ -10,11 +10,12 @@ from modal_proto import api_pb2
10
10
  from ._resolver import Resolver
11
11
  from ._serialization import deserialize, serialize
12
12
  from ._utils.async_utils import TaskContext, synchronize_api
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
16
17
  from .config import logger
17
- from .exception import RequestSizeError, deprecation_error
18
+ from .exception import RequestSizeError
18
19
  from .object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
19
20
 
20
21
 
@@ -57,15 +58,6 @@ class _Dict(_Object, type_prefix="di"):
57
58
  For more examples, see the [guide](/docs/guide/dicts-and-queues#modal-dicts).
58
59
  """
59
60
 
60
- @staticmethod
61
- def new(data: Optional[dict] = None):
62
- """mdmd:hidden"""
63
- message = (
64
- "`Dict.new` is deprecated."
65
- " Please use `Dict.from_name` (for persisted) or `Dict.ephemeral` (for ephemeral) dicts instead."
66
- )
67
- deprecation_error((2024, 3, 19), message)
68
-
69
61
  def __init__(self, data={}):
70
62
  """mdmd:hidden"""
71
63
  raise RuntimeError(
@@ -111,8 +103,9 @@ class _Dict(_Object, type_prefix="di"):
111
103
  yield cls._new_hydrated(response.dict_id, client, None, is_another_app=True)
112
104
 
113
105
  @staticmethod
106
+ @renamed_parameter((2024, 12, 18), "label", "name")
114
107
  def from_name(
115
- label: str,
108
+ name: str,
116
109
  data: Optional[dict] = None,
117
110
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
118
111
  environment_name: Optional[str] = None,
@@ -129,12 +122,12 @@ class _Dict(_Object, type_prefix="di"):
129
122
  d[123] = 456
130
123
  ```
131
124
  """
132
- check_object_name(label, "Dict")
125
+ check_object_name(name, "Dict")
133
126
 
134
127
  async def _load(self: _Dict, resolver: Resolver, existing_object_id: Optional[str]):
135
128
  serialized = _serialize_dict(data if data is not None else {})
136
129
  req = api_pb2.DictGetOrCreateRequest(
137
- deployment_name=label,
130
+ deployment_name=name,
138
131
  namespace=namespace,
139
132
  environment_name=_get_environment_name(environment_name, resolver),
140
133
  object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
@@ -147,8 +140,9 @@ class _Dict(_Object, type_prefix="di"):
147
140
  return _Dict._from_loader(_load, "Dict()", is_another_app=True, hydrate_lazily=True)
148
141
 
149
142
  @staticmethod
143
+ @renamed_parameter((2024, 12, 18), "label", "name")
150
144
  async def lookup(
151
- label: str,
145
+ name: str,
152
146
  data: Optional[dict] = None,
153
147
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
154
148
  client: Optional[_Client] = None,
@@ -166,7 +160,7 @@ class _Dict(_Object, type_prefix="di"):
166
160
  ```
167
161
  """
168
162
  obj = _Dict.from_name(
169
- label,
163
+ name,
170
164
  data=data,
171
165
  namespace=namespace,
172
166
  environment_name=environment_name,
@@ -179,13 +173,14 @@ class _Dict(_Object, type_prefix="di"):
179
173
  return obj
180
174
 
181
175
  @staticmethod
176
+ @renamed_parameter((2024, 12, 18), "label", "name")
182
177
  async def delete(
183
- label: str,
178
+ name: str,
184
179
  *,
185
180
  client: Optional[_Client] = None,
186
181
  environment_name: Optional[str] = None,
187
182
  ):
188
- obj = await _Dict.lookup(label, client=client, environment_name=environment_name)
183
+ obj = await _Dict.lookup(name, client=client, environment_name=environment_name)
189
184
  req = api_pb2.DictDeleteRequest(dict_id=obj.object_id)
190
185
  await retry_transient_errors(obj._client.stub.DictDelete, req)
191
186
 
modal/dict.pyi CHANGED
@@ -8,8 +8,6 @@ import typing_extensions
8
8
  def _serialize_dict(data): ...
9
9
 
10
10
  class _Dict(modal.object._Object):
11
- @staticmethod
12
- def new(data: typing.Optional[dict] = None): ...
13
11
  def __init__(self, data={}): ...
14
12
  @classmethod
15
13
  def ephemeral(
@@ -21,7 +19,7 @@ class _Dict(modal.object._Object):
21
19
  ) -> typing.AsyncContextManager[_Dict]: ...
22
20
  @staticmethod
23
21
  def from_name(
24
- label: str,
22
+ name: str,
25
23
  data: typing.Optional[dict] = None,
26
24
  namespace=1,
27
25
  environment_name: typing.Optional[str] = None,
@@ -29,7 +27,7 @@ class _Dict(modal.object._Object):
29
27
  ) -> _Dict: ...
30
28
  @staticmethod
31
29
  async def lookup(
32
- label: str,
30
+ name: str,
33
31
  data: typing.Optional[dict] = None,
34
32
  namespace=1,
35
33
  client: typing.Optional[modal.client._Client] = None,
@@ -38,7 +36,7 @@ class _Dict(modal.object._Object):
38
36
  ) -> _Dict: ...
39
37
  @staticmethod
40
38
  async def delete(
41
- label: str,
39
+ name: str,
42
40
  *,
43
41
  client: typing.Optional[modal.client._Client] = None,
44
42
  environment_name: typing.Optional[str] = None,
@@ -60,8 +58,6 @@ class _Dict(modal.object._Object):
60
58
 
61
59
  class Dict(modal.object.Object):
62
60
  def __init__(self, data={}): ...
63
- @staticmethod
64
- def new(data: typing.Optional[dict] = None): ...
65
61
  @classmethod
66
62
  def ephemeral(
67
63
  cls: type[Dict],
@@ -72,7 +68,7 @@ class Dict(modal.object.Object):
72
68
  ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Dict]: ...
73
69
  @staticmethod
74
70
  def from_name(
75
- label: str,
71
+ name: str,
76
72
  data: typing.Optional[dict] = None,
77
73
  namespace=1,
78
74
  environment_name: typing.Optional[str] = None,
@@ -82,7 +78,7 @@ class Dict(modal.object.Object):
82
78
  class __lookup_spec(typing_extensions.Protocol):
83
79
  def __call__(
84
80
  self,
85
- label: str,
81
+ name: str,
86
82
  data: typing.Optional[dict] = None,
87
83
  namespace=1,
88
84
  client: typing.Optional[modal.client.Client] = None,
@@ -91,7 +87,7 @@ class Dict(modal.object.Object):
91
87
  ) -> Dict: ...
92
88
  async def aio(
93
89
  self,
94
- label: str,
90
+ name: str,
95
91
  data: typing.Optional[dict] = None,
96
92
  namespace=1,
97
93
  client: typing.Optional[modal.client.Client] = None,
@@ -104,14 +100,14 @@ class Dict(modal.object.Object):
104
100
  class __delete_spec(typing_extensions.Protocol):
105
101
  def __call__(
106
102
  self,
107
- label: str,
103
+ name: str,
108
104
  *,
109
105
  client: typing.Optional[modal.client.Client] = None,
110
106
  environment_name: typing.Optional[str] = None,
111
107
  ): ...
112
108
  async def aio(
113
109
  self,
114
- label: str,
110
+ name: str,
115
111
  *,
116
112
  client: typing.Optional[modal.client.Client] = None,
117
113
  environment_name: typing.Optional[str] = None,