modal 1.0.3.dev10__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 (160) hide show
  1. modal/__init__.py +0 -2
  2. modal/__main__.py +3 -4
  3. modal/_billing.py +80 -0
  4. modal/_clustered_functions.py +7 -3
  5. modal/_clustered_functions.pyi +15 -3
  6. modal/_container_entrypoint.py +51 -69
  7. modal/_functions.py +508 -240
  8. modal/_grpc_client.py +171 -0
  9. modal/_load_context.py +105 -0
  10. modal/_object.py +81 -21
  11. modal/_output.py +58 -45
  12. modal/_partial_function.py +48 -73
  13. modal/_pty.py +7 -3
  14. modal/_resolver.py +26 -46
  15. modal/_runtime/asgi.py +4 -3
  16. modal/_runtime/container_io_manager.py +358 -220
  17. modal/_runtime/container_io_manager.pyi +296 -101
  18. modal/_runtime/execution_context.py +18 -2
  19. modal/_runtime/execution_context.pyi +64 -7
  20. modal/_runtime/gpu_memory_snapshot.py +262 -57
  21. modal/_runtime/user_code_imports.py +28 -58
  22. modal/_serialization.py +90 -6
  23. modal/_traceback.py +42 -1
  24. modal/_tunnel.pyi +380 -12
  25. modal/_utils/async_utils.py +84 -29
  26. modal/_utils/auth_token_manager.py +111 -0
  27. modal/_utils/blob_utils.py +181 -58
  28. modal/_utils/deprecation.py +19 -0
  29. modal/_utils/function_utils.py +91 -47
  30. modal/_utils/grpc_utils.py +89 -66
  31. modal/_utils/mount_utils.py +26 -1
  32. modal/_utils/name_utils.py +17 -3
  33. modal/_utils/task_command_router_client.py +536 -0
  34. modal/_utils/time_utils.py +34 -6
  35. modal/app.py +256 -88
  36. modal/app.pyi +909 -92
  37. modal/billing.py +5 -0
  38. modal/builder/2025.06.txt +18 -0
  39. modal/builder/PREVIEW.txt +18 -0
  40. modal/builder/base-images.json +58 -0
  41. modal/cli/_download.py +19 -3
  42. modal/cli/_traceback.py +3 -2
  43. modal/cli/app.py +4 -4
  44. modal/cli/cluster.py +15 -7
  45. modal/cli/config.py +5 -3
  46. modal/cli/container.py +7 -6
  47. modal/cli/dict.py +22 -16
  48. modal/cli/entry_point.py +12 -5
  49. modal/cli/environment.py +5 -4
  50. modal/cli/import_refs.py +3 -3
  51. modal/cli/launch.py +102 -5
  52. modal/cli/network_file_system.py +11 -12
  53. modal/cli/profile.py +3 -2
  54. modal/cli/programs/launch_instance_ssh.py +94 -0
  55. modal/cli/programs/run_jupyter.py +1 -1
  56. modal/cli/programs/run_marimo.py +95 -0
  57. modal/cli/programs/vscode.py +1 -1
  58. modal/cli/queues.py +57 -26
  59. modal/cli/run.py +91 -23
  60. modal/cli/secret.py +48 -22
  61. modal/cli/token.py +7 -8
  62. modal/cli/utils.py +4 -7
  63. modal/cli/volume.py +31 -25
  64. modal/client.py +15 -85
  65. modal/client.pyi +183 -62
  66. modal/cloud_bucket_mount.py +5 -3
  67. modal/cloud_bucket_mount.pyi +197 -5
  68. modal/cls.py +200 -126
  69. modal/cls.pyi +446 -68
  70. modal/config.py +29 -11
  71. modal/container_process.py +319 -19
  72. modal/container_process.pyi +190 -20
  73. modal/dict.py +290 -71
  74. modal/dict.pyi +835 -83
  75. modal/environments.py +15 -27
  76. modal/environments.pyi +46 -24
  77. modal/exception.py +14 -2
  78. modal/experimental/__init__.py +194 -40
  79. modal/experimental/flash.py +618 -0
  80. modal/experimental/flash.pyi +380 -0
  81. modal/experimental/ipython.py +11 -7
  82. modal/file_io.py +29 -36
  83. modal/file_io.pyi +251 -53
  84. modal/file_pattern_matcher.py +56 -16
  85. modal/functions.pyi +673 -92
  86. modal/gpu.py +1 -1
  87. modal/image.py +528 -176
  88. modal/image.pyi +1572 -145
  89. modal/io_streams.py +458 -128
  90. modal/io_streams.pyi +433 -52
  91. modal/mount.py +216 -151
  92. modal/mount.pyi +225 -78
  93. modal/network_file_system.py +45 -62
  94. modal/network_file_system.pyi +277 -56
  95. modal/object.pyi +93 -17
  96. modal/parallel_map.py +942 -129
  97. modal/parallel_map.pyi +294 -15
  98. modal/partial_function.py +0 -2
  99. modal/partial_function.pyi +234 -19
  100. modal/proxy.py +17 -8
  101. modal/proxy.pyi +36 -3
  102. modal/queue.py +270 -65
  103. modal/queue.pyi +817 -57
  104. modal/runner.py +115 -101
  105. modal/runner.pyi +205 -49
  106. modal/sandbox.py +512 -136
  107. modal/sandbox.pyi +845 -111
  108. modal/schedule.py +1 -1
  109. modal/secret.py +300 -70
  110. modal/secret.pyi +589 -34
  111. modal/serving.py +7 -11
  112. modal/serving.pyi +7 -8
  113. modal/snapshot.py +11 -8
  114. modal/snapshot.pyi +25 -4
  115. modal/token_flow.py +4 -4
  116. modal/token_flow.pyi +28 -8
  117. modal/volume.py +416 -158
  118. modal/volume.pyi +1117 -121
  119. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +10 -9
  120. modal-1.2.3.dev7.dist-info/RECORD +195 -0
  121. modal_docs/mdmd/mdmd.py +17 -4
  122. modal_proto/api.proto +534 -79
  123. modal_proto/api_grpc.py +337 -1
  124. modal_proto/api_pb2.py +1522 -968
  125. modal_proto/api_pb2.pyi +1619 -134
  126. modal_proto/api_pb2_grpc.py +699 -4
  127. modal_proto/api_pb2_grpc.pyi +226 -14
  128. modal_proto/modal_api_grpc.py +175 -154
  129. modal_proto/sandbox_router.proto +145 -0
  130. modal_proto/sandbox_router_grpc.py +105 -0
  131. modal_proto/sandbox_router_pb2.py +149 -0
  132. modal_proto/sandbox_router_pb2.pyi +333 -0
  133. modal_proto/sandbox_router_pb2_grpc.py +203 -0
  134. modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
  135. modal_proto/task_command_router.proto +144 -0
  136. modal_proto/task_command_router_grpc.py +105 -0
  137. modal_proto/task_command_router_pb2.py +149 -0
  138. modal_proto/task_command_router_pb2.pyi +333 -0
  139. modal_proto/task_command_router_pb2_grpc.py +203 -0
  140. modal_proto/task_command_router_pb2_grpc.pyi +75 -0
  141. modal_version/__init__.py +1 -1
  142. modal/requirements/PREVIEW.txt +0 -16
  143. modal/requirements/base-images.json +0 -26
  144. modal-1.0.3.dev10.dist-info/RECORD +0 -179
  145. modal_proto/modal_options_grpc.py +0 -3
  146. modal_proto/options.proto +0 -19
  147. modal_proto/options_grpc.py +0 -3
  148. modal_proto/options_pb2.py +0 -35
  149. modal_proto/options_pb2.pyi +0 -20
  150. modal_proto/options_pb2_grpc.py +0 -4
  151. modal_proto/options_pb2_grpc.pyi +0 -7
  152. /modal/{requirements → builder}/2023.12.312.txt +0 -0
  153. /modal/{requirements → builder}/2023.12.txt +0 -0
  154. /modal/{requirements → builder}/2024.04.txt +0 -0
  155. /modal/{requirements → builder}/2024.10.txt +0 -0
  156. /modal/{requirements → builder}/README.md +0 -0
  157. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
  158. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
  159. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
  160. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/serving.py CHANGED
