modal 0.74.47__py3-none-any.whl → 0.74.49__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.
modal/_functions.py CHANGED
@@ -55,7 +55,7 @@ from ._utils.function_utils import (
55
55
  get_include_source_mode,
56
56
  is_async,
57
57
  )
58
- from ._utils.grpc_utils import retry_transient_errors
58
+ from ._utils.grpc_utils import RetryWarningMessage, retry_transient_errors
59
59
  from ._utils.mount_utils import validate_network_file_systems, validate_volumes
60
60
  from .call_graph import InputInfo, _reconstruct_call_graph
61
61
  from .client import _Client
@@ -80,6 +80,8 @@ from .parallel_map import (
80
80
  _map_async,
81
81
  _map_invocation,
82
82
  _map_sync,
83
+ _spawn_map_async,
84
+ _spawn_map_sync,
83
85
  _starmap_async,
84
86
  _starmap_sync,
85
87
  _SynchronizedQueue,
@@ -134,6 +136,7 @@ class _Invocation:
134
136
  *,
135
137
  client: _Client,
136
138
  function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType",
139
+ from_spawn_map: bool = False,
137
140
  ) -> "_Invocation":
138
141
  assert client.stub
139
142
  function_id = function.object_id
@@ -146,9 +149,26 @@ class _Invocation:
146
149
  pipelined_inputs=[item],
147
150
  function_call_invocation_type=function_call_invocation_type,
148
151
  )
149
- response = await retry_transient_errors(client.stub.FunctionMap, request)
150
- function_call_id = response.function_call_id
151
152
 
153
+ if from_spawn_map:
154
+ request.from_spawn_map = True
155
+ response = await retry_transient_errors(
156
+ client.stub.FunctionMap,
157
+ request,
158
+ max_retries=None,
159
+ max_delay=30.0,
160
+ retry_warning_message=RetryWarningMessage(
161
+ message="Warning: `.spawn_map(...)` for function `{self._function_name}` is waiting to create"
162
+ "more function calls. This may be due to hitting rate limits or function backlog limits.",
163
+ warning_interval=10,
164
+ errors_to_warn_for=[Status.RESOURCE_EXHAUSTED],
165
+ ),
166
+ additional_status_codes=[Status.RESOURCE_EXHAUSTED],
167
+ )
168
+ else:
169
+ response = await retry_transient_errors(client.stub.FunctionMap, request)
170
+
171
+ function_call_id = response.function_call_id
152
172
  if response.pipelined_inputs:
153
173
  assert len(response.pipelined_inputs) == 1
154
174
  input = response.pipelined_inputs[0]
@@ -1396,10 +1416,19 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1396
1416
  return await invocation.run_function()
1397
1417
 
1398
1418
  async def _call_function_nowait(
1399
- self, args, kwargs, function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType"
1419
+ self,
1420
+ args,
1421
+ kwargs,
1422
+ function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType",
1423
+ from_spawn_map: bool = False,
1400
1424
  ) -> _Invocation:
1401
1425
  return await _Invocation.create(
1402
- self, args, kwargs, client=self.client, function_call_invocation_type=function_call_invocation_type
1426
+ self,
1427
+ args,
1428
+ kwargs,
1429
+ client=self.client,
1430
+ function_call_invocation_type=function_call_invocation_type,
1431
+ from_spawn_map=from_spawn_map,
1403
1432
  )
1404
1433
 
1405
1434
  @warn_if_generator_is_not_consumed()
@@ -1603,6 +1632,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1603
1632
  map = MethodWithAio(_map_sync, _map_async, synchronizer)
1604
1633
  starmap = MethodWithAio(_starmap_sync, _starmap_async, synchronizer)
1605
1634
  for_each = MethodWithAio(_for_each_sync, _for_each_async, synchronizer)
1635
+ spawn_map = MethodWithAio(_spawn_map_sync, _spawn_map_async, synchronizer)
1606
1636
 
1607
1637
 
1608
1638
  class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
