modal 0.67.6__py3-none-any.whl → 0.67.8__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 (102) hide show
  1. modal/_clustered_functions.py +2 -2
  2. modal/_clustered_functions.pyi +2 -2
  3. modal/_container_entrypoint.py +5 -4
  4. modal/_output.py +29 -28
  5. modal/_pty.py +2 -2
  6. modal/_resolver.py +6 -5
  7. modal/_resources.py +3 -3
  8. modal/_runtime/asgi.py +7 -6
  9. modal/_runtime/container_io_manager.py +22 -26
  10. modal/_runtime/execution_context.py +2 -2
  11. modal/_runtime/telemetry.py +1 -2
  12. modal/_runtime/user_code_imports.py +11 -13
  13. modal/_serialization.py +3 -7
  14. modal/_traceback.py +5 -5
  15. modal/_tunnel.py +4 -3
  16. modal/_tunnel.pyi +2 -2
  17. modal/_utils/async_utils.py +8 -15
  18. modal/_utils/blob_utils.py +4 -3
  19. modal/_utils/function_utils.py +14 -10
  20. modal/_utils/grpc_testing.py +7 -6
  21. modal/_utils/grpc_utils.py +2 -3
  22. modal/_utils/hash_utils.py +2 -2
  23. modal/_utils/mount_utils.py +5 -4
  24. modal/_utils/package_utils.py +2 -3
  25. modal/_utils/pattern_matcher.py +6 -6
  26. modal/_utils/rand_pb_testing.py +3 -3
  27. modal/_utils/shell_utils.py +2 -1
  28. modal/_vendor/a2wsgi_wsgi.py +62 -72
  29. modal/_vendor/cloudpickle.py +1 -1
  30. modal/_watcher.py +8 -7
  31. modal/app.py +29 -34
  32. modal/app.pyi +102 -97
  33. modal/call_graph.py +6 -6
  34. modal/cli/_download.py +3 -2
  35. modal/cli/_traceback.py +4 -4
  36. modal/cli/app.py +4 -4
  37. modal/cli/container.py +4 -4
  38. modal/cli/dict.py +1 -1
  39. modal/cli/environment.py +2 -3
  40. modal/cli/launch.py +2 -2
  41. modal/cli/network_file_system.py +1 -1
  42. modal/cli/profile.py +1 -1
  43. modal/cli/programs/run_jupyter.py +2 -2
  44. modal/cli/programs/vscode.py +3 -3
  45. modal/cli/queues.py +1 -1
  46. modal/cli/run.py +6 -6
  47. modal/cli/secret.py +3 -3
  48. modal/cli/utils.py +2 -1
  49. modal/cli/volume.py +3 -3
  50. modal/client.py +6 -11
  51. modal/client.pyi +18 -27
  52. modal/cloud_bucket_mount.py +3 -3
  53. modal/cloud_bucket_mount.pyi +2 -2
  54. modal/cls.py +16 -15
  55. modal/cls.pyi +23 -22
  56. modal/config.py +2 -2
  57. modal/dict.py +4 -3
  58. modal/dict.pyi +10 -9
  59. modal/environments.py +3 -3
  60. modal/environments.pyi +3 -3
  61. modal/exception.py +2 -3
  62. modal/functions.py +20 -27
  63. modal/functions.pyi +44 -47
  64. modal/image.py +45 -48
  65. modal/image.pyi +102 -101
  66. modal/io_streams.py +4 -7
  67. modal/io_streams.pyi +14 -13
  68. modal/mount.py +23 -22
  69. modal/mount.pyi +28 -29
  70. modal/network_file_system.py +7 -6
  71. modal/network_file_system.pyi +12 -11
  72. modal/object.py +9 -8
  73. modal/object.pyi +47 -34
  74. modal/output.py +2 -1
  75. modal/parallel_map.py +4 -4
  76. modal/partial_function.py +9 -13
  77. modal/partial_function.pyi +17 -18
  78. modal/queue.py +9 -8
  79. modal/queue.pyi +23 -22
  80. modal/runner.py +8 -7
  81. modal/runner.pyi +8 -14
  82. modal/running_app.py +3 -3
  83. modal/sandbox.py +14 -13
  84. modal/sandbox.pyi +67 -72
  85. modal/scheduler_placement.py +2 -1
  86. modal/secret.py +7 -7
  87. modal/secret.pyi +12 -12
  88. modal/serving.py +4 -3
  89. modal/serving.pyi +5 -4
  90. modal/token_flow.py +3 -2
  91. modal/token_flow.pyi +3 -3
  92. modal/volume.py +7 -12
  93. modal/volume.pyi +17 -16
  94. {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/METADATA +1 -1
  95. modal-0.67.8.dist-info/RECORD +168 -0
  96. modal_docs/mdmd/signatures.py +1 -2
  97. modal_version/_version_generated.py +1 -1
  98. modal-0.67.6.dist-info/RECORD +0 -168
  99. {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/LICENSE +0 -0
  100. {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/WHEEL +0 -0
  101. {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/entry_points.txt +0 -0
  102. {modal-0.67.6.dist-info → modal-0.67.8.dist-info}/top_level.txt +0 -0
modal/cli/profile.py CHANGED
@@ -28,7 +28,7 @@ def current():
28
28
 
29
29
  @profile_cli.command(name="list", help="Show all Modal profiles and highlight the active one.")
30
30
  @synchronizer.create_blocking
31
- async def list(json: Optional[bool] = False):
31
+ async def list_(json: Optional[bool] = False):
32
32
  config = Config()
33
33
  profiles = config_profiles()
34
34
  lookup_coros = [
@@ -8,12 +8,12 @@ import subprocess
8
8
  import threading
9
9
  import time
10
10
  import webbrowser
11
- from typing import Any, Dict
11
+ from typing import Any
12
12
 
13
13
  from modal import App, Image, Mount, Queue, Secret, Volume, forward
14
14
 
15
15
  # Passed by `modal launch` locally via CLI, plumbed to remote runner through secrets.
16
- args: Dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))
16
+ args: dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))
17
17
 
18
18
 
19
19
  app = App()
@@ -8,12 +8,12 @@ import subprocess
8
8
  import threading
9
9
  import time
10
10
  import webbrowser
11
- from typing import Any, Dict, Tuple
11
+ from typing import Any
12
12
 
13
13
  from modal import App, Image, Mount, Queue, Secret, Volume, forward
14
14
 
15
15
  # Passed by `modal launch` locally via CLI, plumbed to remote runner through secrets.
16
- args: Dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))
16
+ args: dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))
17
17
 