@@ -4,13 +4,13 @@ import platform
4
4
  from collections.abc import AsyncGenerator
5
5
  from multiprocessing.context import SpawnProcess
6
6
  from multiprocessing.synchronize import Event
7
- from typing import TYPE_CHECKING, Optional, TypeVar
7
+ from typing import TYPE_CHECKING, Optional
8
8
 
9
9
  from synchronicity.async_wrap import asynccontextmanager
10
10
 
11
11
  from modal._output import OutputManager
12
12
 
13
- from ._utils.async_utils import TaskContext, asyncify, synchronize_api, synchronizer
13
+ from ._utils.async_utils import TaskContext, asyncify, synchronize_api
14
14
  from ._utils.logger import logger
15
15
  from ._watcher import watch
16
16
  from .cli.import_refs import ImportRef, import_app_from_ref
@@ -20,20 +20,16 @@ from .output import _get_output_manager, enable_output
20
20
  from .runner import _run_app, serve_update
21
21
 
22
22
  if TYPE_CHECKING:
23
- from .app import _App
24
- else:
25
- _App = TypeVar("_App")
23
+ import modal.app
26
24
 
27
25
 
28
26
  def _run_serve(
29
27
  import_ref: ImportRef, existing_app_id: str, is_ready: Event, environment_name: str, show_progress: bool
30
28
  ):
