modal 0.73.94__tar.gz → 0.73.96__tar.gz

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 (182) hide show
  1. {modal-0.73.94 → modal-0.73.96}/PKG-INFO +1 -1
  2. {modal-0.73.94 → modal-0.73.96}/modal/_utils/async_utils.py +4 -0
  3. {modal-0.73.94 → modal-0.73.96}/modal/_utils/grpc_utils.py +10 -0
  4. {modal-0.73.94 → modal-0.73.96}/modal/client.pyi +2 -2
  5. {modal-0.73.94 → modal-0.73.96}/modal/parallel_map.py +34 -35
  6. {modal-0.73.94 → modal-0.73.96}/modal/parallel_map.pyi +2 -1
  7. {modal-0.73.94 → modal-0.73.96}/modal.egg-info/PKG-INFO +1 -1
  8. {modal-0.73.94 → modal-0.73.96}/modal_version/_version_generated.py +1 -1
  9. {modal-0.73.94 → modal-0.73.96}/LICENSE +0 -0
  10. {modal-0.73.94 → modal-0.73.96}/README.md +0 -0
  11. {modal-0.73.94 → modal-0.73.96}/modal/__init__.py +0 -0
  12. {modal-0.73.94 → modal-0.73.96}/modal/__main__.py +0 -0
  13. {modal-0.73.94 → modal-0.73.96}/modal/_clustered_functions.py +0 -0
  14. {modal-0.73.94 → modal-0.73.96}/modal/_clustered_functions.pyi +0 -0
  15. {modal-0.73.94 → modal-0.73.96}/modal/_container_entrypoint.py +0 -0
  16. {modal-0.73.94 → modal-0.73.96}/modal/_functions.py +0 -0
  17. {modal-0.73.94 → modal-0.73.96}/modal/_ipython.py +0 -0
  18. {modal-0.73.94 → modal-0.73.96}/modal/_location.py +0 -0
  19. {modal-0.73.94 → modal-0.73.96}/modal/_object.py +0 -0
  20. {modal-0.73.94 → modal-0.73.96}/modal/_output.py +0 -0
  21. {modal-0.73.94 → modal-0.73.96}/modal/_partial_function.py +0 -0
  22. {modal-0.73.94 → modal-0.73.96}/modal/_proxy_tunnel.py +0 -0
  23. {modal-0.73.94 → modal-0.73.96}/modal/_pty.py +0 -0
  24. {modal-0.73.94 → modal-0.73.96}/modal/_resolver.py +0 -0
  25. {modal-0.73.94 → modal-0.73.96}/modal/_resources.py +0 -0
  26. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/__init__.py +0 -0
  27. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/asgi.py +0 -0
  28. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/container_io_manager.py +0 -0
  29. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/container_io_manager.pyi +0 -0
  30. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/execution_context.py +0 -0
  31. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/execution_context.pyi +0 -0
  32. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  33. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/telemetry.py +0 -0
  34. {modal-0.73.94 → modal-0.73.96}/modal/_runtime/user_code_imports.py +0 -0
  35. {modal-0.73.94 → modal-0.73.96}/modal/_serialization.py +0 -0
  36. {modal-0.73.94 → modal-0.73.96}/modal/_traceback.py +0 -0
  37. {modal-0.73.94 → modal-0.73.96}/modal/_tunnel.py +0 -0
  38. {modal-0.73.94 → modal-0.73.96}/modal/_tunnel.pyi +0 -0
  39. {modal-0.73.94 → modal-0.73.96}/modal/_utils/__init__.py +0 -0
  40. {modal-0.73.94 → modal-0.73.96}/modal/_utils/app_utils.py +0 -0
  41. {modal-0.73.94 → modal-0.73.96}/modal/_utils/blob_utils.py +0 -0
  42. {modal-0.73.94 → modal-0.73.96}/modal/_utils/bytes_io_segment_payload.py +0 -0
  43. {modal-0.73.94 → modal-0.73.96}/modal/_utils/deprecation.py +0 -0
  44. {modal-0.73.94 → modal-0.73.96}/modal/_utils/docker_utils.py +0 -0
  45. {modal-0.73.94 → modal-0.73.96}/modal/_utils/function_utils.py +0 -0
  46. {modal-0.73.94 → modal-0.73.96}/modal/_utils/grpc_testing.py +0 -0
  47. {modal-0.73.94 → modal-0.73.96}/modal/_utils/hash_utils.py +0 -0
  48. {modal-0.73.94 → modal-0.73.96}/modal/_utils/http_utils.py +0 -0
  49. {modal-0.73.94 → modal-0.73.96}/modal/_utils/logger.py +0 -0
  50. {modal-0.73.94 → modal-0.73.96}/modal/_utils/mount_utils.py +0 -0
  51. {modal-0.73.94 → modal-0.73.96}/modal/_utils/name_utils.py +0 -0
  52. {modal-0.73.94 → modal-0.73.96}/modal/_utils/package_utils.py +0 -0
  53. {modal-0.73.94 → modal-0.73.96}/modal/_utils/pattern_utils.py +0 -0
  54. {modal-0.73.94 → modal-0.73.96}/modal/_utils/rand_pb_testing.py +0 -0
  55. {modal-0.73.94 → modal-0.73.96}/modal/_utils/shell_utils.py +0 -0
  56. {modal-0.73.94 → modal-0.73.96}/modal/_vendor/__init__.py +0 -0
  57. {modal-0.73.94 → modal-0.73.96}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  58. {modal-0.73.94 → modal-0.73.96}/modal/_vendor/cloudpickle.py +0 -0
  59. {modal-0.73.94 → modal-0.73.96}/modal/_vendor/tblib.py +0 -0
  60. {modal-0.73.94 → modal-0.73.96}/modal/_watcher.py +0 -0
  61. {modal-0.73.94 → modal-0.73.96}/modal/app.py +0 -0
  62. {modal-0.73.94 → modal-0.73.96}/modal/app.pyi +0 -0
  63. {modal-0.73.94 → modal-0.73.96}/modal/call_graph.py +0 -0
  64. {modal-0.73.94 → modal-0.73.96}/modal/cli/__init__.py +0 -0
  65. {modal-0.73.94 → modal-0.73.96}/modal/cli/_download.py +0 -0
  66. {modal-0.73.94 → modal-0.73.96}/modal/cli/_traceback.py +0 -0
  67. {modal-0.73.94 → modal-0.73.96}/modal/cli/app.py +0 -0
  68. {modal-0.73.94 → modal-0.73.96}/modal/cli/config.py +0 -0
  69. {modal-0.73.94 → modal-0.73.96}/modal/cli/container.py +0 -0
  70. {modal-0.73.94 → modal-0.73.96}/modal/cli/dict.py +0 -0
  71. {modal-0.73.94 → modal-0.73.96}/modal/cli/entry_point.py +0 -0
  72. {modal-0.73.94 → modal-0.73.96}/modal/cli/environment.py +0 -0
  73. {modal-0.73.94 → modal-0.73.96}/modal/cli/import_refs.py +0 -0
  74. {modal-0.73.94 → modal-0.73.96}/modal/cli/launch.py +0 -0
  75. {modal-0.73.94 → modal-0.73.96}/modal/cli/network_file_system.py +0 -0
  76. {modal-0.73.94 → modal-0.73.96}/modal/cli/profile.py +0 -0
  77. {modal-0.73.94 → modal-0.73.96}/modal/cli/programs/__init__.py +0 -0
  78. {modal-0.73.94 → modal-0.73.96}/modal/cli/programs/run_jupyter.py +0 -0
  79. {modal-0.73.94 → modal-0.73.96}/modal/cli/programs/vscode.py +0 -0
  80. {modal-0.73.94 → modal-0.73.96}/modal/cli/queues.py +0 -0
  81. {modal-0.73.94 → modal-0.73.96}/modal/cli/run.py +0 -0
  82. {modal-0.73.94 → modal-0.73.96}/modal/cli/secret.py +0 -0
  83. {modal-0.73.94 → modal-0.73.96}/modal/cli/token.py +0 -0
  84. {modal-0.73.94 → modal-0.73.96}/modal/cli/utils.py +0 -0
  85. {modal-0.73.94 → modal-0.73.96}/modal/cli/volume.py +0 -0
  86. {modal-0.73.94 → modal-0.73.96}/modal/client.py +0 -0
  87. {modal-0.73.94 → modal-0.73.96}/modal/cloud_bucket_mount.py +0 -0
  88. {modal-0.73.94 → modal-0.73.96}/modal/cloud_bucket_mount.pyi +0 -0
  89. {modal-0.73.94 → modal-0.73.96}/modal/cls.py +0 -0
  90. {modal-0.73.94 → modal-0.73.96}/modal/cls.pyi +0 -0
  91. {modal-0.73.94 → modal-0.73.96}/modal/config.py +0 -0
  92. {modal-0.73.94 → modal-0.73.96}/modal/container_process.py +0 -0
  93. {modal-0.73.94 → modal-0.73.96}/modal/container_process.pyi +0 -0
  94. {modal-0.73.94 → modal-0.73.96}/modal/dict.py +0 -0
  95. {modal-0.73.94 → modal-0.73.96}/modal/dict.pyi +0 -0
  96. {modal-0.73.94 → modal-0.73.96}/modal/environments.py +0 -0
  97. {modal-0.73.94 → modal-0.73.96}/modal/environments.pyi +0 -0
  98. {modal-0.73.94 → modal-0.73.96}/modal/exception.py +0 -0
  99. {modal-0.73.94 → modal-0.73.96}/modal/experimental.py +0 -0
  100. {modal-0.73.94 → modal-0.73.96}/modal/experimental.pyi +0 -0
  101. {modal-0.73.94 → modal-0.73.96}/modal/extensions/__init__.py +0 -0
  102. {modal-0.73.94 → modal-0.73.96}/modal/extensions/ipython.py +0 -0
  103. {modal-0.73.94 → modal-0.73.96}/modal/file_io.py +0 -0
  104. {modal-0.73.94 → modal-0.73.96}/modal/file_io.pyi +0 -0
  105. {modal-0.73.94 → modal-0.73.96}/modal/file_pattern_matcher.py +0 -0
  106. {modal-0.73.94 → modal-0.73.96}/modal/functions.py +0 -0
  107. {modal-0.73.94 → modal-0.73.96}/modal/functions.pyi +0 -0
  108. {modal-0.73.94 → modal-0.73.96}/modal/gpu.py +0 -0
  109. {modal-0.73.94 → modal-0.73.96}/modal/image.py +0 -0
  110. {modal-0.73.94 → modal-0.73.96}/modal/image.pyi +0 -0
  111. {modal-0.73.94 → modal-0.73.96}/modal/io_streams.py +0 -0
  112. {modal-0.73.94 → modal-0.73.96}/modal/io_streams.pyi +0 -0
  113. {modal-0.73.94 → modal-0.73.96}/modal/mount.py +0 -0
  114. {modal-0.73.94 → modal-0.73.96}/modal/mount.pyi +0 -0
  115. {modal-0.73.94 → modal-0.73.96}/modal/network_file_system.py +0 -0
  116. {modal-0.73.94 → modal-0.73.96}/modal/network_file_system.pyi +0 -0
  117. {modal-0.73.94 → modal-0.73.96}/modal/object.py +0 -0
  118. {modal-0.73.94 → modal-0.73.96}/modal/object.pyi +0 -0
  119. {modal-0.73.94 → modal-0.73.96}/modal/output.py +0 -0
  120. {modal-0.73.94 → modal-0.73.96}/modal/partial_function.py +0 -0
  121. {modal-0.73.94 → modal-0.73.96}/modal/partial_function.pyi +0 -0
  122. {modal-0.73.94 → modal-0.73.96}/modal/proxy.py +0 -0
  123. {modal-0.73.94 → modal-0.73.96}/modal/proxy.pyi +0 -0
  124. {modal-0.73.94 → modal-0.73.96}/modal/py.typed +0 -0
  125. {modal-0.73.94 → modal-0.73.96}/modal/queue.py +0 -0
  126. {modal-0.73.94 → modal-0.73.96}/modal/queue.pyi +0 -0
  127. {modal-0.73.94 → modal-0.73.96}/modal/requirements/2023.12.312.txt +0 -0
  128. {modal-0.73.94 → modal-0.73.96}/modal/requirements/2023.12.txt +0 -0
  129. {modal-0.73.94 → modal-0.73.96}/modal/requirements/2024.04.txt +0 -0
  130. {modal-0.73.94 → modal-0.73.96}/modal/requirements/2024.10.txt +0 -0
  131. {modal-0.73.94 → modal-0.73.96}/modal/requirements/README.md +0 -0
  132. {modal-0.73.94 → modal-0.73.96}/modal/requirements/base-images.json +0 -0
  133. {modal-0.73.94 → modal-0.73.96}/modal/retries.py +0 -0
  134. {modal-0.73.94 → modal-0.73.96}/modal/runner.py +0 -0
  135. {modal-0.73.94 → modal-0.73.96}/modal/runner.pyi +0 -0
  136. {modal-0.73.94 → modal-0.73.96}/modal/running_app.py +0 -0
  137. {modal-0.73.94 → modal-0.73.96}/modal/sandbox.py +0 -0
  138. {modal-0.73.94 → modal-0.73.96}/modal/sandbox.pyi +0 -0
  139. {modal-0.73.94 → modal-0.73.96}/modal/schedule.py +0 -0
  140. {modal-0.73.94 → modal-0.73.96}/modal/scheduler_placement.py +0 -0
  141. {modal-0.73.94 → modal-0.73.96}/modal/secret.py +0 -0
  142. {modal-0.73.94 → modal-0.73.96}/modal/secret.pyi +0 -0
  143. {modal-0.73.94 → modal-0.73.96}/modal/serving.py +0 -0
  144. {modal-0.73.94 → modal-0.73.96}/modal/serving.pyi +0 -0
  145. {modal-0.73.94 → modal-0.73.96}/modal/snapshot.py +0 -0
  146. {modal-0.73.94 → modal-0.73.96}/modal/snapshot.pyi +0 -0
  147. {modal-0.73.94 → modal-0.73.96}/modal/stream_type.py +0 -0
  148. {modal-0.73.94 → modal-0.73.96}/modal/token_flow.py +0 -0
  149. {modal-0.73.94 → modal-0.73.96}/modal/token_flow.pyi +0 -0
  150. {modal-0.73.94 → modal-0.73.96}/modal/volume.py +0 -0
  151. {modal-0.73.94 → modal-0.73.96}/modal/volume.pyi +0 -0
  152. {modal-0.73.94 → modal-0.73.96}/modal.egg-info/SOURCES.txt +0 -0
  153. {modal-0.73.94 → modal-0.73.96}/modal.egg-info/dependency_links.txt +0 -0
  154. {modal-0.73.94 → modal-0.73.96}/modal.egg-info/entry_points.txt +0 -0
  155. {modal-0.73.94 → modal-0.73.96}/modal.egg-info/requires.txt +0 -0
  156. {modal-0.73.94 → modal-0.73.96}/modal.egg-info/top_level.txt +0 -0
  157. {modal-0.73.94 → modal-0.73.96}/modal_docs/__init__.py +0 -0
  158. {modal-0.73.94 → modal-0.73.96}/modal_docs/gen_cli_docs.py +0 -0
  159. {modal-0.73.94 → modal-0.73.96}/modal_docs/gen_reference_docs.py +0 -0
  160. {modal-0.73.94 → modal-0.73.96}/modal_docs/mdmd/__init__.py +0 -0
  161. {modal-0.73.94 → modal-0.73.96}/modal_docs/mdmd/mdmd.py +0 -0
  162. {modal-0.73.94 → modal-0.73.96}/modal_docs/mdmd/signatures.py +0 -0
  163. {modal-0.73.94 → modal-0.73.96}/modal_proto/__init__.py +0 -0
  164. {modal-0.73.94 → modal-0.73.96}/modal_proto/api.proto +0 -0
  165. {modal-0.73.94 → modal-0.73.96}/modal_proto/api_grpc.py +0 -0
  166. {modal-0.73.94 → modal-0.73.96}/modal_proto/api_pb2.py +0 -0
  167. {modal-0.73.94 → modal-0.73.96}/modal_proto/api_pb2.pyi +0 -0
  168. {modal-0.73.94 → modal-0.73.96}/modal_proto/api_pb2_grpc.py +0 -0
  169. {modal-0.73.94 → modal-0.73.96}/modal_proto/api_pb2_grpc.pyi +0 -0
  170. {modal-0.73.94 → modal-0.73.96}/modal_proto/modal_api_grpc.py +0 -0
  171. {modal-0.73.94 → modal-0.73.96}/modal_proto/modal_options_grpc.py +0 -0
  172. {modal-0.73.94 → modal-0.73.96}/modal_proto/options.proto +0 -0
  173. {modal-0.73.94 → modal-0.73.96}/modal_proto/options_grpc.py +0 -0
  174. {modal-0.73.94 → modal-0.73.96}/modal_proto/options_pb2.py +0 -0
  175. {modal-0.73.94 → modal-0.73.96}/modal_proto/options_pb2.pyi +0 -0
  176. {modal-0.73.94 → modal-0.73.96}/modal_proto/options_pb2_grpc.py +0 -0
  177. {modal-0.73.94 → modal-0.73.96}/modal_proto/options_pb2_grpc.pyi +0 -0
  178. {modal-0.73.94 → modal-0.73.96}/modal_proto/py.typed +0 -0
  179. {modal-0.73.94 → modal-0.73.96}/modal_version/__init__.py +0 -0
  180. {modal-0.73.94 → modal-0.73.96}/modal_version/__main__.py +0 -0
  181. {modal-0.73.94 → modal-0.73.96}/pyproject.toml +0 -0
  182. {modal-0.73.94 → modal-0.73.96}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: modal
