modal 0.67.1__py3-none-any.whl → 0.67.22__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 (113) 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 +46 -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 +5 -4
  16. modal/_tunnel.pyi +2 -2
  17. modal/_utils/async_utils.py +53 -17
  18. modal/_utils/blob_utils.py +22 -7
  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 +68 -62
  32. modal/app.pyi +104 -99
  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/import_refs.py +1 -1
  41. modal/cli/launch.py +2 -2
  42. modal/cli/network_file_system.py +1 -1
  43. modal/cli/profile.py +1 -1
  44. modal/cli/programs/run_jupyter.py +2 -2
  45. modal/cli/programs/vscode.py +3 -3
  46. modal/cli/queues.py +1 -1
  47. modal/cli/run.py +6 -6
  48. modal/cli/secret.py +3 -3
  49. modal/cli/utils.py +2 -1
  50. modal/cli/volume.py +3 -3
  51. modal/client.py +6 -11
  52. modal/client.pyi +18 -27
  53. modal/cloud_bucket_mount.py +3 -3
  54. modal/cloud_bucket_mount.pyi +2 -2
  55. modal/cls.py +32 -32
  56. modal/cls.pyi +35 -34
  57. modal/config.py +3 -2
  58. modal/container_process.py +6 -2
  59. modal/dict.py +6 -3
  60. modal/dict.pyi +10 -9
  61. modal/environments.py +3 -3
  62. modal/environments.pyi +3 -3
  63. modal/exception.py +2 -3
  64. modal/functions.py +111 -40
  65. modal/functions.pyi +71 -48
  66. modal/image.py +46 -49
  67. modal/image.pyi +102 -101
  68. modal/io_streams.py +20 -12
  69. modal/io_streams.pyi +24 -14
  70. modal/mount.py +24 -24
  71. modal/mount.pyi +28 -29
  72. modal/network_file_system.py +14 -11
  73. modal/network_file_system.pyi +12 -11
  74. modal/object.py +9 -8
  75. modal/object.pyi +47 -34
  76. modal/output.py +2 -1
  77. modal/parallel_map.py +4 -4
  78. modal/partial_function.py +10 -14
  79. modal/partial_function.pyi +17 -18
  80. modal/queue.py +11 -8
  81. modal/queue.pyi +23 -22
  82. modal/retries.py +38 -0
  83. modal/runner.py +8 -7
  84. modal/runner.pyi +8 -14
  85. modal/running_app.py +3 -3
  86. modal/sandbox.py +20 -13
  87. modal/sandbox.pyi +73 -72
  88. modal/scheduler_placement.py +2 -1
  89. modal/secret.py +7 -7
  90. modal/secret.pyi +12 -12
  91. modal/serving.py +4 -3
  92. modal/serving.pyi +5 -4
  93. modal/token_flow.py +3 -2
  94. modal/token_flow.pyi +3 -3
  95. modal/volume.py +16 -23
  96. modal/volume.pyi +17 -16
  97. {modal-0.67.1.dist-info → modal-0.67.22.dist-info}/METADATA +2 -2
  98. modal-0.67.22.dist-info/RECORD +168 -0
  99. modal_docs/mdmd/signatures.py +1 -2
  100. modal_global_objects/mounts/python_standalone.py +1 -1
  101. modal_proto/api.proto +13 -0
  102. modal_proto/api_grpc.py +16 -0
  103. modal_proto/api_pb2.py +241 -221
  104. modal_proto/api_pb2.pyi +41 -0
  105. modal_proto/api_pb2_grpc.py +33 -0
  106. modal_proto/api_pb2_grpc.pyi +10 -0
  107. modal_proto/modal_api_grpc.py +1 -0
  108. modal_version/_version_generated.py +1 -1
  109. modal-0.67.1.dist-info/RECORD +0 -168
  110. {modal-0.67.1.dist-info → modal-0.67.22.dist-info}/LICENSE +0 -0
  111. {modal-0.67.1.dist-info → modal-0.67.22.dist-info}/WHEEL +0 -0
  112. {modal-0.67.1.dist-info → modal-0.67.22.dist-info}/entry_points.txt +0 -0
  113. {modal-0.67.1.dist-info → modal-0.67.22.dist-info}/top_level.txt +0 -0
modal/retries.py CHANGED
@@ -1,10 +1,14 @@
1
1
  # Copyright Modal Labs 2022
2
+ import asyncio
2
3
  from datetime import timedelta
3
4
 
4
5
  from modal_proto import api_pb2
5
6
 
6
7
  from .exception import InvalidError
7
8
 
9
+ MIN_INPUT_RETRY_DELAY_MS = 1000
10
+ MAX_INPUT_RETRY_DELAY_MS = 24 * 60 * 60 * 1000
11
+
8
12
 
9
13
  class Retries:
10
14
  """Adds a retry policy to a Modal function.
@@ -103,3 +107,37 @@ class Retries:
103
107
  initial_delay_ms=self.initial_delay // timedelta(milliseconds=1),
104
108
  max_delay_ms=self.max_delay // timedelta(milliseconds=1),
105
109
  )
110
+
111
+
112
+ class RetryManager:
113
+ """
114
+ Helper class to apply the specified retry policy.
115
+ """
116
+
117
+ def __init__(self, retry_policy: api_pb2.FunctionRetryPolicy):
118
+ self.retry_policy = retry_policy
119
+ self.attempt_count = 0
120
+
121
+ async def raise_or_sleep(self, exc: Exception):
122
+ """
123
+ Raises an exception if the maximum retry count has been reached, otherwise sleeps for calculated delay.
124
+ """
125
+ self.attempt_count += 1
126
+ if self.attempt_count > self.retry_policy.retries:
127
+ raise exc
128
+ delay_ms = self._retry_delay_ms(self.attempt_count, self.retry_policy)
129
+ await asyncio.sleep(delay_ms / 1000)
130
+
131
+ @staticmethod
132
+ def _retry_delay_ms(attempt_count: int, retry_policy: api_pb2.FunctionRetryPolicy) -> float:
133
+ """
134
+ Computes the amount of time to sleep before retrying based on the backend_coefficient and initial_delay_ms args.
135
+ """
136
+ if attempt_count < 1:
137
+ raise ValueError(f"Cannot compute retry delay. attempt_count must be at least 1, but was {attempt_count}")
138
+ delay_ms = retry_policy.initial_delay_ms * (retry_policy.backoff_coefficient ** (attempt_count - 1))
139
+ if delay_ms < MIN_INPUT_RETRY_DELAY_MS:
140
+ return MIN_INPUT_RETRY_DELAY_MS
141
+ if delay_ms > MAX_INPUT_RETRY_DELAY_MS:
142
+ return MAX_INPUT_RETRY_DELAY_MS
143
+ return delay_ms
modal/runner.py CHANGED
@@ -4,8 +4,9 @@ import dataclasses
4
4
  import os
5
5
  import time
6
6
  import typing
7
+ from collections.abc import AsyncGenerator
7
8
  from multiprocessing.synchronize import Event
8
- from typing import TYPE_CHECKING, Any, AsyncGenerator, Callable, Dict, List, Optional, Tuple, TypeVar
9
+ from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar
9
10
 
10
11
  from grpclib import GRPCError, Status
11
12
  from synchronicity.async_wrap import asynccontextmanager
@@ -123,7 +124,7 @@ async def _init_local_app_from_name(
123
124
  async def _create_all_objects(
124
125
  client: _Client,
125
126
  running_app: RunningApp,
126
- indexed_objects: Dict[str, _Object],
127
+ indexed_objects: dict[str, _Object],
127
128
  environment_name: str,
128
129
  ) -> None:
129
130
  """Create objects that have been defined but not created on the server."""
@@ -171,15 +172,15 @@ async def _publish_app(
171
172
  client: _Client,
172
173
  running_app: RunningApp,
173
174
  app_state: int, # api_pb2.AppState.value
174
- indexed_objects: Dict[str, _Object],
175
+ indexed_objects: dict[str, _Object],
175
176
  name: str = "", # Only relevant for deployments
176
177
  tag: str = "", # Only relevant for deployments
177
- ) -> Tuple[str, List[str]]:
178
+ ) -> tuple[str, list[str]]:
178
179
  """Wrapper for AppPublish RPC."""
179
180
 
180
181
  # Could simplify this function some changing the internal representation to use
181
182
  # function_ids / class_ids rather than the current tag_to_object_id (i.e. "indexed_objects")
182
- def filter_values(full_dict: Dict[str, V], condition: Callable[[V], bool]) -> Dict[str, V]:
183
+ def filter_values(full_dict: dict[str, V], condition: Callable[[V], bool]) -> dict[str, V]:
183
184
  return {k: v for k, v in full_dict.items() if condition(v)}
184
185
 
185
186
  function_ids = filter_values(running_app.tag_to_object_id, _Function._is_id_type)
@@ -453,7 +454,7 @@ class DeployResult:
453
454
  app_id: str
454
455
  app_page_url: str
455
456
  app_logs_url: str
456
- warnings: List[str]
457
+ warnings: list[str]
457
458
 
458
459
 
459
460
  async def _deploy_app(
@@ -556,7 +557,7 @@ async def _deploy_app(
556
557
  )
557
558
 
558
559
 
559
- async def _interactive_shell(_app: _App, cmds: List[str], environment_name: str = "", **kwargs: Any) -> None:
560
+ async def _interactive_shell(_app: _App, cmds: list[str], environment_name: str = "", **kwargs: Any) -> None:
560
561
  """Run an interactive shell (like `bash`) within the image for this app.
561
562
 
562
563
  This is useful for online debugging and interactive exploration of the
modal/runner.pyi CHANGED
@@ -27,17 +27,17 @@ async def _init_local_app_from_name(
27
27
  async def _create_all_objects(
28
28
  client: modal.client._Client,
29
29
  running_app: modal.running_app.RunningApp,
30
- indexed_objects: typing.Dict[str, modal.object._Object],
30
+ indexed_objects: dict[str, modal.object._Object],
31
31
  environment_name: str,
32
32
  ) -> None: ...
33
33
  async def _publish_app(
34
34
  client: modal.client._Client,
35
35
  running_app: modal.running_app.RunningApp,
36
36
  app_state: int,
37
- indexed_objects: typing.Dict[str, modal.object._Object],
37
+ indexed_objects: dict[str, modal.object._Object],
38
38
  name: str = "",
39
39
  tag: str = "",
40
- ) -> typing.Tuple[str, typing.List[str]]: ...
40
+ ) -> tuple[str, list[str]]: ...
41
41
  async def _disconnect(client: modal.client._Client, app_id: str, reason: int, exc_str: str = "") -> None: ...
42
42
  async def _status_based_disconnect(
43
43
  client: modal.client._Client, app_id: str, exc_info: typing.Optional[BaseException] = None
@@ -58,9 +58,9 @@ class DeployResult:
58
58
  app_id: str
59
59
  app_page_url: str
60
60
  app_logs_url: str
61
- warnings: typing.List[str]
61
+ warnings: list[str]
62
62
 
63
- def __init__(self, app_id: str, app_page_url: str, app_logs_url: str, warnings: typing.List[str]) -> None: ...
63
+ def __init__(self, app_id: str, app_page_url: str, app_logs_url: str, warnings: list[str]) -> None: ...
64
64
  def __repr__(self): ...
65
65
  def __eq__(self, other): ...
66
66
  def __setattr__(self, name, value): ...
@@ -75,9 +75,7 @@ async def _deploy_app(
75
75
  environment_name: typing.Optional[str] = None,
76
76
  tag: str = "",
77
77
  ) -> DeployResult: ...
78
- async def _interactive_shell(
79
- _app: _App, cmds: typing.List[str], environment_name: str = "", **kwargs: typing.Any
80
- ) -> None: ...
78
+ async def _interactive_shell(_app: _App, cmds: list[str], environment_name: str = "", **kwargs: typing.Any) -> None: ...
81
79
  def _run_stub(*args: typing.Any, **kwargs: typing.Any): ...
82
80
  def _deploy_stub(*args: typing.Any, **kwargs: typing.Any): ...
83
81
 
@@ -136,12 +134,8 @@ class __deploy_app_spec(typing_extensions.Protocol):
136
134
  deploy_app: __deploy_app_spec
137
135
 
138
136
  class __interactive_shell_spec(typing_extensions.Protocol):
139
- def __call__(
140
- self, _app: _App, cmds: typing.List[str], environment_name: str = "", **kwargs: typing.Any
141
- ) -> None: ...
142
- async def aio(
143
- self, _app: _App, cmds: typing.List[str], environment_name: str = "", **kwargs: typing.Any
144
- ) -> None: ...
137
+ def __call__(self, _app: _App, cmds: list[str], environment_name: str = "", **kwargs: typing.Any) -> None: ...
138
+ async def aio(self, _app: _App, cmds: list[str], environment_name: str = "", **kwargs: typing.Any) -> None: ...
145
139
 
146
140
  interactive_shell: __interactive_shell_spec
147
141
 
modal/running_app.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # Copyright Modal Labs 2024
2
2
  from dataclasses import dataclass, field
3
- from typing import Dict, Optional
3
+ from typing import Optional
4
4
 
5
5
  from google.protobuf.message import Message
6
6
 
@@ -13,7 +13,7 @@ class RunningApp:
13
13
  environment_name: Optional[str] = None
14
14
  app_page_url: Optional[str] = None
15
15
  app_logs_url: Optional[str] = None
16
- tag_to_object_id: Dict[str, str] = field(default_factory=dict)
17
- object_handle_metadata: Dict[str, Optional[Message]] = field(default_factory=dict)
16
+ tag_to_object_id: dict[str, str] = field(default_factory=dict)
17
+ object_handle_metadata: dict[str, Optional[Message]] = field(default_factory=dict)
18
18
  interactive: bool = False
19
19
  client: Optional[_Client] = None
modal/sandbox.py CHANGED
@@ -1,7 +1,8 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
3
  import os
4
- from typing import TYPE_CHECKING, AsyncGenerator, Dict, List, Literal, Optional, Sequence, Tuple, Union, overload
4
+ from collections.abc import AsyncGenerator, Sequence
5
+ from typing import TYPE_CHECKING, Literal, Optional, Union, overload
5
6
 
6
7
  from google.protobuf.message import Message
7
8
  from grpclib import GRPCError, Status
@@ -27,6 +28,7 @@ from .io_streams import StreamReader, StreamWriter, _StreamReader, _StreamWriter
27
28
  from .mount import _Mount
28
29
  from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
29
30
  from .object import _get_environment_name, _Object
31
+ from .proxy import _Proxy
30
32
  from .scheduler_placement import SchedulerPlacement
31
33
  from .secret import _Secret
32
34
  from .stream_type import StreamType
@@ -50,7 +52,7 @@ class _Sandbox(_Object, type_prefix="sb"):
50
52
  _stderr: _StreamReader[str]
51
53
  _stdin: _StreamWriter
52
54
  _task_id: Optional[str] = None
53
- _tunnels: Optional[Dict[int, Tunnel]] = None
55
+ _tunnels: Optional[dict[int, Tunnel]] = None
54
56
 
55
57
  @staticmethod
56
58
  def _new(
@@ -64,14 +66,15 @@ class _Sandbox(_Object, type_prefix="sb"):
64
66
  cloud: Optional[str] = None,
65
67
  region: Optional[Union[str, Sequence[str]]] = None,
66
68
  cpu: Optional[float] = None,
67
- memory: Optional[Union[int, Tuple[int, int]]] = None,
68
- network_file_systems: Dict[Union[str, os.PathLike], _NetworkFileSystem] = {},
69
+ memory: Optional[Union[int, tuple[int, int]]] = None,
70
+ network_file_systems: dict[Union[str, os.PathLike], _NetworkFileSystem] = {},
69
71
  block_network: bool = False,
70
72
  cidr_allowlist: Optional[Sequence[str]] = None,
71
- volumes: Dict[Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]] = {},
73
+ volumes: dict[Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]] = {},
72
74
  pty_info: Optional[api_pb2.PTYInfo] = None,
73
75
  encrypted_ports: Sequence[int] = [],
74
76
  unencrypted_ports: Sequence[int] = [],
77
+ proxy: Optional[_Proxy] = None,
75
78
  _experimental_scheduler_placement: Optional[SchedulerPlacement] = None,
76
79
  ) -> "_Sandbox":
77
80
  """mdmd:hidden"""
@@ -101,8 +104,8 @@ class _Sandbox(_Object, type_prefix="sb"):
101
104
  cloud_bucket_mounts = [(k, v) for k, v in validated_volumes if isinstance(v, _CloudBucketMount)]
102
105
  validated_volumes = [(k, v) for k, v in validated_volumes if isinstance(v, _Volume)]
103
106
 
104
- def _deps() -> List[_Object]:
105
- deps: List[_Object] = [image] + list(mounts) + list(secrets)
107
+ def _deps() -> list[_Object]:
108
+ deps: list[_Object] = [image] + list(mounts) + list(secrets)
106
109
  for _, vol in validated_network_file_systems:
107
110
  deps.append(vol)
108
111
  for _, vol in validated_volumes:
@@ -165,6 +168,7 @@ class _Sandbox(_Object, type_prefix="sb"):
165
168
  worker_id=config.get("worker_id"),
166
169
  open_ports=api_pb2.PortSpecs(ports=open_ports),
167
170
  network_access=network_access,
171
+ proxy_id=(proxy.object_id if proxy else None),
168
172
  )
169
173
 
170
174
  # Note - `resolver.app_id` will be `None` for app-less sandboxes
@@ -186,7 +190,7 @@ class _Sandbox(_Object, type_prefix="sb"):
186
190
  image: Optional[_Image] = None, # The image to run as the container for the sandbox.
187
191
  mounts: Sequence[_Mount] = (), # Mounts to attach to the sandbox.
188
192
  secrets: Sequence[_Secret] = (), # Environment variables to inject into the sandbox.
189
- network_file_systems: Dict[Union[str, os.PathLike], _NetworkFileSystem] = {},
193
+ network_file_systems: dict[Union[str, os.PathLike], _NetworkFileSystem] = {},
190
194
  timeout: Optional[int] = None, # Maximum execution time of the sandbox in seconds.
191
195
  workdir: Optional[str] = None, # Working directory of the sandbox.
192
196
  gpu: GPU_T = None,
@@ -195,11 +199,11 @@ class _Sandbox(_Object, type_prefix="sb"):
195
199
  cpu: Optional[float] = None, # How many CPU cores to request. This is a soft limit.
196
200
  # Specify, in MiB, a memory request which is the minimum memory required.
197
201
  # Or, pass (request, limit) to additionally specify a hard limit in MiB.
198
- memory: Optional[Union[int, Tuple[int, int]]] = None,
202
+ memory: Optional[Union[int, tuple[int, int]]] = None,
199
203
  block_network: bool = False, # Whether to block network access
200
204
  # List of CIDRs the sandbox is allowed to access. If None, all CIDRs are allowed.
201
205
  cidr_allowlist: Optional[Sequence[str]] = None,
202
- volumes: Dict[
206
+ volumes: dict[
203
207
  Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]
204
208
  ] = {}, # Mount points for Modal Volumes and CloudBucketMounts
205
209
  pty_info: Optional[api_pb2.PTYInfo] = None,
@@ -207,6 +211,8 @@ class _Sandbox(_Object, type_prefix="sb"):
207
211
  encrypted_ports: Sequence[int] = [],
208
212
  # List of ports to tunnel into the sandbox without encryption.
209
213
  unencrypted_ports: Sequence[int] = [],
214
+ # Reference to a Modal Proxy to use in front of this Sandbox.
215
+ proxy: Optional[_Proxy] = None,
210
216
  _experimental_scheduler_placement: Optional[
211
217
  SchedulerPlacement
212
218
  ] = None, # Experimental controls over fine-grained scheduling (alpha).
@@ -242,6 +248,7 @@ class _Sandbox(_Object, type_prefix="sb"):
242
248
  pty_info=pty_info,
243
249
  encrypted_ports=encrypted_ports,
244
250
  unencrypted_ports=unencrypted_ports,
251
+ proxy=proxy,
245
252
  _experimental_scheduler_placement=_experimental_scheduler_placement,
246
253
  )
247
254
 
@@ -306,7 +313,7 @@ class _Sandbox(_Object, type_prefix="sb"):
306
313
 
307
314
  return obj
308
315
 
309
- async def set_tags(self, tags: Dict[str, str], *, client: Optional[_Client] = None):
316
+ async def set_tags(self, tags: dict[str, str], *, client: Optional[_Client] = None):
310
317
  """Set tags (key-value pairs) on the Sandbox. Tags can be used to filter results in `Sandbox.list`."""
311
318
  environment_name = _get_environment_name()
312
319
  if client is None:
@@ -341,7 +348,7 @@ class _Sandbox(_Object, type_prefix="sb"):
341
348
  raise SandboxTerminatedError()
342
349
  break
343
350
 
344
- async def tunnels(self, timeout: int = 50) -> Dict[int, Tunnel]:
351
+ async def tunnels(self, timeout: int = 50) -> dict[int, Tunnel]:
345
352
  """Get tunnel metadata for the sandbox.
346
353
 
347
354
  Raises `SandboxTimeoutError` if the tunnels are not available after the timeout.
@@ -531,7 +538,7 @@ class _Sandbox(_Object, type_prefix="sb"):
531
538
 
532
539
  @staticmethod
533
540
  async def list(
534
- *, app_id: Optional[str] = None, tags: Optional[Dict[str, str]] = None, client: Optional[_Client] = None
541
+ *, app_id: Optional[str] = None, tags: Optional[dict[str, str]] = None, client: Optional[_Client] = None
535
542
  ) -> AsyncGenerator["_Sandbox", None]:
536
543
  """List all sandboxes for the current environment or app ID (if specified). If tags are specified, only
537
544
  sandboxes that have at least those tags are returned. Returns an iterator over `Sandbox` objects."""
modal/sandbox.pyi CHANGED
@@ -1,3 +1,4 @@
1
+ import collections.abc
1
2
  import google.protobuf.message
2
3
  import modal._tunnel
3
4
  import modal.app
@@ -10,6 +11,7 @@ import modal.io_streams
10
11
  import modal.mount
11
12
  import modal.network_file_system
12
13
  import modal.object
14
+ import modal.proxy
13
15
  import modal.scheduler_placement
14
16
  import modal.secret
15
17
  import modal.stream_type
@@ -25,33 +27,32 @@ class _Sandbox(modal.object._Object):
25
27
  _stderr: modal.io_streams._StreamReader[str]
26
28
  _stdin: modal.io_streams._StreamWriter
27
29
  _task_id: typing.Optional[str]
28
- _tunnels: typing.Optional[typing.Dict[int, modal._tunnel.Tunnel]]
30
+ _tunnels: typing.Optional[dict[int, modal._tunnel.Tunnel]]
29
31
 
30
32
  @staticmethod
31
33
  def _new(
32
- entrypoint_args: typing.Sequence[str],
34
+ entrypoint_args: collections.abc.Sequence[str],
33
35
  image: modal.image._Image,
34
- mounts: typing.Sequence[modal.mount._Mount],
35
- secrets: typing.Sequence[modal.secret._Secret],
36
+ mounts: collections.abc.Sequence[modal.mount._Mount],
37
+ secrets: collections.abc.Sequence[modal.secret._Secret],
36
38
  timeout: typing.Optional[int] = None,
37
39
  workdir: typing.Optional[str] = None,
38
40
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
39
41
  cloud: typing.Optional[str] = None,
40
- region: typing.Union[str, typing.Sequence[str], None] = None,
42
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
41
43
  cpu: typing.Optional[float] = None,
42
- memory: typing.Union[int, typing.Tuple[int, int], None] = None,
43
- network_file_systems: typing.Dict[
44
- typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem
45
- ] = {},
44
+ memory: typing.Union[int, tuple[int, int], None] = None,
45
+ network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem] = {},
46
46
  block_network: bool = False,
47
- cidr_allowlist: typing.Optional[typing.Sequence[str]] = None,
48
- volumes: typing.Dict[
47
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
48
+ volumes: dict[
49
49
  typing.Union[str, os.PathLike],
50
50
  typing.Union[modal.volume._Volume, modal.cloud_bucket_mount._CloudBucketMount],
51
51
  ] = {},
52
52
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
53
- encrypted_ports: typing.Sequence[int] = [],
54
- unencrypted_ports: typing.Sequence[int] = [],
53
+ encrypted_ports: collections.abc.Sequence[int] = [],
54
+ unencrypted_ports: collections.abc.Sequence[int] = [],
55
+ proxy: typing.Optional[modal.proxy._Proxy] = None,
55
56
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
56
57
  ) -> _Sandbox: ...
57
58
  @staticmethod
@@ -60,36 +61,35 @@ class _Sandbox(modal.object._Object):
60
61
  app: typing.Optional[modal.app._App] = None,
61
62
  environment_name: typing.Optional[str] = None,
62
63
  image: typing.Optional[modal.image._Image] = None,
63
- mounts: typing.Sequence[modal.mount._Mount] = (),
64
- secrets: typing.Sequence[modal.secret._Secret] = (),
65
- network_file_systems: typing.Dict[
66
- typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem
67
- ] = {},
64
+ mounts: collections.abc.Sequence[modal.mount._Mount] = (),
65
+ secrets: collections.abc.Sequence[modal.secret._Secret] = (),
66
+ network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem] = {},
68
67
  timeout: typing.Optional[int] = None,
69
68
  workdir: typing.Optional[str] = None,
70
69
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
71
70
  cloud: typing.Optional[str] = None,
72
- region: typing.Union[str, typing.Sequence[str], None] = None,
71
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
73
72
  cpu: typing.Optional[float] = None,
74
- memory: typing.Union[int, typing.Tuple[int, int], None] = None,
73
+ memory: typing.Union[int, tuple[int, int], None] = None,
75
74
  block_network: bool = False,
76
- cidr_allowlist: typing.Optional[typing.Sequence[str]] = None,
77
- volumes: typing.Dict[
75
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
76
+ volumes: dict[
78
77
  typing.Union[str, os.PathLike],
79
78
  typing.Union[modal.volume._Volume, modal.cloud_bucket_mount._CloudBucketMount],
80
79
  ] = {},
81
80
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
82
- encrypted_ports: typing.Sequence[int] = [],
83
- unencrypted_ports: typing.Sequence[int] = [],
81
+ encrypted_ports: collections.abc.Sequence[int] = [],
82
+ unencrypted_ports: collections.abc.Sequence[int] = [],
83
+ proxy: typing.Optional[modal.proxy._Proxy] = None,
84
84
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
85
85
  client: typing.Optional[modal.client._Client] = None,
86
86
  ) -> _Sandbox: ...
87
87
  def _hydrate_metadata(self, handle_metadata: typing.Optional[google.protobuf.message.Message]): ...
88
88
  @staticmethod
89
89
  async def from_id(sandbox_id: str, client: typing.Optional[modal.client._Client] = None) -> _Sandbox: ...
90
- async def set_tags(self, tags: typing.Dict[str, str], *, client: typing.Optional[modal.client._Client] = None): ...
90
+ async def set_tags(self, tags: dict[str, str], *, client: typing.Optional[modal.client._Client] = None): ...
91
91
  async def wait(self, raise_on_termination: bool = True): ...
92
- async def tunnels(self, timeout: int = 50) -> typing.Dict[int, modal._tunnel.Tunnel]: ...
92
+ async def tunnels(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
93
93
  async def terminate(self): ...
94
94
  async def poll(self) -> typing.Optional[int]: ...
95
95
  async def _get_task_id(self): ...
@@ -102,7 +102,7 @@ class _Sandbox(modal.object._Object):
102
102
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
103
103
  timeout: typing.Optional[int] = None,
104
104
  workdir: typing.Optional[str] = None,
105
- secrets: typing.Sequence[modal.secret._Secret] = (),
105
+ secrets: collections.abc.Sequence[modal.secret._Secret] = (),
106
106
  text: typing.Literal[True] = True,
107
107
  bufsize: typing.Literal[-1, 1] = -1,
108
108
  _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
@@ -116,7 +116,7 @@ class _Sandbox(modal.object._Object):
116
116
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
117
117
  timeout: typing.Optional[int] = None,
118
118
  workdir: typing.Optional[str] = None,
119
- secrets: typing.Sequence[modal.secret._Secret] = (),
119
+ secrets: collections.abc.Sequence[modal.secret._Secret] = (),
120
120
  text: typing.Literal[False] = False,
121
121
  bufsize: typing.Literal[-1, 1] = -1,
122
122
  _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
@@ -133,9 +133,9 @@ class _Sandbox(modal.object._Object):
133
133
  def list(
134
134
  *,
135
135
  app_id: typing.Optional[str] = None,
136
- tags: typing.Optional[typing.Dict[str, str]] = None,
136
+ tags: typing.Optional[dict[str, str]] = None,
137
137
  client: typing.Optional[modal.client._Client] = None,
138
- ) -> typing.AsyncGenerator[_Sandbox, None]: ...
138
+ ) -> collections.abc.AsyncGenerator[_Sandbox, None]: ...
139
139
 
140
140
  class Sandbox(modal.object.Object):
141
141
  _result: typing.Optional[modal_proto.api_pb2.GenericResult]
@@ -143,33 +143,32 @@ class Sandbox(modal.object.Object):
143
143
  _stderr: modal.io_streams.StreamReader[str]
144
144
  _stdin: modal.io_streams.StreamWriter
145
145
  _task_id: typing.Optional[str]
146
- _tunnels: typing.Optional[typing.Dict[int, modal._tunnel.Tunnel]]
146
+ _tunnels: typing.Optional[dict[int, modal._tunnel.Tunnel]]
147
147
 
148
148
  def __init__(self, *args, **kwargs): ...
149
149
  @staticmethod
150
150
  def _new(
151
- entrypoint_args: typing.Sequence[str],
151
+ entrypoint_args: collections.abc.Sequence[str],
152
152
  image: modal.image.Image,
153
- mounts: typing.Sequence[modal.mount.Mount],
154
- secrets: typing.Sequence[modal.secret.Secret],
153
+ mounts: collections.abc.Sequence[modal.mount.Mount],
154
+ secrets: collections.abc.Sequence[modal.secret.Secret],
155
155
  timeout: typing.Optional[int] = None,
156
156
  workdir: typing.Optional[str] = None,
157
157
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
158
158
  cloud: typing.Optional[str] = None,
159
- region: typing.Union[str, typing.Sequence[str], None] = None,
159
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
160
160
  cpu: typing.Optional[float] = None,
161
- memory: typing.Union[int, typing.Tuple[int, int], None] = None,
162
- network_file_systems: typing.Dict[
163
- typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem
164
- ] = {},
161
+ memory: typing.Union[int, tuple[int, int], None] = None,
162
+ network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem] = {},
165
163
  block_network: bool = False,
166
- cidr_allowlist: typing.Optional[typing.Sequence[str]] = None,
167
- volumes: typing.Dict[
164
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
165
+ volumes: dict[
168
166
  typing.Union[str, os.PathLike], typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount]
169
167
  ] = {},
170
168
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
171
- encrypted_ports: typing.Sequence[int] = [],
172
- unencrypted_ports: typing.Sequence[int] = [],
169
+ encrypted_ports: collections.abc.Sequence[int] = [],
170
+ unencrypted_ports: collections.abc.Sequence[int] = [],
171
+ proxy: typing.Optional[modal.proxy.Proxy] = None,
173
172
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
174
173
  ) -> Sandbox: ...
175
174
 
@@ -180,27 +179,28 @@ class Sandbox(modal.object.Object):
180
179
  app: typing.Optional[modal.app.App] = None,
181
180
  environment_name: typing.Optional[str] = None,
182
181
  image: typing.Optional[modal.image.Image] = None,
183
- mounts: typing.Sequence[modal.mount.Mount] = (),
184
- secrets: typing.Sequence[modal.secret.Secret] = (),
185
- network_file_systems: typing.Dict[
182
+ mounts: collections.abc.Sequence[modal.mount.Mount] = (),
183
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
184
+ network_file_systems: dict[
186
185
  typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem
187
186
  ] = {},
188
187
  timeout: typing.Optional[int] = None,
189
188
  workdir: typing.Optional[str] = None,
190
189
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
191
190
  cloud: typing.Optional[str] = None,
192
- region: typing.Union[str, typing.Sequence[str], None] = None,
191
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
193
192
  cpu: typing.Optional[float] = None,
194
- memory: typing.Union[int, typing.Tuple[int, int], None] = None,
193
+ memory: typing.Union[int, tuple[int, int], None] = None,
195
194
  block_network: bool = False,
196
- cidr_allowlist: typing.Optional[typing.Sequence[str]] = None,
197
- volumes: typing.Dict[
195
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
196
+ volumes: dict[
198
197
  typing.Union[str, os.PathLike],
199
198
  typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount],
200
199
  ] = {},
201
200
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
202
- encrypted_ports: typing.Sequence[int] = [],
203
- unencrypted_ports: typing.Sequence[int] = [],
201
+ encrypted_ports: collections.abc.Sequence[int] = [],
202
+ unencrypted_ports: collections.abc.Sequence[int] = [],
203
+ proxy: typing.Optional[modal.proxy.Proxy] = None,
204
204
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
205
205
  client: typing.Optional[modal.client.Client] = None,
206
206
  ) -> Sandbox: ...
@@ -210,27 +210,28 @@ class Sandbox(modal.object.Object):
210
210
  app: typing.Optional[modal.app.App] = None,
211
211
  environment_name: typing.Optional[str] = None,
212
212
  image: typing.Optional[modal.image.Image] = None,
213
- mounts: typing.Sequence[modal.mount.Mount] = (),
214
- secrets: typing.Sequence[modal.secret.Secret] = (),
215
- network_file_systems: typing.Dict[
213
+ mounts: collections.abc.Sequence[modal.mount.Mount] = (),
214
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
215
+ network_file_systems: dict[
216
216
  typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem
217
217
  ] = {},
218
218
  timeout: typing.Optional[int] = None,
219
219
  workdir: typing.Optional[str] = None,
220
220
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
221
221
  cloud: typing.Optional[str] = None,
222
- region: typing.Union[str, typing.Sequence[str], None] = None,
222
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
223
223
  cpu: typing.Optional[float] = None,
224
- memory: typing.Union[int, typing.Tuple[int, int], None] = None,
224
+ memory: typing.Union[int, tuple[int, int], None] = None,
225
225
  block_network: bool = False,
226
- cidr_allowlist: typing.Optional[typing.Sequence[str]] = None,
227
- volumes: typing.Dict[
226
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
227
+ volumes: dict[
228
228
  typing.Union[str, os.PathLike],
229
229
  typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount],
230
230
  ] = {},
231
231
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
232
- encrypted_ports: typing.Sequence[int] = [],
233
- unencrypted_ports: typing.Sequence[int] = [],
232
+ encrypted_ports: collections.abc.Sequence[int] = [],
233
+ unencrypted_ports: collections.abc.Sequence[int] = [],
234
+ proxy: typing.Optional[modal.proxy.Proxy] = None,
234
235
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
235
236
  client: typing.Optional[modal.client.Client] = None,
236
237
  ) -> Sandbox: ...
@@ -246,8 +247,8 @@ class Sandbox(modal.object.Object):
246
247
  from_id: __from_id_spec
247
248
 
248
249
  class __set_tags_spec(typing_extensions.Protocol):
249
- def __call__(self, tags: typing.Dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
250
- async def aio(self, tags: typing.Dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
250
+ def __call__(self, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
251
+ async def aio(self, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
251
252
 
252
253
  set_tags: __set_tags_spec
253
254
 
@@ -258,8 +259,8 @@ class Sandbox(modal.object.Object):
258
259
  wait: __wait_spec
259
260
 
260
261
  class __tunnels_spec(typing_extensions.Protocol):
261
- def __call__(self, timeout: int = 50) -> typing.Dict[int, modal._tunnel.Tunnel]: ...
262
- async def aio(self, timeout: int = 50) -> typing.Dict[int, modal._tunnel.Tunnel]: ...
262
+ def __call__(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
263
+ async def aio(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
263
264
 
264
265
  tunnels: __tunnels_spec
265
266
 
@@ -291,7 +292,7 @@ class Sandbox(modal.object.Object):
291
292
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
292
293
  timeout: typing.Optional[int] = None,
293
294
  workdir: typing.Optional[str] = None,
294
- secrets: typing.Sequence[modal.secret.Secret] = (),
295
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
295
296
  text: typing.Literal[True] = True,
296
297
  bufsize: typing.Literal[-1, 1] = -1,
297
298
  _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
@@ -305,7 +306,7 @@ class Sandbox(modal.object.Object):
305
306
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
306
307
  timeout: typing.Optional[int] = None,
307
308
  workdir: typing.Optional[str] = None,
308
- secrets: typing.Sequence[modal.secret.Secret] = (),
309
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
309
310
  text: typing.Literal[False] = False,
310
311
  bufsize: typing.Literal[-1, 1] = -1,
311
312
  _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
@@ -319,7 +320,7 @@ class Sandbox(modal.object.Object):
319
320
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
320
321
  timeout: typing.Optional[int] = None,
321
322
  workdir: typing.Optional[str] = None,
322
- secrets: typing.Sequence[modal.secret.Secret] = (),
323
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
323
324
  text: typing.Literal[True] = True,
324
325
  bufsize: typing.Literal[-1, 1] = -1,
325
326
  _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
@@ -333,7 +334,7 @@ class Sandbox(modal.object.Object):
333
334
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
334
335
  timeout: typing.Optional[int] = None,
335
336
  workdir: typing.Optional[str] = None,
336
- secrets: typing.Sequence[modal.secret.Secret] = (),
337
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
337
338
  text: typing.Literal[False] = False,
338
339
  bufsize: typing.Literal[-1, 1] = -1,
339
340
  _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
@@ -355,16 +356,16 @@ class Sandbox(modal.object.Object):
355
356
  self,
356
357
  *,
357
358
  app_id: typing.Optional[str] = None,
358
- tags: typing.Optional[typing.Dict[str, str]] = None,
359
+ tags: typing.Optional[dict[str, str]] = None,
359
360
  client: typing.Optional[modal.client.Client] = None,
360
361
  ) -> typing.Generator[Sandbox, None, None]: ...
361
362
  def aio(
362
363
  self,
363
364
  *,
364
365
  app_id: typing.Optional[str] = None,
365
- tags: typing.Optional[typing.Dict[str, str]] = None,
366
+ tags: typing.Optional[dict[str, str]] = None,
366
367
  client: typing.Optional[modal.client.Client] = None,
367
- ) -> typing.AsyncGenerator[Sandbox, None]: ...
368
+ ) -> collections.abc.AsyncGenerator[Sandbox, None]: ...
368
369
 
369
370
  list: __list_spec
370
371
 
@@ -1,5 +1,6 @@
1
1
  # Copyright Modal Labs 2024
2
- from typing import Optional, Sequence, Union
2
+ from collections.abc import Sequence
3
+ from typing import Optional, Union
3
4
 
4
5
  from modal_proto import api_pb2
5
6