modal 1.4.4.dev12__tar.gz → 1.4.4.dev14__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 (227) hide show
  1. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/PKG-INFO +1 -1
  2. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_container_entrypoint.py +1 -2
  3. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_functions.py +152 -60
  4. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_partial_function.py +66 -71
  5. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/container_io_manager.py +28 -26
  6. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/container_io_manager.pyi +7 -13
  7. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/task_lifecycle_manager.py +22 -4
  8. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/task_lifecycle_manager.pyi +14 -2
  9. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/user_code_imports.py +14 -38
  10. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_server.py +7 -7
  11. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_tunnel.py +76 -77
  12. modal-1.4.4.dev14/modal/_tunnel.pyi +424 -0
  13. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_vendor/cloudpickle.py +9 -9
  14. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/app.py +408 -220
  15. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/app.pyi +811 -296
  16. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/app.py +5 -5
  17. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/billing.py +8 -8
  18. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/changelog.py +7 -7
  19. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/container.py +1 -1
  20. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/run.py +6 -6
  21. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/shell.py +1 -1
  22. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/volume.py +9 -9
  23. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/client.py +32 -6
  24. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/client.pyi +107 -25
  25. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cloud_bucket_mount.py +81 -80
  26. modal-1.4.4.dev14/modal/cloud_bucket_mount.pyi +248 -0
  27. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cls.py +96 -45
  28. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cls.pyi +196 -89
  29. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/config.py +24 -7
  30. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/dict.py +145 -104
  31. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/dict.pyi +356 -240
  32. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/exception.py +23 -25
  33. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/experimental/ipython.py +7 -7
  34. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/file_io.py +9 -10
  35. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/file_io.pyi +18 -20
  36. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/file_pattern_matcher.py +16 -16
  37. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/functions.pyi +344 -155
  38. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/image.py +716 -422
  39. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/image.pyi +1213 -596
  40. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/io_streams.py +22 -23
  41. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/io_streams.pyi +54 -56
  42. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/mount.py +14 -15
  43. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/mount.pyi +26 -28
  44. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/network_file_system.py +56 -45
  45. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/network_file_system.pyi +126 -100
  46. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/output.py +7 -7
  47. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/parallel_map.py +88 -68
  48. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/parallel_map.pyi +80 -58
  49. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/partial_function.pyi +66 -71
  50. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/proxy.py +7 -0
  51. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/proxy.pyi +16 -0
  52. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/queue.py +225 -148
  53. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/queue.pyi +554 -331
  54. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/retries.py +37 -37
  55. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/runner.py +10 -11
  56. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/runner.pyi +30 -33
  57. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/sandbox.py +348 -145
  58. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/sandbox.pyi +683 -198
  59. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/sandbox_fs.py +133 -114
  60. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/sandbox_fs.pyi +392 -335
  61. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/schedule.py +41 -39
  62. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/secret.py +153 -106
  63. modal-1.4.4.dev14/modal/secret.pyi +789 -0
  64. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/server.pyi +14 -14
  65. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/serving.py +12 -0
  66. modal-1.4.4.dev14/modal/serving.pyi +99 -0
  67. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/snapshot.py +8 -1
  68. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/snapshot.pyi +18 -2
  69. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/volume.py +204 -153
  70. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/volume.pyi +499 -356
  71. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal.egg-info/PKG-INFO +1 -1
  72. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal.egg-info/SOURCES.txt +1 -0
  73. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_docs/gen_reference_docs.py +13 -22
  74. modal-1.4.4.dev14/modal_docs/mdmd/mdmd.py +550 -0
  75. modal-1.4.4.dev14/modal_docs/mdmd/signatures.py +289 -0
  76. modal-1.4.4.dev14/modal_docs/mdmd/types.py +33 -0
  77. modal-1.4.4.dev14/modal_proto/__init__.py +4 -0
  78. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2.py +17 -17
  79. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2.pyi +11 -2
  80. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_version/__init__.py +1 -1
  81. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/pyproject.toml +4 -0
  82. modal-1.4.4.dev12/modal/_tunnel.pyi +0 -427
  83. modal-1.4.4.dev12/modal/cloud_bucket_mount.pyi +0 -246
  84. modal-1.4.4.dev12/modal/secret.pyi +0 -662
  85. modal-1.4.4.dev12/modal/serving.pyi +0 -59
  86. modal-1.4.4.dev12/modal_docs/mdmd/mdmd.py +0 -289
  87. modal-1.4.4.dev12/modal_docs/mdmd/signatures.py +0 -76
  88. modal-1.4.4.dev12/modal_proto/__init__.py +0 -1
  89. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/LICENSE +0 -0
  90. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/README.md +0 -0
  91. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/__init__.py +0 -0
  92. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/__main__.py +0 -0
  93. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_billing.py +0 -0
  94. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_clustered_functions.py +0 -0
  95. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_clustered_functions.pyi +0 -0
  96. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_environments.py +0 -0
  97. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_function_variants.py +0 -0
  98. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_grpc_client.py +0 -0
  99. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_ipython.py +0 -0
  100. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_load_context.py +0 -0
  101. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_location.py +0 -0
  102. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_logs.py +0 -0
  103. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_object.py +0 -0
  104. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_output/__init__.py +0 -0
  105. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_output/manager.py +0 -0
  106. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_output/pty.py +0 -0
  107. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_output/rich.py +0 -0
  108. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_output/status.py +0 -0
  109. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_resolver.py +0 -0
  110. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_resources.py +0 -0
  111. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/__init__.py +0 -0
  112. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/asgi.py +0 -0
  113. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/execution_context.py +0 -0
  114. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/execution_context.pyi +0 -0
  115. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  116. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/telemetry.py +0 -0
  117. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_runtime/user_code_event_loop.py +0 -0
  118. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_serialization.py +0 -0
  119. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_traceback.py +0 -0
  120. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_type_manager.py +0 -0
  121. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/__init__.py +0 -0
  122. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/app_utils.py +0 -0
  123. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/async_utils.py +0 -0
  124. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/auth_token_manager.py +0 -0
  125. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/blob_utils.py +0 -0
  126. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/browser_utils.py +0 -0
  127. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/bytes_io_segment_payload.py +0 -0
  128. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/deprecation.py +0 -0
  129. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/docker_utils.py +0 -0
  130. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/function_utils.py +0 -0
  131. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/git_utils.py +0 -0
  132. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/grpc_testing.py +0 -0
  133. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/grpc_utils.py +0 -0
  134. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/hash_utils.py +0 -0
  135. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/http_utils.py +0 -0
  136. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/jwt_utils.py +0 -0
  137. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/logger.py +0 -0
  138. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/mount_utils.py +0 -0
  139. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/name_utils.py +0 -0
  140. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/package_utils.py +0 -0
  141. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/pattern_utils.py +0 -0
  142. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/rand_pb_testing.py +0 -0
  143. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/sandbox_fs_utils.py +0 -0
  144. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/shell_utils.py +0 -0
  145. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/task_command_router_client.py +0 -0
  146. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_utils/time_utils.py +0 -0
  147. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_vendor/__init__.py +0 -0
  148. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  149. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_vendor/tblib.py +0 -0
  150. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_vendor/version.py +0 -0
  151. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_watcher.py +0 -0
  152. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/_workspace.py +0 -0
  153. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/billing.py +0 -0
  154. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/2023.12.312.txt +0 -0
  155. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/2023.12.txt +0 -0
  156. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/2024.04.txt +0 -0
  157. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/2024.10.txt +0 -0
  158. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/2025.06.txt +0 -0
  159. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/PREVIEW.txt +0 -0
  160. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/README.md +0 -0
  161. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/builder/base-images.json +0 -0
  162. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/call_graph.py +0 -0
  163. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/__init__.py +0 -0
  164. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/_download.py +0 -0
  165. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/_help.py +0 -0
  166. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/_traceback.py +0 -0
  167. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/bootstrap.py +0 -0
  168. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/cluster.py +0 -0
  169. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/config.py +0 -0
  170. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/dashboard.py +0 -0
  171. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/dict.py +0 -0
  172. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/entry_point.py +0 -0
  173. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/environment.py +0 -0
  174. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/import_refs.py +0 -0
  175. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/launch.py +0 -0
  176. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/logo.py +0 -0
  177. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/network_file_system.py +0 -0
  178. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/profile.py +0 -0
  179. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/programs/__init__.py +0 -0
  180. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/programs/run_jupyter.py +0 -0
  181. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/programs/vscode.py +0 -0
  182. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/queues.py +0 -0
  183. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/secret.py +0 -0
  184. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/selector.py +0 -0
  185. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/token.py +0 -0
  186. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/cli/utils.py +0 -0
  187. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/container_process.py +0 -0
  188. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/container_process.pyi +0 -0
  189. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/environments.py +0 -0
  190. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/environments.pyi +0 -0
  191. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/experimental/__init__.py +0 -0
  192. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/experimental/flash.py +0 -0
  193. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/experimental/flash.pyi +0 -0
  194. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/functions.py +0 -0
  195. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/object.py +0 -0
  196. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/object.pyi +0 -0
  197. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/partial_function.py +0 -0
  198. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/py.typed +0 -0
  199. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/running_app.py +0 -0
  200. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/scheduler_placement.py +0 -0
  201. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/server.py +0 -0
  202. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/stream_type.py +0 -0
  203. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/token_flow.py +0 -0
  204. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/token_flow.pyi +0 -0
  205. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/workspace.py +0 -0
  206. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal/workspace.pyi +0 -0
  207. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal.egg-info/dependency_links.txt +0 -0
  208. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal.egg-info/entry_points.txt +0 -0
  209. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal.egg-info/requires.txt +0 -0
  210. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal.egg-info/top_level.txt +0 -0
  211. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_docs/__init__.py +0 -0
  212. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_docs/gen_cli_docs.py +0 -0
  213. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_docs/gen_cli_docs_main.py +0 -0
  214. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_docs/gen_reference_docs_main.py +0 -0
  215. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_docs/mdmd/__init__.py +0 -0
  216. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/api_grpc.py +0 -0
  217. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/api_pb2.py +0 -0
  218. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/api_pb2.pyi +0 -0
  219. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/api_pb2_grpc.py +0 -0
  220. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/api_pb2_grpc.pyi +0 -0
  221. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/modal_api_grpc.py +0 -0
  222. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/py.typed +0 -0
  223. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/task_command_router_grpc.py +0 -0
  224. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  225. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  226. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/modal_version/__main__.py +0 -0
  227. {modal-1.4.4.dev12 → modal-1.4.4.dev14}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.4.dev12