18
18
 
19
19
  app = App()
@@ -41,7 +41,7 @@ volume = (
41
41
  volumes = {"/home/coder/volume": volume} if volume else {}
42
42
 
43
43
 
44
- def wait_for_port(data: Tuple[str, str], q: Queue):
44
+ def wait_for_port(data: tuple[str, str], q: Queue):
45
45
  start_time = time.monotonic()
46
46
  while True:
47
47
  try:
modal/cli/queues.py CHANGED
@@ -58,7 +58,7 @@ async def delete(name: str, *, yes: bool = YES_OPTION, env: Optional[str] = ENV_
58
58
 
59
59
  @queue_cli.command(name="list", rich_help_panel="Management")
60
60
  @synchronizer.create_blocking
61
- async def list(*, json: bool = False, env: Optional[str] = ENV_OPTION):
61
+ async def list_(*, json: bool = False, env: Optional[str] = ENV_OPTION):
62
62
  """List all named Queues."""
63
63
  env = ensure_env(env)
64
64
 
modal/cli/run.py CHANGED
@@ -9,7 +9,7 @@ import sys
9
9
  import time
10
10
  import typing
11
11
  from functools import partial
12
- from typing import Any, Callable, Dict, Optional, get_type_hints
12
+ from typing import Any, Callable, Optional, get_type_hints
13
13
 
14
14
  import click
15
15
  import typer
@@ -60,7 +60,7 @@ class NoParserAvailable(InvalidError):
60
60
  pass
61
61
 
62
62
 
63
- def _get_signature(f: Callable[..., Any], is_method: bool = False) -> Dict[str, ParameterMetadata]:
63
+ def _get_signature(f: Callable[..., Any], is_method: bool = False) -> dict[str, ParameterMetadata]:
64
64
  try:
65
65
  type_hints = get_type_hints(f)
66
66
  except Exception as exc:
@@ -71,7 +71,7 @@ def _get_signature(f: Callable[..., Any], is_method: bool = False) -> Dict[str,
71
71
  if is_method:
72
72
  self = None # Dummy, doesn't matter
73
73
  f = functools.partial(f, self)
74
- signature: Dict[str, ParameterMetadata] = {}
74
+ signature: dict[str, ParameterMetadata] = {}
75
75
  for param in inspect.signature(f).parameters.values():
76
76
  signature[param.name] = {
77
77
  "name": param.name,
@@ -98,7 +98,7 @@ def _get_param_type_as_str(annot: Any) -> str:
98
98
  return annot_str
99
99
 
100
100
 
101
- def _add_click_options(func, signature: Dict[str, ParameterMetadata]):
101
+ def _add_click_options(func, signature: dict[str, ParameterMetadata]):
102
102
  """Adds @click.option based on function signature
103
103
 
104
104
  Kind of like typer, but using options instead of positional arguments
@@ -148,7 +148,7 @@ def _get_click_command_for_function(app: App, function_tag):
148
148
  if function.is_generator:
149
149
  raise InvalidError("`modal run` is not supported for generator functions")
150
150
 
151
- signature: Dict[str, ParameterMetadata]
151
+ signature: dict[str, ParameterMetadata]
152
152
  cls: Optional[Cls] = None
153
153
  if function.info.user_cls is not None:
154
154
  cls = typing.cast(Cls, app.registered_classes[class_name])
@@ -364,7 +364,7 @@ def shell(
364
364
  default=None, help="Container image tag for inside the shell (if not using REF)."
365
365
  ),
366
366
  add_python: Optional[str] = typer.Option(default=None, help="Add Python to the image (if not using REF)."),
367
- volume: Optional[typing.List[str]] = typer.Option(
367
+ volume: Optional[list[str]] = typer.Option(
368
368
  default=None,
369
369
  help=(
370
370
  "Name of a `modal.Volume` to mount inside the shell at `/mnt/{name}` (if not using REF)."
modal/cli/secret.py CHANGED
@@ -3,7 +3,7 @@ import os
3
3
  import platform
4
4
  import subprocess
5
5
  from tempfile import NamedTemporaryFile
6
- from typing import List, Optional
6
+ from typing import Optional
7
7
 
8
8
  import click
9
9
  import typer
@@ -23,7 +23,7 @@ secret_cli = typer.Typer(name="secret", help="Manage secrets.", no_args_is_help=
23
23
 
24
24
  @secret_cli.command("list", help="List your published secrets.")
25
25
  @synchronizer.create_blocking
26
- async def list(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
26
+ async def list_(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
27
27
  env = ensure_env(env)
28
28
  client = await _Client.from_env()
29
29
  response = await retry_transient_errors(client.stub.SecretList, api_pb2.SecretListRequest(environment_name=env))
@@ -47,7 +47,7 @@ async def list(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
47
47
  @synchronizer.create_blocking
48
48
  async def create(
49
49
  secret_name,
50
- keyvalues: List[str] = typer.Argument(..., help="Space-separated KEY=VALUE items"),
50
+ keyvalues: list[str] = typer.Argument(..., help="Space-separated KEY=VALUE items"),
51
51
  env: Optional[str] = ENV_OPTION,
52
52
  force: bool = typer.Option(False, "--force", help="Overwrite the secret if it already exists."),
53
53
  ):
modal/cli/utils.py CHANGED
@@ -1,8 +1,9 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
+ from collections.abc import Sequence
3
4
  from datetime import datetime
4
5
  from json import dumps
5
- from typing import Optional, Sequence, Union
6
+ from typing import Optional, Union
6
7
 
7
8
  import typer
8
9
  from click import UsageError
modal/cli/volume.py CHANGED
@@ -2,7 +2,7 @@
2
2
  import os
3
3
  import sys
4
4
  from pathlib import Path
5
- from typing import List, Optional
5
+ from typing import Optional
6
6
 
7
7
  import typer
8
8
  from click import UsageError
@@ -108,7 +108,7 @@ async def get(
108
108
  rich_help_panel="Management",
109
109
  )
110
110
  @synchronizer.create_blocking
111
- async def list(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
111
+ async def list_(env: Optional[str] = ENV_OPTION, json: Optional[bool] = False):
112
112
  env = ensure_env(env)
113
113
  client = await _Client.from_env()
114
114
  response = await retry_transient_errors(client.stub.VolumeList, api_pb2.VolumeListRequest(environment_name=env))
@@ -257,7 +257,7 @@ async def rm(
257
257
  @synchronizer.create_blocking
258
258
  async def cp(
259
259
  volume_name: str,
260
- paths: List[str], # accepts multiple paths, last path is treated as destination path
260
+ paths: list[str], # accepts multiple paths, last path is treated as destination path
261
261
  env: Optional[str] = ENV_OPTION,
262
262
  ):
263
263
  ensure_env(env)
modal/client.py CHANGED
@@ -3,17 +3,12 @@ import asyncio
3
3
  import os
4
4
  import platform
5
5
  import warnings
6
+ from collections.abc import AsyncGenerator, AsyncIterator, Collection, Mapping
6
7
  from typing import (
7
8
  Any,
8
- AsyncGenerator,
9
- AsyncIterator,
10
9
  ClassVar,
11
- Collection,
12
- Dict,
13
10
  Generic,
14
- Mapping,
15
11
  Optional,
16
- Tuple,
17
12
  TypeVar,
18
13
  Union,
19
14
  )
@@ -40,7 +35,7 @@ CLIENT_CREATE_ATTEMPT_TIMEOUT: float = 4.0
40
35
  CLIENT_CREATE_TOTAL_TIMEOUT: float = 15.0
41
36
 
42
37
 
43
- def _get_metadata(client_type: int, credentials: Optional[Tuple[str, str]], version: str) -> Dict[str, str]:
38
+ def _get_metadata(client_type: int, credentials: Optional[tuple[str, str]], version: str) -> dict[str, str]:
44
39
  # This implements a simplified version of platform.platform() that's still machine-readable
45
40
  uname: platform.uname_result = platform.uname()
46
41
  if uname.system == "Darwin":
@@ -69,7 +64,7 @@ def _get_metadata(client_type: int, credentials: Optional[Tuple[str, str]], vers
69
64
 
70
65
  ReturnType = TypeVar("ReturnType")
71
66
  _Value = Union[str, bytes]
72
- _MetadataLike = Union[Mapping[str, _Value], Collection[Tuple[str, _Value]]]
67
+ _MetadataLike = Union[Mapping[str, _Value], Collection[tuple[str, _Value]]]
73
68
  RequestType = TypeVar("RequestType", bound=Message)
74
69
  ResponseType = TypeVar("ResponseType", bound=Message)
75
70
 
@@ -85,7 +80,7 @@ class _Client:
85
80
  self,
86
81
  server_url: str,
87
82
  client_type: int,
88
- credentials: Optional[Tuple[str, str]],
83
+ credentials: Optional[tuple[str, str]],
89
84
  version: str = __version__,
90
85
  ):
91
86
  """mdmd:hidden
@@ -201,7 +196,7 @@ class _Client:
201
196
  else:
202
197
  c = config
203
198
 
204
- credentials: Optional[Tuple[str, str]]
199
+ credentials: Optional[tuple[str, str]]
205
200
 
206
201
  if cls._client_from_env_lock is None:
207
202
  cls._client_from_env_lock = asyncio.Lock()
@@ -266,7 +261,7 @@ class _Client:
266
261
  return client
267
262
 
268
263
  @classmethod
269
- async def verify(cls, server_url: str, credentials: Tuple[str, str]) -> None:
264
+ async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
270
265
  """mdmd:hidden
271
266
  Check whether can the client can connect to this server with these credentials; raise if not.
272
267
  """
modal/client.pyi CHANGED
@@ -1,5 +1,6 @@
1
1
  import asyncio.events
2
2
  import asyncio.locks
3
+ import collections.abc
3
4
  import google.protobuf.message
4
5
  import grpclib.client
5
6
  import modal._utils.async_utils
@@ -9,9 +10,7 @@ import synchronicity.combined_types
9
10
  import typing
10
11
  import typing_extensions
11
12
 
12
- def _get_metadata(
13
- client_type: int, credentials: typing.Optional[typing.Tuple[str, str]], version: str
14
- ) -> typing.Dict[str, str]: ...
13
+ def _get_metadata(client_type: int, credentials: typing.Optional[tuple[str, str]], version: str) -> dict[str, str]: ...
15
14
 
16
15
  ReturnType = typing.TypeVar("ReturnType")
17
16
 
@@ -27,11 +26,7 @@ class _Client:
27
26
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
28
27
 
29
28
  def __init__(
30
- self,
31
- server_url: str,
32
- client_type: int,
33
- credentials: typing.Optional[typing.Tuple[str, str]],
34
- version: str = "0.67.6",
29
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.8"
35
30
  ): ...
36
31
  def is_closed(self) -> bool: ...
37
32
  @property
@@ -48,7 +43,7 @@ class _Client:
48
43
  @classmethod
49
44
  async def from_credentials(cls, token_id: str, token_secret: str) -> _Client: ...
50
45
  @classmethod
51
- async def verify(cls, server_url: str, credentials: typing.Tuple[str, str]) -> None: ...
46
+ async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None: ...
52
47
  @classmethod
53
48
  def set_env_client(cls, client: typing.Optional[_Client]): ...
54
49
  async def _call_safely(self, coro, readable_method: str): ...
@@ -61,8 +56,8 @@ class _Client:
61
56
  *,
62
57
  timeout: typing.Optional[float] = None,
63
58
  metadata: typing.Union[
64
- typing.Mapping[str, typing.Union[str, bytes]],
65
- typing.Collection[typing.Tuple[str, typing.Union[str, bytes]]],
59
+ collections.abc.Mapping[str, typing.Union[str, bytes]],
60
+ collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
66
61
  None,
67
62
  ] = None,
68
63
  ) -> typing.Any: ...
@@ -72,11 +67,11 @@ class _Client:
72
67
  request: typing.Any,
73
68
  *,
74
69
  metadata: typing.Union[
75
- typing.Mapping[str, typing.Union[str, bytes]],
76
- typing.Collection[typing.Tuple[str, typing.Union[str, bytes]]],
70
+ collections.abc.Mapping[str, typing.Union[str, bytes]],
71
+ collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
77
72
  None,
78
73
  ],
79
- ) -> typing.AsyncGenerator[typing.Any, None]: ...
74
+ ) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
80
75
 
81
76
  class Client:
82
77
  _client_from_env: typing.ClassVar[typing.Optional[Client]]
@@ -86,11 +81,7 @@ class Client:
86
81
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
87
82
 
88
83
  def __init__(
89
- self,
90
- server_url: str,
91
- client_type: int,
92
- credentials: typing.Optional[typing.Tuple[str, str]],
93
- version: str = "0.67.6",
84
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.8"
94
85
  ): ...
95
86
  def is_closed(self) -> bool: ...
96
87
  @property
@@ -125,7 +116,7 @@ class Client:
125
116
  @classmethod
126
117
  def from_credentials(cls, token_id: str, token_secret: str) -> Client: ...
127
118
  @classmethod
128
- def verify(cls, server_url: str, credentials: typing.Tuple[str, str]) -> None: ...
119
+ def verify(cls, server_url: str, credentials: tuple[str, str]) -> None: ...
129
120
  @classmethod
130
121
  def set_env_client(cls, client: typing.Optional[Client]): ...
131
122
 
@@ -154,8 +145,8 @@ class Client:
154
145
  *,
155
146
  timeout: typing.Optional[float] = None,
156
147
  metadata: typing.Union[
157
- typing.Mapping[str, typing.Union[str, bytes]],
158
- typing.Collection[typing.Tuple[str, typing.Union[str, bytes]]],
148
+ collections.abc.Mapping[str, typing.Union[str, bytes]],
149
+ collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
159
150
  None,
160
151
  ] = None,
161
152
  ) -> typing.Any: ...
