modal 1.0.6.dev58__py3-none-any.whl → 1.2.3.dev7__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.

Potentially problematic release.


This version of modal might be problematic. Click here for more details.

Files changed (147) hide show
  1. modal/__main__.py +3 -4
  2. modal/_billing.py +80 -0
  3. modal/_clustered_functions.py +7 -3
  4. modal/_clustered_functions.pyi +4 -2
  5. modal/_container_entrypoint.py +41 -49
  6. modal/_functions.py +424 -195
  7. modal/_grpc_client.py +171 -0
  8. modal/_load_context.py +105 -0
  9. modal/_object.py +68 -20
  10. modal/_output.py +58 -45
  11. modal/_partial_function.py +36 -11
  12. modal/_pty.py +7 -3
  13. modal/_resolver.py +21 -35
  14. modal/_runtime/asgi.py +4 -3
  15. modal/_runtime/container_io_manager.py +301 -186
  16. modal/_runtime/container_io_manager.pyi +70 -61
  17. modal/_runtime/execution_context.py +18 -2
  18. modal/_runtime/execution_context.pyi +4 -1
  19. modal/_runtime/gpu_memory_snapshot.py +170 -63
  20. modal/_runtime/user_code_imports.py +28 -58
  21. modal/_serialization.py +57 -1
  22. modal/_utils/async_utils.py +33 -12
  23. modal/_utils/auth_token_manager.py +2 -5
  24. modal/_utils/blob_utils.py +110 -53
  25. modal/_utils/function_utils.py +49 -42
  26. modal/_utils/grpc_utils.py +80 -50
  27. modal/_utils/mount_utils.py +26 -1
  28. modal/_utils/name_utils.py +17 -3
  29. modal/_utils/task_command_router_client.py +536 -0
  30. modal/_utils/time_utils.py +34 -6
  31. modal/app.py +219 -83
  32. modal/app.pyi +229 -56
  33. modal/billing.py +5 -0
  34. modal/{requirements → builder}/2025.06.txt +1 -0
  35. modal/{requirements → builder}/PREVIEW.txt +1 -0
  36. modal/cli/_download.py +19 -3
  37. modal/cli/_traceback.py +3 -2
  38. modal/cli/app.py +4 -4
  39. modal/cli/cluster.py +15 -7
  40. modal/cli/config.py +5 -3
  41. modal/cli/container.py +7 -6
  42. modal/cli/dict.py +22 -16
  43. modal/cli/entry_point.py +12 -5
  44. modal/cli/environment.py +5 -4
  45. modal/cli/import_refs.py +3 -3
  46. modal/cli/launch.py +102 -5
  47. modal/cli/network_file_system.py +9 -13
  48. modal/cli/profile.py +3 -2
  49. modal/cli/programs/launch_instance_ssh.py +94 -0
  50. modal/cli/programs/run_jupyter.py +1 -1
  51. modal/cli/programs/run_marimo.py +95 -0
  52. modal/cli/programs/vscode.py +1 -1
  53. modal/cli/queues.py +57 -26
  54. modal/cli/run.py +58 -16
  55. modal/cli/secret.py +48 -22
  56. modal/cli/utils.py +3 -4
  57. modal/cli/volume.py +28 -25
  58. modal/client.py +13 -116
  59. modal/client.pyi +9 -91
  60. modal/cloud_bucket_mount.py +5 -3
  61. modal/cloud_bucket_mount.pyi +5 -1
  62. modal/cls.py +130 -102
  63. modal/cls.pyi +45 -85
  64. modal/config.py +29 -10
  65. modal/container_process.py +291 -13
  66. modal/container_process.pyi +95 -32
  67. modal/dict.py +282 -63
  68. modal/dict.pyi +423 -73
  69. modal/environments.py +15 -27
  70. modal/environments.pyi +5 -15
  71. modal/exception.py +8 -0
  72. modal/experimental/__init__.py +143 -38
  73. modal/experimental/flash.py +247 -78
  74. modal/experimental/flash.pyi +137 -9
  75. modal/file_io.py +14 -28
  76. modal/file_io.pyi +2 -2
  77. modal/file_pattern_matcher.py +25 -16
  78. modal/functions.pyi +134 -61
  79. modal/image.py +255 -86
  80. modal/image.pyi +300 -62
  81. modal/io_streams.py +436 -126
  82. modal/io_streams.pyi +236 -171
  83. modal/mount.py +62 -157
  84. modal/mount.pyi +45 -172
  85. modal/network_file_system.py +30 -53
  86. modal/network_file_system.pyi +16 -76
  87. modal/object.pyi +42 -8
  88. modal/parallel_map.py +821 -113
  89. modal/parallel_map.pyi +134 -0
  90. modal/partial_function.pyi +4 -1
  91. modal/proxy.py +16 -7
  92. modal/proxy.pyi +10 -2
  93. modal/queue.py +263 -61
  94. modal/queue.pyi +409 -66
  95. modal/runner.py +112 -92
  96. modal/runner.pyi +45 -27
  97. modal/sandbox.py +451 -124
  98. modal/sandbox.pyi +513 -67
  99. modal/secret.py +291 -67
  100. modal/secret.pyi +425 -19
  101. modal/serving.py +7 -11
  102. modal/serving.pyi +7 -8
  103. modal/snapshot.py +11 -8
  104. modal/token_flow.py +4 -4
  105. modal/volume.py +344 -98
  106. modal/volume.pyi +464 -68
  107. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
  108. modal-1.2.3.dev7.dist-info/RECORD +195 -0
  109. modal_docs/mdmd/mdmd.py +11 -1
  110. modal_proto/api.proto +399 -67
  111. modal_proto/api_grpc.py +241 -1
  112. modal_proto/api_pb2.py +1395 -1000
  113. modal_proto/api_pb2.pyi +1239 -79
  114. modal_proto/api_pb2_grpc.py +499 -4
  115. modal_proto/api_pb2_grpc.pyi +162 -14
  116. modal_proto/modal_api_grpc.py +175 -160
  117. modal_proto/sandbox_router.proto +145 -0
  118. modal_proto/sandbox_router_grpc.py +105 -0
  119. modal_proto/sandbox_router_pb2.py +149 -0
  120. modal_proto/sandbox_router_pb2.pyi +333 -0
  121. modal_proto/sandbox_router_pb2_grpc.py +203 -0
  122. modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
  123. modal_proto/task_command_router.proto +144 -0
  124. modal_proto/task_command_router_grpc.py +105 -0
  125. modal_proto/task_command_router_pb2.py +149 -0
  126. modal_proto/task_command_router_pb2.pyi +333 -0
  127. modal_proto/task_command_router_pb2_grpc.py +203 -0
  128. modal_proto/task_command_router_pb2_grpc.pyi +75 -0
  129. modal_version/__init__.py +1 -1
  130. modal-1.0.6.dev58.dist-info/RECORD +0 -183
  131. modal_proto/modal_options_grpc.py +0 -3
  132. modal_proto/options.proto +0 -19
  133. modal_proto/options_grpc.py +0 -3
  134. modal_proto/options_pb2.py +0 -35
  135. modal_proto/options_pb2.pyi +0 -20
  136. modal_proto/options_pb2_grpc.py +0 -4
  137. modal_proto/options_pb2_grpc.pyi +0 -7
  138. /modal/{requirements → builder}/2023.12.312.txt +0 -0
  139. /modal/{requirements → builder}/2023.12.txt +0 -0
  140. /modal/{requirements → builder}/2024.04.txt +0 -0
  141. /modal/{requirements → builder}/2024.10.txt +0 -0
  142. /modal/{requirements → builder}/README.md +0 -0
  143. /modal/{requirements → builder}/base-images.json +0 -0
  144. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
  145. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
  146. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
  147. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/environments.pyi CHANGED