3
- Version: 0.73.94
3
+ Version: 0.73.96
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -385,6 +385,10 @@ class AsyncOrSyncIterable:
385
385
  except NestedEventLoops:
386
386
  raise InvalidError(self.nested_async_message)
387
387
 
388
+ async def aclose(self):
389
+ if hasattr(self._async_iterable, "aclose"):
390
+ await self._async_iterable.aclose()
391
+
388
392
 
389
393
  _shutdown_tasks = []
390
394
 
@@ -8,6 +8,7 @@ import typing
8
8
  import urllib.parse
9
9
  import uuid
10
10
  from collections.abc import AsyncIterator
11
+ from dataclasses import dataclass
11
12
  from typing import (
12
13
  Any,
13
14
  Optional,
@@ -68,6 +69,11 @@ RETRYABLE_GRPC_STATUS_CODES = [
68
69
  Status.INTERNAL,
69
70
  ]
70
71
 
72
+ @dataclass
73
+ class RetryWarningMessage:
74
+ message: str
75
+ warning_interval: int
76
+ errors_to_warn_for: typing.List[Status]
71
77
 
72
78
  def create_channel(
73
79
  server_url: str,
@@ -144,6 +150,7 @@ async def retry_transient_errors(
144
150
  attempt_timeout: Optional[float] = None, # timeout for each attempt
145
151
  total_timeout: Optional[float] = None, # timeout for the entire function call
146
152
  attempt_timeout_floor=2.0, # always have at least this much timeout (only for total_timeout)
153
+ retry_warning_message: Optional[RetryWarningMessage] = None
147
154
  ) -> ResponseType:
148
155
  """Retry on transient gRPC failures with back-off until max_retries is reached.
149
156
  If max_retries is None, retry forever."""
@@ -212,6 +219,9 @@ async def retry_transient_errors(
212
219
 
213
220
  n_retries += 1
214
221
 
222
+ if retry_warning_message and n_retries % retry_warning_message.warning_interval == 0:
223
+ logger.warning(retry_warning_message.message)
224
+
215
225
  await asyncio.sleep(delay)
216
226
  delay = min(delay * delay_factor, max_delay)
217
227
 
@@ -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.73.94"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.96"
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.73.94"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.96"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
@@ -5,7 +5,7 @@ import typing
5
5
  from dataclasses import dataclass
6
6
  from typing import Any, Callable, Optional
7
7
 
8
- from grpclib import GRPCError, Status
8
+ from grpclib import Status
9
9
 
10
10
  from modal._runtime.execution_context import current_input_id
11
11
  from modal._utils.async_utils import (
@@ -27,13 +27,17 @@ from modal._utils.function_utils import (
27
27
  _create_input,
28
28
  _process_result,
29
29
  )
30
- from modal._utils.grpc_utils import retry_transient_errors
30
+ from modal._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES, RetryWarningMessage, retry_transient_errors
31
31
  from modal.config import logger
32
32
  from modal_proto import api_pb2
33
33
 
34
34
  if typing.TYPE_CHECKING:
35
35
  import modal.client
36
36
 
37
+ # pump_inputs should retry if it receives any of the standard retryable codes plus RESOURCE_EXHAUSTED.
38
+ PUMP_INPUTS_RETRYABLE_GRPC_STATUS_CODES = RETRYABLE_GRPC_STATUS_CODES + [Status.RESOURCE_EXHAUSTED]
39
+ PUMP_INPUTS_MAX_RETRIES = 8
40
+ PUMP_INPUTS_MAX_RETRY_DELAY=15
37
41
 
38
42
  class _SynchronizedQueue:
39
43
  """mdmd:hidden"""
@@ -136,25 +140,19 @@ async def _map_invocation(
136
140
  logger.debug(
137
141
  f"Pushing {len(items)} inputs to server. Num queued inputs awaiting push is {input_queue.qsize()}."
138
142
  )
139
- while True:
140
- try:
141
- resp = await retry_transient_errors(
142
- client.stub.FunctionPutInputs,
143
- request,
144
- # with 8 retries we log the warning below about every 30 secondswhich isn't too spammy.
145
- max_retries=8,
146
- max_delay=15,
147
- additional_status_codes=[Status.RESOURCE_EXHAUSTED],
148
- )
149
- break
150
- except GRPCError as err:
151
- if err.status != Status.RESOURCE_EXHAUSTED:
152
- raise err
153
- logger.warning(
154
- f"Warning: map progress for function {function._function_name} is limited."
155
- " Common bottlenecks include slow iteration over results, or function backlogs."
156
- )
157
-
143
+ # with 8 retries we log the warning below about every 30 seconds which isn't too spammy.
144
+ retry_warning_message = RetryWarningMessage(
145
+ message=f"Warning: map progress for function {function._function_name} is limited."
146
+ " Common bottlenecks include slow iteration over results, or function backlogs.",
147
+ warning_interval=8,
148
+ errors_to_warn_for=[Status.RESOURCE_EXHAUSTED])
149
+ resp = await retry_transient_errors(
150
+ client.stub.FunctionPutInputs,
151
+ request,
152
+ max_retries=None,
153
+ max_delay=PUMP_INPUTS_MAX_RETRY_DELAY,
154
+ additional_status_codes=[Status.RESOURCE_EXHAUSTED],
155
+ retry_warning_message=retry_warning_message)
158
156
  count_update()
159
157
  for item in resp.inputs:
160
158
  pending_outputs.setdefault(item.input_id, 0)
@@ -312,7 +310,7 @@ def _map_sync(
312
310
 
313
311
  @warn_if_generator_is_not_consumed(function_name="Function.map.aio")
314
312
  async def _map_async(
315
- self,
313
+ self: "modal.functions.Function",
316
314
  *input_iterators: typing.Union[
317
315
  typing.Iterable[Any], typing.AsyncIterable[Any]
318
316
  ], # one input iterator per argument in the mapped-over function/generator
@@ -339,19 +337,20 @@ async def _map_async(
339
337
  async for args in streamer:
340
338
  await raw_input_queue.put.aio((args, kwargs))
341
339
  await raw_input_queue.put.aio(None) # end-of-input sentinel
342
-
343
- feed_input_task = asyncio.create_task(feed_queue())
344
-
345
- try:
346
- # note that `map()` and `map.aio()` are not synchronicity-wrapped, since
347
- # they accept executable code in the form of
348
- # iterators that we don't want to run inside the synchronicity thread.
349
- # Instead, we delegate to `._map()` with a safer Queue as input
350
- async with aclosing(self._map.aio(raw_input_queue, order_outputs, return_exceptions)) as map_output_stream:
351
- async for output in map_output_stream:
352
- yield output
353
- finally:
354
- feed_input_task.cancel() # should only be needed in case of exceptions
340
+ if False:
341
+ # make this a never yielding generator so we can async_merge it below
342
+ # this is important so any exception raised in feed_queue will be propagated
343
+ yield
344
+
345
+ # note that `map()` and `map.aio()` are not synchronicity-wrapped, since
346
+ # they accept executable code in the form of
347
+ # iterators that we don't want to run inside the synchronicity thread.
348
+ # Instead, we delegate to `._map()` with a safer Queue as input
349
+ async with aclosing(
350
+ async_merge(self._map.aio(raw_input_queue, order_outputs, return_exceptions), feed_queue())
351
+ ) as map_output_stream:
352
+ async for output in map_output_stream:
353
+ yield output
355
354
 
356
355
 
357
356
  def _for_each_sync(self, *input_iterators, kwargs={}, ignore_exceptions: bool = False):
@@ -2,6 +2,7 @@ import collections.abc
2
2
  import modal._functions
3
3
  import modal._utils.async_utils
4
4
  import modal.client
5
+ import modal.functions
5
6
  import typing
6
7
  import typing_extensions
7
8
 
@@ -52,7 +53,7 @@ def _map_sync(
52
53
  self, *input_iterators, kwargs={}, order_outputs: bool = True, return_exceptions: bool = False
53
54
  ) -> modal._utils.async_utils.AsyncOrSyncIterable: ...
54
55
  def _map_async(
55
- self,
56
+ self: modal.functions.Function,
56
57
  *input_iterators: typing.Union[typing.Iterable[typing.Any], typing.AsyncIterable[typing.Any]],
57
58
  kwargs={},
58
59
  order_outputs: bool = True,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: modal
3
- Version: 0.73.94
3
+ Version: 0.73.96
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 94 # git: 3f8ec72
4
+ build_number = 96 # git: 9b83eca
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes