modal 0.67.6__py3-none-any.whl → 0.67.11__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 (103) 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 +12 -14
  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 +30 -30
  55. modal/cls.pyi +35 -34
  56. modal/config.py +3 -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 +105 -35
  63. modal/functions.pyi +71 -48
  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/retries.py +38 -0
  81. modal/runner.py +8 -7
  82. modal/runner.pyi +8 -14
  83. modal/running_app.py +3 -3
  84. modal/sandbox.py +14 -13
  85. modal/sandbox.pyi +67 -72
  86. modal/scheduler_placement.py +2 -1
  87. modal/secret.py +7 -7
  88. modal/secret.pyi +12 -12
  89. modal/serving.py +4 -3
  90. modal/serving.pyi +5 -4
  91. modal/token_flow.py +3 -2
  92. modal/token_flow.pyi +3 -3
  93. modal/volume.py +7 -12
  94. modal/volume.pyi +17 -16
  95. {modal-0.67.6.dist-info → modal-0.67.11.dist-info}/METADATA +1 -1
  96. modal-0.67.11.dist-info/RECORD +168 -0
  97. modal_docs/mdmd/signatures.py +1 -2
  98. modal_version/_version_generated.py +1 -1
  99. modal-0.67.6.dist-info/RECORD +0 -168
  100. {modal-0.67.6.dist-info → modal-0.67.11.dist-info}/LICENSE +0 -0
  101. {modal-0.67.6.dist-info → modal-0.67.11.dist-info}/WHEEL +0 -0
  102. {modal-0.67.6.dist-info → modal-0.67.11.dist-info}/entry_points.txt +0 -0
  103. {modal-0.67.6.dist-info → modal-0.67.11.dist-info}/top_level.txt +0 -0
@@ -35,10 +35,8 @@ from concurrent.futures import ThreadPoolExecutor
35
35
  from types import TracebackType
