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/parallel_map.pyi CHANGED
@@ -1,9 +1,11 @@
1
1
  import asyncio
2
2
  import asyncio.events
3
+ import asyncio.queues
3
4
  import collections.abc
4
5
  import enum
5
6
  import modal._functions
6
7
  import modal._utils.async_utils
8
+ import modal._utils.grpc_utils
7
9
  import modal.client
8
10
  import modal.functions
9
11
  import modal.retries
@@ -60,6 +62,80 @@ class _OutputValue:
60
62
  """Return self==value."""
61
63
  ...
62
64
 
65
+ class InputPreprocessor:
66
+ """Constructs FunctionPutInputsItem objects from the raw-input queue, and puts them in the processed-input queue."""
67
+ def __init__(
68
+ self,
69
+ client: modal.client._Client,
70
+ *,
71
+ raw_input_queue: _SynchronizedQueue,
72
+ processed_input_queue: asyncio.queues.Queue,
73
+ function: modal._functions._Function,
74
+ created_callback: collections.abc.Callable[[int], None],
75
+ done_callback: collections.abc.Callable[[], None],
76
+ ):
77
+ """Initialize self. See help(type(self)) for accurate signature."""
78
+ ...
79
+
80
+ def input_iter(self): ...
81
+ def create_input_factory(self): ...
82
+ def drain_input_generator(self): ...
83
+
84
+ class InputPumper:
85
+ """Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
86
+ def __init__(
87
+ self,
88
+ client: modal.client._Client,
89
+ *,
90
+ input_queue: asyncio.queues.Queue,
91
+ function: modal._functions._Function,
92
+ function_call_id: str,
93
+ max_batch_size: int,
94
+ map_items_manager: typing.Optional[_MapItemsManager] = None,
95
+ ):
96
+ """Initialize self. See help(type(self)) for accurate signature."""
97
+ ...
98
+
99
+ def pump_inputs(self): ...
100
+ @property
101
+ def _function_inputs_retry(self) -> modal._utils.grpc_utils.Retry: ...
102
+
103
+ class SyncInputPumper(InputPumper):
104
+ """Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
105
+ def __init__(
106
+ self,
107
+ client: modal.client._Client,
108
+ *,
109
+ input_queue: asyncio.queues.Queue,
110
+ retry_queue: modal._utils.async_utils.TimestampPriorityQueue,
111
+ function: modal._functions._Function,
112
+ function_call_jwt: str,
113
+ function_call_id: str,
114
+ map_items_manager: _MapItemsManager,
115
+ ):
116
+ """Initialize self. See help(type(self)) for accurate signature."""
117
+ ...
118
+
119
+ def retry_inputs(self): ...
120
+
121
+ class AsyncInputPumper(InputPumper):
122
+ """Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
123
+ def __init__(
124
+ self,
125
+ client: modal.client._Client,
126
+ *,
127
+ input_queue: asyncio.queues.Queue,
128
+ function: modal._functions._Function,
129
+ function_call_id: str,
130
+ ):
131
+ """Initialize self. See help(type(self)) for accurate signature."""
132
+ ...
133
+
134
+ def pump_inputs(self): ...
135
+
136
+ async def _spawn_map_invocation(
137
+ function: modal._functions._Function, raw_input_queue: _SynchronizedQueue, client: modal.client._Client
138
+ ) -> tuple[str, int]: ...
63
139
  def _map_invocation(
64
140
  function: modal._functions._Function,
65
141
  raw_input_queue: _SynchronizedQueue,
@@ -70,6 +146,23 @@ def _map_invocation(
70
146
  count_update_callback: typing.Optional[collections.abc.Callable[[int, int], None]],
71
147
  function_call_invocation_type: int,
72
148
  ): ...
149
+ def _map_invocation_inputplane(
150
+ function: modal._functions._Function,
151
+ raw_input_queue: _SynchronizedQueue,
152
+ client: modal.client._Client,
153
+ order_outputs: bool,
154
+ return_exceptions: bool,
155
+ wrap_returned_exceptions: bool,
156
+ count_update_callback: typing.Optional[collections.abc.Callable[[int, int], None]],
157
+ ) -> typing.AsyncGenerator[typing.Any, None]:
158
+ """Input-plane implementation of a function map invocation.
159
+
160
+ This is analogous to `_map_invocation`, but instead of the control-plane
161
+ `FunctionMap` / `FunctionPutInputs` / `FunctionGetOutputs` RPCs it speaks
162
+ the input-plane protocol consisting of `MapStartOrContinue`, `MapAwait`, and `MapCheckInputs`.
163
+ """
164
+ ...
165
+
73
166
  def _map_helper(
74
167
  self: modal.functions.Function,
75
168
  async_input_gen: typing.AsyncGenerator[typing.Any, None],
@@ -162,6 +255,33 @@ def _map_sync(
162
255
  """