31
- # subprocess entrypoint
32
- _app = import_app_from_ref(import_ref, base_cmd="modal serve")
33
- blocking_app = synchronizer._translate_out(_app)
29
+ app = import_app_from_ref(import_ref, base_cmd="modal serve")
34
30
 
35
31
  with enable_output(show_progress=show_progress):
36
- serve_update(blocking_app, existing_app_id, is_ready, environment_name)
32
+ serve_update(app, existing_app_id, is_ready, environment_name)
37
33
 
38
34
 
39
35
  async def _restart_serve(
@@ -97,12 +93,12 @@ async def _run_watch_loop(
97
93
 
98
94
  @asynccontextmanager
99
95
  async def _serve_app(
100
- app: "_App",
96
+ app: "modal.app._App",
101
97
  import_ref: ImportRef,
102
98
  *,
103
99
  _watcher: Optional[AsyncGenerator[set[str], None]] = None, # for testing
104
100
  environment_name: Optional[str] = None,
105
- ) -> AsyncGenerator["_App", None]:
101
+ ) -> AsyncGenerator["modal.app._App", None]:
106
102
  if environment_name is None:
107
103
  environment_name = config.get("environment")
108
104
 
modal/serving.pyi CHANGED
@@ -1,4 +1,5 @@
1
1
  import collections.abc
2
+ import modal.app
2
3
  import modal.cli.import_refs
3
4
  import multiprocessing.context
4
5
  import multiprocessing.synchronize
@@ -6,8 +7,6 @@ import synchronicity.combined_types
6
7
  import typing
7
8
  import typing_extensions
8
9
 
9
- _App = typing.TypeVar("_App")
10
-
11
10
  def _run_serve(
12
11
  import_ref: modal.cli.import_refs.ImportRef,
13
12
  existing_app_id: str,
@@ -27,31 +26,31 @@ async def _run_watch_loop(
27
26
  environment_name: str,
28
27
  ): ...
29
28
  def _serve_app(
30
- app: _App,
29
+ app: modal.app._App,
31
30
  import_ref: modal.cli.import_refs.ImportRef,
32
31
  *,
33
32
  _watcher: typing.Optional[collections.abc.AsyncGenerator[set[str], None]] = None,
34
33
  environment_name: typing.Optional[str] = None,
35
- ) -> typing.AsyncContextManager[_App]: ...
34
+ ) -> typing.AsyncContextManager[modal.app._App]: ...
36
35
 
37
36
  class __serve_app_spec(typing_extensions.Protocol):
38
37
  def __call__(
39
38
  self,
40
39
  /,
41
- app: _App,
40
+ app: modal.app.App,
42
41
  import_ref: modal.cli.import_refs.ImportRef,
43
42
  *,
44
43
  _watcher: typing.Optional[typing.Generator[set[str], None, None]] = None,
45
44
  environment_name: typing.Optional[str] = None,
46
- ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[_App]: ...
45
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[modal.app.App]: ...
47
46
  def aio(
48
47
  self,
49
48
  /,
50
- app: _App,
49
+ app: modal.app.App,
51
50
  import_ref: modal.cli.import_refs.ImportRef,
52
51
  *,
53
52
  _watcher: typing.Optional[collections.abc.AsyncGenerator[set[str], None]] = None,
54
53
  environment_name: typing.Optional[str] = None,
55
- ) -> typing.AsyncContextManager[_App]: ...
54
+ ) -> typing.AsyncContextManager[modal.app.App]: ...
56
55
 
57
56
  serve_app: __serve_app_spec
modal/snapshot.py CHANGED
@@ -3,10 +3,10 @@ from typing import Optional
3
3
 
4
4
  from modal_proto import api_pb2
5
5
 
6
+ from ._load_context import LoadContext
6
7
  from ._object import _Object
7
8
  from ._resolver import Resolver
8
9
  from ._utils.async_utils import synchronize_api
9
- from ._utils.grpc_utils import retry_transient_errors
10
10
  from .client import _Client
11
11
 
12
12
 
@@ -24,16 +24,19 @@ class _SandboxSnapshot(_Object, type_prefix="sn"):
24
24
  """
25
25
  Construct a `SandboxSnapshot` object from a sandbox snapshot ID.
26
26
  """
27
- if client is None:
28
- client = await _Client.from_env()
29
-
30
- async def _load(self: _SandboxSnapshot, resolver: Resolver, existing_object_id: Optional[str]):
31
- await retry_transient_errors(
32
- client.stub.SandboxSnapshotGet, api_pb2.SandboxSnapshotGetRequest(snapshot_id=sandbox_snapshot_id)
27
+ # TODO: remove this - from_id constructor should not do io:
28
+ client = client or await _Client.from_env()
29
+
30
+ async def _load(
31
+ self: _SandboxSnapshot, resolver: Resolver, load_context: LoadContext, existing_object_id: Optional[str]
32
+ ):
33
+ await load_context.client.stub.SandboxSnapshotGet(
34
+ api_pb2.SandboxSnapshotGetRequest(snapshot_id=sandbox_snapshot_id)
33
35
  )
34
36
 
35
37
  rep = "SandboxSnapshot()"
36
- obj = _SandboxSnapshot._from_loader(_load, rep)
38
+ obj = _SandboxSnapshot._from_loader(_load, rep, load_context_overrides=LoadContext(client=client))
39
+ # TODO: should this be a _Object._new_hydrated instead?
37
40
  obj._hydrate(sandbox_snapshot_id, client, None)
38
41
 
39
42
  return obj
modal/snapshot.pyi CHANGED
@@ -5,14 +5,35 @@ import typing
5
5
  import typing_extensions
6
6
 
7
7
  class _SandboxSnapshot(modal._object._Object):
8
+ """> Sandbox memory snapshots are in **early preview**.
9
+
10
+ A `SandboxSnapshot` object lets you interact with a stored Sandbox snapshot that was created by calling
11
+ `._experimental_snapshot()` on a Sandbox instance. This includes both the filesystem and memory state of
12
+ the original Sandbox at the time the snapshot was taken.
13
+ """
8
14
  @staticmethod
9
- async def from_id(sandbox_snapshot_id: str, client: typing.Optional[modal.client._Client] = None): ...
15
+ async def from_id(sandbox_snapshot_id: str, client: typing.Optional[modal.client._Client] = None):
16
+ """Construct a `SandboxSnapshot` object from a sandbox snapshot ID."""
17
+ ...
10
18
 
11
19
  class SandboxSnapshot(modal.object.Object):
12
- def __init__(self, *args, **kwargs): ...
20
+ """> Sandbox memory snapshots are in **early preview**.
21
+
22
+ A `SandboxSnapshot` object lets you interact with a stored Sandbox snapshot that was created by calling
23
+ `._experimental_snapshot()` on a Sandbox instance. This includes both the filesystem and memory state of
24
+ the original Sandbox at the time the snapshot was taken.
25
+ """
26
+ def __init__(self, *args, **kwargs):
27
+ """mdmd:hidden"""
28
+ ...
13
29
 
14
30
  class __from_id_spec(typing_extensions.Protocol):
15
- def __call__(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None): ...
16
- async def aio(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None): ...
31
+ def __call__(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None):
32
+ """Construct a `SandboxSnapshot` object from a sandbox snapshot ID."""
33
+ ...
34
+
35
+ async def aio(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None):
36
+ """Construct a `SandboxSnapshot` object from a sandbox snapshot ID."""
37
+ ...
17
38
 
18
39
  from_id: __from_id_spec
modal/token_flow.py CHANGED
@@ -6,11 +6,11 @@ from collections.abc import AsyncGenerator
6
6
  from typing import Optional
7
7
 
8
8
  import aiohttp.web
9
- from rich.console import Console
10
9
  from synchronicity.async_wrap import asynccontextmanager
11
10
 
12
11
  from modal_proto import api_pb2
13
12
 
13
+ from ._output import make_console
14
14
  from ._utils.async_utils import synchronize_api
15
15
  from ._utils.http_utils import run_temporary_http_server
16
16
  from .client import _Client
@@ -56,7 +56,7 @@ class _TokenFlow:
56
56
  req = api_pb2.TokenFlowWaitRequest(
57
57
  token_flow_id=self.token_flow_id, timeout=timeout, wait_secret=self.wait_secret
58
58
  )
59
- resp = await self.stub.TokenFlowWait(req, timeout=(timeout + grpc_extra_timeout))
59
+ resp = await self.stub.TokenFlowWait(req, retry=None, timeout=timeout + grpc_extra_timeout)
60
60
  if not resp.timeout:
61
61
  return resp
62
62
  else:
@@ -76,7 +76,7 @@ async def _new_token(
76
76
  ):
77
77
  server_url = config.get("server_url", profile=profile)
78
78
 
79
- console = Console()
79
+ console = make_console()
80
80
 
81
81
  result: Optional[api_pb2.TokenFlowWaitResponse] = None
82
82
  async with _Client.anonymous(server_url) as client:
@@ -133,7 +133,7 @@ async def _set_token(
133
133
  ):
134
134
  # TODO add server_url as a parameter for verification?
135
135
  server_url = config.get("server_url", profile=profile)
136
- console = Console()
136
+ console = make_console()
137
137
  if verify:
138
138
  console.print(f"Verifying token against [blue]{server_url}[/blue]")
139
139
  await _Client.verify(server_url, (token_id, token_secret))
modal/token_flow.pyi CHANGED
@@ -5,13 +5,21 @@ import typing
5
5
  import typing_extensions
6
6
 
7
7
  class _TokenFlow:
8
- def __init__(self, client: modal.client._Client): ...
8
+ def __init__(self, client: modal.client._Client):
9
+ """Initialize self. See help(type(self)) for accurate signature."""
10
+ ...
11
+
9
12
  def start(
10
13
  self, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
11
- ) -> typing.AsyncContextManager[tuple[str, str, str]]: ...
14
+ ) -> typing.AsyncContextManager[tuple[str, str, str]]:
15
+ """mdmd:hidden"""
16
+ ...
17
+
12
18
  async def finish(
13
19
  self, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
14
- ) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]: ...
20
+ ) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
21
+ """mdmd:hidden"""
22
+ ...
15
23
 