@@ -165,11 +156,11 @@ class Client:
165
156
  request: typing.Any,
166
157
  *,
167
158
  metadata: typing.Union[
168
- typing.Mapping[str, typing.Union[str, bytes]],
169
- typing.Collection[typing.Tuple[str, typing.Union[str, bytes]]],
159
+ collections.abc.Mapping[str, typing.Union[str, bytes]],
160
+ collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
170
161
  None,
171
162
  ],
172
- ) -> typing.AsyncGenerator[typing.Any, None]: ...
163
+ ) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
173
164
 
174
165
  class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
175
166
  wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
@@ -184,8 +175,8 @@ class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
184
175
  *,
185
176
  timeout: typing.Optional[float] = None,
186
177
  metadata: typing.Union[
187
- typing.Mapping[str, typing.Union[str, bytes]],
188
- typing.Collection[typing.Tuple[str, typing.Union[str, bytes]]],
178
+ collections.abc.Mapping[str, typing.Union[str, bytes]],
179
+ collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
189
180
  None,
190
181
  ] = None,
191
182
  ) -> ResponseType: ...
@@ -1,6 +1,6 @@
1
1
  # Copyright Modal Labs 2022
2
2
  from dataclasses import dataclass
3
- from typing import List, Optional, Tuple
3
+ from typing import Optional
4
4
  from urllib.parse import urlparse