163
256
  ...
164
257
 
258
+ async def _experimental_spawn_map_async(self, *input_iterators, kwargs={}) -> modal._functions._FunctionCall: ...
259
+ async def _spawn_map_helper(
260
+ self: modal.functions.Function, async_input_gen, kwargs={}
261
+ ) -> modal._functions._FunctionCall: ...
262
+ def _experimental_spawn_map_sync(self, *input_iterators, kwargs={}) -> modal._functions._FunctionCall:
263
+ """mdmd:hidden
264
+ Spawn parallel execution over a set of inputs, returning as soon as the inputs are created.
265
+
266
+ Unlike `modal.Function.map`, this method does not block on completion of the remote execution but
267
+ returns a `modal.FunctionCall` object that can be used to poll status and retrieve results later.
268
+
269
+ Takes one iterator argument per argument in the function being mapped over.
270
+
271
+ Example:
272
+ ```python
273
+ @app.function()
274
+ def my_func(a, b):
275
+ return a ** b
276
+
277
+
278
+ @app.local_entrypoint()
279
+ def main():
280
+ fc = my_func.spawn_map([1, 2], [3, 4])
281
+ ```
282
+ """
283
+ ...
284
+
165
285
  async def _spawn_map_async(self, *input_iterators, kwargs={}) -> None:
166
286
  """This runs in an event loop on the main thread. It consumes inputs from the input iterators and creates async
167
287
  function calls for each.
@@ -260,10 +380,12 @@ class _MapItemContext:
260
380
  input: modal_proto.api_pb2.FunctionInput,
261
381
  retry_manager: modal.retries.RetryManager,
262
382
  sync_client_retries_enabled: bool,
383
+ is_input_plane_instance: bool = False,
263
384
  ):
264
385
  """Initialize self. See help(type(self)) for accurate signature."""
265
386
  ...
266
387
 
388
+ def handle_map_start_or_continue_response(self, attempt_token: str): ...
267
389
  def handle_put_inputs_response(self, item: modal_proto.api_pb2.FunctionPutInputsResponseItem): ...
268
390
  async def handle_get_outputs_response(
269
391
  self,
@@ -279,7 +401,9 @@ class _MapItemContext:
279
401
  ...
280
402
 
281
403
  async def prepare_item_for_retry(self) -> modal_proto.api_pb2.FunctionRetryInputsItem: ...
404
+ def set_retry_policy(self, retry_policy: modal_proto.api_pb2.FunctionRetryPolicy): ...
282
405
  def handle_retry_response(self, input_jwt: str): ...
406
+ async def create_map_start_or_continue_item(self, idx: int) -> modal_proto.api_pb2.MapStartOrContinueItem: ...
283
407
 
284
408
  class _MapItemsManager:
285
409
  def __init__(
@@ -289,22 +413,32 @@ class _MapItemsManager:
289
413
  retry_queue: modal._utils.async_utils.TimestampPriorityQueue,
290
414
  sync_client_retries_enabled: bool,
291
415
  max_inputs_outstanding: int,
416
+ is_input_plane_instance: bool = False,
292
417
  ):
293
418
  """Initialize self. See help(type(self)) for accurate signature."""
294
419
  ...
295
420
 
421
+ def set_retry_policy(self, retry_policy: modal_proto.api_pb2.FunctionRetryPolicy): ...
296
422
  async def add_items(self, items: list[modal_proto.api_pb2.FunctionPutInputsItem]): ...
423
+ async def add_items_inputplane(self, items: list[modal_proto.api_pb2.MapStartOrContinueItem]): ...
297
424
  async def prepare_items_for_retry(
298
425
  self, retriable_idxs: list[int]
299
426
  ) -> list[modal_proto.api_pb2.FunctionRetryInputsItem]: ...
427
+ def update_items_retry_policy(self, retry_policy: modal_proto.api_pb2.FunctionRetryPolicy): ...
300
428
  def get_input_jwts_waiting_for_output(self) -> list[str]:
301
429
  """Returns a list of input_jwts for inputs that are waiting for output."""
302
430
  ...
303
431
 
432
+ def get_input_idxs_waiting_for_output(self) -> list[tuple[int, str]]:
433
+ """Returns a list of input_idxs for inputs that are waiting for output."""
434
+ ...
435
+
304
436
  def _remove_item(self, item_idx: int): ...
305
437
  def get_item_context(self, item_idx: int) -> _MapItemContext: ...
438
+ def handle_put_continue_response(self, items: list[tuple[int, str]]): ...
306
439
  def handle_put_inputs_response(self, items: list[modal_proto.api_pb2.FunctionPutInputsResponseItem]): ...
307
440
  def handle_retry_response(self, input_jwts: list[str]): ...
441
+ async def handle_check_inputs_response(self, response: list[tuple[int, bool]]): ...
308
442
  async def handle_get_outputs_response(
309
443
  self, item: modal_proto.api_pb2.FunctionGetOutputsItem, now_seconds: int
310
444
  ) -> _OutputType: ...
@@ -329,7 +329,10 @@ def batched(
329
329
  ...
330
330
 
331
331
  def concurrent(
332
- _warn_parentheses_missing=None, *, max_inputs: int, target_inputs: typing.Optional[int] = None
332
+ _warn_parentheses_missing=None,
333
+ *,
334
+ max_inputs: typing.Optional[int] = None,
335
+ target_inputs: typing.Optional[int] = None,
333
336
  ) -> collections.abc.Callable[
334
337
  [
335
338
  typing.Union[
modal/proxy.py CHANGED
@@ -3,9 +3,11 @@ from typing import Optional
3
3
 
4
4
  from modal_proto import api_pb2
5
5
 
6
- from ._object import _get_environment_name, _Object
6
+ from ._load_context import LoadContext
7
+ from ._object import _Object
7
8
  from ._resolver import Resolver
8
9
  from ._utils.async_utils import synchronize_api
10
+ from .client import _Client
9
11
 
10
12
 
11
13
  class _Proxy(_Object, type_prefix="pr"):
@@ -20,6 +22,7 @@ class _Proxy(_Object, type_prefix="pr"):
20
22
  name: str,
21
23
  *,
22
24
  environment_name: Optional[str] = None,
25
+ client: Optional[_Client] = None,
23
26
  ) -> "_Proxy":
24
27
  """Reference a Proxy by its name.
25
28
 
@@ -28,15 +31,21 @@ class _Proxy(_Object, type_prefix="pr"):
28
31
 
29
32
  """
30
33
 
31
- async def _load(self: _Proxy, resolver: Resolver, existing_object_id: Optional[str]):
34
+ async def _load(self: _Proxy, resolver: Resolver, load_context: LoadContext, existing_object_id: Optional[str]):
32
35
  req = api_pb2.ProxyGetRequest(
33
36
  name=name,
34
- environment_name=_get_environment_name(environment_name, resolver),
37
+ environment_name=load_context.environment_name,
35
38
  )
36
- response: api_pb2.ProxyGetResponse = await resolver.client.stub.ProxyGet(req)
37
- self._hydrate(response.proxy.proxy_id, resolver.client, None)
38
-
39
- return _Proxy._from_loader(_load, "Proxy()", is_another_app=True)
39
+ response: api_pb2.ProxyGetResponse = await load_context.client.stub.ProxyGet(req)
40
+ self._hydrate(response.proxy.proxy_id, load_context.client, None)
41
+
42
+ rep = _Proxy._repr(name, environment_name)
43
+ return _Proxy._from_loader(
44
+ _load,
45
+ rep,
46
+ is_another_app=True,
47
+ load_context_overrides=LoadContext(client=client, environment_name=environment_name),
48
+ )
40
49
 
41
50
 
42
51
  Proxy = synchronize_api(_Proxy, target_module=__name__)
modal/proxy.pyi CHANGED
@@ -1,4 +1,5 @@
1
1
  import modal._object
2
+ import modal.client
2
3
  import modal.object
3
4
  import typing
4
5
 
@@ -9,7 +10,12 @@ class _Proxy(modal._object._Object):
9
10
  a database. See [the guide](https://modal.com/docs/guide/proxy-ips) for more information.
10
11
  """
11
12
  @staticmethod
12
- def from_name(name: str, *, environment_name: typing.Optional[str] = None) -> _Proxy:
13
+ def from_name(
14
+ name: str,
15
+ *,
16
+ environment_name: typing.Optional[str] = None,
17
+ client: typing.Optional[modal.client._Client] = None,
18
+ ) -> _Proxy:
13
19
  """Reference a Proxy by its name.
14
20
 
15
21
  In contrast to most other Modal objects, new Proxy objects must be
@@ -28,7 +34,9 @@ class Proxy(modal.object.Object):
28
34
  ...
29
35
 
30
36
  @staticmethod
31
- def from_name(name: str, *, environment_name: typing.Optional[str] = None) -> Proxy:
37
+ def from_name(
38
+ name: str, *, environment_name: typing.Optional[str] = None, client: typing.Optional[modal.client.Client] = None
39
+ ) -> Proxy:
32
40
  """Reference a Proxy by its name.
33
41
 
34
42
  In contrast to most other Modal objects, new Proxy objects must be