36
36
  from typing import (
37
37
  Any,
38
- Awaitable,
39
38
  Callable,
40
39
  Dict,
41
- Iterable,
42
40
  List,
43
41
  Literal,
44
42
  Optional,
@@ -48,6 +46,7 @@ from typing import (
48
46
  TypedDict,
49
47
  Union,
50
48
  )
49
+ from collections.abc import Awaitable, Iterable
51
50
 
52
51
 
53
52
  ## BEGIN a2wsgi/asgi_typing.py
@@ -73,11 +72,11 @@ class HTTPScope(TypedDict):
73
72
  raw_path: NotRequired[bytes]
74
73
  query_string: bytes
75
74
  root_path: str
76
- headers: Iterable[Tuple[bytes, bytes]]
77
- client: NotRequired[Tuple[str, int]]
78
- server: NotRequired[Tuple[str, Optional[int]]]
79
- state: NotRequired[Dict[str, Any]]
80
- extensions: NotRequired[Dict[str, Dict[object, object]]]
75
+ headers: Iterable[tuple[bytes, bytes]]
76
+ client: NotRequired[tuple[str, int]]
77
+ server: NotRequired[tuple[str, Optional[int]]]
78
+ state: NotRequired[dict[str, Any]]
79
+ extensions: NotRequired[dict[str, dict[object, object]]]
81
80
 
82
81
 
83
82
  class WebSocketScope(TypedDict):
@@ -89,18 +88,18 @@ class WebSocketScope(TypedDict):
89
88
  raw_path: bytes
90
89
  query_string: bytes
91
90
  root_path: str
92
- headers: Iterable[Tuple[bytes, bytes]]
93
- client: NotRequired[Tuple[str, int]]
94
- server: NotRequired[Tuple[str, Optional[int]]]
91
+ headers: Iterable[tuple[bytes, bytes]]
92
+ client: NotRequired[tuple[str, int]]
93
+ server: NotRequired[tuple[str, Optional[int]]]
95
94
  subprotocols: Iterable[str]
96
- state: NotRequired[Dict[str, Any]]
97
- extensions: NotRequired[Dict[str, Dict[object, object]]]
95
+ state: NotRequired[dict[str, Any]]
96
+ extensions: NotRequired[dict[str, dict[object, object]]]
98
97
 
99
98
 
100
99
  class LifespanScope(TypedDict):
101
100
  type: Literal["lifespan"]
102
101
  asgi: ASGIVersions
103
- state: NotRequired[Dict[str, Any]]
102
+ state: NotRequired[dict[str, Any]]
104
103
 
105
104
 
106
105
  WWWScope = Union[HTTPScope, WebSocketScope]
@@ -116,7 +115,7 @@ class HTTPRequestEvent(TypedDict):
116
115
  class HTTPResponseStartEvent(TypedDict):
117
116
  type: Literal["http.response.start"]
118
117
  status: int
119
- headers: NotRequired[Iterable[Tuple[bytes, bytes]]]
118
+ headers: NotRequired[Iterable[tuple[bytes, bytes]]]
120
119
  trailers: NotRequired[bool]
121
120
 
122
121
 
@@ -137,7 +136,7 @@ class WebSocketConnectEvent(TypedDict):
137
136
  class WebSocketAcceptEvent(TypedDict):
138
137
  type: Literal["websocket.accept"]
139
138
  subprotocol: NotRequired[str]
140
- headers: NotRequired[Iterable[Tuple[bytes, bytes]]]
139
+ headers: NotRequired[Iterable[tuple[bytes, bytes]]]
141
140
 
142
141
 
143
142
  class WebSocketReceiveEvent(TypedDict):
@@ -223,56 +222,47 @@ ASGIApp = Callable[[Scope, Receive, Send], Awaitable[None]]
223
222
 
224
223
  ## BEGIN a2wsgi/wsgi_typing.py
225
224
 
226
- CGIRequiredDefined = TypedDict(
227
- "CGIRequiredDefined",
228
- {
229
- # The HTTP request method, such as GET or POST. This cannot ever be an
230
- # empty string, and so is always required.
231
- "REQUEST_METHOD": str,
232
- # When HTTP_HOST is not set, these variables can be combined to determine
233
- # a default.
234
- # SERVER_NAME and SERVER_PORT are required strings and must never be empty.
235
- "SERVER_NAME": str,
236
- "SERVER_PORT": str,
237
- # The version of the protocol the client used to send the request.
238
- # Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and
239
- # may be used by the application to determine how to treat any HTTP
240
- # request headers. (This variable should probably be called REQUEST_PROTOCOL,
241
- # since it denotes the protocol used in the request, and is not necessarily
242
- # the protocol that will be used in the server's response. However, for
243
- # compatibility with CGI we have to keep the existing name.)
244
- "SERVER_PROTOCOL": str,
245
- },
246
- )
247
-
248
- CGIOptionalDefined = TypedDict(
249
- "CGIOptionalDefined",
250
- {
251
- "REQUEST_URI": str,
252
- "REMOTE_ADDR": str,
253
- "REMOTE_PORT": str,
254
- # The initial portion of the request URL’s “path” that corresponds to the
255
- # application object, so that the application knows its virtual “location”.
256
- # This may be an empty string, if the application corresponds to the “root”
257
- # of the server.
258
- "SCRIPT_NAME": str,
259
- # The remainder of the request URL’s “path”, designating the virtual
260
- # “location” of the request’s target within the application. This may be an
261
- # empty string, if the request URL targets the application root and does
262
- # not have a trailing slash.
263
- "PATH_INFO": str,
264
- # The portion of the request URL that follows the “?”, if any. May be empty
265
- # or absent.
266
- "QUERY_STRING": str,
267
- # The contents of any Content-Type fields in the HTTP request. May be empty
268
- # or absent.
269
- "CONTENT_TYPE": str,
270
- # The contents of any Content-Length fields in the HTTP request. May be empty
271
- # or absent.
272
- "CONTENT_LENGTH": str,
273
- },
274
- total=False,
275
- )
225
+ class CGIRequiredDefined(TypedDict):
226
+ # The HTTP request method, such as GET or POST. This cannot ever be an
227
+ # empty string, and so is always required.
228
+ REQUEST_METHOD: str
229
+ # When HTTP_HOST is not set, these variables can be combined to determine
230
+ # a default.
231
+ # SERVER_NAME and SERVER_PORT are required strings and must never be empty.
232
+ SERVER_NAME: str
233
+ SERVER_PORT: str
234
+ # The version of the protocol the client used to send the request.
235
+ # Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and
236
+ # may be used by the application to determine how to treat any HTTP
237
+ # request headers. (This variable should probably be called REQUEST_PROTOCOL,
238
+ # since it denotes the protocol used in the request, and is not necessarily
239
+ # the protocol that will be used in the server's response. However, for
240
+ # compatibility with CGI we have to keep the existing name.)
241
+ SERVER_PROTOCOL: str
242
+
243
+ class CGIOptionalDefined(TypedDict, total=False):
244
+ REQUEST_URI: str
245
+ REMOTE_ADDR: str
246
+ REMOTE_PORT: str
247
+ # The initial portion of the request URL’s “path” that corresponds to the
248
+ # application object, so that the application knows its virtual “location”.
249
+ # This may be an empty string, if the application corresponds to the “root”
250
+ # of the server.
251
+ SCRIPT_NAME: str
252
+ # The remainder of the request URL’s “path”, designating the virtual
253
+ # “location” of the request’s target within the application. This may be an
254
+ # empty string, if the request URL targets the application root and does
255
+ # not have a trailing slash.
256
+ PATH_INFO: str
257
+ # The portion of the request URL that follows the “?”, if any. May be empty
258
+ # or absent.
259
+ QUERY_STRING: str
260
+ # The contents of any Content-Type fields in the HTTP request. May be empty
261
+ # or absent.
262
+ CONTENT_TYPE: str
263
+ # The contents of any Content-Length fields in the HTTP request. May be empty
264
+ # or absent.
265
+ CONTENT_LENGTH: str
276
266
 
277
267
 
278
268
  class InputStream(Protocol):
@@ -308,7 +298,7 @@ class InputStream(Protocol):
308
298
  """
309
299
  raise NotImplementedError
310
300
 
311
- def readlines(self, hint: int = -1, /) -> List[bytes]:
301
+ def readlines(self, hint: int = -1, /) -> list[bytes]:
312
302
  """
313
303
  Note that the hint argument to readlines() is optional for both caller and
314
304
  implementer. The application is free not to supply it, and the server or gateway
@@ -349,14 +339,14 @@ class ErrorStream(Protocol):
349
339
  def write(self, s: str, /) -> Any:
350
340
  raise NotImplementedError
351
341
 
352
- def writelines(self, seq: List[str], /) -> Any:
342
+ def writelines(self, seq: list[str], /) -> Any:
353
343
  raise NotImplementedError
354
344
 
355
345
 
356
346
  WSGIDefined = TypedDict(
357
347
  "WSGIDefined",
358
348
  {
359
- "wsgi.version": Tuple[int, int], # e.g. (1, 0)
349
+ "wsgi.version": tuple[int, int], # e.g. (1, 0)
360
350
  "wsgi.url_scheme": str, # e.g. "http" or "https"
361
351
  "wsgi.input": InputStream,
362
352
  "wsgi.errors": ErrorStream,
@@ -381,7 +371,7 @@ class Environ(CGIRequiredDefined, CGIOptionalDefined, WSGIDefined):
381
371
  """
382
372
 
383
373
 
384
- ExceptionInfo = Tuple[Type[BaseException], BaseException, Optional[TracebackType]]
374
+ ExceptionInfo = tuple[type[BaseException], BaseException, Optional[TracebackType]]
385
375
 
386
376
  # https://peps.python.org/pep-3333/#the-write-callable
387
377
  WriteCallable = Callable[[bytes], None]
@@ -391,7 +381,7 @@ class StartResponse(Protocol):
391
381
  def __call__(
392
382
  self,
393
383
  status: str,
394
- response_headers: List[Tuple[str, str]],
384
+ response_headers: list[tuple[str, str]],
395
385
  exc_info: Optional[ExceptionInfo] = None,
396
386
  /,
397
387
  ) -> WriteCallable:
@@ -460,7 +450,7 @@ class Body:
460
450
  self.buffer.clear()
461
451
  return result
462
452
 
463
- def readlines(self, hint: int = -1) -> typing.List[bytes]:
453
+ def readlines(self, hint: int = -1) -> list[bytes]:
464
454
  if not self.has_more:
465
455
  return []
466
456
  if hint == -1:
@@ -626,7 +616,7 @@ class WSGIResponder:
626
616
  def start_response(
627
617
  self,
628
618
  status: str,
629
- response_headers: typing.List[typing.Tuple[str, str]],
619
+ response_headers: list[tuple[str, str]],
630
620
  exc_info: typing.Optional[ExceptionInfo] = None,
631
621
  ) -> WriteCallable:
632
622
  self.exc_info = exc_info
@@ -256,7 +256,7 @@ def _should_pickle_by_reference(obj, name=None):
256
256
  return False
257
257
  return obj.__name__ in sys.modules
258
258
  else:
259
- raise TypeError("cannot check importability of {} instances".format(type(obj).__name__))
259
+ raise TypeError(f"cannot check importability of {type(obj).__name__} instances")
260
260
 
261
261
 
262
262
  def _lookup_module_and_qualname(obj, name=None):
modal/_watcher.py CHANGED
@@ -1,7 +1,8 @@
1
1
  # Copyright Modal Labs 2022
2
2
  from collections import defaultdict
3
+ from collections.abc import AsyncGenerator
3
4
  from pathlib import Path
4
- from typing import AsyncGenerator, Dict, List, Optional, Set, Tuple
5
+ from typing import Optional
5
6
 
6
7
  from rich.tree import Tree
7
8
  from watchfiles import Change, DefaultFilter, awatch
@@ -21,7 +22,7 @@ class AppFilesFilter(DefaultFilter):
21
22
  # Watching specific files is discouraged on Linux, so to watch a file we watch its
22
23
  # containing directory and then filter that directory's changes for relevant files.
23
24
  # https://github.com/notify-rs/notify/issues/394
24
- dir_filters: Dict[Path, Optional[Set[Path]]],
25
+ dir_filters: dict[Path, Optional[set[Path]]],
25
26
  ) -> None:
26
27
  self.dir_filters = dir_filters
27
28
  super().__init__()
@@ -54,7 +55,7 @@ class AppFilesFilter(DefaultFilter):
54
55
  return super().__call__(change, path)
55
56
 
56
57
 
57
- async def _watch_paths(paths: Set[Path], watch_filter: AppFilesFilter) -> AsyncGenerator[Set[str], None]:
58
+ async def _watch_paths(paths: set[Path], watch_filter: AppFilesFilter) -> AsyncGenerator[set[str], None]:
58
59
  try:
59
60
  async for changes in awatch(*paths, step=500, watch_filter=watch_filter):
60
61
  changed_paths = {stringpath for _, stringpath in changes}
@@ -64,7 +65,7 @@ async def _watch_paths(paths: Set[Path], watch_filter: AppFilesFilter) -> AsyncG
64
65
  pass
65
66
 
66
67
 
67
- def _print_watched_paths(paths: Set[Path]):
68
+ def _print_watched_paths(paths: set[Path]):
68
69
  msg = "️️⚡️ Serving... hit Ctrl-C to stop!"
69
70
 
70
71
  output_tree = Tree(msg, guide_style="gray50")
@@ -76,9 +77,9 @@ def _print_watched_paths(paths: Set[Path]):
76
77
  output_mgr.print(output_tree)
77
78
 
78
79
 
79
- def _watch_args_from_mounts(mounts: List[_Mount]) -> Tuple[Set[Path], AppFilesFilter]:
80
+ def _watch_args_from_mounts(mounts: list[_Mount]) -> tuple[set[Path], AppFilesFilter]:
80
81
  paths = set()
81
- dir_filters: Dict[Path, Optional[Set[Path]]] = defaultdict(set)
82
+ dir_filters: dict[Path, Optional[set[Path]]] = defaultdict(set)
82
83
  for mount in mounts:
83
84
  # TODO(elias): Make this part of the mount class instead, since it uses so much internals
84
85
  for entry in mount._entries:
@@ -94,7 +95,7 @@ def _watch_args_from_mounts(mounts: List[_Mount]) -> Tuple[Set[Path], AppFilesFi
94
95
  return paths, watch_filter
95
96
 
96
97
 
97
- async def watch(mounts: List[_Mount]) -> AsyncGenerator[Set[str], None]:
98
+ async def watch(mounts: list[_Mount]) -> AsyncGenerator[set[str], None]:
98
99
  paths, watch_filter = _watch_args_from_mounts(mounts)
99
100
 
100
101
  _print_watched_paths(paths)
modal/app.py CHANGED
@@ -2,19 +2,14 @@
2
2
  import inspect
3
3
  import typing
4
4
  import warnings
5
+ from collections.abc import AsyncGenerator, Coroutine, Sequence
5
6
  from pathlib import PurePosixPath
6
7
  from textwrap import dedent
7
8
  from typing import (
8
9
  Any,
9
- AsyncGenerator,
10
10
  Callable,
11
11
  ClassVar,
12
- Coroutine,
13
- Dict,
14
- List,
15
12
  Optional,
16
- Sequence,
17
- Tuple,
18
13
  Union,
19
14
  overload,
20
15
  )
@@ -87,14 +82,14 @@ class _LocalEntrypoint:
87
82
  LocalEntrypoint = synchronize_api(_LocalEntrypoint)
88
83
 
89
84
 
90
- def check_sequence(items: typing.Sequence[typing.Any], item_type: typing.Type[typing.Any], error_msg: str) -> None:
85
+ def check_sequence(items: typing.Sequence[typing.Any], item_type: type[typing.Any], error_msg: str) -> None:
91
86
  if not isinstance(items, (list, tuple)):
92
87
  raise InvalidError(error_msg)
93
88
  if not all(isinstance(v, item_type) for v in items):
94
89
  raise InvalidError(error_msg)
95
90
 
96
91
 
97
- CLS_T = typing.TypeVar("CLS_T", bound=typing.Type[Any])
92
+ CLS_T = typing.TypeVar("CLS_T", bound=type[Any])
98
93
 
99
94
 
100
95
  P = typing_extensions.ParamSpec("P")
@@ -172,20 +167,20 @@ class _App:
172
167
  In this example, the secret and schedule are registered with the app.
173
168
  """
174
169
 
175
- _all_apps: ClassVar[Dict[Optional[str], List["_App"]]] = {}
170
+ _all_apps: ClassVar[dict[Optional[str], list["_App"]]] = {}
176
171
  _container_app: ClassVar[Optional[RunningApp]] = None
177
172
 
178
173
  _name: Optional[str]
179
174
  _description: Optional[str]
180
- _functions: Dict[str, _Function]
181
- _classes: Dict[str, _Cls]
175
+ _functions: dict[str, _Function]
176
+ _classes: dict[str, _Cls]
182
177
 
183
178
  _image: Optional[_Image]
184
179
  _mounts: Sequence[_Mount]
185
180
  _secrets: Sequence[_Secret]
186
- _volumes: Dict[Union[str, PurePosixPath], _Volume]
187
- _web_endpoints: List[str] # Used by the CLI
188
- _local_entrypoints: Dict[str, _LocalEntrypoint]
181
+ _volumes: dict[Union[str, PurePosixPath], _Volume]
182
+ _web_endpoints: list[str] # Used by the CLI
183
+ _local_entrypoints: dict[str, _LocalEntrypoint]
189
184
 
190
185
  # Running apps only (container apps or running local)
191
186
  _app_id: Optional[str] # Kept after app finishes
@@ -199,7 +194,7 @@ class _App:
199
194
  image: Optional[_Image] = None, # default image for all functions (default is `modal.Image.debian_slim()`)
200
195
  mounts: Sequence[_Mount] = [], # default mounts for all functions
201
196
  secrets: Sequence[_Secret] = [], # default secrets for all functions
202
- volumes: Dict[Union[str, PurePosixPath], _Volume] = {}, # default volumes for all functions
197
+ volumes: dict[Union[str, PurePosixPath], _Volume] = {}, # default volumes for all functions
203
198
  ) -> None:
204
199
  """Construct a new app, optionally with default image, mounts, secrets, or volumes.
205
200
 
@@ -509,22 +504,22 @@ class _App:
509
504
  hydrate_objects(self._classes)
510
505
 
511
506
  @property
512
- def registered_functions(self) -> Dict[str, _Function]:
507
+ def registered_functions(self) -> dict[str, _Function]:
513
508
  """All modal.Function objects registered on the app."""
514
509
  return self._functions
515
510
 
516
511
  @property
517
- def registered_classes(self) -> Dict[str, _Function]:
512
+ def registered_classes(self) -> dict[str, _Function]:
518
513
  """All modal.Cls objects registered on the app."""
519
514
  return self._classes
520
515
 
521
516
  @property
522
- def registered_entrypoints(self) -> Dict[str, _LocalEntrypoint]:
517
+ def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
523
518
  """All local CLI entrypoints registered on the app."""
524
519
  return self._local_entrypoints
525
520
 
526
521
  @property
527
- def indexed_objects(self) -> Dict[str, _Object]:
522
+ def indexed_objects(self) -> dict[str, _Object]:
528
523
  deprecation_warning(
529
524
  (2024, 11, 25),
530
525
  "`app.indexed_objects` is deprecated! Use `app.registered_functions` or `app.registered_classes` instead.",
@@ -532,7 +527,7 @@ class _App:
532
527
  return dict(**self._functions, **self._classes)
533
528
 
534
529
  @property
535
- def registered_web_endpoints(self) -> List[str]:
530
+ def registered_web_endpoints(self) -> list[str]:
536
531
  """Names of web endpoint (ie. webhook) functions registered on the app."""
537
532
  return self._web_endpoints
538
533
 
@@ -611,24 +606,24 @@ class _App:
611
606
  schedule: Optional[Schedule] = None, # An optional Modal Schedule for the function
612
607
  secrets: Sequence[_Secret] = (), # Optional Modal Secret objects with environment variables for the container
613
608
  gpu: Union[
614
- GPU_T, List[GPU_T]
609
+ GPU_T, list[GPU_T]
615
610
  ] = None, # GPU request as string ("any", "T4", ...), object (`modal.GPU.A100()`, ...), or a list of either
616
611
  serialized: bool = False, # Whether to send the function over using cloudpickle.
617
612
  mounts: Sequence[_Mount] = (), # Modal Mounts added to the container
618
- network_file_systems: Dict[
613
+ network_file_systems: dict[
619
614
  Union[str, PurePosixPath], _NetworkFileSystem
620
615
  ] = {}, # Mountpoints for Modal NetworkFileSystems
621
- volumes: Dict[
616
+ volumes: dict[
622
617
  Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]
623
618
  ] = {}, # Mount points for Modal Volumes & CloudBucketMounts
624
619
  allow_cross_region_volumes: bool = False, # Whether using network file systems from other regions is allowed.
625
620
  # Specify, in fractional CPU cores, how many CPU cores to request.
626
621
  # Or, pass (request, limit) to additionally specify a hard limit in fractional CPU cores.
627
622
  # CPU throttling will prevent a container from exceeding its specified limit.
628
- cpu: Optional[Union[float, Tuple[float, float]]] = None,
623
+ cpu: Optional[Union[float, tuple[float, float]]] = None,
629
624
  # Specify, in MiB, a memory request which is the minimum memory required.
630
625
  # Or, pass (request, limit) to additionally specify a hard limit in MiB.
631
- memory: Optional[Union[int, Tuple[int, int]]] = None,
626
+ memory: Optional[Union[int, tuple[int, int]]] = None,
632
627
  ephemeral_disk: Optional[int] = None, # Specify, in MiB, the ephemeral disk size for the Function.
633
628
  proxy: Optional[_Proxy] = None, # Reference to a Modal Proxy to use in front of this function.
634
629
  retries: Optional[Union[int, Retries]] = None, # Number of times to retry each input in case of failure.
@@ -817,24 +812,24 @@ class _App:
817
812
  image: Optional[_Image] = None, # The image to run as the container for the function
818
813
  secrets: Sequence[_Secret] = (), # Optional Modal Secret objects with environment variables for the container
819
814
  gpu: Union[
820
- GPU_T, List[GPU_T]
815
+ GPU_T, list[GPU_T]
821
816
  ] = None, # GPU request as string ("any", "T4", ...), object (`modal.GPU.A100()`, ...), or a list of either
822
817
  serialized: bool = False, # Whether to send the function over using cloudpickle.
823
818
  mounts: Sequence[_Mount] = (),
824
- network_file_systems: Dict[
819
+ network_file_systems: dict[
825
820
  Union[str, PurePosixPath], _NetworkFileSystem
826
821
  ] = {}, # Mountpoints for Modal NetworkFileSystems
827
- volumes: Dict[
822
+ volumes: dict[
828
823
  Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]
829
824
  ] = {}, # Mount points for Modal Volumes & CloudBucketMounts
830
825
  allow_cross_region_volumes: bool = False, # Whether using network file systems from other regions is allowed.
831
826
  # Specify, in fractional CPU cores, how many CPU cores to request.
832
827
  # Or, pass (request, limit) to additionally specify a hard limit in fractional CPU cores.
833
828
  # CPU throttling will prevent a container from exceeding its specified limit.
834
- cpu: Optional[Union[float, Tuple[float, float]]] = None,
829
+ cpu: Optional[Union[float, tuple[float, float]]] = None,
835
830
  # Specify, in MiB, a memory request which is the minimum memory required.
836
831
  # Or, pass (request, limit) to additionally specify a hard limit in MiB.
837
- memory: Optional[Union[int, Tuple[int, int]]] = None,
832
+ memory: Optional[Union[int, tuple[int, int]]] = None,
838
833
  ephemeral_disk: Optional[int] = None, # Specify, in MiB, the ephemeral disk size for the Function.
839
834
  proxy: Optional[_Proxy] = None, # Reference to a Modal Proxy to use in front of this function.
840
835
  retries: Optional[Union[int, Retries]] = None, # Number of times to retry each input in case of failure.
@@ -948,7 +943,7 @@ class _App:
948
943
  image: Optional[_Image] = None, # The image to run as the container for the sandbox.
949
944
  mounts: Sequence[_Mount] = (), # Mounts to attach to the sandbox.
950
945
  secrets: Sequence[_Secret] = (), # Environment variables to inject into the sandbox.
951
- network_file_systems: Dict[Union[str, PurePosixPath], _NetworkFileSystem] = {},
946
+ network_file_systems: dict[Union[str, PurePosixPath], _NetworkFileSystem] = {},
952
947
  timeout: Optional[int] = None, # Maximum execution time of the sandbox in seconds.
953
948
  workdir: Optional[str] = None, # Working directory of the sandbox.
954
949
  gpu: GPU_T = None,
@@ -957,12 +952,12 @@ class _App:
957
952
  # Specify, in fractional CPU cores, how many CPU cores to request.
958
953
  # Or, pass (request, limit) to additionally specify a hard limit in fractional CPU cores.
959
954
  # CPU throttling will prevent a container from exceeding its specified limit.
960
- cpu: Optional[Union[float, Tuple[float, float]]] = None,
955
+ cpu: Optional[Union[float, tuple[float, float]]] = None,
961
956
  # Specify, in MiB, a memory request which is the minimum memory required.
962
957
  # Or, pass (request, limit) to additionally specify a hard limit in MiB.
963
- memory: Optional[Union[int, Tuple[int, int]]] = None,
958
+ memory: Optional[Union[int, tuple[int, int]]] = None,
964
959
  block_network: bool = False, # Whether to block network access
965
- volumes: Dict[
960
+ volumes: dict[
966
961
  Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]
967
962
  ] = {}, # Mount points for Modal Volumes and CloudBucketMounts
968
963
  pty_info: Optional[api_pb2.PTYInfo] = None,