5
5
 
6
6
  from modal_proto import api_pb2
@@ -116,9 +116,9 @@ class _CloudBucketMount:
116
116
  requester_pays: bool = False
117
117
 
118
118
 
119
- def cloud_bucket_mounts_to_proto(mounts: List[Tuple[str, _CloudBucketMount]]) -> List[api_pb2.CloudBucketMount]:
119
+ def cloud_bucket_mounts_to_proto(mounts: list[tuple[str, _CloudBucketMount]]) -> list[api_pb2.CloudBucketMount]:
120
120
  """Helper function to convert `CloudBucketMount` to a list of protobufs that can be passed to the server."""
121
- cloud_bucket_mounts: List[api_pb2.CloudBucketMount] = []
121
+ cloud_bucket_mounts: list[api_pb2.CloudBucketMount] = []
122
122
 
123
123
  for path, mount in mounts:
124
124
  # crude mapping from mount arguments to type.
@@ -23,8 +23,8 @@ class _CloudBucketMount:
23
23
  def __eq__(self, other): ...
24
24
 
25
25
  def cloud_bucket_mounts_to_proto(
26
- mounts: typing.List[typing.Tuple[str, _CloudBucketMount]],
27
- ) -> typing.List[modal_proto.api_pb2.CloudBucketMount]: ...
26
+ mounts: list[tuple[str, _CloudBucketMount]],
27
+ ) -> list[modal_proto.api_pb2.CloudBucketMount]: ...
28
28
 