@@ -45,10 +45,8 @@ class _Environment(modal._object._Object):
45
45
 
46
46
  def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
47
47
  @staticmethod
48
- def from_name(name: str, *, create_if_missing: bool = False): ...
49
- @staticmethod
50
- async def lookup(
51
- name: str, client: typing.Optional[modal.client._Client] = None, create_if_missing: bool = False
48
+ def from_name(
49
+ name: str, *, create_if_missing: bool = False, client: typing.Optional[modal.client._Client] = None
52
50
  ): ...
53
51
 
54
52
  class Environment(modal.object.Object):
@@ -60,17 +58,9 @@ class Environment(modal.object.Object):
60
58
 
61
59
  def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
62
60
  @staticmethod
63
- def from_name(name: str, *, create_if_missing: bool = False): ...
64
-
65
- class __lookup_spec(typing_extensions.Protocol):
66
- def __call__(
67
- self, /, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
68
- ): ...
69
- async def aio(
70
- self, /, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
71
- ): ...
72
-
73
- lookup: __lookup_spec
61
+ def from_name(
62
+ name: str, *, create_if_missing: bool = False, client: typing.Optional[modal.client.Client] = None
63
+ ): ...
74
64
 
75
65
  async def _get_environment_cached(name: str, client: modal.client._Client) -> _Environment: ...
76
66
 
modal/exception.py CHANGED
@@ -26,6 +26,10 @@ class Error(Exception):
26
26
  """
27
27
 
28
28
 
29
+ class AlreadyExistsError(Error):
30
+ """Raised when a resource creation conflicts with an existing resource."""
31
+
32
+
29
33
  class RemoteError(Error):
30
34
  """Raised when an error occurs on the Modal server."""
31
35
 
@@ -38,6 +42,10 @@ class SandboxTimeoutError(TimeoutError):
38
42
  """Raised when a Sandbox exceeds its execution duration limit and times out."""
39
43
 
40
44
 
45
+ class ExecTimeoutError(TimeoutError):
46
+ """Raised when a container process exceeds its execution duration limit and times out."""
47
+
48
+
41
49
  class SandboxTerminatedError(Error):
42
50
  """Raised when a Sandbox is terminated for an internal reason."""
43
51
 
@@ -1,5 +1,6 @@
1
1
  # Copyright Modal Labs 2025
2
2
  import os
3
+ import shlex
3
4
  from dataclasses import dataclass
4
5
  from pathlib import Path
5
6
  from typing import Literal, Optional, Union
@@ -12,15 +13,13 @@ from .._object import _get_environment_name
12
13
  from .._partial_function import _clustered
13
14
  from .._runtime.container_io_manager import _ContainerIOManager
14
15
  from .._utils.async_utils import synchronize_api, synchronizer
15
- from .._utils.deprecation import deprecation_warning
16
- from .._utils.grpc_utils import retry_transient_errors
17
16
  from ..app import _App
18
17
  from ..client import _Client
19
- from ..cls import _Cls, _Obj
18
+ from ..cls import _Cls
20
19
  from ..exception import InvalidError
21
20
  from ..image import DockerfileSpec, ImageBuilderVersion, _Image, _ImageRegistryConfig
22
21
  from ..secret import _Secret
23
- from .flash import flash_forward, flash_prometheus_autoscaler # noqa: F401
22
+ from .flash import flash_forward, flash_get_containers, flash_prometheus_autoscaler # noqa: F401
24
23
 
25
24
 
26
25
  def stop_fetching_inputs():
@@ -116,7 +115,7 @@ async def get_app_objects(
116
115
 
117
116
  app = await _App.lookup(app_name, environment_name=environment_name, client=client)
118
117
  req = api_pb2.AppGetLayoutRequest(app_id=app.app_id)
119
- app_layout_resp = await retry_transient_errors(client.stub.AppGetLayout, req)
118
+ app_layout_resp = await client.stub.AppGetLayout(req)
120
119
 
121
120
  app_objects: dict[str, Union[_Function, _Cls]] = {}
122
121
 
@@ -212,47 +211,153 @@ async def raw_registry_image(
212
211
  )
213
212
 
214
213
 
214
+ def _install_cuda_command() -> str:
215
+ """Command to install CUDA Toolkit (nvcc) inside a container."""
216
+ arch = "x86_64" # instruction set architecture for the CPU, all Modal machines are x86_64
217
+ distro = "debian12" # the distribution and version number of our OS (GNU/Linux)
218
+ filename = "cuda-keyring_1.1-1_all.deb" # NVIDIA signing key file
219
+ cuda_keyring_url = f"https://developer.download.nvidia.com/compute/cuda/repos/{distro}/{arch}/{filename}"
220
+
221
+ major, minor = 12, 8
222
+ max_cuda_version = f"{major}-{minor}"
223
+
224
+ return (
225
+ f"wget {cuda_keyring_url} && "
226
+ + f"dpkg -i {filename} && "
227
+ + f"rm -f {filename} && "
228
+ + f"apt-get update && apt-get install -y cuda-nvcc-{max_cuda_version}"
229
+ )
230
+
231
+
232
+ @synchronizer.create_blocking
233
+ async def notebook_base_image(*, python_version: Optional[str] = None, force_build: bool = False) -> _Image:
234
+ """Default image used for Modal notebook kernels, with common libraries.
235
+
236
+ This can be used to bootstrap development workflows quickly. We don't
237
+ recommend using this image for production Modal Functions though, as it may
238
+ change at any time in the future.
239
+ """
240
+ # Include several common packages, as well as kernelshim dependencies (except 'modal').
241
+ # These packages aren't pinned, so they may change over time with builds.
242
+ #
243
+ # We plan to use `--exclude-newer` in the future, with date-specific image builds.
244
+ base_image = _Image.debian_slim(python_version=python_version)
245
+
246
+ environment_packages: list[str] = [
247
+ "accelerate",
248
+ "aiohttp",
249
+ "altair",
250
+ "anthropic",
251
+ "asyncpg",
252
+ "beautifulsoup4",
253
+ "bokeh",
254
+ "boto3[crt]",
255
+ "click",
256
+ "diffusers[torch,flax]",
257
+ "dm-sonnet",
258
+ "flax",
259
+ "ftfy",
260
+ "h5py",
261
+ "urllib3",
262
+ "httpx",
263
+ "huggingface-hub",
264
+ "ipywidgets",
265
+ "jax[cuda12]",
266
+ "keras",
267
+ "matplotlib",
268
+ "nbformat",
269
+ "numba",
270
+ "numpy",
271
+ "openai",
272
+ "optax",
273
+ "pandas",
274
+ "plotly[express]",
275
+ "polars",
276
+ "psycopg2",
277
+ "requests",
278
+ "safetensors",
279
+ "scikit-image",
280
+ "scikit-learn",
281
+ "scipy",
282
+ "seaborn",
283
+ "sentencepiece",
284
+ "sqlalchemy",
285
+ "statsmodels",
286
+ "sympy",
287
+ "tabulate",
288
+ "tensorboard",
289
+ "toml",
290
+ "transformers",
291
+ "triton",
292
+ "typer",
293
+ "vega-datasets",
294
+ "watchfiles",
295
+ "websockets",
296
+ ]
297
+
298
+ # Kernelshim dependencies. (see NOTEBOOK_KERNELSHIM_DEPENDENCIES)
299
+ kernelshim_packages: list[str] = [
300
+ "authlib>=1.3",
301
+ "basedpyright>=1.28",
302
+ "fastapi>=0.100",
303
+ "ipykernel>=6",
304
+ "pydantic>=2",
305
+ "pyzmq>=26",
306
+ "ruff>=0.11",
307
+ "uvicorn>=0.32",
308
+ ]
309
+
310
+ commands: list[str] = [
311
+ "apt-get update",
312
+ "apt-get install -y "
313
+ + "libpq-dev pkg-config cmake git curl wget unzip zip libsqlite3-dev openssh-server vim ffmpeg",
314
+ _install_cuda_command(),
315
+ # Install uv since it's faster than pip for installing packages.
316
+ "pip install uv",
317
+ # https://github.com/astral-sh/uv/issues/11480
318
+ "pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu129",
319
+ f"uv pip install --system {shlex.join(sorted(environment_packages))}",
320
+ f"uv pip install --system {shlex.join(sorted(kernelshim_packages))}",
321
+ ]
322
+
323
+ def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
324
+ return DockerfileSpec(
325
+ commands=[
326
+ "FROM base",
327
+ *(f"RUN {cmd}" for cmd in commands),
328
+ "ENV PATH=/usr/local/cuda/bin:$PATH",
329
+ ],
330
+ context_files={},
331
+ )
332
+
333
+ return _Image._from_args(
334
+ base_images={"base": base_image},
335
+ dockerfile_function=build_dockerfile,
336
+ force_build=force_build,
337
+ _namespace=api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
338
+ )
339
+
340
+
215
341
  @synchronizer.create_blocking
216
- async def update_autoscaler(
217
- obj: Union[_Function, _Obj],
342
+ async def image_delete(
343
+ image_id: str,
218
344
  *,
219
- min_containers: Optional[int] = None,
220
- max_containers: Optional[int] = None,
221
- buffer_containers: Optional[int] = None,
222
- scaledown_window: Optional[int] = None,
223
345
  client: Optional[_Client] = None,
224
346
  ) -> None:
225
- """Update the autoscaler settings for a Function or Obj (instance of a Cls).
347
+ """Delete an Image by its ID.
348
+
349
+ Deletion is irreversible and will prevent Apps from using the Image.
226
350
 
227
351
  This is an experimental interface for a feature that we will be adding to
228
- replace the existing `.keep_warm()` method. The stable form of this interface
229
- may look different (i.e., it may be a standalone function or a method).
352
+ the main Image class. The stable form of this interface may look different.
230
353
 
354
+ Note: When building an Image, each chained method call will create an
355
+ intermediate Image layer, each with its own ID. Deleting an Image will not
356
+ delete any of its intermediate layers, only the image identified by the
357
+ provided ID.
231
358
  """
232
- deprecation_warning(
233
- (2025, 5, 5),
234
- "The modal.experimental.update_autoscaler(...) function is now deprecated in favor of"
235
- " a stable `.update_autoscaler(...) method on the corresponding object.",
236
- show_source=True,
237
- )
238
-
239
- settings = api_pb2.AutoscalerSettings(
240
- min_containers=min_containers,
241
- max_containers=max_containers,
242
- buffer_containers=buffer_containers,
243
- scaledown_window=scaledown_window,
244
- )
245
-
246
359
  if client is None:
247
360
  client = await _Client.from_env()
248
361
 
249
- if isinstance(obj, _Function):
250
- f = obj
251
- else:
252
- assert obj._cls._class_service_function is not None
253
- await obj._cls._class_service_function.hydrate(client=client)
254
- f = obj._cached_service_function()
255
- await f.hydrate(client=client)
256
-
257
- request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=f.object_id, settings=settings)
258
- await retry_transient_errors(client.stub.FunctionUpdateSchedulingParams, request)
362
+ req = api_pb2.ImageDeleteRequest(image_id=image_id)
363
+ await client.stub.ImageDelete(req)