16
24
  SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
17
25
 
@@ -21,20 +29,30 @@ class TokenFlow:
21
29
  class __start_spec(typing_extensions.Protocol[SUPERSELF]):
22
30
  def __call__(
23
31
  self, /, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
24
- ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[tuple[str, str, str]]: ...
32
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[tuple[str, str, str]]:
33
+ """mdmd:hidden"""
34
+ ...
35
+
25
36
  def aio(
26
37
  self, /, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
27
- ) -> typing.AsyncContextManager[tuple[str, str, str]]: ...
38
+ ) -> typing.AsyncContextManager[tuple[str, str, str]]:
39
+ """mdmd:hidden"""
40
+ ...
28
41
 
29
42
  start: __start_spec[typing_extensions.Self]
30
43
 
31
44
  class __finish_spec(typing_extensions.Protocol[SUPERSELF]):
32
45
  def __call__(
33
46
  self, /, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
34
- ) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]: ...
47
+ ) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
48
+ """mdmd:hidden"""
49
+ ...
50
+
35
51
  async def aio(
36
52
  self, /, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
37
- ) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]: ...
53
+ ) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
54
+ """mdmd:hidden"""
55
+ ...
38
56
 
39
57
  finish: __finish_spec[typing_extensions.Self]
40
58
 
@@ -55,4 +73,6 @@ async def _set_token(
55
73
  verify: bool = True,
56
74
  server_url: typing.Optional[str] = None,
57
75
  ): ...
58
- def _open_url(url: str) -> bool: ...
76
+ def _open_url(url: str) -> bool:
77
+ """Opens url in web browser, making sure we use a modern one (not Lynx etc)"""
78
+ ...