modal 1.1.2.dev7__tar.gz → 1.1.2.dev9__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.

Potentially problematic release.


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

Files changed (188) hide show
  1. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/PKG-INFO +1 -1
  2. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_functions.py +20 -2
  3. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/client.pyi +2 -2
  4. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/functions.pyi +39 -6
  5. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/parallel_map.py +131 -0
  6. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/parallel_map.pyi +42 -0
  7. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal.egg-info/PKG-INFO +1 -1
  8. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_version/__init__.py +1 -1
  9. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/LICENSE +0 -0
  10. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/README.md +0 -0
  11. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/__init__.py +0 -0
  12. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/__main__.py +0 -0
  13. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_clustered_functions.py +0 -0
  14. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_clustered_functions.pyi +0 -0
  15. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_container_entrypoint.py +0 -0
  16. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_ipython.py +0 -0
  17. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_location.py +0 -0
  18. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_object.py +0 -0
  19. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_output.py +0 -0
  20. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_partial_function.py +0 -0
  21. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_pty.py +0 -0
  22. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_resolver.py +0 -0
  23. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_resources.py +0 -0
  24. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/__init__.py +0 -0
  25. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/asgi.py +0 -0
  26. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/container_io_manager.py +0 -0
  27. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/container_io_manager.pyi +0 -0
  28. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/execution_context.py +0 -0
  29. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/execution_context.pyi +0 -0
  30. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  31. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/telemetry.py +0 -0
  32. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_runtime/user_code_imports.py +0 -0
  33. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_serialization.py +0 -0
  34. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_traceback.py +0 -0
  35. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_tunnel.py +0 -0
  36. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_tunnel.pyi +0 -0
  37. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_type_manager.py +0 -0
  38. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/__init__.py +0 -0
  39. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/app_utils.py +0 -0
  40. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/async_utils.py +0 -0
  41. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/auth_token_manager.py +0 -0
  42. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/blob_utils.py +0 -0
  43. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/bytes_io_segment_payload.py +0 -0
  44. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/deprecation.py +0 -0
  45. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/docker_utils.py +0 -0
  46. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/function_utils.py +0 -0
  47. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/git_utils.py +0 -0
  48. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/grpc_testing.py +0 -0
  49. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/grpc_utils.py +0 -0
  50. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/hash_utils.py +0 -0
  51. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/http_utils.py +0 -0
  52. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/jwt_utils.py +0 -0
  53. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/logger.py +0 -0
  54. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/mount_utils.py +0 -0
  55. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/name_utils.py +0 -0
  56. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/package_utils.py +0 -0
  57. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/pattern_utils.py +0 -0
  58. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/rand_pb_testing.py +0 -0
  59. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/shell_utils.py +0 -0
  60. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_utils/time_utils.py +0 -0
  61. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_vendor/__init__.py +0 -0
  62. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  63. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_vendor/cloudpickle.py +0 -0
  64. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_vendor/tblib.py +0 -0
  65. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/_watcher.py +0 -0
  66. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/app.py +0 -0
  67. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/app.pyi +0 -0
  68. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/2023.12.312.txt +0 -0
  69. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/2023.12.txt +0 -0
  70. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/2024.04.txt +0 -0
  71. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/2024.10.txt +0 -0
  72. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/2025.06.txt +0 -0
  73. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/PREVIEW.txt +0 -0
  74. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/README.md +0 -0
  75. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/builder/base-images.json +0 -0
  76. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/call_graph.py +0 -0
  77. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/__init__.py +0 -0
  78. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/_download.py +0 -0
  79. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/_traceback.py +0 -0
  80. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/app.py +0 -0
  81. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/cluster.py +0 -0
  82. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/config.py +0 -0
  83. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/container.py +0 -0
  84. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/dict.py +0 -0
  85. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/entry_point.py +0 -0
  86. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/environment.py +0 -0
  87. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/import_refs.py +0 -0
  88. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/launch.py +0 -0
  89. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/network_file_system.py +0 -0
  90. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/profile.py +0 -0
  91. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/programs/__init__.py +0 -0
  92. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/programs/run_jupyter.py +0 -0
  93. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/programs/vscode.py +0 -0
  94. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/queues.py +0 -0
  95. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/run.py +0 -0
  96. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/secret.py +0 -0
  97. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/token.py +0 -0
  98. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/utils.py +0 -0
  99. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cli/volume.py +0 -0
  100. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/client.py +0 -0
  101. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cloud_bucket_mount.py +0 -0
  102. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cloud_bucket_mount.pyi +0 -0
  103. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cls.py +0 -0
  104. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/cls.pyi +0 -0
  105. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/config.py +0 -0
  106. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/container_process.py +0 -0
  107. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/container_process.pyi +0 -0
  108. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/dict.py +0 -0
  109. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/dict.pyi +0 -0
  110. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/environments.py +0 -0
  111. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/environments.pyi +0 -0
  112. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/exception.py +0 -0
  113. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/experimental/__init__.py +0 -0
  114. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/experimental/flash.py +0 -0
  115. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/experimental/flash.pyi +0 -0
  116. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/experimental/ipython.py +0 -0
  117. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/file_io.py +0 -0
  118. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/file_io.pyi +0 -0
  119. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/file_pattern_matcher.py +0 -0
  120. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/functions.py +0 -0
  121. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/gpu.py +0 -0
  122. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/image.py +0 -0
  123. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/image.pyi +0 -0
  124. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/io_streams.py +0 -0
  125. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/io_streams.pyi +0 -0
  126. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/mount.py +0 -0
  127. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/mount.pyi +0 -0
  128. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/network_file_system.py +0 -0
  129. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/network_file_system.pyi +0 -0
  130. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/object.py +0 -0
  131. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/object.pyi +0 -0
  132. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/output.py +0 -0
  133. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/partial_function.py +0 -0
  134. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/partial_function.pyi +0 -0
  135. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/proxy.py +0 -0
  136. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/proxy.pyi +0 -0
  137. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/py.typed +0 -0
  138. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/queue.py +0 -0
  139. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/queue.pyi +0 -0
  140. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/retries.py +0 -0
  141. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/runner.py +0 -0
  142. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/runner.pyi +0 -0
  143. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/running_app.py +0 -0
  144. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/sandbox.py +0 -0
  145. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/sandbox.pyi +0 -0
  146. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/schedule.py +0 -0
  147. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/scheduler_placement.py +0 -0
  148. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/secret.py +0 -0
  149. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/secret.pyi +0 -0
  150. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/serving.py +0 -0
  151. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/serving.pyi +0 -0
  152. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/snapshot.py +0 -0
  153. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/snapshot.pyi +0 -0
  154. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/stream_type.py +0 -0
  155. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/token_flow.py +0 -0
  156. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/token_flow.pyi +0 -0
  157. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/volume.py +0 -0
  158. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal/volume.pyi +0 -0
  159. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal.egg-info/SOURCES.txt +0 -0
  160. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal.egg-info/dependency_links.txt +0 -0
  161. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal.egg-info/entry_points.txt +0 -0
  162. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal.egg-info/requires.txt +0 -0
  163. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal.egg-info/top_level.txt +0 -0
  164. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_docs/__init__.py +0 -0
  165. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_docs/gen_cli_docs.py +0 -0
  166. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_docs/gen_reference_docs.py +0 -0
  167. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_docs/mdmd/__init__.py +0 -0
  168. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_docs/mdmd/mdmd.py +0 -0
  169. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_docs/mdmd/signatures.py +0 -0
  170. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/__init__.py +0 -0
  171. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/api.proto +0 -0
  172. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/api_grpc.py +0 -0
  173. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/api_pb2.py +0 -0
  174. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/api_pb2.pyi +0 -0
  175. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/api_pb2_grpc.py +0 -0
  176. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/api_pb2_grpc.pyi +0 -0
  177. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/modal_api_grpc.py +0 -0
  178. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/modal_options_grpc.py +0 -0
  179. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/options.proto +0 -0
  180. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/options_grpc.py +0 -0
  181. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/options_pb2.py +0 -0
  182. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/options_pb2.pyi +0 -0
  183. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/options_pb2_grpc.py +0 -0
  184. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/options_pb2_grpc.pyi +0 -0
  185. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_proto/py.typed +0 -0
  186. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/modal_version/__main__.py +0 -0
  187. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/pyproject.toml +0 -0
  188. {modal-1.1.2.dev7 → modal-1.1.2.dev9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.2.dev7
3
+ Version: 1.1.2.dev9
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -71,6 +71,8 @@ from .mount import _get_client_mount, _Mount
71
71
  from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
72
72
  from .output import _get_output_manager
73
73
  from .parallel_map import (
74
+ _experimental_spawn_map_async,
75
+ _experimental_spawn_map_sync,
74
76
  _for_each_async,
75
77
  _for_each_sync,
76
78
  _map_async,
@@ -78,6 +80,7 @@ from .parallel_map import (
78
80
  _map_invocation_inputplane,
79
81
  _map_sync,
80
82
  _spawn_map_async,
83
+ _spawn_map_invocation,
81
84
  _spawn_map_sync,
82
85
  _starmap_async,
83
86
  _starmap_sync,
@@ -351,8 +354,7 @@ class _Invocation:
351
354
 
352
355
  class _InputPlaneInvocation:
353
356
  """Internal client representation of a single-input call to a Modal Function using the input
354
- plane server API. As of 4/22/2025, this class is experimental and not used in production.
355
- It is OK to make breaking changes to this class."""
357
+ plane server API."""
356
358
 
357
359
  stub: ModalClientModal
358
360
 
@@ -1543,6 +1545,21 @@ Use the `Function.get_web_url()` method instead.
1543
1545
  async for item in stream:
1544
1546
  yield item
1545
1547
 
1548
+ @live_method
1549
+ async def _spawn_map(self, input_queue: _SynchronizedQueue) -> "_FunctionCall[ReturnType]":
1550
+ self._check_no_web_url("spawn_map")
1551
+ if self._is_generator:
1552
+ raise InvalidError("A generator function cannot be called with `.spawn_map(...)`.")
1553
+
1554
+ assert self._function_name
1555
+ function_call_id = await _spawn_map_invocation(
1556
+ self,
1557
+ input_queue,
1558
+ self.client,
1559
+ )
1560
+ fc: _FunctionCall[ReturnType] = _FunctionCall._new_hydrated(function_call_id, self.client, None)
1561
+ return fc
1562
+
1546
1563
  async def _call_function(self, args, kwargs) -> ReturnType:
1547
1564
  invocation: Union[_Invocation, _InputPlaneInvocation]
1548
1565
  if self._input_plane_url:
@@ -1789,6 +1806,7 @@ Use the `Function.get_web_url()` method instead.
1789
1806
  starmap = MethodWithAio(_starmap_sync, _starmap_async, synchronizer)
1790
1807
  for_each = MethodWithAio(_for_each_sync, _for_each_async, synchronizer)
1791
1808
  spawn_map = MethodWithAio(_spawn_map_sync, _spawn_map_async, synchronizer)
1809
+ experimental_spawn_map = MethodWithAio(_experimental_spawn_map_sync, _experimental_spawn_map_async, synchronizer)
1792
1810
 
1793
1811
 
1794
1812
  class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.1.2.dev7",
36
+ version: str = "1.1.2.dev9",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -164,7 +164,7 @@ class Client:
164
164
  server_url: str,
165
165
  client_type: int,
166
166
  credentials: typing.Optional[tuple[str, str]],
167
- version: str = "1.1.2.dev7",
167
+ version: str = "1.1.2.dev9",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -405,6 +405,12 @@ class Function(
405
405
 
406
406
  _map: ___map_spec[typing_extensions.Self]
407
407
 
408
+ class ___spawn_map_spec(typing_extensions.Protocol[ReturnType_INNER, SUPERSELF]):
409
+ def __call__(self, /, input_queue: modal.parallel_map.SynchronizedQueue) -> FunctionCall[ReturnType_INNER]: ...
410
+ async def aio(self, /, input_queue: modal.parallel_map.SynchronizedQueue) -> FunctionCall[ReturnType_INNER]: ...
411
+
412
+ _spawn_map: ___spawn_map_spec[modal._functions.ReturnType, typing_extensions.Self]
413
+
408
414
  class ___call_function_spec(typing_extensions.Protocol[ReturnType_INNER, SUPERSELF]):
409
415
  def __call__(self, /, args, kwargs) -> ReturnType_INNER: ...
410
416
  async def aio(self, /, args, kwargs) -> ReturnType_INNER: ...
@@ -427,7 +433,7 @@ class Function(
427
433
 
428
434
  _call_generator: ___call_generator_spec[typing_extensions.Self]
429
435
 
430
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
436
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
431
437
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
432
438
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
433
439
  ...
@@ -436,7 +442,7 @@ class Function(
436
442
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
437
443
  ...
438
444
 
439
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
445
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
440
446
 
441
447
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
442
448
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -463,7 +469,7 @@ class Function(
463
469
  """
464
470
  ...
465
471
 
466
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
472
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
467
473
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
468
474
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
469
475
 
@@ -487,7 +493,7 @@ class Function(
487
493
  ...
488
494
 
489
495
  _experimental_spawn: ___experimental_spawn_spec[
490
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
496
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
491
497
  ]
492
498
 
493
499
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -496,7 +502,7 @@ class Function(
496
502
 
497
503
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
498
504
 
499
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
505
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
500
506
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
501
507
  """Calls the function with the given arguments, without waiting for the results.
502
508
 
@@ -517,7 +523,7 @@ class Function(
517
523
  """
518
524
  ...
519
525
 
520
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
526
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
521
527
 
522
528
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
523
529
  """Return the inner Python object wrapped by this Modal Function."""
@@ -693,6 +699,33 @@ class Function(
693
699
 
694
700
  spawn_map: __spawn_map_spec[typing_extensions.Self]
695
701
 
702
+ class __experimental_spawn_map_spec(typing_extensions.Protocol[SUPERSELF]):
703
+ def __call__(self, /, *input_iterators, kwargs={}) -> modal._functions._FunctionCall:
704
+ """Spawn parallel execution over a set of inputs, exiting as soon as the inputs are created (without waiting
705
+ for the map to complete).
706
+
707
+ Takes one iterator argument per argument in the function being mapped over.
708
+
709
+ Example:
710
+ ```python
711
+ @app.function()
712
+ def my_func(a):
713
+ return a ** 2
714
+
715
+
716
+ @app.local_entrypoint()
717
+ def main():
718
+ fc = my_func.spawn_map([1, 2, 3, 4])
719
+ ```
720
+
721
+ Returns a FunctionCall object that can be used to retrieve results
722
+ """
723
+ ...
724
+
725
+ async def aio(self, /, *input_iterators, kwargs={}) -> modal._functions._FunctionCall: ...
726
+
727
+ experimental_spawn_map: __experimental_spawn_map_spec[typing_extensions.Self]
728
+
696
729
  class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Object):
697
730
  """A reference to an executed function call.
698
731
 
@@ -260,6 +260,89 @@ class SyncInputPumper(InputPumper):
260
260
  yield
261
261
 
262
262
 
263
+ class AsyncInputPumper(InputPumper):
264
+ def __init__(
265
+ self,
266
+ client: "modal.client._Client",
267
+ *,
268
+ input_queue: asyncio.Queue,
269
+ function: "modal.functions._Function",
270
+ function_call_id: str,
271
+ ):
272
+ super().__init__(client, input_queue=input_queue, function=function, function_call_id=function_call_id)
273
+
274
+
275
+ async def _spawn_map_invocation(
276
+ function: "modal.functions._Function", raw_input_queue: _SynchronizedQueue, client: "modal.client._Client"
277
+ ) -> str:
278
+ assert client.stub
279
+ request = api_pb2.FunctionMapRequest(
280
+ function_id=function.object_id,
281
+ parent_input_id=current_input_id() or "",
282
+ function_call_type=api_pb2.FUNCTION_CALL_TYPE_MAP,
283
+ function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC,
284
+ )
285
+ response: api_pb2.FunctionMapResponse = await retry_transient_errors(client.stub.FunctionMap, request)
286
+ function_call_id = response.function_call_id
287
+
288
+ have_all_inputs = False
289
+ inputs_created = 0
290
+
291
+ def set_inputs_created(set_inputs_created):
292
+ nonlocal inputs_created
293
+ assert set_inputs_created is None or set_inputs_created > inputs_created
294
+ inputs_created = set_inputs_created
295
+
296
+ def set_have_all_inputs():
297
+ nonlocal have_all_inputs
298
+ have_all_inputs = True
299
+
300
+ input_queue: asyncio.Queue[api_pb2.FunctionPutInputsItem | None] = asyncio.Queue()
301
+ input_preprocessor = InputPreprocessor(
302
+ client=client,
303
+ raw_input_queue=raw_input_queue,
304
+ processed_input_queue=input_queue,
305
+ function=function,
306
+ created_callback=set_inputs_created,
307
+ done_callback=set_have_all_inputs,
308
+ )
309
+
310
+ input_pumper = AsyncInputPumper(
311
+ client=client,
312
+ input_queue=input_queue,
313
+ function=function,
314
+ function_call_id=function_call_id,
315
+ )
316
+
317
+ def log_stats():
318
+ logger.debug(
319
+ f"have_all_inputs={have_all_inputs} inputs_created={inputs_created} inputs_sent={input_pumper.inputs_sent} "
320
+ )
321
+
322
+ async def log_task():
323
+ while True:
324
+ log_stats()
325
+ try:
326
+ await asyncio.sleep(10)
327
+ except asyncio.CancelledError:
328
+ # Log final stats before exiting
329
+ log_stats()
330
+ break
331
+
332
+ async def consume_generator(gen):
333
+ async for _ in gen:
334
+ pass
335
+
336
+ log_debug_stats_task = asyncio.create_task(log_task())
337
+ await asyncio.gather(
338
+ consume_generator(input_preprocessor.drain_input_generator()),
339
+ consume_generator(input_pumper.pump_inputs()),
340
+ )
341
+ log_debug_stats_task.cancel()
342
+ await log_debug_stats_task
343
+ return function_call_id
344
+
345
+
263
346
  async def _map_invocation(
264
347
  function: "modal.functions._Function",
265
348
  raw_input_queue: _SynchronizedQueue,
@@ -1063,6 +1146,54 @@ def _map_sync(
1063
1146
  )
1064
1147
 
1065
1148
 
1149
+ async def _experimental_spawn_map_async(self, *input_iterators, kwargs={}) -> "modal.functions._FunctionCall":
1150
+ async_input_gen = async_zip(*[sync_or_async_iter(it) for it in input_iterators])
1151
+ return await _spawn_map_helper(self, async_input_gen, kwargs)
1152
+
1153
+
1154
+ async def _spawn_map_helper(
1155
+ self: "modal.functions.Function", async_input_gen, kwargs={}
1156
+ ) -> "modal.functions._FunctionCall":
1157
+ raw_input_queue: Any = SynchronizedQueue() # type: ignore
1158
+ await raw_input_queue.init.aio()
1159
+
1160
+ async def feed_queue():
1161
+ async with aclosing(async_input_gen) as streamer:
1162
+ async for args in streamer:
1163
+ await raw_input_queue.put.aio((args, kwargs))
1164
+ await raw_input_queue.put.aio(None) # end-of-input sentinel
1165
+
1166
+ fc, _ = await asyncio.gather(self._spawn_map.aio(raw_input_queue), feed_queue())
1167
+ return fc
1168
+
1169
+
1170
+ def _experimental_spawn_map_sync(self, *input_iterators, kwargs={}) -> "modal.functions._FunctionCall":
1171
+ """Spawn parallel execution over a set of inputs, exiting as soon as the inputs are created (without waiting
1172
+ for the map to complete).
1173
+
1174
+ Takes one iterator argument per argument in the function being mapped over.
1175
+
1176
+ Example:
1177
+ ```python
1178
+ @app.function()
1179
+ def my_func(a):
1180
+ return a ** 2
1181
+
1182
+
1183
+ @app.local_entrypoint()
1184
+ def main():
1185
+ fc = my_func.spawn_map([1, 2, 3, 4])
1186
+ ```
1187
+
1188
+ Returns a FunctionCall object that can be used to retrieve results
1189
+ """
1190
+
1191
+ return run_coroutine_in_temporary_event_loop(
1192
+ _experimental_spawn_map_async(self, *input_iterators, kwargs=kwargs),
1193
+ "You can't run Function.spawn_map() from an async function. Use Function.spawn_map.aio() instead.",
1194
+ )
1195
+
1196
+
1066
1197
  async def _spawn_map_async(self, *input_iterators, kwargs={}) -> None:
1067
1198
  """This runs in an event loop on the main thread. It consumes inputs from the input iterators and creates async
1068
1199
  function calls for each.
@@ -123,6 +123,22 @@ class SyncInputPumper(InputPumper):
123
123
 
124
124
  def retry_inputs(self): ...
125
125
 
126
+ class AsyncInputPumper(InputPumper):
127
+ """Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
128
+ def __init__(
129
+ self,
130
+ client: modal.client._Client,
131
+ *,
132
+ input_queue: asyncio.queues.Queue,
133
+ function: modal._functions._Function,
134
+ function_call_id: str,
135
+ ):
136
+ """Initialize self. See help(type(self)) for accurate signature."""
137
+ ...
138
+
139
+ async def _spawn_map_invocation(
140
+ function: modal._functions._Function, raw_input_queue: _SynchronizedQueue, client: modal.client._Client
141
+ ) -> str: ...
126
142
  def _map_invocation(
127
143
  function: modal._functions._Function,
128
144
  raw_input_queue: _SynchronizedQueue,
@@ -242,6 +258,32 @@ def _map_sync(
242
258
  """
243
259
  ...
244
260
 
261
+ async def _experimental_spawn_map_async(self, *input_iterators, kwargs={}) -> modal._functions._FunctionCall: ...
262
+ async def _spawn_map_helper(
263
+ self: modal.functions.Function, async_input_gen, kwargs={}
264
+ ) -> modal._functions._FunctionCall: ...
265
+ def _experimental_spawn_map_sync(self, *input_iterators, kwargs={}) -> modal._functions._FunctionCall:
266
+ """Spawn parallel execution over a set of inputs, exiting as soon as the inputs are created (without waiting
267
+ for the map to complete).
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):
275
+ return a ** 2
276
+
277
+
278
+ @app.local_entrypoint()
279
+ def main():
280
+ fc = my_func.spawn_map([1, 2, 3, 4])
281
+ ```
282
+
283
+ Returns a FunctionCall object that can be used to retrieve results
284
+ """
285
+ ...
286
+
245
287
  async def _spawn_map_async(self, *input_iterators, kwargs={}) -> None:
246
288
  """This runs in an event loop on the main thread. It consumes inputs from the input iterators and creates async
247
289
  function calls for each.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.2.dev7
3
+ Version: 1.1.2.dev9
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
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.1.2.dev7"
4
+ __version__ = "1.1.2.dev9"
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