@@ -432,6 +432,19 @@ def warn_if_generator_is_not_consumed(function_name: Optional[str] = None):
432
432
  return decorator
433
433
 
434
434
 
435
+ def run_coroutine_in_temporary_event_loop(coro: typing.Coroutine[None, None, T], nested_async_message: str) -> T:
436
+ """Compatibility function to run an async coroutine in a temporary event loop.
437
+
438
+ This is needed for compatibility with the async implementation of Function.spawn_map. The future plan is
439
+ to have separate implementations so there is no issue with nested event loops.
440
+ """
441
+ try:
442
+ with Runner() as runner:
443
+ return runner.run(coro)
444
+ except NestedEventLoops:
445
+ raise InvalidError(nested_async_message)
446
+
447
+
435
448
  class AsyncOrSyncIterable:
436
449
  """Compatibility class for non-synchronicity wrapped async iterables to get
437
450
  both async and sync interfaces in the same way that synchronicity does (but on the main thread)
modal/_utils/logger.py CHANGED
@@ -4,33 +4,42 @@ import os
4
4
 
5
5
 
6
6
  def configure_logger(logger: logging.Logger, log_level: str, log_format: str):
7
+ from modal.config import config
8
+
7
9
  ch = logging.StreamHandler()
8
10
  log_level_numeric = logging.getLevelName(log_level.upper())
9
11
  logger.setLevel(log_level_numeric)
10
12
  ch.setLevel(log_level_numeric)
11
-
13
+ datefmt = "%Y-%m-%dT%H:%M:%S%z"
12
14
  if log_format.upper() == "JSON":
13
15
  # This is primarily for modal internal use.
14
16
  # pythonjsonlogger is already installed in the environment.
15
17
  from pythonjsonlogger import jsonlogger
16
18
 
17
- json_formatter = jsonlogger.JsonFormatter(
18
- fmt=(
19
+ if not (log_format_pattern := config.get("log_pattern")):
20
+ log_format_pattern = (
19
21
  "%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] "
20
22
  "[dd.service=%(dd.service)s dd.env=%(dd.env)s dd.version=%(dd.version)s dd.trace_id=%(dd.trace_id)s "
21
23
  "dd.span_id=%(dd.span_id)s] "
22
24
  "- %(message)s"
23
- ),
24
- datefmt="%Y-%m-%dT%H:%M:%S%z",
25
- )
25
+ )
26
26
 
27
+ json_formatter = jsonlogger.JsonFormatter(
28
+ fmt=log_format_pattern,
29
+ datefmt=datefmt,
30
+ )
27
31
  ch.setFormatter(json_formatter)
28
32
  else:
29
- ch.setFormatter(logging.Formatter("[%(threadName)s] %(asctime)s %(message)s", datefmt="%Y-%m-%dT%H:%M:%S%z"))
33
+ if not (log_format_pattern := config.get("log_pattern")):
34
+ # TODO: use `%(name)s` instead of `modal-client` as soon as we unify the loggers we use
35
+ log_format_pattern = "[modal-client] %(asctime)s %(message)s"
36
+
37
+ ch.setFormatter(logging.Formatter(log_format_pattern, datefmt=datefmt))
30
38
 
31
39
  logger.addHandler(ch)
32
40
 
33
41
 
42
+ # TODO: remove this distinct logger in favor of the one in modal.config?
34
43
  log_level = os.environ.get("MODAL_LOGLEVEL", "WARNING")
35
44
  log_format = os.environ.get("MODAL_LOG_FORMAT", "STRING")
36
45
 
modal/client.pyi CHANGED
@@ -27,7 +27,7 @@ class _Client:
27
27
  _snapshotted: bool
28
28
 
29
29
  def __init__(
30
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.47"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.49"
31
31
  ): ...
32
32
  def is_closed(self) -> bool: ...
33
33
  @property
@@ -85,7 +85,7 @@ class Client:
85
85
  _snapshotted: bool
86
86
 
87
87
  def __init__(
88
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.47"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.49"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
modal/config.py CHANGED
@@ -70,6 +70,11 @@ Other possible configuration options are:
70
70
  * `traceback` (in the .toml file) / `MODAL_TRACEBACK` (as an env var).
71
71
  Defaults to False. Enables printing full tracebacks on unexpected CLI
72
72
  errors, which can be useful for debugging client issues.
73
+ * `log_pattern` (in the .toml file) / MODAL_LOG_PATTERN` (as an env var).
74
+ Defaults to "[modal-client] %(asctime)s %(message)s"
75
+ The log formatting pattern that will be used by the modal client itself.
76
+ See https://docs.python.org/3/library/logging.html#logrecord-attributes for available
77
+ log attributes.
73
78
 
74
79
  Meta-configuration
75
80
  ------------------
@@ -216,6 +221,7 @@ class _Setting(typing.NamedTuple):
216
221
  _SETTINGS = {
217
222
  "loglevel": _Setting("WARNING", lambda s: s.upper()),
218
223
  "log_format": _Setting("STRING", lambda s: s.upper()),
224
+ "log_pattern": _Setting(), # optional override of the formatting pattern
219
225
  "server_url": _Setting("https://api.modal.com"),
220
226
  "token_id": _Setting(),
221
227
  "token_secret": _Setting(),
modal/functions.pyi CHANGED
@@ -201,8 +201,12 @@ class Function(
201
201
  _call_function: ___call_function_spec[modal._functions.ReturnType, typing_extensions.Self]
202
202
 
203
203
  class ___call_function_nowait_spec(typing_extensions.Protocol[SUPERSELF]):
204
- def __call__(self, args, kwargs, function_call_invocation_type: int) -> modal._functions._Invocation: ...
205
- async def aio(self, args, kwargs, function_call_invocation_type: int) -> modal._functions._Invocation: ...
204
+ def __call__(
205
+ self, args, kwargs, function_call_invocation_type: int, from_spawn_map: bool = False
206
+ ) -> modal._functions._Invocation: ...
207
+ async def aio(
208
+ self, args, kwargs, function_call_invocation_type: int, from_spawn_map: bool = False
209
+ ) -> modal._functions._Invocation: ...
206
210
 
207
211
  _call_function_nowait: ___call_function_nowait_spec[typing_extensions.Self]
208
212
 
@@ -218,11 +222,11 @@ class Function(
218
222
 
219
223
  _call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
220
224
 
221
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
225
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
222
226
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
223
227
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
224
228
 
225
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
229
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
226
230
 
227
231
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
228
232
  def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -237,19 +241,19 @@ class Function(
237
241
  self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
238
242
  ) -> modal._functions.OriginalReturnType: ...
239
243
 
240
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
244
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
241
245
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
242
246
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
243
247
 
244
248
  _experimental_spawn: ___experimental_spawn_spec[
245
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
249
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
246
250
  ]
247
251
 
248
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
252
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
249
253
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
250
254
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
251
255
 
252
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
256
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
253
257
 
254
258
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
255
259
 
@@ -307,6 +311,12 @@ class Function(
307
311
 
308
312
  for_each: __for_each_spec[typing_extensions.Self]
309
313
 
314
+ class __spawn_map_spec(typing_extensions.Protocol[SUPERSELF]):
315
+ def __call__(self, *input_iterators, kwargs={}) -> None: ...
316
+ async def aio(self, *input_iterators, kwargs={}) -> None: ...
317
+
318
+ spawn_map: __spawn_map_spec[typing_extensions.Self]
319
+
310
320
  class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Object):
311
321
  _is_generator: bool
312
322
 
modal/parallel_map.py CHANGED
@@ -13,10 +13,12 @@ from modal._utils.async_utils import (
13
13
  AsyncOrSyncIterable,
14
14
  TimestampPriorityQueue,
15
15
  aclosing,
16
+ async_map,
16
17
  async_map_ordered,
17
18
  async_merge,
18
19
  async_zip,
19
20
  queue_batch_iterator,
21
+ run_coroutine_in_temporary_event_loop,
20
22
  sync_or_async_iter,
21
23
  synchronize_api,
22
24
  synchronizer,
@@ -480,8 +482,60 @@ async def _map_async(
480
482
  yield output
481
483
 
482
484
 
485
+ async def _spawn_map_async(self, *input_iterators, kwargs={}) -> None:
486
+ """mdmd:hidden
487
+ This runs in an event loop on the main thread. It consumes inputs from the input iterators and creates async
488
+ function calls for each.
489
+ """
490
+
491
+ def _call_with_args(args):
492
+ """
493
+ Returns co-routine that invokes a function with the given arguments.
494
+
495
+ On RESOURCE_EXHAUSTED, it will retry indefinitely with exponential backoff up to 30 seconds. Every 10 retriable
496
+ errors, log a warning that the function call is waiting to be created.
497
+ """
498
+
499
+ return self._call_function_nowait.aio(
500
+ args, kwargs, api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC, from_spawn_map=True
501
+ )
502
+
503
+ input_gen = async_zip(*[sync_or_async_iter(it) for it in input_iterators])
504
+
505
+ # TODO(gongy): Can improve this by creating async_foreach method which foregoes async_merge.
506
+ async for _ in async_map(input_gen, _call_with_args, concurrency=256):
507
+ pass
508
+
509
+
510
+ def _spawn_map_sync(self, *input_iterators, kwargs={}) -> None:
511
+ """Spawn parallel execution over a set of inputs, exiting as soon as the inputs are created (without waiting
512
+ for the map to complete).
513
+
514
+ Takes one iterator argument per argument in the function being mapped over.
515
+
516
+ Example:
517
+ ```python
518
+ @app.function()
519
+ def my_func(a):
520
+ return a ** 2
521
+
522
+
523
+ @app.local_entrypoint()
524
+ def main():
525
+ my_func.spawn_map([1, 2, 3, 4])
526
+ ```
527
+
528
+ Programmatic retrieval of results will be supported in a future update.
529
+ """
530
+
531
+ return run_coroutine_in_temporary_event_loop(
532
+ _spawn_map_async(self, *input_iterators, kwargs=kwargs),
533
+ "You can't run Function.spawn_map() from an async function. Use Function.map.aio() instead.",
534
+ )
535
+
536
+
483
537
  def _for_each_sync(self, *input_iterators, kwargs={}, ignore_exceptions: bool = False):
484
- """Execute function for all inputs, ignoring outputs.
538
+ """Execute function for all inputs, ignoring outputs. Waits for completion of the inputs.
485
539
 
486
540
  Convenient alias for `.map()` in cases where the function just needs to be called.
487
541
  as the caller doesn't have to consume the generator to process the inputs.
modal/parallel_map.pyi CHANGED
@@ -65,6 +65,8 @@ def _map_async(
65
65
  order_outputs: bool = True,
66
66
  return_exceptions: bool = False,
67
67
  ) -> typing.AsyncGenerator[typing.Any, None]: ...
68
+ async def _spawn_map_async(self, *input_iterators, kwargs={}) -> None: ...
69
+ def _spawn_map_sync(self, *input_iterators, kwargs={}) -> None: ...
68
70
  def _for_each_sync(self, *input_iterators, kwargs={}, ignore_exceptions: bool = False): ...
69
71
  async def _for_each_async(self, *input_iterators, kwargs={}, ignore_exceptions: bool = False): ...
70
72
  def _starmap_async(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.47
3
+ Version: 0.74.49
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -3,7 +3,7 @@ modal/__main__.py,sha256=sTJcc9EbDuCKSwg3tL6ZckFw9WWdlkXW8mId1IvJCNc,2846
3
3
  modal/_clustered_functions.py,sha256=kTf-9YBXY88NutC1akI-gCbvf01RhMPCw-zoOI_YIUE,2700
4
4
  modal/_clustered_functions.pyi,sha256=vllkegc99A0jrUOWa8mdlSbdp6uz36TsHhGxysAOpaQ,771
5
5
  modal/_container_entrypoint.py,sha256=2Zx9O_EMJg0H77EdnC2vGKs6uFMWwbP1NLFf-qYmWmU,28962
6
- modal/_functions.py,sha256=HJwXHoIOP950GUhTeu1g3PUK70ioIf4-ZbUG5DxuQZg,76157
6
+ modal/_functions.py,sha256=3Nx7snIerJEqcyCRGMTr3MReWklMpURItVz-o5qX7tE,77262
7
7
  modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
8
8
  modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
9
9
  modal/_object.py,sha256=6ve4sI2nRAnjPCuAXdSoUplaXfzC9MqRlF_ZLULwwy0,11472
@@ -22,12 +22,12 @@ modal/app.py,sha256=r-9vVU1lrR1CWtJEo60fuaianvxY_oOXZyv1Qx1DEkI,51231
22
22
  modal/app.pyi,sha256=0QNtnUpAFbOPcbwCt119ge7OmoBqMFw5SajLgdE5eOw,28600
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=U-YKSw0n7J1ZLREt9cbEJCtmHe5YoPKFxl0xlkan2yc,15565
25
- modal/client.pyi,sha256=tj9p_GtxYsMo506k549rJbF0Q0iu8wrymsYU4WUDz6Y,7593
25
+ modal/client.pyi,sha256=aMgJftZr5pTStBb0n4tz8sgLrlKIaRZdrBMMEh9rFVg,7593
26
26
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
27
27
  modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
28
28
  modal/cls.py,sha256=aHoMEWMZUN7bOezs3tRPxzS1FP3gTxZBORVjbPmtxyg,35338
29
29
  modal/cls.pyi,sha256=B--Y4xEOo3GRE3QiiFdIE8jnIKEeBcOtwAbXvg2Z8H4,12012
30
- modal/config.py,sha256=nKlX60bC1O-qAEsbGq-efRX1q25h13RyVnoM_0bnhSw,12229
30
+ modal/config.py,sha256=_2KU5jG-vZQ4vDLYQ83r4aQdnjnN00TY12DtSJMGYsA,12617
31
31
  modal/container_process.py,sha256=vvyK3DVPUMsuqvkKdUiQ49cDLF9JawGrxpglLk5vfgI,6208
32
32
  modal/container_process.pyi,sha256=bXs2KHe7nxVuLAm6RRBqXCvDKelANGX9gFY8qIuZYDs,2898
33
33
  modal/dict.py,sha256=7NJVI05hisF9gTuJMYestH9X0LIOaE9PPqbCeYtwSRs,13365
@@ -39,7 +39,7 @@ modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
39
39
  modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
40
40
  modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
41
41
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
42
- modal/functions.pyi,sha256=ol1tE9OAmXn9f3UwXg-q2LuYN3t_mxjsz1k-QCCo-_A,15642
42
+ modal/functions.pyi,sha256=N0vF8PfDKQLsWEKjtr0oWU6Ls6a07hj_1G8Bj2grdvk,16010
43
43
  modal/gpu.py,sha256=Kbhs_u49FaC2Zi0TjCdrpstpRtT5eZgecynmQi5IZVE,6752
44
44
  modal/image.py,sha256=ZCghS6l1O7pezXcdMHk6RoJpW3qWszfWGJTW38lNXaU,92797
45
45
  modal/image.pyi,sha256=ddbegF532pDLiVANOJdtJiYoDbbF3mAFrsCiyvIu7jU,25632
@@ -52,8 +52,8 @@ modal/network_file_system.pyi,sha256=C_ZiXmpdkTObapVhAPlRmB4ofmM2D7SdKlUCZVg-1IQ
52
52
  modal/object.py,sha256=bTeskuY8JFrESjU4_UL_nTwYlBQdOLmVaOX3X6EMxsg,164
53
53
  modal/object.pyi,sha256=kyJkRQcVv3ct7zSAxvvXcuhBVeH914v80uSlqeS7cA4,5632
54
54
  modal/output.py,sha256=q4T9uHduunj4NwY-YSwkHGgjZlCXMuJbfQ5UFaAGRAc,1968
55
- modal/parallel_map.py,sha256=AriAKT1i4kn45pFW8vAC11teSsuTx3ugIkjPHn97Bfk,33870
56
- modal/parallel_map.pyi,sha256=I8a3W57-3arsTIUYrW39B3INBrnrY99MLMSBco5whoY,5722
55
+ modal/parallel_map.py,sha256=UAqwhlsMQzhVtc9b2xzwH0__T3auN5tPX0h8a9ZHIMA,35737
56
+ modal/parallel_map.pyi,sha256=bLh_D57e5KOIkmP4WW-C_rMSzfxRUNfdtpNJKj4jWA4,5865
57
57
  modal/partial_function.py,sha256=SwuAAj2wj4SO6F6nkSnwNZrczEmm9w9YdlQTHh6hr04,1195
58
58
  modal/partial_function.pyi,sha256=NFWz1aCAs2B3-GnPf1cTatWRZOLnYpFKCnjP_X9iNRs,6411
59
59
  modal/proxy.py,sha256=XEjIHzZvbD3UW4YWyDzbDuNFq6hDUxyPPxupl2qwULY,1429
@@ -91,7 +91,7 @@ modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5
91
91
  modal/_runtime/user_code_imports.py,sha256=78wJyleqY2RVibqcpbDQyfWVBVT9BjyHPeoV9WdwV5Y,17720
92
92
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
93
93
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
94
- modal/_utils/async_utils.py,sha256=b2TJyKY1Hq7df7M-fo3qlFM95mGdo3dCuqRPPcV5hsE,27445
94
+ modal/_utils/async_utils.py,sha256=zjdtdA54zvNL_RuREmN5NWFhhiRcNh8z0jT2rBc5RgY,28001
95
95
  modal/_utils/blob_utils.py,sha256=IexC2Jbtqp_Tkmy62ayfgzTYte0UPCNufB_v-DO21g8,18585
96
96
  modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
97
97
  modal/_utils/deprecation.py,sha256=EXP1beU4pmEqEzWMLw6E3kUfNfpmNA_VOp6i0EHi93g,4856
@@ -103,7 +103,7 @@ modal/_utils/grpc_utils.py,sha256=VmVHEFxEWXNLrgYwLk0vFwwls8g97Pg6B4RGz_5RA1w,92
103
103
  modal/_utils/hash_utils.py,sha256=zg3J6OGxTFGSFri1qQ12giDz90lWk8bzaxCTUCRtiX4,3034
104
104
  modal/_utils/http_utils.py,sha256=yeTFsXYr0rYMEhB7vBP7audG9Uc7OLhzKBANFDZWVt0,2451
105
105
  modal/_utils/jwt_utils.py,sha256=fxH9plyrbAemTbjSsQtzIdDXE9QXxvMC4DiUZ16G0aA,1360
106
- modal/_utils/logger.py,sha256=ePzdudrtx9jJCjuO6-bcL_kwUJfi4AwloUmIiNtqkY0,1330
106
+ modal/_utils/logger.py,sha256=NgbMKFT9chYYt_TU01DdIior5ByYr2gZtrWIk1SFRLc,1782
107
107
  modal/_utils/mount_utils.py,sha256=gGCgIlWwYiJbUtgFY2GJcWYismYvazbMAeUOgf7NhFQ,3205
108
108
  modal/_utils/name_utils.py,sha256=TW1iyJedvDNPEJ5UVp93u8xuD5J2gQL_CUt1mgov_aI,1939
109
109
  modal/_utils/package_utils.py,sha256=LcL2olGN4xaUzu2Tbv-C-Ft9Qp6bsLxEfETOAVd-mjU,2073
@@ -145,7 +145,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
145
145
  modal/requirements/PREVIEW.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
146
146
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
147
147
  modal/requirements/base-images.json,sha256=57vMSqzMbLBxw5tFWSaMiIkkVEps4JfX5PAtXGnkS4U,740
148
- modal-0.74.47.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
148
+ modal-0.74.49.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
149
149
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
150
150
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
151
151
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -153,10 +153,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
153
153
  modal_docs/mdmd/mdmd.py,sha256=Irx49MCCTlBOP4FBdLR--JrpA3-WhsVeriq0LGgsRic,6232
154
154
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
155
155
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
156
- modal_proto/api.proto,sha256=PsfT4IqkQdVqsF6o_S5mM9Yb1JpniqcRkCzgcn3h16c,94226
156
+ modal_proto/api.proto,sha256=r1eiUMvVau4QqFmHrZD5nfQ67d-enn3O83HtGndkuos,94253
157
157
  modal_proto/api_grpc.py,sha256=kTSfs9yI_YtxkPuATpSQ1_Nvd7TtjK03H61RupX1bpM,114421
158
- modal_proto/api_pb2.py,sha256=eckMxlyef8RCBDGPqFl4W7Q7pkvHnU0qF3tN9i_5z-M,332381
159
- modal_proto/api_pb2.pyi,sha256=4siyIFPPqBL-JgfTfZkImKGA-A98-T7WRQvNeMexKdM,451890
158
+ modal_proto/api_pb2.py,sha256=xJ2zpxvjBC3RI1_GXVcin9upB3SpkN5lh2xIZbvZU9Y,332430
159
+ modal_proto/api_pb2.pyi,sha256=HZ2gYP9m94RcGz-cjWfusEkCq05Z5oyzPB1SvE3yE0Q,452052
160
160
  modal_proto/api_pb2_grpc.py,sha256=-KPQMzXmTYwgF23_tGpODCVK79iOdV1sRsw5mN-byMw,247448
161
161
  modal_proto/api_pb2_grpc.pyi,sha256=ls1qcby7goTrlE6BluSWpo73cW_ajvh3rOe41azMBWM,57929
162
162
  modal_proto/modal_api_grpc.py,sha256=X-sgFt2CYE5cahn41gc0oDNfK0sWIOs7AlmHUWKMB1k,15234
@@ -170,9 +170,9 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
170
170
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
171
  modal_version/__init__.py,sha256=m94xZNWIjH8oUtJk4l9xfovzDJede2o7X-q0MHVECtM,470
172
172
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
173
- modal_version/_version_generated.py,sha256=zrcmBl0ErwwtiUv3SCR_M-MBsRZUeqEdSX7FGGaIOfo,149
174
- modal-0.74.47.dist-info/METADATA,sha256=aEQmTDRjIXTMwMID0L7j2KSj1B7tb31Mm8KiLQC9p34,2451
175
- modal-0.74.47.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
- modal-0.74.47.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
- modal-0.74.47.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
- modal-0.74.47.dist-info/RECORD,,
173
+ modal_version/_version_generated.py,sha256=-h2evheb0h6ZeCRd4OY4P-xlD3f4lAxmMFdV25T_ZpM,149
174
+ modal-0.74.49.dist-info/METADATA,sha256=WVM_5pHEfZr-wAqhPek3gvwIZMAWoKWpdeUjIw-Bveo,2451
175
+ modal-0.74.49.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
+ modal-0.74.49.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
+ modal-0.74.49.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
+ modal-0.74.49.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -1673,6 +1673,7 @@ message FunctionMapRequest {
1673
1673
  FunctionCallType function_call_type = 4;
1674
1674
  repeated FunctionPutInputsItem pipelined_inputs = 5;
1675
1675
  FunctionCallInvocationType function_call_invocation_type = 6;
1676
+ bool from_spawn_map = 7;
1676
1677
  }
1677
1678
 
1678
1679
  message FunctionMapResponse {