29
29
  class CloudBucketMount:
30
30
  bucket_name: str
modal/cls.py CHANGED
@@ -2,7 +2,8 @@
2
2
  import inspect
3
3
  import os
4
4
  import typing
5
- from typing import Any, Callable, Collection, Dict, List, Optional, Tuple, Type, TypeVar, Union
5
+ from collections.abc import Collection
6
+ from typing import Any, Callable, Optional, TypeVar, Union
6
7
 
7
8
  from google.protobuf.message import Message
8
9
  from grpclib import GRPCError, Status
@@ -76,10 +77,10 @@ class _Obj:
76
77
 
77
78
  All this class does is to return `Function` objects."""
78
79
 
79
- _functions: Dict[str, _Function]
80
+ _functions: dict[str, _Function]
80
81
  _entered: bool
81
82
  _user_cls_instance: Optional[Any] = None
82
- _construction_args: Tuple[tuple, Dict[str, Any]]
83
+ _construction_args: tuple[tuple, dict[str, Any]]
83
84
 
84
85
  _instance_service_function: Optional[_Function]
85
86
 
@@ -91,7 +92,7 @@ class _Obj:
91
92
  self,
92
93
  user_cls: type,
93
94
  class_service_function: Optional[_Function], # only None for <v0.63 classes
94
- classbound_methods: Dict[str, _Function],
95
+ classbound_methods: dict[str, _Function],
95
96
  from_other_workspace: bool,
96
97
  options: Optional[api_pb2.FunctionOptions],
97
98
  args,
@@ -244,9 +245,9 @@ class _Cls(_Object, type_prefix="cs"):
244
245
  _class_service_function: Optional[
245
246
  _Function
246
247
  ] # The _Function serving *all* methods of the class, used for version >=v0.63
247
- _method_functions: Optional[Dict[str, _Function]] = None # Placeholder _Functions for each method
248
+ _method_functions: Optional[dict[str, _Function]] = None # Placeholder _Functions for each method
248
249
  _options: Optional[api_pb2.FunctionOptions]
249
- _callables: Dict[str, Callable[..., Any]]
250
+ _callables: dict[str, Callable[..., Any]]
250
251
  _from_other_workspace: Optional[bool] # Functions require FunctionBindParams before invocation.
251
252
  _app: Optional["modal.app._App"] = None # not set for lookups
252
253
 
@@ -265,7 +266,7 @@ class _Cls(_Object, type_prefix="cs"):
265
266
  self._callables = other._callables
266
267
  self._from_other_workspace = other._from_other_workspace
267
268
 
268
- def _get_partial_functions(self) -> Dict[str, _PartialFunction]:
269
+ def _get_partial_functions(self) -> dict[str, _PartialFunction]:
269
270
  if not self._user_cls:
270
271
  raise AttributeError("You can only get the partial functions of a local Cls instance")
271
272
  return _find_partial_methods_for_user_cls(self._user_cls, _PartialFunctionFlags.all())
@@ -344,8 +345,8 @@ class _Cls(_Object, type_prefix="cs"):
344
345
  # validate signature
345
346
  _Cls.validate_construction_mechanism(user_cls)
346
347
 
347
- method_functions: Dict[str, _Function] = {}
348
- partial_functions: Dict[str, _PartialFunction] = _find_partial_methods_for_user_cls(
348
+ method_functions: dict[str, _Function] = {}
349
+ partial_functions: dict[str, _PartialFunction] = _find_partial_methods_for_user_cls(
349
350
  user_cls, _PartialFunctionFlags.FUNCTION
350
351
  )
351
352
 
@@ -361,11 +362,11 @@ class _Cls(_Object, type_prefix="cs"):
361
362
  partial_function.wrapped = True
362
363
 
363
364
  # Get all callables
364
- callables: Dict[str, Callable] = {
365
+ callables: dict[str, Callable] = {
365
366
  k: pf.raw_f for k, pf in _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.all()).items()
366
367
  }
367
368
 
368
- def _deps() -> List[_Function]:
369
+ def _deps() -> list[_Function]:
369
370
  return [class_service_function]
370
371
 
371
372
  async def _load(self: "_Cls", resolver: Resolver, existing_object_id: Optional[str]):
@@ -392,7 +393,7 @@ class _Cls(_Object, type_prefix="cs"):
392
393
 
393
394
  @classmethod
394
395
  def from_name(
395
- cls: Type["_Cls"],
396
+ cls: type["_Cls"],
396
397
  app_name: str,
397
398
  tag: str,
398
399
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
@@ -454,11 +455,11 @@ class _Cls(_Object, type_prefix="cs"):
454
455
 
455
456
  def with_options(
456
457
  self: "_Cls",
457
- cpu: Optional[Union[float, Tuple[float, float]]] = None,
458
- memory: Optional[Union[int, Tuple[int, int]]] = None,
458
+ cpu: Optional[Union[float, tuple[float, float]]] = None,
459
+ memory: Optional[Union[int, tuple[int, int]]] = None,
459
460
  gpu: GPU_T = None,
460
461
  secrets: Collection[_Secret] = (),
461
- volumes: Dict[Union[str, os.PathLike], _Volume] = {},
462
+ volumes: dict[Union[str, os.PathLike], _Volume] = {},
462
463
  retries: Optional[Union[int, Retries]] = None,
463
464
  timeout: Optional[int] = None,
464
465
  concurrency_limit: Optional[int] = None,
modal/cls.pyi CHANGED
@@ -1,3 +1,4 @@
1
+ import collections.abc
1
2
  import google.protobuf.message
2
3
  import inspect
3
4
  import modal.app
@@ -20,10 +21,10 @@ def _use_annotation_parameters(user_cls) -> bool: ...
20
21
  def _get_class_constructor_signature(user_cls: type) -> inspect.Signature: ...
21
22
 
22
23
  class _Obj:
23
- _functions: typing.Dict[str, modal.functions._Function]
24
+ _functions: dict[str, modal.functions._Function]
24
25
  _entered: bool
25
26
  _user_cls_instance: typing.Optional[typing.Any]
26
- _construction_args: typing.Tuple[tuple, typing.Dict[str, typing.Any]]
27
+ _construction_args: tuple[tuple, dict[str, typing.Any]]
27
28
  _instance_service_function: typing.Optional[modal.functions._Function]
28
29
 
29
30
  def _uses_common_service_function(self): ...
@@ -31,7 +32,7 @@ class _Obj:
31
32
  self,
32
33
  user_cls: type,
33
34
  class_service_function: typing.Optional[modal.functions._Function],
34
- classbound_methods: typing.Dict[str, modal.functions._Function],
35
+ classbound_methods: dict[str, modal.functions._Function],
35
36
  from_other_workspace: bool,
36
37
  options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
37
38
  args,
@@ -49,17 +50,17 @@ class _Obj:
49
50
  def __getattr__(self, k): ...
50
51
 
51
52
  class Obj:
52
- _functions: typing.Dict[str, modal.functions.Function]
53
+ _functions: dict[str, modal.functions.Function]
53
54
  _entered: bool
54
55
  _user_cls_instance: typing.Optional[typing.Any]
55
- _construction_args: typing.Tuple[tuple, typing.Dict[str, typing.Any]]
56
+ _construction_args: tuple[tuple, dict[str, typing.Any]]
56
57
  _instance_service_function: typing.Optional[modal.functions.Function]
57
58
 
58
59
  def __init__(
59
60
  self,
60
61
  user_cls: type,
61
62
  class_service_function: typing.Optional[modal.functions.Function],
62
- classbound_methods: typing.Dict[str, modal.functions.Function],
63
+ classbound_methods: dict[str, modal.functions.Function],
63
64
  from_other_workspace: bool,
64
65
  options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
65
66
  args,
@@ -86,15 +87,15 @@ class Obj:
86
87
  class _Cls(modal.object._Object):
87
88
  _user_cls: typing.Optional[type]
88
89
  _class_service_function: typing.Optional[modal.functions._Function]
89
- _method_functions: typing.Optional[typing.Dict[str, modal.functions._Function]]
90
+ _method_functions: typing.Optional[dict[str, modal.functions._Function]]
90
91
  _options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
91
- _callables: typing.Dict[str, typing.Callable[..., typing.Any]]
92
+ _callables: dict[str, typing.Callable[..., typing.Any]]
92
93
  _from_other_workspace: typing.Optional[bool]
93
94
  _app: typing.Optional[modal.app._App]
94
95
 
95
96
  def _initialize_from_empty(self): ...
96
97
  def _initialize_from_other(self, other: _Cls): ...
97
- def _get_partial_functions(self) -> typing.Dict[str, modal.partial_function._PartialFunction]: ...
98
+ def _get_partial_functions(self) -> dict[str, modal.partial_function._PartialFunction]: ...
98
99
  def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
99
100
  @staticmethod
100
101
  def validate_construction_mechanism(user_cls): ...
@@ -103,7 +104,7 @@ class _Cls(modal.object._Object):
103
104
  def _uses_common_service_function(self): ...
104
105
  @classmethod
105
106
  def from_name(
106
- cls: typing.Type[_Cls],
107
+ cls: type[_Cls],
107
108
  app_name: str,
108
109
  tag: str,
109
110
  namespace=1,
@@ -112,11 +113,11 @@ class _Cls(modal.object._Object):
112
113
  ) -> _Cls: ...
113
114
  def with_options(
114
115
  self: _Cls,
115
- cpu: typing.Union[float, typing.Tuple[float, float], None] = None,
116
- memory: typing.Union[int, typing.Tuple[int, int], None] = None,
116
+ cpu: typing.Union[float, tuple[float, float], None] = None,
117
+ memory: typing.Union[int, tuple[int, int], None] = None,
117
118
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
118
- secrets: typing.Collection[modal.secret._Secret] = (),
119
- volumes: typing.Dict[typing.Union[str, os.PathLike], modal.volume._Volume] = {},
119
+ secrets: collections.abc.Collection[modal.secret._Secret] = (),
120
+ volumes: dict[typing.Union[str, os.PathLike], modal.volume._Volume] = {},
120
121
  retries: typing.Union[int, modal.retries.Retries, None] = None,
121
122
  timeout: typing.Optional[int] = None,
122
123
  concurrency_limit: typing.Optional[int] = None,
@@ -138,16 +139,16 @@ class _Cls(modal.object._Object):
138
139
  class Cls(modal.object.Object):
139
140
  _user_cls: typing.Optional[type]
140
141
  _class_service_function: typing.Optional[modal.functions.Function]
141
- _method_functions: typing.Optional[typing.Dict[str, modal.functions.Function]]
142
+ _method_functions: typing.Optional[dict[str, modal.functions.Function]]
142
143
  _options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
143
- _callables: typing.Dict[str, typing.Callable[..., typing.Any]]
144
+ _callables: dict[str, typing.Callable[..., typing.Any]]
144
145
  _from_other_workspace: typing.Optional[bool]
145
146
  _app: typing.Optional[modal.app.App]
146
147
 
147
148
  def __init__(self, *args, **kwargs): ...
148
149
  def _initialize_from_empty(self): ...
149
150
  def _initialize_from_other(self, other: Cls): ...
150
- def _get_partial_functions(self) -> typing.Dict[str, modal.partial_function.PartialFunction]: ...
151
+ def _get_partial_functions(self) -> dict[str, modal.partial_function.PartialFunction]: ...
151
152
  def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
152
153
  @staticmethod
153
154
  def validate_construction_mechanism(user_cls): ...
@@ -156,7 +157,7 @@ class Cls(modal.object.Object):
156
157
  def _uses_common_service_function(self): ...
157
158
  @classmethod
158
159
  def from_name(
159
- cls: typing.Type[Cls],
160
+ cls: type[Cls],
160
161
  app_name: str,
161
162
  tag: str,
162
163
  namespace=1,
@@ -165,11 +166,11 @@ class Cls(modal.object.Object):
165
166
  ) -> Cls: ...
166
167
  def with_options(
167
168
  self: Cls,
168
- cpu: typing.Union[float, typing.Tuple[float, float], None] = None,
169
- memory: typing.Union[int, typing.Tuple[int, int], None] = None,
169
+ cpu: typing.Union[float, tuple[float, float], None] = None,
170
+ memory: typing.Union[int, tuple[int, int], None] = None,
170
171
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
171
- secrets: typing.Collection[modal.secret.Secret] = (),
172
- volumes: typing.Dict[typing.Union[str, os.PathLike], modal.volume.Volume] = {},
172
+ secrets: collections.abc.Collection[modal.secret.Secret] = (),
173
+ volumes: dict[typing.Union[str, os.PathLike], modal.volume.Volume] = {},
173
174
  retries: typing.Union[int, modal.retries.Retries, None] = None,
174
175
  timeout: typing.Optional[int] = None,
175
176
  concurrency_limit: typing.Optional[int] = None,
modal/config.py CHANGED
@@ -80,7 +80,7 @@ import os
80
80
  import typing
81
81
  import warnings
82
82
  from textwrap import dedent
83
- from typing import Any, Dict, Optional
83
+ from typing import Any, Optional
84
84
 
85
85
  from google.protobuf.empty_pb2 import Empty
86
86
 
@@ -282,7 +282,7 @@ configure_logger(logger, config["loglevel"], config["log_format"])
282
282
 
283
283
 
284
284
  def _store_user_config(
285
- new_settings: Dict[str, Any], profile: Optional[str] = None, active_profile: Optional[str] = None
285
+ new_settings: dict[str, Any], profile: Optional[str] = None, active_profile: Optional[str] = None
286
286
  ):
287
287
  """Internal method, used by the CLI to set tokens."""
288
288
  if profile is None: