modal 0.67.0__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 +81 -69
  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.0.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.0.dist-info/RECORD +0 -168
  110. {modal-0.67.0.dist-info → modal-0.67.22.dist-info}/LICENSE +0 -0
  111. {modal-0.67.0.dist-info → modal-0.67.22.dist-info}/WHEEL +0 -0
  112. {modal-0.67.0.dist-info → modal-0.67.22.dist-info}/entry_points.txt +0 -0
  113. {modal-0.67.0.dist-info → modal-0.67.22.dist-info}/top_level.txt +0 -0
modal/image.pyi CHANGED
@@ -1,3 +1,4 @@
1
+ import collections.abc
1
2
  import google.protobuf.message
2
3
  import modal.client
3
4
  import modal.cloud_bucket_mount
@@ -29,9 +30,9 @@ def _get_modal_requirements_path(
29
30
  ) -> str: ...
30
31
  def _get_modal_requirements_command(version: typing.Literal["2023.12", "2024.04", "2024.10"]) -> str: ...
31
32
  def _flatten_str_args(
32
- function_name: str, arg_name: str, args: typing.Sequence[typing.Union[str, typing.List[str]]]
33
- ) -> typing.List[str]: ...
34
- def _validate_packages(packages: typing.List[str]) -> bool: ...
33
+ function_name: str, arg_name: str, args: collections.abc.Sequence[typing.Union[str, list[str]]]
34
+ ) -> list[str]: ...
35
+ def _validate_packages(packages: list[str]) -> bool: ...
35
36
  def _warn_invalid_packages(old_command: str) -> None: ...