3
+ Version: 1.4.4.dev14
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -401,9 +401,8 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
401
401
  function_def._experimental_group_size,
402
402
  )
403
403
 
404
- with service.execution_context(
404
+ with service.function_execution_context(
405
405
  event_loop=event_loop,
406
- snapshot=container_io_manager.memory_snapshot,
407
406
  container_io_manager=container_io_manager,
408
407
  ) as finalized_functions:
409
408
  # This context managers handles pre/post snapshot lifecycle of the user code,
@@ -1165,21 +1165,26 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1165
1165
  Subsequent deployments of the App containing this Function will reset the autoscaler back to
1166
1166
  its static configuration.
1167
1167
 
1168
- Examples:
1168
+ Args:
1169
+ min_containers: Minimum number of containers to keep running, or `None` to leave unchanged.
1170
+ max_containers: Maximum concurrent containers, or `None` to leave unchanged.
1171
+ buffer_containers: Extra containers to keep warm beyond demand, or `None` to leave unchanged.
1172
+ scaledown_window: Seconds idle containers wait before scaling down, or `None` to leave unchanged.
1169
1173
 
1170
- ```python notest
1171
- f = modal.Function.from_name("my-app", "function")
1174
+ Examples:
1175
+ ```python notest
1176
+ f = modal.Function.from_name("my-app", "function")
1172
1177
 
1173
- # Always have at least 2 containers running, with an extra buffer when the Function is active
1174
- f.update_autoscaler(min_containers=2, buffer_containers=1)
1178
+ # Always have at least 2 containers running, with an extra buffer when the Function is active
1179
+ f.update_autoscaler(min_containers=2, buffer_containers=1)
1175
1180
 
1176
- # Limit this Function to avoid spinning up more than 5 containers
1177
- f.update_autoscaler(max_containers=5)
1181
+ # Limit this Function to avoid spinning up more than 5 containers
1182
+ f.update_autoscaler(max_containers=5)
1178
1183
 
1179
- # Extend the scaledown window to increase the amount of time that idle containers stay alive
1180
- f.update_autoscaler(scaledown_window=300)
1184
+ # Extend the scaledown window to increase the amount of time that idle containers stay alive
1185
+ f.update_autoscaler(scaledown_window=300)
1181
1186
 
1182
- ```
1187
+ ```
1183
1188
 
1184
1189
  """
1185
1190
  if self._is_method:
@@ -1255,9 +1260,19 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1255
1260
  object with metadata from Modal servers until the first
1256
1261
  time it is actually used.
1257
1262
 
1258
- ```python
1259
- f = modal.Function.from_name("other-app", "function")
1260
- ```
1263
+ Args:
1264
+ app_name: Name of the deployed App.
1265
+ name: Name of the Function within that App. For class methods, use `Cls.from_name` instead.
1266
+ environment_name: Environment to look up the App in; defaults to the active environment.
1267
+ client: Modal client to use; defaults to `Client.from_env()` when omitted.
1268
+
1269
+ Returns:
1270
+ A lazy `Function` handle.
1271
+
1272
+ Examples:
1273
+ ```python
1274
+ f = modal.Function.from_name("other-app", "function")
1275
+ ```
1261
1276
  """
1262
1277
  if "." in name:
1263
1278
  raise InvalidError(
@@ -1388,13 +1403,18 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1388
1403
  async def get_web_url(self) -> str | None:
1389
1404
  """URL for addressing a Web Function via HTTP.
1390
1405
 
1391
- Returns None when this is not a Web Function.
1406
+ Returns:
1407
+ The HTTPS URL for the web endpoint, or `None` if this Function is not a web endpoint.
1392
1408
  """
1393
1409
  return self._web_url
1394
1410
 
1395
1411
  @live_method
1396
1412
  async def _experimental_get_flash_urls(self) -> list[str] | None:
1397
- """URL of the flash service for the function."""
1413
+ """URL of the flash service for the function.
1414
+
1415
+ Returns:
1416
+ Flash service URLs when configured, or `None`.
1417
+ """
1398
1418
  return list(self._experimental_flash_urls) if self._experimental_flash_urls else None
1399
1419
 
1400
1420
  def _apply_dynamic_config(
@@ -1681,8 +1701,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1681
1701
  @synchronizer.no_io_translation
1682
1702
  @live_method
1683
1703
  async def remote(self, *args: P.args, **kwargs: P.kwargs) -> ReturnType:
1684
- """
1685
- Calls the function remotely, executing it with the given arguments and returning the execution's result.
1704
+ """Calls the function remotely, executing it with the given arguments and returning the execution's result.
1705
+
1706
+ Args:
1707
+ *args: Positional arguments forwarded to the deployed function.
1708
+ **kwargs: Keyword arguments forwarded to the deployed function.
1709
+
1710
+ Returns:
1711
+ The value returned by the remote function.
1686
1712
  """
1687
1713
  # TODO: Generics/TypeVars
1688
1714
  self._check_no_web_url("remote")
@@ -1696,8 +1722,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1696
1722
  @synchronizer.no_io_translation
1697
1723
  @live_method_gen
1698
1724
  async def remote_gen(self, *args, **kwargs) -> AsyncGenerator[Any, None]:
1699
- """
1700
- Calls the generator remotely, executing it with the given arguments and returning the execution's result.
1725
+ """Calls the generator remotely, executing it with the given arguments.
1726
+
1727
+ Args:
1728
+ *args: Positional arguments forwarded to the deployed generator function.
1729
+ **kwargs: Keyword arguments forwarded to the deployed generator function.
1730
+
1731
+ Yields:
1732
+ Values produced by the remote generator.
1701
1733
  """
1702
1734
  # TODO: Generics/TypeVars
1703
1735
  self._check_no_web_url("remote_gen")
@@ -1727,12 +1759,18 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1727
1759
 
1728
1760
  @synchronizer.nowrap
1729
1761
  def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType:
1730
- """
1731
- Calls the function locally, executing it with the given arguments and returning the execution's result.
1762
+ """Calls the function locally, executing it with the given arguments and returning the execution's result.
1732
1763
 
1733
1764
  The function will execute in the same environment as the caller, just like calling the underlying function
1734
1765
  directly in Python. In particular, only secrets available in the caller environment will be available
1735
1766
  through environment variables.
1767
+
1768
+ Args:
1769
+ *args: Positional arguments passed to the underlying Python callable.
1770
+ **kwargs: Keyword arguments passed to the underlying Python callable.
1771
+
1772
+ Returns:
1773
+ The return value of the local call (or a coroutine for async functions).
1736
1774
  """
1737
1775
  # TODO(erikbern): it would be nice to remove the nowrap thing, but right now that would cause
1738
1776
  # "user code" to run on the synchronicity thread, which seems bad
@@ -1785,9 +1823,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1785
1823
 
1786
1824
  This experimental version of the spawn method allows up to 1 million inputs to be spawned.
1787
1825
 
1788
- Returns a `modal.FunctionCall` object, that can later be polled or
1789
- waited for using `.get(timeout=...)`.
1790
1826
  Conceptually similar to `multiprocessing.pool.apply_async`, or a Future/Promise in other contexts.
1827
+
1828
+ Args:
1829
+ *args: Positional arguments forwarded to the remote function.
1830
+ **kwargs: Keyword arguments forwarded to the remote function.
1831
+
1832
+ Returns:
1833
+ A `modal.FunctionCall` handle; poll or await results with `.get(timeout=...)`.
1791
1834
  """
1792
1835
  self._check_no_web_url("_experimental_spawn")
1793
1836
  if self._is_generator:
@@ -1817,10 +1860,16 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1817
1860
  async def spawn(self, *args: P.args, **kwargs: P.kwargs) -> "_FunctionCall[ReturnType]":
1818
1861
  """Calls the function with the given arguments, without waiting for the results.
1819
1862
 
1820
- Returns a [`modal.FunctionCall`](https://modal.com/docs/reference/modal.FunctionCall) object
1821
- that can later be polled or waited for using
1822
- [`.get(timeout=...)`](https://modal.com/docs/reference/modal.FunctionCall#get).
1823
1863
  Conceptually similar to `multiprocessing.pool.apply_async`, or a Future/Promise in other contexts.
1864
+
1865
+ Args:
1866
+ *args: Positional arguments forwarded to the remote function.
1867
+ **kwargs: Keyword arguments forwarded to the remote function.
1868
+
1869
+ Returns:
1870
+ A [`modal.FunctionCall`](https://modal.com/docs/reference/modal.FunctionCall) object
1871
+ that can later be polled or waited for using
1872
+ [`.get(timeout=...)`](https://modal.com/docs/reference/modal.FunctionCall#get).
1824
1873
  """
1825
1874
  self._check_no_web_url("spawn")
1826
1875
  if self._is_generator:
@@ -1834,13 +1883,21 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1834
1883
  return fc
1835
1884
 
1836
1885
  def get_raw_f(self) -> Callable[..., Any]:
1837
- """Return the inner Python object wrapped by this Modal Function."""
1886
+ """Return the inner Python object wrapped by this Modal Function.
1887
+
1888
+ Returns:
1889
+ The original function object registered with Modal.
1890
+ """
1838
1891
  assert self._raw_f is not None
1839
1892
  return self._raw_f
1840
1893
 
1841
1894
  @live_method
1842
1895
  async def get_current_stats(self) -> FunctionStats:
1843
- """Return a `FunctionStats` object describing the current function's queue and runner counts."""
1896
+ """Return a `FunctionStats` object describing the current function's queue and runner counts.
1897
+
1898
+ Returns:
1899
+ Snapshot counts for backlog, runners, and running inputs.
1900
+ """
1844
1901
  resp = await self.client.stub.FunctionGetCurrentStats(
1845
1902
  api_pb2.FunctionGetCurrentStatsRequest(function_id=self.object_id),
1846
1903
  retry=Retry(total_timeout=10.0),
@@ -1888,7 +1945,11 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1888
1945
 
1889
1946
  @live_method
1890
1947
  async def num_inputs(self) -> int:
1891
- """Get the number of inputs in the function call."""
1948
+ """Get the number of inputs in the function call.
1949
+
1950
+ Returns:
1951
+ How many inputs this function call includes (e.g. `1` for `.spawn()`, more for `.spawn_map()`).
1952
+ """
1892
1953
  if self._num_inputs is None:
1893
1954
  request = api_pb2.FunctionCallFromIdRequest(function_call_id=self.object_id)
1894
1955
  resp = await self.client.stub.FunctionCallFromId(request)
@@ -1898,6 +1959,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1898
1959
  @live_method
1899
1960
  async def get(self, timeout: float | None = None, *, index: int = 0) -> ReturnType:
1900
1961
  """Get the result of the index-th input of the function call.
1962
+
1901
1963
  `.spawn()` calls have a single output, so only specifying `index=0` is valid.
1902
1964
  A non-zero index is useful when your function has multiple outputs, like via `.spawn_map()`.
1903
1965
 
@@ -1906,6 +1968,13 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1906
1968
  which can be set to `0` to poll for an output immediately.
1907
1969
 
1908
1970
  The returned coroutine is not cancellation-safe.
1971
+
1972
+ Args:
1973
+ timeout: Maximum seconds to wait for a result, or `None` to wait indefinitely.
1974
+ index: Which input's result to retrieve (typically `0` for `.spawn()`).
1975
+
1976
+ Returns:
1977
+ The deserialized return value from that input.
1909
1978
  """
1910
1979
  return await self._invocation().poll_function(timeout=timeout, index=index)
1911
1980
 
@@ -1915,21 +1984,28 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1915
1984
 
1916
1985
  Optionally, specify a range [start, end) to iterate over.
1917
1986
 
1918
- Example:
1919
- ```python
1920
- @app.function()
1921
- def my_func(a):
1922
- return a ** 2
1987
+ If `end` is not provided, it will iterate over all results.
1923
1988
 
1989
+ Args:
1990
+ start: First input index to include (inclusive).
1991
+ end: One past the last index to include, or `None` for all remaining inputs.
1924
1992
 
1925
- @app.local_entrypoint()
1926
- def main():
1927
- fc = my_func.spawn_map([1, 2, 3, 4])
1928
- assert list(fc.iter()) == [1, 4, 9, 16]
1929
- assert list(fc.iter(start=1, end=3)) == [4, 9]
1930
- ```
1993
+ Yields:
1994
+ Each result value in index order.
1931
1995
 
1932
- If `end` is not provided, it will iterate over all results.
1996
+ Examples:
1997
+ ```python
1998
+ @app.function()
1999
+ def my_func(a):
2000
+ return a ** 2
2001
+
2002
+
2003
+ @app.local_entrypoint()
2004
+ def main():
2005
+ fc = my_func.spawn_map([1, 2, 3, 4])
2006
+ assert list(fc.iter()) == [1, 4, 9, 16]
2007
+ assert list(fc.iter(start=1, end=3)) == [4, 9]
2008
+ ```
1933
2009
  """
1934
2010
  num_inputs = await self.num_inputs()
1935
2011
  if end is None:
@@ -1946,6 +2022,9 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1946
2022
 
1947
2023
  See [`modal.call_graph`](https://modal.com/docs/reference/modal.call_graph) reference page
1948
2024
  for documentation on the structure of the returned `InputInfo` items.
2025
+
2026
+ Returns:
2027
+ A list of `InputInfo` nodes describing the call graph.
1949
2028
  """
1950
2029
  assert self._client and self._client.stub
1951
2030
  request = api_pb2.FunctionGetCallGraphRequest(function_call_id=self.object_id)
@@ -1963,6 +2042,9 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1963
2042
 
1964
2043
  If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
1965
2044
  causing any non-cancelled inputs on those containers to be rescheduled in new containers.
2045
+
2046
+ Args:
2047
+ terminate_containers: If True, forcibly terminate workers running cancelled inputs.
1966
2048
  """
1967
2049
  request = api_pb2.FunctionCallCancelRequest(
1968
2050
  function_call_id=self.object_id, terminate_containers=terminate_containers
@@ -1977,20 +2059,26 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1977
2059
  ) -> "modal.functions.FunctionCall[Any]":
1978
2060
  """Instantiate a FunctionCall object from an existing ID.
1979
2061
 
1980
- Examples:
2062
+ Note that it's only necessary to re-instantiate the `FunctionCall` with this method
2063
+ if you no longer have access to the original object returned from `Function.spawn`.
1981
2064
 
1982
- ```python notest
1983
- # Spawn a FunctionCall and keep track of its object ID
1984
- fc = my_func.spawn()
1985
- fc_id = fc.object_id
2065
+ Args:
2066
+ function_call_id: Object ID of an existing function call (e.g. from `FunctionCall.object_id`).
2067
+ client: Modal client to use; defaults to `Client.from_env()` when omitted.
1986
2068
 
1987
- # Later, use the ID to re-instantiate the FunctionCall object
1988
- fc = FunctionCall.from_id(fc_id)
1989
- result = fc.get()
1990
- ```
2069
+ Returns:
2070
+ A `FunctionCall` handle for the given ID.
1991
2071
 
1992
- Note that it's only necessary to re-instantiate the `FunctionCall` with this method
1993
- if you no longer have access to the original object returned from `Function.spawn`.
2072
+ Examples:
2073
+ ```python notest
2074
+ # Spawn a FunctionCall and keep track of its object ID
2075
+ fc = my_func.spawn()
2076
+ fc_id = fc.object_id
2077
+
2078
+ # Later, use the ID to re-instantiate the FunctionCall object
2079
+ fc = FunctionCall.from_id(fc_id)
2080
+ result = fc.get()
2081
+ ```
1994
2082
 
1995
2083
  """
1996
2084
  _client = typing.cast(_Client, synchronizer._translate_in(client))
@@ -2013,17 +2101,21 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
2013
2101
 
2014
2102
  Accepts a variable number of `FunctionCall` objects, as returned by `Function.spawn()`.
2015
2103
 
2016
- Returns a list of results from each FunctionCall, or raises an exception
2017
- from the first failing function call.
2104
+ Raises an exception from the first failing function call.
2018
2105
 
2019
- Examples:
2106
+ Args:
2107
+ *function_calls: `FunctionCall` instances to wait on (same order as the returned sequence).
2020
2108
 
2021
- ```python notest
2022
- fc1 = slow_func_1.spawn()
2023
- fc2 = slow_func_2.spawn()
2109
+ Returns:
2110
+ Results in the same order as `function_calls` (like `asyncio.gather`).
2024
2111
 
2025
- result_1, result_2 = modal.FunctionCall.gather(fc1, fc2)
2026
- ```
2112
+ Examples:
2113
+ ```python notest
2114
+ fc1 = slow_func_1.spawn()
2115
+ fc2 = slow_func_2.spawn()
2116
+
2117
+ result_1, result_2 = modal.FunctionCall.gather(fc1, fc2)
2118
+ ```
2027
2119
 
2028
2120
  *Added in v0.73.69*: This method replaces the deprecated `modal.functions.gather` function.
2029
2121
  """
@@ -288,16 +288,15 @@ def _method(
288
288
  ) -> _MethodDecoratorType:
289
289
  """Decorator for methods that should be transformed into a Modal Function registered against this class's App.
290
290
 
291
- **Usage:**
292
-
293
- ```python
294
- @app.cls(cpu=8)
295
- class MyCls:
296
-
297
- @modal.method()
298
- def f(self):
299
- ...
300
- ```
291
+ Examples:
292
+ ```python
293
+ @app.cls(cpu=8)
294
+ class MyCls:
295
+
296
+ @modal.method()
297
+ def f(self):
298
+ ...
299
+ ```
301
300
  """
302
301
  if _warn_parentheses_missing is not None:
303
302
  raise InvalidError(
@@ -423,16 +422,15 @@ def _asgi_app(
423
422
  Asynchronous Server Gateway Interface (ASGI) is a standard for Python
424
423
  web apps, supported by all popular Python web libraries.
425
424
 
426
- **Usage:**
427
-
428
- ```python
429
- from typing import Callable
425
+ Examples:
426
+ ```python
427
+ from typing import Callable
430
428
 
431
- @app.function()
432
- @modal.asgi_app()
433
- def create_asgi() -> Callable:
434
- ...
435
- ```
429
+ @app.function()
430
+ @modal.asgi_app()
431
+ def create_asgi() -> Callable:
432
+ ...
433
+ ```
436
434
 
437
435
  To learn how to use Modal with popular web frameworks, see the
438
436
  [guide on Web Functions](https://modal.com/docs/guide/webhooks).
@@ -481,16 +479,15 @@ def _wsgi_app(
481
479
  which is compatible with ASGI and supports additional functionality such as web sockets.
482
480
  Modal supports ASGI via [`asgi_app`](https://modal.com/docs/reference/modal.asgi_app).
483
481
 
484
- **Usage:**
485
-
486
- ```python
487
- from typing import Callable
482
+ Examples:
483
+ ```python
484
+ from typing import Callable
488
485
 
489
- @app.function()
490
- @modal.wsgi_app()
491
- def create_wsgi() -> Callable:
492
- ...
493
- ```
486
+ @app.function()
487
+ @modal.wsgi_app()
488
+ def create_wsgi() -> Callable:
489
+ ...
490
+ ```
494
491
 
495
492
  To learn how to use this decorator with popular web frameworks, see the
496
493
  [guide on Web Functions](https://modal.com/docs/guide/webhooks).
@@ -539,16 +536,15 @@ def _web_server(
539
536
  HTTP server listening on a container port. This is useful for servers written in other languages
540
537
  like Rust, as well as integrating with non-ASGI frameworks like aiohttp and Tornado.
541
538
 
542
- **Usage:**
539
+ Examples:
540
+ ```python
541
+ import subprocess
543
542
 
544
- ```python
545
- import subprocess
546
-
547
- @app.function()
548
- @modal.web_server(8000)
549
- def my_file_server():
550
- subprocess.Popen("python -m http.server -d / 8000", shell=True)
551
- ```
543
+ @app.function()
544
+ @modal.web_server(8000)
545
+ def my_file_server():
546
+ subprocess.Popen("python -m http.server -d / 8000", shell=True)
547
+ ```
552
548
 
553
549
  The above example starts a simple file server, displaying the contents of the root directory.
554
550
  Here, requests to the URL will go to external port 8000 on the container. The
@@ -651,25 +647,24 @@ def _batched(
651
647
  ]:
652
648
  """Decorator for functions or class methods that should be batched.
653
649
 
654
- **Usage**
655
-
656
- ```python
657
- # Stack the decorator under `@app.function()` to enable dynamic batching
658
- @app.function()
659
- @modal.batched(max_batch_size=4, wait_ms=1000)
660
- async def batched_multiply(xs: list[int], ys: list[int]) -> list[int]:
661
- return [x * y for x, y in zip(xs, ys)]
662
-
663
- # call batched_multiply with individual inputs
664
- # batched_multiply.remote.aio(2, 100)
665
-
666
- # With `@app.cls()`, apply the decorator to a method (this may change in the future)
667
- @app.cls()
668
- class BatchedClass:
650
+ Examples:
651
+ ```python
652
+ # Stack the decorator under `@app.function()` to enable dynamic batching
653
+ @app.function()
669
654
  @modal.batched(max_batch_size=4, wait_ms=1000)
670
- def batched_multiply(self, xs: list[int], ys: list[int]) -> list[int]:
655
+ async def batched_multiply(xs: list[int], ys: list[int]) -> list[int]:
671
656
  return [x * y for x, y in zip(xs, ys)]
672
- ```
657
+
658
+ # call batched_multiply with individual inputs
659
+ # batched_multiply.remote.aio(2, 100)
660
+
661
+ # With `@app.cls()`, apply the decorator to a method (this may change in the future)
662
+ @app.cls()
663
+ class BatchedClass:
664
+ @modal.batched(max_batch_size=4, wait_ms=1000)
665
+ def batched_multiply(self, xs: list[int], ys: list[int]) -> list[int]:
666
+ return [x * y for x, y in zip(xs, ys)]
667
+ ```
673
668
 
674
669
  See the [dynamic batching guide](https://modal.com/docs/guide/dynamic-batching) for more information.
675
670
  """
@@ -728,25 +723,25 @@ def _concurrent(
728
723
  arrival rate of inputs increases. This can trade-off a small increase in average
729
724
  latency to avoid larger tail latencies from input queuing.
730
725
 
731
- **Examples:**
732
- ```python
733
- # Stack the decorator under `@app.function()` to enable input concurrency
734
- @app.function()
735
- @modal.concurrent(max_inputs=100)
736
- async def f(data):
737
- # Async function; will be scheduled as asyncio task
738
- ...
739
-
740
- # With `@app.cls()`, apply the decorator at the class level, not on individual methods
741
- @app.cls()
742
- @modal.concurrent(max_inputs=100, target_inputs=80)
743
- class C:
744
- @modal.method()
745
- def f(self, data):
746
- # Sync function; must be thread-safe
726
+ Examples:
727
+ ```python
728
+ # Stack the decorator under `@app.function()` to enable input concurrency
729
+ @app.function()
730
+ @modal.concurrent(max_inputs=100)
731
+ async def f(data):
732
+ # Async function; will be scheduled as asyncio task
747
733
  ...
748
734
 
749
- ```
735
+ # With `@app.cls()`, apply the decorator at the class level, not on individual methods
736
+ @app.cls()
737
+ @modal.concurrent(max_inputs=100, target_inputs=80)
738
+ class C:
739
+ @modal.method()
740
+ def f(self, data):
741
+ # Sync function; must be thread-safe
742
+ ...
743
+
744
+ ```
750
745
 
751
746
  *Added in v0.73.148:* This decorator replaces the `allow_concurrent_inputs` parameter
752
747
  in `@app.function()` and `@app.cls()`.
@@ -956,42 +956,25 @@ class _ContainerIOManager:
956
956
  outputs = await io_context.output_items(started_at, output_data)
957
957
  await self._send_outputs(started_at, outputs)
958
958
 
959
- async def memory_snapshot(self) -> None:
960
- """Message server indicating that function is ready to be checkpointed."""
961
- if self.checkpoint_id:
962
- logger.debug(f"Checkpoint ID: {self.checkpoint_id} (Memory Snapshot ID)")
963
- else:
964
- raise ValueError("No checkpoint ID provided for memory snapshot")
965
-
959
+ @asynccontextmanager
960
+ async def snapshot_context_manager(self) -> AsyncGenerator[None, None]:
966
961
  # Pause heartbeats since they keep the client connection open which causes the snapshotter to crash
967
962
  async with self.heartbeat_condition:
968
- # Snapshot GPU memory.
969
- if self.function_def._experimental_enable_gpu_snapshot and self.function_def.resources.gpu_config.gpu_type:
970
- logger.debug("GPU memory snapshot enabled. Attempting to snapshot GPU memory.")
971
-
972
- self._task_lifecycle_manager.create_cuda_checkpoint()
973
-
974
963
  # Notify the heartbeat loop that the snapshot phase has begun in order to
975
964
  # prevent it from sending heartbeat RPCs
976
965
  self._waiting_for_memory_snapshot = True
977
966
  self.heartbeat_condition.notify_all()
978
-
979
- await self._client.stub.ContainerCheckpoint(
980
- api_pb2.ContainerCheckpointRequest(checkpoint_id=self.checkpoint_id)
981
- )
982
-
983
- await self._client._close(prep_for_restore=True)
984
-
985
- logger.debug("Memory snapshot request sent. Connection closed.")
986
- await self._task_lifecycle_manager.memory_restore()
967
+ try:
968
+ yield
969
+ finally:
970
+ # Turn heartbeats back on. This is safe since the snapshot RPC
971
+ # and the restore phase has finished.
972
+ self._waiting_for_memory_snapshot = False
973
+ self.heartbeat_condition.notify_all()
987
974
  # Reset current input state
988
975
  self.current_input_id = None
989
976
  self.current_inputs = {}
990
977
  self.current_input_started_at = None
991
- # Turn heartbeats back on. This is safe since the snapshot RPC
992
- # and the restore phase has finished.
993
- self._waiting_for_memory_snapshot = False
994
- self.heartbeat_condition.notify_all()
995
978
 
996
979
  async def interact(self, from_breakpoint: bool = False):
997
980
  if self._is_interactivity_enabled:
@@ -1009,6 +992,25 @@ class _ContainerIOManager:
1009
992
  logger.error("Failed to start PTY shell.")
1010
993
  raise e
1011
994
 
995
+ def _install_breakpoint_hook(self) -> None:
996
+ # sys.breakpointhook is synchronous, so call the blocking wrapper for async interact().
997
+ container_io_manager: Any = synchronizer._translate_out(self)
998
+
999
+ # Install hooks for interactive functions.
1000
+ def breakpoint_wrapper(*args: Any, **kwargs: Any) -> None:
1001
+ # note: it would be nice to not have breakpoint_wrapper() included in the backtrace
1002
+ container_io_manager.interact(from_breakpoint=True)
1003
+ import pdb # noqa: T100
1004
+
1005
+ current_frame = inspect.currentframe()
1006
+ if current_frame is not None:
1007
+ frame = current_frame.f_back
1008
+ pdb.Pdb().set_trace(frame)
1009
+ else:
1010
+ raise RuntimeError("No current frame found")
1011
+
1012
+ sys.breakpointhook = breakpoint_wrapper
1013
+
1012
1014
  @property
1013
1015
  def target_concurrency(self) -> int:
1014
1016
  return self._target_concurrency