36
37
  def _make_pip_install_args(
37
38
  find_links: typing.Optional[str] = None,
@@ -49,10 +50,10 @@ class _ImageRegistryConfig:
49
50
  def get_proto(self) -> modal_proto.api_pb2.ImageRegistryConfig: ...
50
51
 
51
52
  class DockerfileSpec:
52
- commands: typing.List[str]
53
- context_files: typing.Dict[str, str]
53
+ commands: list[str]
54
+ context_files: dict[str, str]
54
55
 
55
- def __init__(self, commands: typing.List[str], context_files: typing.Dict[str, str]) -> None: ...
56
+ def __init__(self, commands: list[str], context_files: dict[str, str]) -> None: ...
56
57
  def __repr__(self): ...
57
58
  def __eq__(self, other): ...
58
59
 
@@ -62,9 +63,9 @@ async def _image_await_build_result(
62
63
 
63
64
  class _Image(modal.object._Object):
64
65
  force_build: bool
65
- inside_exceptions: typing.List[Exception]
66
- _serve_mounts: typing.FrozenSet[modal.mount._Mount]
67
- _deferred_mounts: typing.Sequence[modal.mount._Mount]
66
+ inside_exceptions: list[Exception]
67
+ _serve_mounts: frozenset[modal.mount._Mount]
68
+ _deferred_mounts: collections.abc.Sequence[modal.mount._Mount]
68
69
  _metadata: typing.Optional[modal_proto.api_pb2.ImageMetadata]
69
70
 
70
71
  def _initialize_from_empty(self): ...
@@ -77,11 +78,11 @@ class _Image(modal.object._Object):
77
78
  @staticmethod
78
79
  def _from_args(
79
80
  *,
80
- base_images: typing.Optional[typing.Dict[str, _Image]] = None,
81
+ base_images: typing.Optional[dict[str, _Image]] = None,
81
82
  dockerfile_function: typing.Optional[
82
83
  typing.Callable[[typing.Literal["2023.12", "2024.04", "2024.10"]], DockerfileSpec]
83
84
  ] = None,
84
- secrets: typing.Optional[typing.Sequence[modal.secret._Secret]] = None,
85
+ secrets: typing.Optional[collections.abc.Sequence[modal.secret._Secret]] = None,
85
86
  gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
86
87
  build_function: typing.Optional[modal.functions._Function] = None,
87
88
  build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
@@ -94,7 +95,7 @@ class _Image(modal.object._Object):
94
95
  def extend(
95
96
  self,
96
97
  *,
97
- secrets: typing.Optional[typing.Sequence[modal.secret._Secret]] = None,
98
+ secrets: typing.Optional[collections.abc.Sequence[modal.secret._Secret]] = None,
98
99
  gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
99
100
  build_function: typing.Optional[modal.functions._Function] = None,
100
101
  build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
@@ -120,14 +121,14 @@ class _Image(modal.object._Object):
120
121
  ) -> _Image: ...
121
122
  def pip_install(
122
123
  self,
123
- *packages: typing.Union[str, typing.List[str]],
124
+ *packages: typing.Union[str, list[str]],
124
125
  find_links: typing.Optional[str] = None,
125
126
  index_url: typing.Optional[str] = None,
126
127
  extra_index_url: typing.Optional[str] = None,
127
128
  pre: bool = False,
128
129
  extra_options: str = "",
129
130
  force_build: bool = False,
130
- secrets: typing.Sequence[modal.secret._Secret] = [],
131
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
131
132
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
132
133
  ) -> _Image: ...
133
134
  def pip_install_private_repos(
@@ -140,7 +141,7 @@ class _Image(modal.object._Object):
140
141
  pre: bool = False,
141
142
  extra_options: str = "",
142
143
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
143
- secrets: typing.Sequence[modal.secret._Secret] = [],
144
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
144
145
  force_build: bool = False,
145
146
  ) -> _Image: ...
146
147
  def pip_install_from_requirements(
@@ -153,13 +154,13 @@ class _Image(modal.object._Object):
153
154
  pre: bool = False,
154
155
  extra_options: str = "",
155
156
  force_build: bool = False,
156
- secrets: typing.Sequence[modal.secret._Secret] = [],
157
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
157
158
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
158
159
  ) -> _Image: ...
159
160
  def pip_install_from_pyproject(
160
161
  self,
161
162
  pyproject_toml: str,
162
- optional_dependencies: typing.List[str] = [],
163
+ optional_dependencies: list[str] = [],
163
164
  *,
164
165
  find_links: typing.Optional[str] = None,
165
166
  index_url: typing.Optional[str] = None,
@@ -167,7 +168,7 @@ class _Image(modal.object._Object):
167
168
  pre: bool = False,
168
169
  extra_options: str = "",
169
170
  force_build: bool = False,
170
- secrets: typing.Sequence[modal.secret._Secret] = [],
171
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
171
172
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
172
173
  ) -> _Image: ...
173
174
  def poetry_install_from_file(
@@ -177,28 +178,28 @@ class _Image(modal.object._Object):
177
178
  ignore_lockfile: bool = False,
178
179
  old_installer: bool = False,
179
180
  force_build: bool = False,
180
- with_: typing.List[str] = [],
181
- without: typing.List[str] = [],
182
- only: typing.List[str] = [],
181
+ with_: list[str] = [],
182
+ without: list[str] = [],
183
+ only: list[str] = [],
183
184
  *,
184
- secrets: typing.Sequence[modal.secret._Secret] = [],
185
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
185
186
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
186
187
  ) -> _Image: ...
187
188
  def dockerfile_commands(
188
189
  self,
189
- *dockerfile_commands: typing.Union[str, typing.List[str]],
190
- context_files: typing.Dict[str, str] = {},
191
- secrets: typing.Sequence[modal.secret._Secret] = [],
190
+ *dockerfile_commands: typing.Union[str, list[str]],
191
+ context_files: dict[str, str] = {},
192
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
192
193
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
193
194
  context_mount: typing.Optional[modal.mount._Mount] = None,
194
195
  force_build: bool = False,
195
196
  ) -> _Image: ...
196
- def entrypoint(self, entrypoint_commands: typing.List[str]) -> _Image: ...
197
- def shell(self, shell_commands: typing.List[str]) -> _Image: ...
197
+ def entrypoint(self, entrypoint_commands: list[str]) -> _Image: ...
198
+ def shell(self, shell_commands: list[str]) -> _Image: ...
198
199
  def run_commands(
199
200
  self,
200
- *commands: typing.Union[str, typing.List[str]],
201
- secrets: typing.Sequence[modal.secret._Secret] = [],
201
+ *commands: typing.Union[str, list[str]],
202
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
202
203
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
203
204
  force_build: bool = False,
204
205
  ) -> _Image: ...
@@ -206,10 +207,10 @@ class _Image(modal.object._Object):
206
207
  def conda(python_version: typing.Optional[str] = None, force_build: bool = False): ...
207
208
  def conda_install(
208
209
  self,
209
- *packages: typing.Union[str, typing.List[str]],
210
- channels: typing.List[str] = [],
210
+ *packages: typing.Union[str, list[str]],
211
+ channels: list[str] = [],
211
212
  force_build: bool = False,
212
- secrets: typing.Sequence[modal.secret._Secret] = [],
213
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
213
214
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
214
215
  ): ...
215
216
  def conda_update_from_environment(
@@ -217,33 +218,33 @@ class _Image(modal.object._Object):
217
218
  environment_yml: str,
218
219
  force_build: bool = False,
219
220
  *,
220
- secrets: typing.Sequence[modal.secret._Secret] = [],
221
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
221
222
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
222
223
  ): ...
223
224
  @staticmethod
224
225
  def micromamba(python_version: typing.Optional[str] = None, force_build: bool = False) -> _Image: ...
225
226
  def micromamba_install(
226
227
  self,
227
- *packages: typing.Union[str, typing.List[str]],
228
+ *packages: typing.Union[str, list[str]],
228
229
  spec_file: typing.Optional[str] = None,
229
- channels: typing.List[str] = [],
230
+ channels: list[str] = [],
230
231
  force_build: bool = False,
231
- secrets: typing.Sequence[modal.secret._Secret] = [],
232
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
232
233
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
233
234
  ) -> _Image: ...
234
235
  @staticmethod
235
236
  def _registry_setup_commands(
236
237
  tag: str,
237
238
  builder_version: typing.Literal["2023.12", "2024.04", "2024.10"],
238
- setup_commands: typing.List[str],
239
+ setup_commands: list[str],
239
240
  add_python: typing.Optional[str] = None,
240
- ) -> typing.List[str]: ...
241
+ ) -> list[str]: ...
241
242
  @staticmethod
242
243
  def from_registry(
243
244
  tag: str,
244
245
  *,
245
246
  secret: typing.Optional[modal.secret._Secret] = None,
246
- setup_dockerfile_commands: typing.List[str] = [],
247
+ setup_dockerfile_commands: list[str] = [],
247
248
  force_build: bool = False,
248
249
  add_python: typing.Optional[str] = None,
249
250
  **kwargs,
@@ -253,7 +254,7 @@ class _Image(modal.object._Object):
253
254
  tag: str,
254
255
  secret: typing.Optional[modal.secret._Secret] = None,
255
256
  *,
256
- setup_dockerfile_commands: typing.List[str] = [],
257
+ setup_dockerfile_commands: list[str] = [],
257
258
  force_build: bool = False,
258
259
  add_python: typing.Optional[str] = None,
259
260
  **kwargs,
@@ -263,7 +264,7 @@ class _Image(modal.object._Object):
263
264
  tag: str,
264
265
  secret: typing.Optional[modal.secret._Secret] = None,
265
266
  *,
266
- setup_dockerfile_commands: typing.List[str] = [],
267
+ setup_dockerfile_commands: list[str] = [],
267
268
  force_build: bool = False,
268
269
  add_python: typing.Optional[str] = None,
269
270
  **kwargs,
@@ -274,7 +275,7 @@ class _Image(modal.object._Object):
274
275
  context_mount: typing.Optional[modal.mount._Mount] = None,
275
276
  force_build: bool = False,
276
277
  *,
277
- secrets: typing.Sequence[modal.secret._Secret] = [],
278
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
278
279
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
279
280
  add_python: typing.Optional[str] = None,
280
281
  ) -> _Image: ...
@@ -282,24 +283,24 @@ class _Image(modal.object._Object):
282
283
  def debian_slim(python_version: typing.Optional[str] = None, force_build: bool = False) -> _Image: ...
283
284
  def apt_install(
284
285
  self,
285
- *packages: typing.Union[str, typing.List[str]],
286
+ *packages: typing.Union[str, list[str]],
286
287
  force_build: bool = False,
287
- secrets: typing.Sequence[modal.secret._Secret] = [],
288
+ secrets: collections.abc.Sequence[modal.secret._Secret] = [],
288
289
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
289
290
  ) -> _Image: ...
290
291
  def run_function(
291
292
  self,
292
293
  raw_f: typing.Callable[..., typing.Any],
293
- secrets: typing.Sequence[modal.secret._Secret] = (),
294
+ secrets: collections.abc.Sequence[modal.secret._Secret] = (),
294
295
  gpu: typing.Union[
295
- None, bool, str, modal.gpu._GPUConfig, typing.List[typing.Union[None, bool, str, modal.gpu._GPUConfig]]
296
+ None, bool, str, modal.gpu._GPUConfig, list[typing.Union[None, bool, str, modal.gpu._GPUConfig]]
296
297
  ] = None,
297
- mounts: typing.Sequence[modal.mount._Mount] = (),
298
- volumes: typing.Dict[
298
+ mounts: collections.abc.Sequence[modal.mount._Mount] = (),
299
+ volumes: dict[
299
300
  typing.Union[str, pathlib.PurePosixPath],
300
301
  typing.Union[modal.volume._Volume, modal.cloud_bucket_mount._CloudBucketMount],
301
302
  ] = {},
302
- network_file_systems: typing.Dict[
303
+ network_file_systems: dict[
303
304
  typing.Union[str, pathlib.PurePosixPath], modal.network_file_system._NetworkFileSystem
304
305
  ] = {},
305
306
  cpu: typing.Optional[float] = None,
@@ -307,20 +308,20 @@ class _Image(modal.object._Object):
307
308
  timeout: typing.Optional[int] = 3600,
308
309
  force_build: bool = False,
309
310
  cloud: typing.Optional[str] = None,
310
- region: typing.Union[str, typing.Sequence[str], None] = None,
311
- args: typing.Sequence[typing.Any] = (),
312
- kwargs: typing.Dict[str, typing.Any] = {},
311
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
312
+ args: collections.abc.Sequence[typing.Any] = (),
313
+ kwargs: dict[str, typing.Any] = {},
313
314
  ) -> _Image: ...
314
- def env(self, vars: typing.Dict[str, str]) -> _Image: ...
315
+ def env(self, vars: dict[str, str]) -> _Image: ...
315
316
  def workdir(self, path: typing.Union[str, pathlib.PurePosixPath]) -> _Image: ...
316
317
  def imports(self): ...
317
318
  def _logs(self) -> typing.AsyncGenerator[str, None]: ...
318
319
 
319
320
  class Image(modal.object.Object):
320
321
  force_build: bool
321
- inside_exceptions: typing.List[Exception]
322
- _serve_mounts: typing.FrozenSet[modal.mount.Mount]
323
- _deferred_mounts: typing.Sequence[modal.mount.Mount]
322
+ inside_exceptions: list[Exception]
323
+ _serve_mounts: frozenset[modal.mount.Mount]
324
+ _deferred_mounts: collections.abc.Sequence[modal.mount.Mount]
324
325
  _metadata: typing.Optional[modal_proto.api_pb2.ImageMetadata]
325
326
 
326
327
  def __init__(self, *args, **kwargs): ...
@@ -334,11 +335,11 @@ class Image(modal.object.Object):
334
335
  @staticmethod
335
336
  def _from_args(
336
337
  *,
337
- base_images: typing.Optional[typing.Dict[str, Image]] = None,
338
+ base_images: typing.Optional[dict[str, Image]] = None,
338
339
  dockerfile_function: typing.Optional[
339
340
  typing.Callable[[typing.Literal["2023.12", "2024.04", "2024.10"]], DockerfileSpec]
340
341
  ] = None,
341
- secrets: typing.Optional[typing.Sequence[modal.secret.Secret]] = None,
342
+ secrets: typing.Optional[collections.abc.Sequence[modal.secret.Secret]] = None,
342
343
  gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
343
344
  build_function: typing.Optional[modal.functions.Function] = None,
344
345
  build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
@@ -351,7 +352,7 @@ class Image(modal.object.Object):
351
352
  def extend(
352
353
  self,
353
354
  *,
354
- secrets: typing.Optional[typing.Sequence[modal.secret.Secret]] = None,
355
+ secrets: typing.Optional[collections.abc.Sequence[modal.secret.Secret]] = None,
355
356
  gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
356
357
  build_function: typing.Optional[modal.functions.Function] = None,
357
358
  build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
@@ -377,14 +378,14 @@ class Image(modal.object.Object):
377
378
  ) -> Image: ...
378
379
  def pip_install(
379
380
  self,
380
- *packages: typing.Union[str, typing.List[str]],
381
+ *packages: typing.Union[str, list[str]],
381
382
  find_links: typing.Optional[str] = None,
382
383
  index_url: typing.Optional[str] = None,
383
384
  extra_index_url: typing.Optional[str] = None,
384
385
  pre: bool = False,
385
386
  extra_options: str = "",
386
387
  force_build: bool = False,
387
- secrets: typing.Sequence[modal.secret.Secret] = [],
388
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
388
389
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
389
390
  ) -> Image: ...
390
391
  def pip_install_private_repos(
@@ -397,7 +398,7 @@ class Image(modal.object.Object):
397
398
  pre: bool = False,
398
399
  extra_options: str = "",
399
400
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
400
- secrets: typing.Sequence[modal.secret.Secret] = [],
401
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
401
402
  force_build: bool = False,
402
403
  ) -> Image: ...
403
404
  def pip_install_from_requirements(
@@ -410,13 +411,13 @@ class Image(modal.object.Object):
410
411
  pre: bool = False,
411
412
  extra_options: str = "",
412
413
  force_build: bool = False,
413
- secrets: typing.Sequence[modal.secret.Secret] = [],
414
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
414
415
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
415
416
  ) -> Image: ...
416
417
  def pip_install_from_pyproject(
417
418
  self,
418
419
  pyproject_toml: str,
419
- optional_dependencies: typing.List[str] = [],
420
+ optional_dependencies: list[str] = [],
420
421
  *,
421
422
  find_links: typing.Optional[str] = None,
422
423
  index_url: typing.Optional[str] = None,
@@ -424,7 +425,7 @@ class Image(modal.object.Object):
424
425
  pre: bool = False,
425
426
  extra_options: str = "",
426
427
  force_build: bool = False,
427
- secrets: typing.Sequence[modal.secret.Secret] = [],
428
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
428
429
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
429
430
  ) -> Image: ...
430
431
  def poetry_install_from_file(
@@ -434,28 +435,28 @@ class Image(modal.object.Object):
434
435
  ignore_lockfile: bool = False,
435
436
  old_installer: bool = False,
436
437
  force_build: bool = False,
437
- with_: typing.List[str] = [],
438
- without: typing.List[str] = [],
439
- only: typing.List[str] = [],
438
+ with_: list[str] = [],
439
+ without: list[str] = [],
440
+ only: list[str] = [],
440
441
  *,
441
- secrets: typing.Sequence[modal.secret.Secret] = [],
442
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
442
443
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
443
444
  ) -> Image: ...
444
445
  def dockerfile_commands(
445
446
  self,
446
- *dockerfile_commands: typing.Union[str, typing.List[str]],
447
- context_files: typing.Dict[str, str] = {},
448
- secrets: typing.Sequence[modal.secret.Secret] = [],
447
+ *dockerfile_commands: typing.Union[str, list[str]],
448
+ context_files: dict[str, str] = {},
449
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
449
450
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
450
451
  context_mount: typing.Optional[modal.mount.Mount] = None,
451
452
  force_build: bool = False,
452
453
  ) -> Image: ...
453
- def entrypoint(self, entrypoint_commands: typing.List[str]) -> Image: ...
454
- def shell(self, shell_commands: typing.List[str]) -> Image: ...
454
+ def entrypoint(self, entrypoint_commands: list[str]) -> Image: ...
455
+ def shell(self, shell_commands: list[str]) -> Image: ...
455
456
  def run_commands(
456
457
  self,
457
- *commands: typing.Union[str, typing.List[str]],
458
- secrets: typing.Sequence[modal.secret.Secret] = [],
458
+ *commands: typing.Union[str, list[str]],
459
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
459
460
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
460
461
  force_build: bool = False,
461
462
  ) -> Image: ...
@@ -463,10 +464,10 @@ class Image(modal.object.Object):
463
464
  def conda(python_version: typing.Optional[str] = None, force_build: bool = False): ...
464
465
  def conda_install(
465
466
  self,
466
- *packages: typing.Union[str, typing.List[str]],
467
- channels: typing.List[str] = [],
467
+ *packages: typing.Union[str, list[str]],
468
+ channels: list[str] = [],
468
469
  force_build: bool = False,
469
- secrets: typing.Sequence[modal.secret.Secret] = [],
470
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
470
471
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
471
472
  ): ...
472
473
  def conda_update_from_environment(
@@ -474,33 +475,33 @@ class Image(modal.object.Object):
474
475
  environment_yml: str,
475
476
  force_build: bool = False,
476
477
  *,
477
- secrets: typing.Sequence[modal.secret.Secret] = [],
478
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
478
479
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
479
480
  ): ...
480
481
  @staticmethod
481
482
  def micromamba(python_version: typing.Optional[str] = None, force_build: bool = False) -> Image: ...
482
483
  def micromamba_install(
483
484
  self,
484
- *packages: typing.Union[str, typing.List[str]],
485
+ *packages: typing.Union[str, list[str]],
485
486
  spec_file: typing.Optional[str] = None,
486
- channels: typing.List[str] = [],
487
+ channels: list[str] = [],
487
488
  force_build: bool = False,
488
- secrets: typing.Sequence[modal.secret.Secret] = [],
489
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
489
490
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
490
491
  ) -> Image: ...
491
492
  @staticmethod
492
493
  def _registry_setup_commands(
493
494
  tag: str,
494
495
  builder_version: typing.Literal["2023.12", "2024.04", "2024.10"],
495
- setup_commands: typing.List[str],
496
+ setup_commands: list[str],
496
497
  add_python: typing.Optional[str] = None,
497
- ) -> typing.List[str]: ...
498
+ ) -> list[str]: ...
498
499
  @staticmethod
499
500
  def from_registry(
500
501
  tag: str,
501
502
  *,
502
503
  secret: typing.Optional[modal.secret.Secret] = None,
503
- setup_dockerfile_commands: typing.List[str] = [],
504
+ setup_dockerfile_commands: list[str] = [],
504
505
  force_build: bool = False,
505
506
  add_python: typing.Optional[str] = None,
506
507
  **kwargs,
@@ -510,7 +511,7 @@ class Image(modal.object.Object):
510
511
  tag: str,
511
512
  secret: typing.Optional[modal.secret.Secret] = None,
512
513
  *,
513
- setup_dockerfile_commands: typing.List[str] = [],
514
+ setup_dockerfile_commands: list[str] = [],
514
515
  force_build: bool = False,
515
516
  add_python: typing.Optional[str] = None,
516
517
  **kwargs,
@@ -520,7 +521,7 @@ class Image(modal.object.Object):
520
521
  tag: str,
521
522
  secret: typing.Optional[modal.secret.Secret] = None,
522
523
  *,
523
- setup_dockerfile_commands: typing.List[str] = [],
524
+ setup_dockerfile_commands: list[str] = [],
524
525
  force_build: bool = False,
525
526
  add_python: typing.Optional[str] = None,
526
527
  **kwargs,
@@ -531,7 +532,7 @@ class Image(modal.object.Object):
531
532
  context_mount: typing.Optional[modal.mount.Mount] = None,
532
533
  force_build: bool = False,
533
534
  *,
534
- secrets: typing.Sequence[modal.secret.Secret] = [],
535
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
535
536
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
536
537
  add_python: typing.Optional[str] = None,
537
538
  ) -> Image: ...
@@ -539,24 +540,24 @@ class Image(modal.object.Object):
539
540
  def debian_slim(python_version: typing.Optional[str] = None, force_build: bool = False) -> Image: ...
540
541
  def apt_install(
541
542
  self,
542
- *packages: typing.Union[str, typing.List[str]],
543
+ *packages: typing.Union[str, list[str]],
543
544
  force_build: bool = False,
544
- secrets: typing.Sequence[modal.secret.Secret] = [],
545
+ secrets: collections.abc.Sequence[modal.secret.Secret] = [],
545
546
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
546
547
  ) -> Image: ...
547
548
  def run_function(
548
549
  self,
549
550
  raw_f: typing.Callable[..., typing.Any],
550
- secrets: typing.Sequence[modal.secret.Secret] = (),
551
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
551
552
  gpu: typing.Union[
552
- None, bool, str, modal.gpu._GPUConfig, typing.List[typing.Union[None, bool, str, modal.gpu._GPUConfig]]
553
+ None, bool, str, modal.gpu._GPUConfig, list[typing.Union[None, bool, str, modal.gpu._GPUConfig]]
553
554
  ] = None,
554
- mounts: typing.Sequence[modal.mount.Mount] = (),
555
- volumes: typing.Dict[
555
+ mounts: collections.abc.Sequence[modal.mount.Mount] = (),
556
+ volumes: dict[
556
557
  typing.Union[str, pathlib.PurePosixPath],
557
558
  typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount],
558
559
  ] = {},
559
- network_file_systems: typing.Dict[
560
+ network_file_systems: dict[
560
561
  typing.Union[str, pathlib.PurePosixPath], modal.network_file_system.NetworkFileSystem
561
562
  ] = {},
562
563
  cpu: typing.Optional[float] = None,
@@ -564,11 +565,11 @@ class Image(modal.object.Object):
564
565
  timeout: typing.Optional[int] = 3600,
565
566
  force_build: bool = False,
566
567
  cloud: typing.Optional[str] = None,
567
- region: typing.Union[str, typing.Sequence[str], None] = None,
568
- args: typing.Sequence[typing.Any] = (),
569
- kwargs: typing.Dict[str, typing.Any] = {},
568
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
569
+ args: collections.abc.Sequence[typing.Any] = (),
570
+ kwargs: dict[str, typing.Any] = {},
570
571
  ) -> Image: ...
571
- def env(self, vars: typing.Dict[str, str]) -> Image: ...
572
+ def env(self, vars: dict[str, str]) -> Image: ...
572
573
  def workdir(self, path: typing.Union[str, pathlib.PurePosixPath]) -> Image: ...
573
574
  def imports(self): ...
574
575
 
@@ -578,4 +579,4 @@ class Image(modal.object.Object):
578
579
 
579
580
  _logs: ___logs_spec
580
581
 
581
- SUPPORTED_PYTHON_SERIES: typing.Dict[typing.Literal["2023.12", "2024.04", "2024.10"], typing.List[str]]
582
+ SUPPORTED_PYTHON_SERIES: dict[typing.Literal["2023.12", "2024.04", "2024.10"], list[str]]
modal/io_streams.py CHANGED
@@ -1,14 +1,11 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
+ from collections.abc import AsyncGenerator, AsyncIterator
3
4
  from typing import (
4
5
  TYPE_CHECKING,
5
- AsyncGenerator,
6
- AsyncIterator,
7
6
  Generic,
8
- List,
9
7
  Literal,
10
8
  Optional,
11
- Tuple,
12
9
  TypeVar,
13
10
  Union,
14
11
  cast,
@@ -31,7 +28,7 @@ if TYPE_CHECKING:
31
28
 
32
29
  async def _sandbox_logs_iterator(
33
30
  sandbox_id: str, file_descriptor: "api_pb2.FileDescriptor.ValueType", last_entry_id: str, client: _Client
34
- ) -> AsyncGenerator[Tuple[Optional[bytes], str], None]:
31
+ ) -> AsyncGenerator[tuple[Optional[bytes], str], None]:
35
32
  req = api_pb2.SandboxGetLogsRequest(
36
33
  sandbox_id=sandbox_id,
37
34
  file_descriptor=file_descriptor,
@@ -137,7 +134,7 @@ class _StreamReader(Generic[T]):
137
134
  # Container process streams need to be consumed as they are produced,
138
135
  # otherwise the process will block. Use a buffer to store the stream
139
136
  # until the client consumes it.
140
- self._container_process_buffer: List[Optional[bytes]] = []
137
+ self._container_process_buffer: list[Optional[bytes]] = []
141
138
  self._consume_container_process_task = asyncio.create_task(self._consume_container_process_stream())
142
139
 
143
140
  @property
@@ -208,7 +205,7 @@ class _StreamReader(Generic[T]):
208
205
  break
209
206
  raise exc
210
207
 
211
- async def _stream_container_process(self) -> AsyncGenerator[Tuple[Optional[bytes], str], None]:
208
+ async def _stream_container_process(self) -> AsyncGenerator[tuple[Optional[bytes], str], None]:
212
209
  """Streams the container process buffer to the reader."""
213
210
  entry_id = 0
214
211
  if self._last_entry_id:
@@ -227,7 +224,7 @@ class _StreamReader(Generic[T]):
227
224
 
228
225
  entry_id += 1
229
226
 
230
- async def _get_logs(self) -> AsyncGenerator[Optional[bytes], None]:
227
+ async def _get_logs(self, skip_empty_messages: bool = True) -> AsyncGenerator[Optional[bytes], None]:
231
228
  """Streams sandbox or process logs from the server to the reader.
232
229
 
233
230
  Logs returned by this method may contain partial or multiple lines at a time.
@@ -256,6 +253,11 @@ class _StreamReader(Generic[T]):
256
253
 
257
254
  async for message, entry_id in iterator:
258
255
  self._last_entry_id = entry_id
256
+ # Empty messages are sent when the process boots up. Don't yield them unless
257
+ # we're using the empty message to signal process liveness.
258
+ if skip_empty_messages and message == b"":
259
+ continue
260
+
259
261
  yield message
260
262
  if message is None:
261
263
  completed = True
@@ -311,6 +313,11 @@ class _StreamReader(Generic[T]):
311
313
  else:
312
314
  return cast(T, value)
313
315
 
316
+ async def aclose(self):
317
+ """mdmd:hidden"""
318
+ if self._stream:
319
+ await self._stream.aclose()
320
+
314
321
 
315
322
  MAX_BUFFER_SIZE = 2 * 1024 * 1024
316
323
 
@@ -385,13 +392,14 @@ class _StreamWriter:
385
392
 
386
393
  **Usage**
387
394
 
388
- ```python
389
- # Synchronous
395
+ ```python notest
390
396
  writer.write(data)
391
397
  writer.drain()
398
+ ```
392
399
 
393
- # Async
394
- writer.write(data)
400
+ Async usage:
401
+ ```python notest
402
+ writer.write(data) # not a blocking operation
395
403
  await writer.drain.aio()
396
404
  ```
397
405
  """